Merge "Merge "Refactor runtime hidden API flag from negative to positive" am: 46b4862d20 am: d0f327cc60 am: 2c426bc3f4 -s ours"
diff --git a/Android.bp b/Android.bp
index 03abf75..e65ba0f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -249,8 +249,7 @@
"core/java/android/os/storage/IStorageEventListener.aidl",
"core/java/android/os/storage/IStorageShutdownObserver.aidl",
"core/java/android/os/storage/IObbActionListener.aidl",
- "core/java/android/security/IConfirmationPromptCallback.aidl",
- "core/java/android/security/IKeystoreService.aidl",
+ ":keystore_aidl",
"core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
"core/java/android/service/autofill/IAutoFillService.aidl",
"core/java/android/service/autofill/IAutofillFieldClassificationService.aidl",
@@ -643,6 +642,7 @@
"system/netd/server/binder",
"system/vold/binder",
"system/bt/binder",
+ "system/security/keystore/binder",
],
},
diff --git a/core/java/Android.bp b/core/java/Android.bp
index f7c5c57..fb27f74 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,33 +7,3 @@
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
-
-// only used by key_store_service
-cc_library_shared {
- name: "libkeystore_aidl",
- srcs: ["android/security/IKeystoreService.aidl",
- "android/security/IConfirmationPromptCallback.aidl"],
- aidl: {
- export_aidl_headers: true,
- include_dirs: [
- "frameworks/base/core/java/",
- "system/security/keystore/",
- ],
- },
- shared_libs: [
- "libbinder",
- "libcutils",
- "libhardware",
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libkeystore_parcelables",
- "libselinux",
- "libutils",
- ],
- export_shared_lib_headers: [
- "libbinder",
- "libkeystore_parcelables",
- ],
-}
diff --git a/core/java/android/security/IConfirmationPromptCallback.aidl b/core/java/android/security/IConfirmationPromptCallback.aidl
deleted file mode 100644
index 96a1a04..0000000
--- a/core/java/android/security/IConfirmationPromptCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IConfirmationPromptCallback {
- oneway void onConfirmationPromptCompleted(in int result, in byte[] dataThatWasConfirmed);
-}
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
deleted file mode 100644
index 738eb68..0000000
--- a/core/java/android/security/IKeystoreService.aidl
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security;
-
-import android.security.keymaster.ExportResult;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keymaster.KeymasterBlob;
-import android.security.keymaster.OperationResult;
-import android.security.KeystoreArguments;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IKeystoreService {
- int getState(int userId);
- byte[] get(String name, int uid);
- int insert(String name, in byte[] item, int uid, int flags);
- int del(String name, int uid);
- int exist(String name, int uid);
- String[] list(String namePrefix, int uid);
- int reset();
- int onUserPasswordChanged(int userId, String newPassword);
- int lock(int userId);
- int unlock(int userId, String userPassword);
- int isEmpty(int userId);
- int generate(String name, int uid, int keyType, int keySize, int flags,
- in KeystoreArguments args);
- int import_key(String name, in byte[] data, int uid, int flags);
- byte[] sign(String name, in byte[] data);
- int verify(String name, in byte[] data, in byte[] signature);
- byte[] get_pubkey(String name);
- String grant(String name, int granteeUid);
- int ungrant(String name, int granteeUid);
- long getmtime(String name, int uid);
- int is_hardware_backed(String string);
- int clear_uid(long uid);
-
- // Keymaster 0.4 methods
- int addRngEntropy(in byte[] data, int flags);
- int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
- int flags, out KeyCharacteristics characteristics);
- int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
- int uid, out KeyCharacteristics characteristics);
- int importKey(String alias, in KeymasterArguments arguments, int format,
- in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics);
- ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
- in KeymasterBlob appId, int uid);
- OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
- in KeymasterArguments params, in byte[] entropy, int uid);
- OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
- OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature,
- in byte[] entropy);
- int abort(IBinder handle);
- boolean isOperationAuthorized(IBinder token);
- int addAuthToken(in byte[] authToken);
- int onUserAdded(int userId, int parentId);
- int onUserRemoved(int userId);
- int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain);
- int attestDeviceIds(in KeymasterArguments params, out KeymasterCertificateChain chain);
- int onDeviceOffBody();
- int importWrappedKey(in String wrappedKeyAlias, in byte[] wrappedKey,
- in String wrappingKeyAlias, in byte[] maskingKey, in KeymasterArguments arguments,
- in long rootSid, in long fingerprintSid,
- out KeyCharacteristics characteristics);
- int presentConfirmationPrompt(IBinder listener, String promptText, in byte[] extraData,
- in String locale, in int uiOptionsAsFlags);
- int cancelConfirmationPrompt(IBinder listener);
-}
diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl
deleted file mode 100644
index dc8ed50..0000000
--- a/core/java/android/security/KeystoreArguments.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security;
-
-/* @hide */
-parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h";
diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl
deleted file mode 100644
index 1748653..0000000
--- a/core/java/android/security/keymaster/ExportResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl
deleted file mode 100644
index 32e75ad..0000000
--- a/core/java/android/security/keymaster/KeyCharacteristics.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl
deleted file mode 100644
index 44d9f09..0000000
--- a/core/java/android/security/keymaster/KeymasterArguments.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
deleted file mode 100644
index 5c5db9e..0000000
--- a/core/java/android/security/keymaster/KeymasterBlob.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
deleted file mode 100644
index ddb5cae..0000000
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl
deleted file mode 100644
index db689d4..0000000
--- a/core/java/android/security/keymaster/OperationResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 72f95fb..0b03281 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -41,6 +41,7 @@
"com_android_server_tv_TvUinputBridge.cpp",
"com_android_server_tv_TvInputHal.cpp",
"com_android_server_vr_VrManagerService.cpp",
+ "com_android_server_UsbAlsaJackDetector.cpp",
"com_android_server_UsbDeviceManager.cpp",
"com_android_server_UsbDescriptorParser.cpp",
"com_android_server_UsbMidiDevice.cpp",
@@ -97,6 +98,7 @@
"libgui",
"libusbhost",
"libsuspend",
+ "libtinyalsa",
"libEGL",
"libGLESv2",
"libnetutils",
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
new file mode 100644
index 0000000..e9d4482
--- /dev/null
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbAlsaJackDetectorJNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <asm/byteorder.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define DRIVER_NAME "/dev/usb_accessory"
+
+#define USB_IN_JACK_NAME "USB in Jack"
+#define USB_OUT_JACK_NAME "USB out Jack"
+
+namespace android
+{
+
+static jboolean is_jack_connected(jint card, const char* control) {
+ struct mixer* card_mixer = mixer_open(card);
+ if (card_mixer == NULL) {
+ return true;
+ }
+ struct mixer_ctl* ctl = mixer_get_ctl_by_name(card_mixer, control);
+ if (!ctl) {
+ return true;
+ }
+ mixer_ctl_update(ctl);
+ int val = mixer_ctl_get_value(ctl, 0);
+ ALOGI("JACK %s - value %d\n", control, val);
+ mixer_close(card_mixer);
+
+ return val != 0;
+}
+
+static jboolean android_server_UsbAlsaJackDetector_hasJackDetect(JNIEnv* /* env */,
+ jobject /* thiz */,
+ jint card)
+{
+ struct mixer* card_mixer = mixer_open(card);
+ if (card_mixer == NULL) {
+ return false;
+ }
+
+ jboolean has_jack = false;
+ if ((mixer_get_ctl_by_name(card_mixer, USB_IN_JACK_NAME) != NULL) ||
+ (mixer_get_ctl_by_name(card_mixer, USB_OUT_JACK_NAME) != NULL)) {
+ has_jack = true;
+ }
+ mixer_close(card_mixer);
+ return has_jack;
+}
+
+
+static jboolean android_server_UsbAlsaJackDetector_inputJackConnected(JNIEnv* /* env */,
+ jobject /* thiz */,
+ jint card)
+{
+ return is_jack_connected(card, USB_IN_JACK_NAME);
+}
+
+
+static jboolean android_server_UsbAlsaJackDetector_outputJackConnected(JNIEnv* /* env */,
+ jobject /* thiz */,
+ jint card)
+{
+ return is_jack_connected(card, USB_OUT_JACK_NAME);
+}
+
+static void android_server_UsbAlsaJackDetector_jackDetect(JNIEnv* env,
+ jobject thiz,
+ jint card) {
+ jclass jdclass = env->GetObjectClass(thiz);
+ jmethodID method_jackDetectCallback = env->GetMethodID(jdclass, "jackDetectCallback", "()Z");
+ if (method_jackDetectCallback == NULL) {
+ ALOGE("Can't find jackDetectCallback");
+ return;
+ }
+
+ struct mixer* m = mixer_open(card);
+ if (!m) {
+ ALOGE("Jack detect unable to open mixer\n");
+ return;
+ }
+ mixer_subscribe_events(m, 1);
+ do {
+
+ // Wait for a mixer event. Retry if interrupted, exit on error.
+ int retval;
+ do {
+ retval = mixer_wait_event(m, -1);
+ } while (retval == -EINTR);
+ if (retval < 0) {
+ break;
+ }
+ mixer_consume_event(m);
+ } while (env->CallBooleanMethod(thiz, method_jackDetectCallback));
+
+ mixer_close(m);
+ return;
+}
+
+static const JNINativeMethod method_table[] = {
+ { "nativeHasJackDetect", "(I)Z", (void*)android_server_UsbAlsaJackDetector_hasJackDetect },
+ { "nativeInputJackConnected", "(I)Z",
+ (void*)android_server_UsbAlsaJackDetector_inputJackConnected },
+ { "nativeOutputJackConnected", "(I)Z",
+ (void*)android_server_UsbAlsaJackDetector_outputJackConnected },
+ { "nativeJackDetect", "(I)Z", (void*)android_server_UsbAlsaJackDetector_jackDetect },
+};
+
+int register_android_server_UsbAlsaJackDetector(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("com/android/server/usb/UsbAlsaJackDetector");
+ if (clazz == NULL) {
+ ALOGE("Can't find com/android/server/usb/UsbAlsaJackDetector");
+ return -1;
+ }
+
+ if (!jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
+ method_table, NELEM(method_table))) {
+ ALOGE("Can't register UsbAlsaJackDetector native methods");
+ return -1;
+ }
+
+ return 0;
+}
+
+}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index bf2a637..0ebef37 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -34,6 +34,7 @@
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
+int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbMidiDevice(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
@@ -82,6 +83,7 @@
register_android_server_AlarmManagerService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbMidiDevice(env);
+ register_android_server_UsbAlsaJackDetector(env);
register_android_server_UsbHostManager(env);
register_android_server_vr_VrManagerService(env);
register_android_server_VibratorService(env);
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 7480e56..9d4db00 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -17,9 +17,14 @@
package com.android.server.usb;
import android.annotation.NonNull;
+import android.media.AudioSystem;
+import android.media.IAudioService;
+import android.os.RemoteException;
import android.service.usb.UsbAlsaDeviceProto;
+import android.util.Slog;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.server.audio.AudioService;
/**
* Represents the ALSA specification, and attributes of an ALSA device.
@@ -30,25 +35,31 @@
private final int mCardNum;
private final int mDeviceNum;
- private final boolean mHasPlayback;
- private final boolean mHasCapture;
+ private final String mDeviceAddress;
+ private final boolean mHasOutput;
+ private final boolean mHasInput;
private final boolean mIsInputHeadset;
private final boolean mIsOutputHeadset;
- private final String mDeviceAddress;
+ private boolean mSelected = false;
+ private int mOutputState;
+ private int mInputState;
+ private UsbAlsaJackDetector mJackDetector;
+ private IAudioService mAudioService;
private String mDeviceName = "";
private String mDeviceDescription = "";
- public UsbAlsaDevice(int card, int device, String deviceAddress,
- boolean hasPlayback, boolean hasCapture,
+ public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
+ boolean hasOutput, boolean hasInput,
boolean isInputHeadset, boolean isOutputHeadset) {
+ mAudioService = audioService;
mCardNum = card;
mDeviceNum = device;
mDeviceAddress = deviceAddress;
- mHasPlayback = hasPlayback;
- mHasCapture = hasCapture;
+ mHasOutput = hasOutput;
+ mHasInput = hasInput;
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
}
@@ -75,71 +86,187 @@
}
/**
- * @returns true if the device supports playback.
+ * @returns the ALSA card/device address string.
*/
- public boolean hasPlayback() {
- return mHasPlayback;
+ public String getAlsaCardDeviceString() {
+ if (mCardNum < 0 || mDeviceNum < 0) {
+ Slog.e(TAG, "Invalid alsa card or device alsaCard: " + mCardNum
+ + " alsaDevice: " + mDeviceNum);
+ return null;
+ }
+ return AudioService.makeAlsaAddressString(mCardNum, mDeviceNum);
}
/**
- * @returns true if the device supports capture (recording).
+ * @returns true if the device supports output.
*/
- public boolean hasCapture() {
- return mHasCapture;
+ public boolean hasOutput() {
+ return mHasOutput;
}
/**
- * @returns true if the device is a headset for purposes of capture.
+ * @returns true if the device supports input (recording).
+ */
+ public boolean hasInput() {
+ return mHasInput;
+ }
+
+ /**
+ * @returns true if the device is a headset for purposes of input.
*/
public boolean isInputHeadset() {
return mIsInputHeadset;
}
/**
- * @returns true if the device is a headset for purposes of playback.
+ * @returns true if the device is a headset for purposes of output.
*/
public boolean isOutputHeadset() {
return mIsOutputHeadset;
}
/**
+ * @returns true if input jack is detected or jack detection is not supported.
+ */
+ private synchronized boolean isInputJackConnected() {
+ if (mJackDetector == null) {
+ return true; // If jack detect isn't supported, say it's connected.
+ }
+ return mJackDetector.isInputJackConnected();
+ }
+
+ /**
+ * @returns true if input jack is detected or jack detection is not supported.
+ */
+ private synchronized boolean isOutputJackConnected() {
+ if (mJackDetector == null) {
+ return true; // if jack detect isn't supported, say it's connected.
+ }
+ return mJackDetector.isOutputJackConnected();
+ }
+
+ /** Begins a jack-detection thread. */
+ private synchronized void startJackDetect() {
+ // If no jack detect capabilities exist, mJackDetector will be null.
+ mJackDetector = UsbAlsaJackDetector.startJackDetect(this);
+ }
+
+ /** Stops a jack-detection thread. */
+ private synchronized void stopJackDetect() {
+ if (mJackDetector != null) {
+ mJackDetector.pleaseStop();
+ }
+ mJackDetector = null;
+ }
+
+ /** Start using this device as the selected USB Audio Device. */
+ public synchronized void start() {
+ mSelected = true;
+ mInputState = 0;
+ mOutputState = 0;
+ startJackDetect();
+ updateWiredDeviceConnectionState(true);
+ }
+
+ /** Stop using this device as the selected USB Audio Device. */
+ public synchronized void stop() {
+ stopJackDetect();
+ updateWiredDeviceConnectionState(false);
+ mSelected = false;
+ }
+
+ /** Updates AudioService with the connection state of the alsaDevice.
+ * Checks ALSA Jack state for inputs and outputs before reporting.
+ */
+ public synchronized void updateWiredDeviceConnectionState(boolean enable) {
+ if (!mSelected) {
+ Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!");
+ return;
+ }
+ String alsaCardDeviceString = getAlsaCardDeviceString();
+ if (alsaCardDeviceString == null) {
+ return;
+ }
+ try {
+ // Output Device
+ if (mHasOutput) {
+ int device = mIsOutputHeadset
+ ? AudioSystem.DEVICE_OUT_USB_HEADSET
+ : AudioSystem.DEVICE_OUT_USB_DEVICE;
+ if (DEBUG) {
+ Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
+ + " addr:" + alsaCardDeviceString
+ + " name:" + mDeviceName);
+ }
+ boolean connected = isOutputJackConnected();
+ Slog.i(TAG, "OUTPUT JACK connected: " + connected);
+ int outputState = (enable && connected) ? 1 : 0;
+ if (outputState != mOutputState) {
+ mOutputState = outputState;
+ mAudioService.setWiredDeviceConnectionState(device, outputState,
+ alsaCardDeviceString,
+ mDeviceName, TAG);
+ }
+ }
+
+ // Input Device
+ if (mHasInput) {
+ int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET
+ : AudioSystem.DEVICE_IN_USB_DEVICE;
+ boolean connected = isInputJackConnected();
+ Slog.i(TAG, "INPUT JACK connected: " + connected);
+ int inputState = (enable && connected) ? 1 : 0;
+ if (inputState != mInputState) {
+ mInputState = inputState;
+ mAudioService.setWiredDeviceConnectionState(
+ device, inputState, alsaCardDeviceString,
+ mDeviceName, TAG);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
+ }
+ }
+
+
+ /**
* @Override
* @returns a string representation of the object.
*/
- public String toString() {
+ public synchronized String toString() {
return "UsbAlsaDevice: [card: " + mCardNum
+ ", device: " + mDeviceNum
+ ", name: " + mDeviceName
- + ", hasPlayback: " + mHasPlayback
- + ", hasCapture: " + mHasCapture + "]";
+ + ", hasOutput: " + mHasOutput
+ + ", hasInput: " + mHasInput + "]";
}
/**
* Write a description of the device to a dump stream.
*/
- public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ public synchronized void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
long token = dump.start(idName, id);
dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum);
dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum);
dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName);
- dump.write("has_playback", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasPlayback);
- dump.write("has_capture", UsbAlsaDeviceProto.HAS_CAPTURE, mHasCapture);
+ dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput);
+ dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput);
dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress);
dump.end(token);
}
// called by logDevices
- String toShortString() {
+ synchronized String toShortString() {
return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]";
}
- String getDeviceName() {
+ synchronized String getDeviceName() {
return mDeviceName;
}
- void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
+ synchronized void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
mDeviceName = deviceName;
mDeviceDescription = deviceDescription;
}
@@ -155,8 +282,8 @@
UsbAlsaDevice other = (UsbAlsaDevice) obj;
return (mCardNum == other.mCardNum
&& mDeviceNum == other.mDeviceNum
- && mHasPlayback == other.mHasPlayback
- && mHasCapture == other.mHasCapture
+ && mHasOutput == other.mHasOutput
+ && mHasInput == other.mHasInput
&& mIsInputHeadset == other.mIsInputHeadset
&& mIsOutputHeadset == other.mIsOutputHeadset);
}
@@ -170,8 +297,8 @@
int result = 1;
result = prime * result + mCardNum;
result = prime * result + mDeviceNum;
- result = prime * result + (mHasPlayback ? 0 : 1);
- result = prime * result + (mHasCapture ? 0 : 1);
+ result = prime * result + (mHasOutput ? 0 : 1);
+ result = prime * result + (mHasInput ? 0 : 1);
result = prime * result + (mIsInputHeadset ? 0 : 1);
result = prime * result + (mIsOutputHeadset ? 0 : 1);
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
new file mode 100644
index 0000000..c498847
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+/**
+ * Detects and reports ALSA jack state and events.
+ */
+public final class UsbAlsaJackDetector implements Runnable {
+ private static final String TAG = "UsbAlsaJackDetector";
+
+ private static native boolean nativeHasJackDetect(int card);
+ private native boolean nativeJackDetect(int card);
+ private native boolean nativeOutputJackConnected(int card);
+ private native boolean nativeInputJackConnected(int card);
+
+ private boolean mStopJackDetect = false;
+ private UsbAlsaDevice mAlsaDevice;
+
+ /* use startJackDetect to create a UsbAlsaJackDetector */
+ private UsbAlsaJackDetector(UsbAlsaDevice device) {
+ mAlsaDevice = device;
+ }
+
+ /** If jack detection is detected on the given Alsa Device,
+ * create and return a UsbAlsaJackDetector which will update wired device state
+ * each time a jack detection event is registered.
+ *
+ * @returns UsbAlsaJackDetector if jack detect is supported, or null.
+ */
+ public static UsbAlsaJackDetector startJackDetect(UsbAlsaDevice device) {
+ if (!nativeHasJackDetect(device.getCardNum())) {
+ return null;
+ }
+ UsbAlsaJackDetector jackDetector = new UsbAlsaJackDetector(device);
+
+ // This thread will exit once the USB device disappears.
+ // It can also be convinced to stop with pleaseStop().
+ new Thread(jackDetector, "USB jack detect thread").start();
+ return jackDetector;
+ }
+
+ public boolean isInputJackConnected() {
+ return nativeInputJackConnected(mAlsaDevice.getCardNum());
+ }
+
+ public boolean isOutputJackConnected() {
+ return nativeOutputJackConnected(mAlsaDevice.getCardNum());
+ }
+
+ /**
+ * Stop the jack detect thread from calling back into UsbAlsaDevice.
+ * This doesn't force the thread to stop (which is deprecated in java and dangerous due to
+ * locking issues), but will cause the thread to exit at the next safe opportunity.
+ */
+ public void pleaseStop() {
+ synchronized (this) {
+ mStopJackDetect = true;
+ }
+ }
+
+ /**
+ * Called by nativeJackDetect each time a jack detect event is reported.
+ * @return false when the jackDetect thread should stop. true otherwise.
+ */
+ public boolean jackDetectCallback() {
+ synchronized (this) {
+ if (mStopJackDetect) {
+ return false;
+ }
+ mAlsaDevice.updateWiredDeviceConnectionState(true);
+ }
+ return true;
+ }
+
+ /**
+ * This will call jackDetectCallback each time it detects a jack detect event.
+ * If jackDetectCallback returns false, this function will return.
+ */
+ public void run() {
+ nativeJackDetect(mAlsaDevice.getCardNum());
+ }
+}
+
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 0c5f8f1..2f1c516 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -20,11 +20,9 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbDevice;
-import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.midi.MidiDeviceInfo;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.usb.UsbAlsaManagerProto;
@@ -32,7 +30,6 @@
import com.android.internal.alsa.AlsaCardsParser;
import com.android.internal.util.dump.DualDumpOutputStream;
-import com.android.server.audio.AudioService;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import libcore.io.IoUtils;
@@ -58,6 +55,7 @@
// this is needed to map USB devices to ALSA Audio Devices, especially to remove an
// ALSA device when we are notified that its associated USB device has been removed.
private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>();
+ private UsbAlsaDevice mSelectedDevice;
/**
* List of connected MIDI devices
@@ -78,17 +76,19 @@
ServiceManager.getService(Context.AUDIO_SERVICE));
}
- // Notifies AudioService when a device is added or removed
- // audioDevice - the AudioDevice that was added or removed
- // enabled - if true, we're connecting a device (it's arrived), else disconnecting
- private void notifyDeviceState(UsbAlsaDevice alsaDevice, boolean enabled) {
+ /**
+ * Select the AlsaDevice to be used for AudioService.
+ * AlsaDevice.start() notifies AudioService of it's connected state.
+ *
+ * @param alsaDevice The selected UsbAlsaDevice for system USB audio.
+ */
+ private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) {
if (DEBUG) {
- Slog.d(TAG, "notifyDeviceState " + enabled + " " + alsaDevice);
+ Slog.d(TAG, "selectAlsaDevice " + alsaDevice);
}
- if (mAudioService == null) {
- Slog.e(TAG, "no AudioService");
- return;
+ if (mSelectedDevice != null) {
+ deselectAlsaDevice();
}
// FIXME Does not yet handle the case where the setting is changed
@@ -102,40 +102,14 @@
return;
}
- int state = (enabled ? 1 : 0);
- int cardNum = alsaDevice.getCardNum();
- int deviceNum = alsaDevice.getDeviceNum();
- if (cardNum < 0 || deviceNum < 0) {
- Slog.e(TAG, "Invalid alsa card or device alsaCard: " + cardNum
- + " alsaDevice: " + deviceNum);
- return;
- }
+ mSelectedDevice = alsaDevice;
+ alsaDevice.start();
+ }
- String address = AudioService.makeAlsaAddressString(cardNum, deviceNum);
- try {
- // Playback Device
- if (alsaDevice.hasPlayback()) {
- int device = alsaDevice.isOutputHeadset()
- ? AudioSystem.DEVICE_OUT_USB_HEADSET
- : AudioSystem.DEVICE_OUT_USB_DEVICE;
- if (DEBUG) {
- Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
- " addr:" + address + " name:" + alsaDevice.getDeviceName());
- }
- mAudioService.setWiredDeviceConnectionState(
- device, state, address, alsaDevice.getDeviceName(), TAG);
- }
-
- // Capture Device
- if (alsaDevice.hasCapture()) {
- int device = alsaDevice.isInputHeadset()
- ? AudioSystem.DEVICE_IN_USB_HEADSET
- : AudioSystem.DEVICE_IN_USB_DEVICE;
- mAudioService.setWiredDeviceConnectionState(
- device, state, address, alsaDevice.getDeviceName(), TAG);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
+ private synchronized void deselectAlsaDevice() {
+ if (mSelectedDevice != null) {
+ mSelectedDevice.stop();
+ mSelectedDevice = null;
}
}
@@ -168,7 +142,7 @@
Slog.d(TAG, " alsaDevice:" + alsaDevice);
}
if (alsaDevice != null) {
- notifyDeviceState(alsaDevice, true /*enabled*/);
+ selectAlsaDevice(alsaDevice);
}
return alsaDevice;
} else {
@@ -202,16 +176,21 @@
if (hasInput || hasOutput) {
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
- UsbAlsaDevice alsaDevice =
- new UsbAlsaDevice(cardRec.getCardNum(), 0 /*device*/, deviceAddress,
- hasOutput, hasInput, isInputHeadset, isOutputHeadset);
- alsaDevice.setDeviceNameAndDescription(
- cardRec.getCardName(), cardRec.getCardDescription());
- mAlsaDevices.add(0, alsaDevice);
- // Select it
+ if (mAudioService == null) {
+ Slog.e(TAG, "no AudioService");
+ return;
+ }
+
+ UsbAlsaDevice alsaDevice =
+ new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
+ deviceAddress, hasOutput, hasInput,
+ isInputHeadset, isOutputHeadset);
if (alsaDevice != null) {
- notifyDeviceState(alsaDevice, true /*enabled*/);
+ alsaDevice.setDeviceNameAndDescription(
+ cardRec.getCardName(), cardRec.getCardDescription());
+ mAlsaDevices.add(0, alsaDevice);
+ selectAlsaDevice(alsaDevice);
}
}
@@ -256,7 +235,7 @@
}
}
- /* package */ void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
+ /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
if (DEBUG) {
Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")");
}
@@ -264,13 +243,9 @@
// Audio
UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress);
Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
- if (alsaDevice != null) {
- if (alsaDevice.hasPlayback() || alsaDevice.hasCapture()) {
- notifyDeviceState(alsaDevice, false /*enabled*/);
-
- // if there any external devices left, select one of them
- selectDefaultDevice();
- }
+ if (alsaDevice != null && alsaDevice == mSelectedDevice) {
+ deselectAlsaDevice();
+ selectDefaultDevice(); // if there any external devices left, select one of them
}
// MIDI