Loading media/java/android/media/AudioSystem.java +12 −0 Original line number Diff line number Diff line Loading @@ -903,6 +903,18 @@ public class AudioSystem } } /** * Returns a human readable name for a given device type * @param device a native device type, NOT an AudioDeviceInfo type * @return a string describing the device type */ public static @NonNull String getDeviceName(int device) { if ((device & DEVICE_BIT_IN) != 0) { return getInputDeviceName(device); } return getOutputDeviceName(device); } // phone state, match audio_mode??? public static final int PHONE_STATE_OFFCALL = 0; public static final int PHONE_STATE_RINGING = 1; Loading services/core/java/com/android/server/audio/AudioDeviceBroker.java +6 −14 Original line number Diff line number Diff line Loading @@ -600,19 +600,11 @@ import java.io.PrintWriter; sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); } /*package*/ void cancelA2dpDockTimeout() { mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } /*package*/ void postA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); } /*package*/ boolean hasScheduledA2dpDockTimeout() { return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } // must be called synchronized on mConnectedDevices /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) { return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, Loading @@ -621,8 +613,8 @@ import java.io.PrintWriter; new BtHelper.BluetoothA2dpDeviceInfo(btDevice))); } /*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) { sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs); /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) { sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs); } /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) { Loading Loading @@ -781,7 +773,7 @@ import java.io.PrintWriter; } } break; case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_TIMEOUT: // msg.obj == address of BTA2DP device synchronized (mDeviceStateLock) { mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1); Loading Loading @@ -945,7 +937,7 @@ import java.io.PrintWriter; private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7; private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8; private static final int MSG_BT_HEADSET_CNCT_FAILED = 9; private static final int MSG_IL_BTA2DP_DOCK_TIMEOUT = 10; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11; private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12; private static final int MSG_REPORT_NEW_ROUTES = 13; Loading Loading @@ -981,7 +973,7 @@ import java.io.PrintWriter; case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: Loading Loading @@ -1071,7 +1063,7 @@ import java.io.PrintWriter; case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: if (sLastDeviceConnectMsgTime >= time) { Loading services/core/java/com/android/server/audio/AudioDeviceInventory.java +87 −41 Original line number Diff line number Diff line Loading @@ -59,10 +59,21 @@ public class AudioDeviceInventory { private static final String TAG = "AS.AudioDeviceInventory"; // Actual list of connected devices // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices private final Object mDevicesLock = new Object(); // List of connected devices // Key for map created from DeviceInfo.makeDeviceListKey() @GuardedBy("mDevicesLock") private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>(); // List of devices actually connected to AudioPolicy (through AudioSystem), only one // by device type, which is used as the key, value is the DeviceInfo generated key. // For the moment only for A2DP sink devices. // TODO: extend to all device types @GuardedBy("mDevicesLock") private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>(); // List of preferred devices for strategies private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>(); Loading Loading @@ -94,25 +105,30 @@ public class AudioDeviceInventory { */ private static class DeviceInfo { final int mDeviceType; final String mDeviceName; final String mDeviceAddress; final @NonNull String mDeviceName; final @NonNull String mDeviceAddress; int mDeviceCodecFormat; DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) { mDeviceType = deviceType; mDeviceName = deviceName; mDeviceAddress = deviceAddress; mDeviceName = deviceName == null ? "" : deviceName; mDeviceAddress = deviceAddress == null ? "" : deviceAddress; mDeviceCodecFormat = deviceCodecFormat; } @Override public String toString() { return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType) + " name:" + mDeviceName + " (" + AudioSystem.getDeviceName(mDeviceType) + ") name:" + mDeviceName + " addr:" + mDeviceAddress + " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]"; } String getKey() { return makeDeviceListKey(mDeviceType, mDeviceAddress); } /** * Generate a unique key for the mConnectedDevices List by composing the device "type" * and the "address" associated with a specific instance of that device type Loading Loading @@ -147,6 +163,14 @@ public class AudioDeviceInventory { pw.println("\n" + prefix + "Preferred devices for strategy:"); mPreferredDevices.forEach((strategy, device) -> { pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); }); pw.println("\n" + prefix + "Connected devices:"); mConnectedDevices.forEach((key, deviceInfo) -> { pw.println(" " + prefix + deviceInfo.toString()); }); pw.println("\n" + prefix + "APM Connected device (A2DP sink only):"); mApmConnectedDevices.forEach((keyType, valueAddress) -> { pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) + " (" + AudioSystem.getDeviceName(keyType) + ") addr:" + valueAddress); }); } //------------------------------------------------------------ Loading @@ -158,7 +182,8 @@ public class AudioDeviceInventory { */ // Always executed on AudioDeviceBroker message queue /*package*/ void onRestoreDevices() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { AudioSystem.setDeviceConnectionState( di.mDeviceType, Loading @@ -168,7 +193,6 @@ public class AudioDeviceInventory { di.mDeviceCodecFormat); } } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, device) -> { AudioSystem.setPreferredDeviceForStrategy(strategy, device); }); Loading @@ -187,6 +211,9 @@ public class AudioDeviceInventory { + state + " vol=" + a2dpVolume); } String address = btDevice.getAddress(); if (address == null) { address = ""; } if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } Loading @@ -198,7 +225,7 @@ public class AudioDeviceInventory { + " codec=" + a2dpCodec + " vol=" + a2dpVolume)); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress()); final DeviceInfo di = mConnectedDevices.get(key); Loading Loading @@ -238,7 +265,7 @@ public class AudioDeviceInventory { address = ""; } synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey( AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); Loading @@ -261,7 +288,7 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "onSetHearingAidConnectionState addr=" + address)); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, btDevice.getAddress()); final DeviceInfo di = mConnectedDevices.get(key); Loading Loading @@ -297,7 +324,7 @@ public class AudioDeviceInventory { "onBluetoothA2dpActiveDeviceChange addr=" + address + " event=" + BtHelper.a2dpDeviceEventToString(event))); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)")); Loading Loading @@ -340,7 +367,7 @@ public class AudioDeviceInventory { } /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { makeA2dpDeviceUnavailableNow(address, a2dpCodec); } } Loading Loading @@ -377,7 +404,7 @@ public class AudioDeviceInventory { AudioDeviceInventory.WiredDeviceConnectionState wdcs) { AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { mDeviceBroker.setBluetoothA2dpOnInt(true, Loading Loading @@ -405,7 +432,7 @@ public class AudioDeviceInventory { } /*package*/ void onToggleHdmi() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { // Is HDMI connected? final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); final DeviceInfo di = mConnectedDevices.get(key); Loading Loading @@ -472,7 +499,7 @@ public class AudioDeviceInventory { + Integer.toHexString(device) + " address:" + address + " name:" + deviceName + ")"); } synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); if (AudioService.DEBUG_DEVICES) { Slog.i(TAG, "deviceKey:" + deviceKey); Loading Loading @@ -511,7 +538,7 @@ public class AudioDeviceInventory { /*package*/ void disconnectA2dp() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices mConnectedDevices.values().forEach(deviceInfo -> { Loading @@ -531,7 +558,7 @@ public class AudioDeviceInventory { } /*package*/ void disconnectA2dpSink() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices mConnectedDevices.values().forEach(deviceInfo -> { Loading @@ -544,7 +571,7 @@ public class AudioDeviceInventory { } /*package*/ void disconnectHearingAid() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_HEARING_AID devices mConnectedDevices.values().forEach(deviceInfo -> { Loading @@ -568,7 +595,7 @@ public class AudioDeviceInventory { // from AudioSystem /*package*/ int checkSendBecomingNoisyIntent(int device, @AudioService.ConnectionState int state, int musicDevice) { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { return checkSendBecomingNoisyIntentInt(device, state, musicDevice); } } Loading @@ -595,7 +622,7 @@ public class AudioDeviceInventory { if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { throw new IllegalArgumentException("invalid profile " + profile); } synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) { @AudioService.ConnectionState int asState = (state == BluetoothA2dp.STATE_CONNECTED) Loading Loading @@ -635,7 +662,7 @@ public class AudioDeviceInventory { /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, String address, String name, String caller) { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE); mDeviceBroker.postSetWiredDeviceConnectionState( new WiredDeviceConnectionState(type, state, address, name, caller), Loading @@ -648,7 +675,7 @@ public class AudioDeviceInventory { @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, boolean suppressNoisyIntent, int musicDevice) { int delay; synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if (!suppressNoisyIntent) { int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0; delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID, Loading @@ -665,39 +692,58 @@ public class AudioDeviceInventory { //------------------------------------------------------------------- // Internal utilities @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpDeviceAvailable(String address, String name, String eventSource, int a2dpCodec) { // enable A2DP before notifying A2DP connection to avoid unnecessary processing in // audio policy manager mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource); // at this point there could be another A2DP device already connected in APM, but it // doesn't matter as this new one will overwrite the previous one AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); // Reset A2DP suspend state each time a new sink is connected AudioSystem.setParameters("A2dpSuspended=false"); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address), new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec)); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec); final String diKey = di.getKey(); mConnectedDevices.put(diKey, di); // on a connection always overwrite the device seen by AudioPolicy, see comment above when // calling AudioSystem mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey); mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); setCurrentAudioRouteNameIfPossible(name); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { if (address == null) { return; } final String deviceToRemoveKey = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); mConnectedDevices.remove(deviceToRemoveKey); if (!mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) .equals(deviceToRemoveKey)) { // removing A2DP device not currently used by AudioPolicy, log but don't act on it AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( "A2DP device " + address + " made unavailable, was not used")).printLog(TAG)); return; } // device to remove was visible by APM, update APM mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address)); mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpDeviceUnavailableLater(String address, int delayMs) { // prevent any activity on the A2DP audio output to avoid unwanted // reconnection of the sink. Loading @@ -711,11 +757,11 @@ public class AudioDeviceInventory { // the device will be made unavailable later, so consider it disconnected right away mConnectedDevices.remove(deviceKey); // send the delayed message to make the device unavailable later mDeviceBroker.setA2dpDockTimeout(address, a2dpCodec, delayMs); mDeviceBroker.setA2dpTimeout(address, a2dpCodec, delayMs); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, "", Loading @@ -726,7 +772,7 @@ public class AudioDeviceInventory { address, AudioSystem.AUDIO_FORMAT_DEFAULT)); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", Loading @@ -735,7 +781,7 @@ public class AudioDeviceInventory { DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address)); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeHearingAidDeviceAvailable( String address, String name, int streamType, String eventSource) { final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, Loading @@ -755,7 +801,7 @@ public class AudioDeviceInventory { setCurrentAudioRouteNameIfPossible(name); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", Loading @@ -766,7 +812,7 @@ public class AudioDeviceInventory { setCurrentAudioRouteNameIfPossible(null); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void setCurrentAudioRouteNameIfPossible(String name) { synchronized (mCurAudioRoutes) { if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) { Loading @@ -779,7 +825,7 @@ public class AudioDeviceInventory { } } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private boolean isCurrentDeviceConnected() { return mConnectedDevices.values().stream().anyMatch(deviceInfo -> TextUtils.equals(deviceInfo.mDeviceName, mCurAudioRoutes.bluetoothName)); Loading Loading @@ -807,7 +853,7 @@ public class AudioDeviceInventory { // must be called before removing the device from mConnectedDevices // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying // from AudioSystem @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private int checkSendBecomingNoisyIntentInt(int device, @AudioService.ConnectionState int state, int musicDevice) { if (state != AudioService.CONNECTION_STATE_DISCONNECTED) { Loading Loading @@ -1015,7 +1061,7 @@ public class AudioDeviceInventory { public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, device.getAddress()); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { return (mConnectedDevices.get(key) != null); } } Loading Loading
media/java/android/media/AudioSystem.java +12 −0 Original line number Diff line number Diff line Loading @@ -903,6 +903,18 @@ public class AudioSystem } } /** * Returns a human readable name for a given device type * @param device a native device type, NOT an AudioDeviceInfo type * @return a string describing the device type */ public static @NonNull String getDeviceName(int device) { if ((device & DEVICE_BIT_IN) != 0) { return getInputDeviceName(device); } return getOutputDeviceName(device); } // phone state, match audio_mode??? public static final int PHONE_STATE_OFFCALL = 0; public static final int PHONE_STATE_RINGING = 1; Loading
services/core/java/com/android/server/audio/AudioDeviceBroker.java +6 −14 Original line number Diff line number Diff line Loading @@ -600,19 +600,11 @@ import java.io.PrintWriter; sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); } /*package*/ void cancelA2dpDockTimeout() { mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } /*package*/ void postA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); } /*package*/ boolean hasScheduledA2dpDockTimeout() { return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT); } // must be called synchronized on mConnectedDevices /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) { return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, Loading @@ -621,8 +613,8 @@ import java.io.PrintWriter; new BtHelper.BluetoothA2dpDeviceInfo(btDevice))); } /*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) { sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs); /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) { sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs); } /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) { Loading Loading @@ -781,7 +773,7 @@ import java.io.PrintWriter; } } break; case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_TIMEOUT: // msg.obj == address of BTA2DP device synchronized (mDeviceStateLock) { mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1); Loading Loading @@ -945,7 +937,7 @@ import java.io.PrintWriter; private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7; private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8; private static final int MSG_BT_HEADSET_CNCT_FAILED = 9; private static final int MSG_IL_BTA2DP_DOCK_TIMEOUT = 10; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11; private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12; private static final int MSG_REPORT_NEW_ROUTES = 13; Loading Loading @@ -981,7 +973,7 @@ import java.io.PrintWriter; case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: Loading Loading @@ -1071,7 +1063,7 @@ import java.io.PrintWriter; case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: if (sLastDeviceConnectMsgTime >= time) { Loading
services/core/java/com/android/server/audio/AudioDeviceInventory.java +87 −41 Original line number Diff line number Diff line Loading @@ -59,10 +59,21 @@ public class AudioDeviceInventory { private static final String TAG = "AS.AudioDeviceInventory"; // Actual list of connected devices // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices private final Object mDevicesLock = new Object(); // List of connected devices // Key for map created from DeviceInfo.makeDeviceListKey() @GuardedBy("mDevicesLock") private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>(); // List of devices actually connected to AudioPolicy (through AudioSystem), only one // by device type, which is used as the key, value is the DeviceInfo generated key. // For the moment only for A2DP sink devices. // TODO: extend to all device types @GuardedBy("mDevicesLock") private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>(); // List of preferred devices for strategies private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>(); Loading Loading @@ -94,25 +105,30 @@ public class AudioDeviceInventory { */ private static class DeviceInfo { final int mDeviceType; final String mDeviceName; final String mDeviceAddress; final @NonNull String mDeviceName; final @NonNull String mDeviceAddress; int mDeviceCodecFormat; DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) { mDeviceType = deviceType; mDeviceName = deviceName; mDeviceAddress = deviceAddress; mDeviceName = deviceName == null ? "" : deviceName; mDeviceAddress = deviceAddress == null ? "" : deviceAddress; mDeviceCodecFormat = deviceCodecFormat; } @Override public String toString() { return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType) + " name:" + mDeviceName + " (" + AudioSystem.getDeviceName(mDeviceType) + ") name:" + mDeviceName + " addr:" + mDeviceAddress + " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]"; } String getKey() { return makeDeviceListKey(mDeviceType, mDeviceAddress); } /** * Generate a unique key for the mConnectedDevices List by composing the device "type" * and the "address" associated with a specific instance of that device type Loading Loading @@ -147,6 +163,14 @@ public class AudioDeviceInventory { pw.println("\n" + prefix + "Preferred devices for strategy:"); mPreferredDevices.forEach((strategy, device) -> { pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); }); pw.println("\n" + prefix + "Connected devices:"); mConnectedDevices.forEach((key, deviceInfo) -> { pw.println(" " + prefix + deviceInfo.toString()); }); pw.println("\n" + prefix + "APM Connected device (A2DP sink only):"); mApmConnectedDevices.forEach((keyType, valueAddress) -> { pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) + " (" + AudioSystem.getDeviceName(keyType) + ") addr:" + valueAddress); }); } //------------------------------------------------------------ Loading @@ -158,7 +182,8 @@ public class AudioDeviceInventory { */ // Always executed on AudioDeviceBroker message queue /*package*/ void onRestoreDevices() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { AudioSystem.setDeviceConnectionState( di.mDeviceType, Loading @@ -168,7 +193,6 @@ public class AudioDeviceInventory { di.mDeviceCodecFormat); } } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, device) -> { AudioSystem.setPreferredDeviceForStrategy(strategy, device); }); Loading @@ -187,6 +211,9 @@ public class AudioDeviceInventory { + state + " vol=" + a2dpVolume); } String address = btDevice.getAddress(); if (address == null) { address = ""; } if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } Loading @@ -198,7 +225,7 @@ public class AudioDeviceInventory { + " codec=" + a2dpCodec + " vol=" + a2dpVolume)); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress()); final DeviceInfo di = mConnectedDevices.get(key); Loading Loading @@ -238,7 +265,7 @@ public class AudioDeviceInventory { address = ""; } synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey( AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); Loading @@ -261,7 +288,7 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "onSetHearingAidConnectionState addr=" + address)); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, btDevice.getAddress()); final DeviceInfo di = mConnectedDevices.get(key); Loading Loading @@ -297,7 +324,7 @@ public class AudioDeviceInventory { "onBluetoothA2dpActiveDeviceChange addr=" + address + " event=" + BtHelper.a2dpDeviceEventToString(event))); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)")); Loading Loading @@ -340,7 +367,7 @@ public class AudioDeviceInventory { } /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { makeA2dpDeviceUnavailableNow(address, a2dpCodec); } } Loading Loading @@ -377,7 +404,7 @@ public class AudioDeviceInventory { AudioDeviceInventory.WiredDeviceConnectionState wdcs) { AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { mDeviceBroker.setBluetoothA2dpOnInt(true, Loading Loading @@ -405,7 +432,7 @@ public class AudioDeviceInventory { } /*package*/ void onToggleHdmi() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { // Is HDMI connected? final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); final DeviceInfo di = mConnectedDevices.get(key); Loading Loading @@ -472,7 +499,7 @@ public class AudioDeviceInventory { + Integer.toHexString(device) + " address:" + address + " name:" + deviceName + ")"); } synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); if (AudioService.DEBUG_DEVICES) { Slog.i(TAG, "deviceKey:" + deviceKey); Loading Loading @@ -511,7 +538,7 @@ public class AudioDeviceInventory { /*package*/ void disconnectA2dp() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices mConnectedDevices.values().forEach(deviceInfo -> { Loading @@ -531,7 +558,7 @@ public class AudioDeviceInventory { } /*package*/ void disconnectA2dpSink() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices mConnectedDevices.values().forEach(deviceInfo -> { Loading @@ -544,7 +571,7 @@ public class AudioDeviceInventory { } /*package*/ void disconnectHearingAid() { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_HEARING_AID devices mConnectedDevices.values().forEach(deviceInfo -> { Loading @@ -568,7 +595,7 @@ public class AudioDeviceInventory { // from AudioSystem /*package*/ int checkSendBecomingNoisyIntent(int device, @AudioService.ConnectionState int state, int musicDevice) { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { return checkSendBecomingNoisyIntentInt(device, state, musicDevice); } } Loading @@ -595,7 +622,7 @@ public class AudioDeviceInventory { if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { throw new IllegalArgumentException("invalid profile " + profile); } synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) { @AudioService.ConnectionState int asState = (state == BluetoothA2dp.STATE_CONNECTED) Loading Loading @@ -635,7 +662,7 @@ public class AudioDeviceInventory { /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, String address, String name, String caller) { synchronized (mConnectedDevices) { synchronized (mDevicesLock) { int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE); mDeviceBroker.postSetWiredDeviceConnectionState( new WiredDeviceConnectionState(type, state, address, name, caller), Loading @@ -648,7 +675,7 @@ public class AudioDeviceInventory { @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, boolean suppressNoisyIntent, int musicDevice) { int delay; synchronized (mConnectedDevices) { synchronized (mDevicesLock) { if (!suppressNoisyIntent) { int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0; delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID, Loading @@ -665,39 +692,58 @@ public class AudioDeviceInventory { //------------------------------------------------------------------- // Internal utilities @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpDeviceAvailable(String address, String name, String eventSource, int a2dpCodec) { // enable A2DP before notifying A2DP connection to avoid unnecessary processing in // audio policy manager mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource); // at this point there could be another A2DP device already connected in APM, but it // doesn't matter as this new one will overwrite the previous one AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); // Reset A2DP suspend state each time a new sink is connected AudioSystem.setParameters("A2dpSuspended=false"); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address), new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec)); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec); final String diKey = di.getKey(); mConnectedDevices.put(diKey, di); // on a connection always overwrite the device seen by AudioPolicy, see comment above when // calling AudioSystem mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey); mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); setCurrentAudioRouteNameIfPossible(name); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { if (address == null) { return; } final String deviceToRemoveKey = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); mConnectedDevices.remove(deviceToRemoveKey); if (!mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) .equals(deviceToRemoveKey)) { // removing A2DP device not currently used by AudioPolicy, log but don't act on it AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( "A2DP device " + address + " made unavailable, was not used")).printLog(TAG)); return; } // device to remove was visible by APM, update APM mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address)); mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpDeviceUnavailableLater(String address, int delayMs) { // prevent any activity on the A2DP audio output to avoid unwanted // reconnection of the sink. Loading @@ -711,11 +757,11 @@ public class AudioDeviceInventory { // the device will be made unavailable later, so consider it disconnected right away mConnectedDevices.remove(deviceKey); // send the delayed message to make the device unavailable later mDeviceBroker.setA2dpDockTimeout(address, a2dpCodec, delayMs); mDeviceBroker.setA2dpTimeout(address, a2dpCodec, delayMs); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, "", Loading @@ -726,7 +772,7 @@ public class AudioDeviceInventory { address, AudioSystem.AUDIO_FORMAT_DEFAULT)); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", Loading @@ -735,7 +781,7 @@ public class AudioDeviceInventory { DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address)); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeHearingAidDeviceAvailable( String address, String name, int streamType, String eventSource) { final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, Loading @@ -755,7 +801,7 @@ public class AudioDeviceInventory { setCurrentAudioRouteNameIfPossible(name); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", Loading @@ -766,7 +812,7 @@ public class AudioDeviceInventory { setCurrentAudioRouteNameIfPossible(null); } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private void setCurrentAudioRouteNameIfPossible(String name) { synchronized (mCurAudioRoutes) { if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) { Loading @@ -779,7 +825,7 @@ public class AudioDeviceInventory { } } @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private boolean isCurrentDeviceConnected() { return mConnectedDevices.values().stream().anyMatch(deviceInfo -> TextUtils.equals(deviceInfo.mDeviceName, mCurAudioRoutes.bluetoothName)); Loading Loading @@ -807,7 +853,7 @@ public class AudioDeviceInventory { // must be called before removing the device from mConnectedDevices // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying // from AudioSystem @GuardedBy("mConnectedDevices") @GuardedBy("mDevicesLock") private int checkSendBecomingNoisyIntentInt(int device, @AudioService.ConnectionState int state, int musicDevice) { if (state != AudioService.CONNECTION_STATE_DISCONNECTED) { Loading Loading @@ -1015,7 +1061,7 @@ public class AudioDeviceInventory { public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, device.getAddress()); synchronized (mConnectedDevices) { synchronized (mDevicesLock) { return (mConnectedDevices.get(key) != null); } } Loading