Loading services/core/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ cc_library_static { "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", Loading Loading @@ -97,6 +98,7 @@ cc_defaults { "libgui", "libusbhost", "libsuspend", "libtinyalsa", "libEGL", "libGLESv2", "libnetutils", Loading services/core/jni/com_android_server_UsbAlsaJackDetector.cpp 0 → 100644 +152 −0 Original line number Diff line number Diff line /* * 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; } } services/core/jni/onload.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ int register_android_server_PowerManagerService(JNIEnv* env); 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); Loading Loading @@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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); Loading services/usb/java/com/android/server/usb/UsbAlsaDevice.java +155 −28 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -30,25 +35,31 @@ public final class UsbAlsaDevice { 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; } Loading @@ -75,71 +86,187 @@ public final class UsbAlsaDevice { } /** * @returns true if the device supports playback. * @returns the ALSA card/device address string. */ 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 output. */ public boolean hasPlayback() { return mHasPlayback; public boolean hasOutput() { return mHasOutput; } /** * @returns true if the device supports capture (recording). * @returns true if the device supports input (recording). */ public boolean hasCapture() { return mHasCapture; public boolean hasInput() { return mHasInput; } /** * @returns true if the device is a headset for purposes of capture. * @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; } Loading @@ -155,8 +282,8 @@ public final class UsbAlsaDevice { 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); } Loading @@ -170,8 +297,8 @@ public final class UsbAlsaDevice { 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); Loading services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * 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()); } } Loading
services/core/jni/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ cc_library_static { "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", Loading Loading @@ -97,6 +98,7 @@ cc_defaults { "libgui", "libusbhost", "libsuspend", "libtinyalsa", "libEGL", "libGLESv2", "libnetutils", Loading
services/core/jni/com_android_server_UsbAlsaJackDetector.cpp 0 → 100644 +152 −0 Original line number Diff line number Diff line /* * 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; } }
services/core/jni/onload.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ int register_android_server_PowerManagerService(JNIEnv* env); 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); Loading Loading @@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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); Loading
services/usb/java/com/android/server/usb/UsbAlsaDevice.java +155 −28 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -30,25 +35,31 @@ public final class UsbAlsaDevice { 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; } Loading @@ -75,71 +86,187 @@ public final class UsbAlsaDevice { } /** * @returns true if the device supports playback. * @returns the ALSA card/device address string. */ 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 output. */ public boolean hasPlayback() { return mHasPlayback; public boolean hasOutput() { return mHasOutput; } /** * @returns true if the device supports capture (recording). * @returns true if the device supports input (recording). */ public boolean hasCapture() { return mHasCapture; public boolean hasInput() { return mHasInput; } /** * @returns true if the device is a headset for purposes of capture. * @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; } Loading @@ -155,8 +282,8 @@ public final class UsbAlsaDevice { 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); } Loading @@ -170,8 +297,8 @@ public final class UsbAlsaDevice { 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); Loading
services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * 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()); } }