diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 3123ee6dd4d71..e73cf87ba9f3d 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1237,9 +1237,6 @@ public class AudioSystem public static final Set DEVICE_IN_ALL_SCO_SET; /** @hide */ public static final Set DEVICE_IN_ALL_USB_SET; - /** @hide */ - public static final Set DEVICE_IN_ALL_BLE_SET; - static { DEVICE_IN_ALL_SET = new HashSet<>(); DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION); @@ -1279,66 +1276,6 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); - - DEVICE_IN_ALL_BLE_SET = new HashSet<>(); - DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET); - } - - /** @hide */ - public static boolean isBluetoothDevice(int deviceType) { - return isBluetoothA2dpOutDevice(deviceType) - || isBluetoothScoDevice(deviceType) - || isBluetoothLeDevice(deviceType); - } - - /** @hide */ - public static boolean isBluetoothOutDevice(int deviceType) { - return isBluetoothA2dpOutDevice(deviceType) - || isBluetoothScoOutDevice(deviceType) - || isBluetoothLeOutDevice(deviceType); - } - - /** @hide */ - public static boolean isBluetoothInDevice(int deviceType) { - return isBluetoothScoInDevice(deviceType) - || isBluetoothLeInDevice(deviceType); - } - - /** @hide */ - public static boolean isBluetoothA2dpOutDevice(int deviceType) { - return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType); - } - - /** @hide */ - public static boolean isBluetoothScoOutDevice(int deviceType) { - return DEVICE_OUT_ALL_SCO_SET.contains(deviceType); - } - - /** @hide */ - public static boolean isBluetoothScoInDevice(int deviceType) { - return DEVICE_IN_ALL_SCO_SET.contains(deviceType); - } - - /** @hide */ - public static boolean isBluetoothScoDevice(int deviceType) { - return isBluetoothScoOutDevice(deviceType) - || isBluetoothScoInDevice(deviceType); - } - - /** @hide */ - public static boolean isBluetoothLeOutDevice(int deviceType) { - return DEVICE_OUT_ALL_BLE_SET.contains(deviceType); - } - - /** @hide */ - public static boolean isBluetoothLeInDevice(int deviceType) { - return DEVICE_IN_ALL_BLE_SET.contains(deviceType); - } - - /** @hide */ - public static boolean isBluetoothLeDevice(int deviceType) { - return isBluetoothLeOutDevice(deviceType) - || isBluetoothLeInDevice(deviceType); } /** @hide */ diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 462942e4cb276..2da9fa6088db5 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -87,14 +87,14 @@ import java.util.concurrent.atomic.AtomicBoolean; private final @NonNull Context mContext; /** ID for Communication strategy retrieved form audio policy manager */ - /*package*/ int mCommunicationStrategyId = -1; + private int mCommunicationStrategyId = -1; /** ID for Accessibility strategy retrieved form audio policy manager */ private int mAccessibilityStrategyId = -1; /** Active communication device reported by audio policy manager */ - /*package*/ AudioDeviceInfo mActiveCommunicationDevice; + private AudioDeviceInfo mActiveCommunicationDevice; /** Last preferred device set for communication strategy */ private AudioDeviceAttributes mPreferredCommunicationDevice; @@ -750,19 +750,6 @@ import java.util.concurrent.atomic.AtomicBoolean; mIsLeOutput = false; } - BtDeviceInfo(@NonNull BtDeviceInfo src, int state) { - mDevice = src.mDevice; - mState = state; - mProfile = src.mProfile; - mSupprNoisy = src.mSupprNoisy; - mVolume = src.mVolume; - mIsLeOutput = src.mIsLeOutput; - mEventSource = src.mEventSource; - mAudioSystemDevice = src.mAudioSystemDevice; - mMusicDevice = src.mMusicDevice; - mCodec = src.mCodec; - } - // redefine equality op so we can match messages intended for this device @Override public boolean equals(Object o) { @@ -829,7 +816,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * @param info struct with the (dis)connection information */ /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) { - if (data.mPreviousDevice != null + if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null && data.mPreviousDevice.equals(data.mNewDevice)) { final String name = TextUtils.emptyIfNull(data.mNewDevice.getName()); new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR @@ -838,8 +825,7 @@ import java.util.concurrent.atomic.AtomicBoolean; .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) .record(); synchronized (mDeviceStateLock) { - postBluetoothDeviceConfigChange(createBtDeviceInfo(data, data.mNewDevice, - BluetoothProfile.STATE_CONNECTED)); + postBluetoothA2dpDeviceConfigChange(data.mNewDevice); } } else { synchronized (mDeviceStateLock) { @@ -1064,8 +1050,8 @@ import java.util.concurrent.atomic.AtomicBoolean; new AudioModeInfo(mode, pid, uid)); } - /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) { - sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info); + /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { + sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); } /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode, @@ -1322,10 +1308,6 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state); } - /*package*/ void postNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) { - sendLMsgNoDelay(MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED, SENDMSG_QUEUE, btDevice); - } - /*package*/ static final class CommunicationDeviceInfo { final @NonNull IBinder mCb; // Identifies the requesting client for death handler final int mPid; // Requester process ID @@ -1401,11 +1383,9 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, - boolean connect, @Nullable BluetoothDevice btDevice) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) { synchronized (mDeviceStateLock) { - return mDeviceInventory.handleDeviceConnection( - attributes, connect, false /*for test*/, btDevice); + return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/); } } @@ -1646,10 +1626,13 @@ import java.util.concurrent.atomic.AtomicBoolean; (String) msg.obj, msg.arg1); } break; - case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: + case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: + final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; synchronized (mDeviceStateLock) { - mDeviceInventory.onBluetoothDeviceConfigChange( - (BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE); + final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice); + mDeviceInventory.onBluetoothA2dpDeviceConfigChange( + new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), + BtHelper.EVENT_DEVICE_CONFIG_CHANGE); } break; case MSG_BROADCAST_AUDIO_BECOMING_NOISY: @@ -1807,10 +1790,6 @@ import java.util.concurrent.atomic.AtomicBoolean; final int capturePreset = msg.arg1; mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); } break; - case MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED: { - final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; - BtHelper.onNotifyPreferredAudioProfileApplied(btDevice); - } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1846,7 +1825,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; // process change of A2DP device configuration, obj is BluetoothDevice - private static final int MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE = 11; + 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; @@ -1886,15 +1865,13 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48; private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49; - private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 50; - private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_IL_BTLEAUDIO_TIMEOUT: - case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: + case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: @@ -1986,7 +1963,7 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_IL_BTLEAUDIO_TIMEOUT: - case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: + case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: if (sLastDeviceConnectMsgTime >= time) { // add a little delay to make sure messages are ordered as expected time = sLastDeviceConnectMsgTime + 30; @@ -2006,7 +1983,7 @@ import java.util.concurrent.atomic.AtomicBoolean; static { MESSAGES_MUTE_MUSIC = new HashSet<>(); MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE); - MESSAGES_MUTE_MUSIC.add(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE); + MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE); MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT); MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE); } @@ -2027,7 +2004,7 @@ import java.util.concurrent.atomic.AtomicBoolean; // Do not mute on bluetooth event if music is playing on a wired headset. if ((message == MSG_L_SET_BT_ACTIVE_DEVICE || message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT - || message == MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE) + || message == MSG_L_A2DP_DEVICE_CONFIG_CHANGE) && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) && hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET, mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) { @@ -2166,19 +2143,18 @@ import java.util.concurrent.atomic.AtomicBoolean; if (preferredCommunicationDevice == null) { AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice(); if (defaultDevice != null) { - mDeviceInventory.setPreferredDevicesForStrategy( + setPreferredDevicesForStrategySync( mCommunicationStrategyId, Arrays.asList(defaultDevice)); - mDeviceInventory.setPreferredDevicesForStrategy( + setPreferredDevicesForStrategySync( mAccessibilityStrategyId, Arrays.asList(defaultDevice)); } else { - mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId); - mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId); + removePreferredDevicesForStrategySync(mCommunicationStrategyId); + removePreferredDevicesForStrategySync(mAccessibilityStrategyId); } - mDeviceInventory.applyConnectedDevicesRoles(); } else { - mDeviceInventory.setPreferredDevicesForStrategy( + setPreferredDevicesForStrategySync( mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice)); - mDeviceInventory.setPreferredDevicesForStrategy( + setPreferredDevicesForStrategySync( mAccessibilityStrategyId, Arrays.asList(preferredCommunicationDevice)); } onUpdatePhoneStrategyDevice(preferredCommunicationDevice); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 1eb39f7352afb..228bc87cc20b9 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -34,36 +34,26 @@ import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.IStrategyNonDefaultDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; -import android.media.MediaRecorder.AudioSource; -import android.media.audiopolicy.AudioProductStrategy; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.SafeCloseable; import android.os.Binder; -import android.os.Bundle; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.utils.EventLogger; -import com.google.android.collect.Sets; - import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -185,26 +175,18 @@ public class AudioDeviceInventory { final RemoteCallbackList mDevRoleCapturePresetDispatchers = new RemoteCallbackList(); - final List mStrategies; - /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { - this(broker, AudioSystemAdapter.getDefaultAdapter()); + mDeviceBroker = broker; + mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); } //----------------------------------------------------------- /** for mocking only, allows to inject AudioSystem adapter */ /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) { - this(null, audioSystem); + mDeviceBroker = null; + mAudioSystem = audioSystem; } - private AudioDeviceInventory(@Nullable AudioDeviceBroker broker, - @Nullable AudioSystemAdapter audioSystem) { - mDeviceBroker = broker; - mAudioSystem = audioSystem; - mStrategies = AudioProductStrategy.getAudioProductStrategies(); - mBluetoothDualModeEnabled = SystemProperties.getBoolean( - "persist.bluetooth.enable_dual_mode_audio", false); - } /*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; } @@ -221,13 +203,8 @@ public class AudioDeviceInventory { int mDeviceCodecFormat; final UUID mSensorUuid; - /** Disabled operating modes for this device. Use a negative logic so that by default - * an empty list means all modes are allowed. - * See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */ - @NonNull ArraySet mDisabledModes = new ArraySet(0); - DeviceInfo(int deviceType, String deviceName, String deviceAddress, - int deviceCodecFormat, @Nullable UUID sensorUuid) { + int deviceCodecFormat, UUID sensorUuid) { mDeviceType = deviceType; mDeviceName = deviceName == null ? "" : deviceName; mDeviceAddress = deviceAddress == null ? "" : deviceAddress; @@ -235,31 +212,11 @@ public class AudioDeviceInventory { mSensorUuid = sensorUuid; } - void setModeDisabled(String mode) { - mDisabledModes.add(mode); - } - void setModeEnabled(String mode) { - mDisabledModes.remove(mode); - } - boolean isModeEnabled(String mode) { - return !mDisabledModes.contains(mode); - } - boolean isOutputOnlyModeEnabled() { - return isModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); - } - boolean isDuplexModeEnabled() { - return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); - } - DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) { this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null); } - DeviceInfo(int deviceType, String deviceName, String deviceAddress) { - this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT); - } - @Override public String toString() { return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType) @@ -267,8 +224,7 @@ public class AudioDeviceInventory { + ") name:" + mDeviceName + " addr:" + mDeviceAddress + " codec: " + Integer.toHexString(mDeviceCodecFormat) - + " sensorUuid: " + Objects.toString(mSensorUuid) - + " disabled modes: " + mDisabledModes + "]"; + + " sensorUuid: " + Objects.toString(mSensorUuid) + "]"; } @NonNull String getKey() { @@ -320,18 +276,9 @@ public class AudioDeviceInventory { pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) + " (" + AudioSystem.getDeviceName(keyType) + ") addr:" + valueAddress); }); - pw.println("\n" + prefix + "Preferred devices for capture preset:"); mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { pw.println(" " + prefix + "capturePreset:" + capturePreset + " devices:" + devices); }); - pw.println("\n" + prefix + "Applied devices roles for strategies:"); - mAppliedStrategyRoles.forEach((key, devices) -> { - pw.println(" " + prefix + "strategy: " + key.first - + " role:" + key.second + " devices:" + devices); }); - pw.println("\n" + prefix + "Applied devices roles for presets:"); - mAppliedPresetRoles.forEach((key, devices) -> { - pw.println(" " + prefix + "preset: " + key.first - + " role:" + key.second + " devices:" + devices); }); } //------------------------------------------------------------ @@ -352,16 +299,15 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } - mAppliedStrategyRoles.clear(); - applyConnectedDevicesRoles_l(); } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, devices) -> { - setPreferredDevicesForStrategy(strategy, devices); }); + mAudioSystem.setDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); }); } synchronized (mNonDefaultDevices) { mNonDefaultDevices.forEach((strategy, devices) -> { - addDevicesRoleForStrategy( + mAudioSystem.setDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); }); } synchronized (mPreferredDevicesForCapturePreset) { @@ -434,7 +380,8 @@ public class AudioDeviceInventory { btInfo.mVolume * 10, btInfo.mAudioSystemDevice, "onSetBtActiveDevice"); } - makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice"); + makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + "onSetBtActiveDevice", btInfo.mCodec); } break; case BluetoothProfile.HEARING_AID: @@ -450,7 +397,10 @@ public class AudioDeviceInventory { if (switchToUnavailable) { makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice); } else if (switchToAvailable) { - makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice"); + makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10, + btInfo.mAudioSystemDevice, + "onSetBtActiveDevice"); } break; default: throw new IllegalArgumentException("Invalid profile " @@ -461,30 +411,30 @@ public class AudioDeviceInventory { @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothDeviceConfigChange( - @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) { + /*package*/ void onBluetoothA2dpDeviceConfigChange( + @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId - + "onBluetoothDeviceConfigChange") - .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event)); + + "onBluetoothA2dpDeviceConfigChange") + .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); - final BluetoothDevice btDevice = btInfo.mDevice; + final BluetoothDevice btDevice = btInfo.getBtDevice(); if (btDevice == null) { mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record(); return; } if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice); + Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice); } - int volume = btInfo.mVolume; - @AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec; + int a2dpVolume = btInfo.getVolume(); + @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec(); String address = btDevice.getAddress(); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "onBluetoothDeviceConfigChange addr=" + address - + " event=" + BtHelper.deviceEventToString(event))); + "onBluetoothA2dpDeviceConfigChange addr=" + address + + " event=" + BtHelper.a2dpDeviceEventToString(event))); synchronized (mDevicesLock) { if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) { @@ -499,55 +449,54 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { - Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange"); + Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange"); mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } mmi.set(MediaMetrics.Property.ADDRESS, address) .set(MediaMetrics.Property.ENCODING, - AudioSystem.audioFormatToString(audioCodec)) - .set(MediaMetrics.Property.INDEX, volume) + AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.INDEX, a2dpVolume) .set(MediaMetrics.Property.NAME, di.mDeviceName); - - if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { - boolean a2dpCodecChange = false; - if (btInfo.mProfile == BluetoothProfile.A2DP) { - if (di.mDeviceCodecFormat != audioCodec) { - di.mDeviceCodecFormat = audioCodec; - mConnectedDevices.replace(key, di); - a2dpCodecChange = true; - } - final int res = mAudioSystem.handleDeviceConfigChange( - btInfo.mAudioSystemDevice, address, - BtHelper.getName(btDevice), audioCodec); - - if (res != AudioSystem.AUDIO_STATUS_OK) { - AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "APM handleDeviceConfigChange failed for A2DP device addr=" - + address + " codec=" - + AudioSystem.audioFormatToString(audioCodec)) - .printLog(TAG)); - - // force A2DP device disconnection in case of error so that AudioService - // state is consistent with audio policy manager state - setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo, - BluetoothProfile.STATE_DISCONNECTED)); - } else { - AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( - "APM handleDeviceConfigChange success for A2DP device addr=" - + address - + " codec=" + AudioSystem.audioFormatToString(audioCodec)) - .printLog(TAG)); - - } + if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) { + // Device is connected + if (a2dpVolume != -1) { + mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, + // convert index to internal representation in VolumeStreamState + a2dpVolume * 10, + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + "onBluetoothA2dpDeviceConfigChange"); } - if (!a2dpCodecChange) { - updateBluetoothPreferredModes_l(); - mDeviceBroker.postNotifyPreferredAudioProfileApplied(btDevice); + } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { + if (di.mDeviceCodecFormat != a2dpCodec) { + di.mDeviceCodecFormat = a2dpCodec; + mConnectedDevices.replace(key, di); } } + final int res = mAudioSystem.handleDeviceConfigChange( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, + BtHelper.getName(btDevice), a2dpCodec); + + if (res != AudioSystem.AUDIO_STATUS_OK) { + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "APM handleDeviceConfigChange failed for A2DP device addr=" + address + + " codec=" + AudioSystem.audioFormatToString(a2dpCodec)) + .printLog(TAG)); + + int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); + // force A2DP device disconnection in case of error so that AudioService state is + // consistent with audio policy manager state + setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice, + BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED, + musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); + } else { + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "APM handleDeviceConfigChange success for A2DP device addr=" + address + + " codec=" + AudioSystem.audioFormatToString(a2dpCodec)) + .printLog(TAG)); + } } mmi.record(); } @@ -629,7 +578,7 @@ public class AudioDeviceInventory { } if (!handleDeviceConnection(wdcs.mAttributes, - wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) { + wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) { // change of connection state failed, bailout mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") .record(); @@ -763,35 +712,23 @@ public class AudioDeviceInventory { /*package*/ int setPreferredDevicesForStrategySync(int strategy, @NonNull List devices) { - final int status = setPreferredDevicesForStrategy(strategy, devices); + int status = AudioSystem.ERROR; + + try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { + AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( + "setPreferredDevicesForStrategySync, strategy: " + strategy + + " devices: " + devices)).printLog(TAG)); + status = mAudioSystem.setDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + } + if (status == AudioSystem.SUCCESS) { mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices); } return status; } - /*package*/ int setPreferredDevicesForStrategy(int strategy, - @NonNull List devices) { - int status = AudioSystem.ERROR; - try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { - AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( - "setPreferredDevicesForStrategy, strategy: " + strategy - + " devices: " + devices)).printLog(TAG)); - status = setDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); - } - return status; - } - /*package*/ int removePreferredDevicesForStrategySync(int strategy) { - final int status = removePreferredDevicesForStrategy(strategy); - if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy); - } - return status; - } - - /*package*/ int removePreferredDevicesForStrategy(int strategy) { int status = AudioSystem.ERROR; try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { @@ -799,9 +736,13 @@ public class AudioDeviceInventory { "removePreferredDevicesForStrategySync, strategy: " + strategy)).printLog(TAG)); - status = clearDevicesRoleForStrategy( + status = mAudioSystem.clearDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_PREFERRED); } + + if (status == AudioSystem.SUCCESS) { + mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy); + } return status; } @@ -816,7 +757,7 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( "setDeviceAsNonDefaultForStrategySync, strategy: " + strategy + " device: " + device)).printLog(TAG)); - status = addDevicesRoleForStrategy( + status = mAudioSystem.setDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); } @@ -838,7 +779,7 @@ public class AudioDeviceInventory { "removeDeviceAsNonDefaultForStrategySync, strategy: " + strategy + " devices: " + device)).printLog(TAG)); - status = removeDevicesRoleForStrategy( + status = mAudioSystem.removeDevicesRoleForStrategy( strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); } @@ -871,70 +812,33 @@ public class AudioDeviceInventory { /*package*/ int setPreferredDevicesForCapturePresetSync( int capturePreset, @NonNull List devices) { - final int status = setPreferredDevicesForCapturePreset(capturePreset, devices); + int status = AudioSystem.ERROR; + + try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { + status = mAudioSystem.setDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + } + if (status == AudioSystem.SUCCESS) { mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices); } return status; } - private int setPreferredDevicesForCapturePreset( - int capturePreset, @NonNull List devices) { - int status = AudioSystem.ERROR; - try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { - status = setDevicesRoleForCapturePreset( - capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); - } - return status; - } - /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) { - final int status = clearPreferredDevicesForCapturePreset(capturePreset); + int status = AudioSystem.ERROR; + + try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { + status = mAudioSystem.clearDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED); + } + if (status == AudioSystem.SUCCESS) { mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset); } return status; } - private int clearPreferredDevicesForCapturePreset(int capturePreset) { - int status = AudioSystem.ERROR; - - try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { - status = clearDevicesRoleForCapturePreset( - capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED); - } - return status; - } - - private int addDevicesRoleForCapturePreset(int capturePreset, int role, - @NonNull List devices) { - return addDevicesRole(mAppliedPresetRoles, (p, r, d) -> { - return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); - }, capturePreset, role, devices); - } - - private int removeDevicesRoleForCapturePreset(int capturePreset, int role, - @NonNull List devices) { - return removeDevicesRole(mAppliedPresetRoles, (p, r, d) -> { - return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); - }, capturePreset, role, devices); - } - - private int setDevicesRoleForCapturePreset(int capturePreset, int role, - @NonNull List devices) { - return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> { - return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); - }, (p, r, d) -> { - return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); - }, capturePreset, role, devices); - } - - private int clearDevicesRoleForCapturePreset(int capturePreset, int role) { - return clearDevicesRole(mAppliedPresetRoles, (p, r, d) -> { - return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); - }, capturePreset, role); - } - /*package*/ void registerCapturePresetDevicesRoleDispatcher( @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { mDevRoleCapturePresetDispatchers.register(dispatcher); @@ -945,208 +849,7 @@ public class AudioDeviceInventory { mDevRoleCapturePresetDispatchers.unregister(dispatcher); } - private int addDevicesRoleForStrategy(int strategy, int role, - @NonNull List devices) { - return addDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.setDevicesRoleForStrategy(s, r, d); - }, strategy, role, devices); - } - - private int removeDevicesRoleForStrategy(int strategy, int role, - @NonNull List devices) { - return removeDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); - }, strategy, role, devices); - } - - private int setDevicesRoleForStrategy(int strategy, int role, - @NonNull List devices) { - return setDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.setDevicesRoleForStrategy(s, r, d); - }, (s, r, d) -> { - return mAudioSystem.clearDevicesRoleForStrategy(s, r); - }, strategy, role, devices); - } - - private int clearDevicesRoleForStrategy(int strategy, int role) { - return clearDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.clearDevicesRoleForStrategy(s, r); - }, strategy, role); - } - - //------------------------------------------------------------ - // Cache for applied roles for strategies and devices. The cache avoids reapplying the - // same list of devices for a given role and strategy and the corresponding systematic - // redundant work in audio policy manager and audio flinger. - // The key is the pair and the value is the current list of devices. - - private final ArrayMap, List> - mAppliedStrategyRoles = new ArrayMap<>(); - - // Cache for applied roles for capture presets and devices. The cache avoids reapplying the - // same list of devices for a given role and capture preset and the corresponding systematic - // redundant work in audio policy manager and audio flinger. - // The key is the pair and the value is the current list of devices. - private final ArrayMap, List> - mAppliedPresetRoles = new ArrayMap<>(); - - interface AudioSystemInterface { - int deviceRoleAction(int usecase, int role, @Nullable List devices); - } - - private int addDevicesRole( - ArrayMap, List> rolesMap, - AudioSystemInterface asi, - int useCase, int role, @NonNull List devices) { - synchronized (rolesMap) { - Pair key = new Pair<>(useCase, role); - List roleDevices = new ArrayList<>(); - List appliedDevices = new ArrayList<>(); - - if (rolesMap.containsKey(key)) { - roleDevices = rolesMap.get(key); - for (AudioDeviceAttributes device : devices) { - if (!roleDevices.contains(device)) { - appliedDevices.add(device); - } - } - } else { - appliedDevices.addAll(devices); - } - if (appliedDevices.isEmpty()) { - return AudioSystem.SUCCESS; - } - final int status = asi.deviceRoleAction(useCase, role, appliedDevices); - if (status == AudioSystem.SUCCESS) { - roleDevices.addAll(appliedDevices); - rolesMap.put(key, roleDevices); - } - return status; - } - } - - private int removeDevicesRole( - ArrayMap, List> rolesMap, - AudioSystemInterface asi, - int useCase, int role, @NonNull List devices) { - synchronized (rolesMap) { - Pair key = new Pair<>(useCase, role); - if (!rolesMap.containsKey(key)) { - return AudioSystem.SUCCESS; - } - List roleDevices = rolesMap.get(key); - List appliedDevices = new ArrayList<>(); - for (AudioDeviceAttributes device : devices) { - if (roleDevices.contains(device)) { - appliedDevices.add(device); - } - } - if (appliedDevices.isEmpty()) { - return AudioSystem.SUCCESS; - } - final int status = asi.deviceRoleAction(useCase, role, appliedDevices); - if (status == AudioSystem.SUCCESS) { - roleDevices.removeAll(appliedDevices); - if (roleDevices.isEmpty()) { - rolesMap.remove(key); - } else { - rolesMap.put(key, roleDevices); - } - } - return status; - } - } - - private int setDevicesRole( - ArrayMap, List> rolesMap, - AudioSystemInterface addOp, - AudioSystemInterface clearOp, - int useCase, int role, @NonNull List devices) { - synchronized (rolesMap) { - Pair key = new Pair<>(useCase, role); - List roleDevices = new ArrayList<>(); - List appliedDevices = new ArrayList<>(); - - if (rolesMap.containsKey(key)) { - roleDevices = rolesMap.get(key); - boolean equal = false; - if (roleDevices.size() == devices.size()) { - roleDevices.retainAll(devices); - equal = roleDevices.size() == devices.size(); - } - if (!equal) { - clearOp.deviceRoleAction(useCase, role, null); - roleDevices.clear(); - appliedDevices.addAll(devices); - } - } else { - appliedDevices.addAll(devices); - } - if (appliedDevices.isEmpty()) { - return AudioSystem.SUCCESS; - } - final int status = addOp.deviceRoleAction(useCase, role, appliedDevices); - if (status == AudioSystem.SUCCESS) { - roleDevices.addAll(appliedDevices); - rolesMap.put(key, roleDevices); - } - return status; - } - } - - private int clearDevicesRole( - ArrayMap, List> rolesMap, - AudioSystemInterface asi, int useCase, int role) { - synchronized (rolesMap) { - Pair key = new Pair<>(useCase, role); - if (!rolesMap.containsKey(key)) { - return AudioSystem.SUCCESS; - } - final int status = asi.deviceRoleAction(useCase, role, null); - if (status == AudioSystem.SUCCESS) { - rolesMap.remove(key); - } - return status; - } - } - - @GuardedBy("mDevicesLock") - private void purgeDevicesRoles_l() { - purgeRoles(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); }); - purgeRoles(mAppliedPresetRoles, (p, r, d) -> { - return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); }); - } - - @GuardedBy("mDevicesLock") - private void purgeRoles( - ArrayMap, List> rolesMap, - AudioSystemInterface asi) { - synchronized (rolesMap) { - Iterator, List>> itRole = - rolesMap.entrySet().iterator(); - while (itRole.hasNext()) { - Map.Entry, List> entry = - itRole.next(); - Pair keyRole = entry.getKey(); - Iterator itDev = rolesMap.get(keyRole).iterator(); - while (itDev.hasNext()) { - AudioDeviceAttributes ada = itDev.next(); - final String devKey = DeviceInfo.makeDeviceListKey(ada.getInternalType(), - ada.getAddress()); - if (mConnectedDevices.get(devKey) == null) { - asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada)); - itDev.remove(); - } - } - if (rolesMap.get(keyRole).isEmpty()) { - itRole.remove(); - } - } - } - } - -//----------------------------------------------------------------------- + //----------------------------------------------------------------------- /** * Check if a device is in the list of connected devices @@ -1168,11 +871,10 @@ public class AudioDeviceInventory { * @param connect true if connection * @param isForTesting if true, not calling AudioSystem for the connection as this is * just for testing - * @param btDevice the corresponding Bluetooth device when relevant. * @return false if an error was reported by AudioSystem */ /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect, - boolean isForTesting, @Nullable BluetoothDevice btDevice) { + boolean isForTesting) { int device = attributes.getInternalType(); String address = attributes.getAddress(); String deviceName = attributes.getName(); @@ -1187,7 +889,6 @@ public class AudioDeviceInventory { .set(MediaMetrics.Property.MODE, connect ? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT) .set(MediaMetrics.Property.NAME, deviceName); - boolean status = false; synchronized (mDevicesLock) { final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); if (AudioService.DEBUG_DEVICES) { @@ -1215,33 +916,24 @@ public class AudioDeviceInventory { .record(); return false; } - mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address)); + mConnectedDevices.put(deviceKey, new DeviceInfo( + device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); - status = true; + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); + return true; } else if (!connect && isConnected) { mAudioSystem.setDeviceConnectionState(attributes, AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); - status = true; - } - if (status) { - if (AudioSystem.isBluetoothScoDevice(device)) { - updateBluetoothPreferredModes_l(); - if (connect) { - mDeviceBroker.postNotifyPreferredAudioProfileApplied(btDevice); - } else { - purgeDevicesRoles_l(); - } - } mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); - } else { - Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey - + ", deviceSpec=" + di + ", connect=" + connect); - mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); + return true; } + Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey + + ", deviceSpec=" + di + ", connect=" + connect); } - return status; + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); + return false; } @@ -1450,20 +1142,15 @@ public class AudioDeviceInventory { // Internal utilities @GuardedBy("mDevicesLock") - private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo, - String eventSource) { - final String address = btInfo.mDevice.getAddress(); - final String name = BtHelper.getName(btInfo.mDevice); - final int a2dpCodec = btInfo.mCodec; - + 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, true /*fromA2dp*/, 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 - AudioDeviceAttributes ada = new AudioDeviceAttributes( - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name); - final int res = mAudioSystem.setDeviceConnectionState(ada, + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name), AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec); // TODO: log in MediaMetrics once distinction between connection failure and @@ -1485,7 +1172,8 @@ public class AudioDeviceInventory { // The convention for head tracking sensors associated with A2DP devices is to // use a UUID derived from the MAC address as follows: // time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address - UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); + UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address)); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec, sensorUuid); final String diKey = di.getKey(); @@ -1496,206 +1184,6 @@ public class AudioDeviceInventory { mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/); - - updateBluetoothPreferredModes_l(); - mDeviceBroker.postNotifyPreferredAudioProfileApplied(btInfo.mDevice); - } - - static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER, - AudioSource.VOICE_RECOGNITION, AudioSource.VOICE_COMMUNICATION, - AudioSource.UNPROCESSED, AudioSource.VOICE_PERFORMANCE, AudioSource.HOTWORD}; - - // reflects system property persist.bluetooth.enable_dual_mode_audio - final boolean mBluetoothDualModeEnabled; - /** - * Goes over all connected Bluetooth devices and set the audio policy device role to DISABLED - * or not according to their own and other devices modes. - * The top priority is given to LE devices, then SCO ,then A2DP. - */ - @GuardedBy("mDevicesLock") - private void applyConnectedDevicesRoles_l() { - if (!mBluetoothDualModeEnabled) { - return; - } - DeviceInfo leOutDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET); - DeviceInfo leInDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET); - DeviceInfo a2dpDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET); - DeviceInfo scoOutDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET); - DeviceInfo scoInDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET); - boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled()); - boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled()) - || (leInDevice != null && leInDevice.isDuplexModeEnabled()); - AudioDeviceAttributes communicationDevice = - mDeviceBroker.mActiveCommunicationDevice == null - ? null : ((mDeviceBroker.isInCommunication() - && mDeviceBroker.mActiveCommunicationDevice != null) - ? new AudioDeviceAttributes(mDeviceBroker.mActiveCommunicationDevice) - : null); - - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, "applyConnectedDevicesRoles_l\n - leOutDevice: " + leOutDevice - + "\n - leInDevice: " + leInDevice - + "\n - a2dpDevice: " + a2dpDevice - + "\n - scoOutDevice: " + scoOutDevice - + "\n - scoInDevice: " + scoInDevice - + "\n - disableA2dp: " + disableA2dp - + ", disableSco: " + disableSco); - } - - for (DeviceInfo di : mConnectedDevices.values()) { - if (!AudioSystem.isBluetoothDevice(di.mDeviceType)) { - continue; - } - AudioDeviceAttributes ada = - new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, di.mDeviceName); - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, " + checking Device: " + ada); - } - if (ada.equalTypeAddress(communicationDevice)) { - continue; - } - - if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) { - for (AudioProductStrategy strategy : mStrategies) { - boolean disable = false; - if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) { - if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { - disable = disableSco || !di.isDuplexModeEnabled(); - } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { - disable = !di.isDuplexModeEnabled(); - } - } else { - if (AudioSystem.isBluetoothA2dpOutDevice(di.mDeviceType)) { - disable = disableA2dp || !di.isOutputOnlyModeEnabled(); - } else if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { - disable = disableSco || !di.isOutputOnlyModeEnabled(); - } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { - disable = !di.isOutputOnlyModeEnabled(); - } - } - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, " - strategy: " + strategy.getId() - + ", disable: " + disable); - } - if (disable) { - addDevicesRoleForStrategy(strategy.getId(), - AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); - } else { - removeDevicesRoleForStrategy(strategy.getId(), - AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); - } - } - } - if (AudioSystem.isBluetoothInDevice(di.mDeviceType)) { - for (int capturePreset : CAPTURE_PRESETS) { - boolean disable = false; - if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { - disable = disableSco || !di.isDuplexModeEnabled(); - } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { - disable = !di.isDuplexModeEnabled(); - } - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, " - capturePreset: " + capturePreset - + ", disable: " + disable); - } - if (disable) { - addDevicesRoleForCapturePreset(capturePreset, - AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); - } else { - removeDevicesRoleForCapturePreset(capturePreset, - AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); - } - } - } - } - } - - /* package */ void applyConnectedDevicesRoles() { - synchronized (mDevicesLock) { - applyConnectedDevicesRoles_l(); - } - } - - @GuardedBy("mDevicesLock") - int checkProfileIsConnected(int profile) { - switch (profile) { - case BluetoothProfile.HEADSET: - if (getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null - || getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) { - return profile; - } - break; - case BluetoothProfile.A2DP: - if (getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) { - return profile; - } - break; - case BluetoothProfile.LE_AUDIO: - case BluetoothProfile.LE_AUDIO_BROADCAST: - if (getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null - || getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) { - return profile; - } - break; - default: - break; - } - return 0; - } - - @GuardedBy("mDevicesLock") - private void updateBluetoothPreferredModes_l() { - if (!mBluetoothDualModeEnabled) { - return; - } - HashSet processedAddresses = new HashSet<>(0); - for (DeviceInfo di : mConnectedDevices.values()) { - if (!AudioSystem.isBluetoothDevice(di.mDeviceType) - || processedAddresses.contains(di.mDeviceAddress)) { - continue; - } - Bundle preferredProfiles = BtHelper.getPreferredAudioProfiles(di.mDeviceAddress); - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, "updateBluetoothPreferredModes_l processing device address: " - + di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles); - } - for (DeviceInfo di2 : mConnectedDevices.values()) { - if (!AudioSystem.isBluetoothDevice(di2.mDeviceType) - || !di.mDeviceAddress.equals(di2.mDeviceAddress)) { - continue; - } - int profile = BtHelper.getProfileFromType(di2.mDeviceType); - if (profile == 0) { - continue; - } - int preferredProfile = checkProfileIsConnected( - preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX)); - if (preferredProfile == profile || preferredProfile == 0) { - di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); - } else { - di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); - } - preferredProfile = checkProfileIsConnected( - preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY)); - if (preferredProfile == profile || preferredProfile == 0) { - di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); - } else { - di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); - } - } - processedAddresses.add(di.mDeviceAddress); - } - applyConnectedDevicesRoles_l(); } @GuardedBy("mDevicesLock") @@ -1743,9 +1231,6 @@ public class AudioDeviceInventory { // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/); mmi.record(); - - updateBluetoothPreferredModes_l(); - purgeDevicesRoles_l(); } @GuardedBy("mDevicesLock") @@ -1775,7 +1260,8 @@ public class AudioDeviceInventory { AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), - new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address)); + new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", + address, AudioSystem.AUDIO_FORMAT_DEFAULT)); } @GuardedBy("mDevicesLock") @@ -1801,7 +1287,8 @@ public class AudioDeviceInventory { AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address), - new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address)); + new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, + address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postApplyVolumeOnDevice(streamType, AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); @@ -1839,56 +1326,29 @@ public class AudioDeviceInventory { * @return true if a DEVICE_OUT_HEARING_AID is connected, false otherwise. */ boolean isHearingAidConnected() { - return getFirstConnectedDeviceOfTypes( - Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null; - } - - /** - * Returns a DeviceInfo for the fist connected device matching one of the supplied types - */ - private DeviceInfo getFirstConnectedDeviceOfTypes(Set internalTypes) { - List devices = getConnectedDevicesOfTypes(internalTypes); - return devices.isEmpty() ? null : devices.get(0); - } - - /** - * Returns a list of connected devices matching one one of the supplied types - */ - private List getConnectedDevicesOfTypes(Set internalTypes) { - ArrayList devices = new ArrayList<>(); synchronized (mDevicesLock) { for (DeviceInfo di : mConnectedDevices.values()) { - if (internalTypes.contains(di.mDeviceType)) { - devices.add(di); + if (di.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) { + return true; } } + return false; } - return devices; - } - - /* package */ AudioDeviceAttributes getDeviceOfType(int type) { - DeviceInfo di = getFirstConnectedDeviceOfTypes(Sets.newHashSet(type)); - return di == null ? null : new AudioDeviceAttributes( - di.mDeviceType, di.mDeviceAddress, di.mDeviceName); } @GuardedBy("mDevicesLock") - private void makeLeAudioDeviceAvailable( - AudioDeviceBroker.BtDeviceInfo btInfo, int streamType, String eventSource) { - final String address = btInfo.mDevice.getAddress(); - final String name = BtHelper.getName(btInfo.mDevice); - final int volumeIndex = btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10; - final int device = btInfo.mAudioSystemDevice; - + private void makeLeAudioDeviceAvailable(String address, String name, int streamType, + int volumeIndex, int device, String eventSource) { if (device != AudioSystem.DEVICE_NONE) { /* Audio Policy sees Le Audio similar to A2DP. Let's make sure * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set */ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); - AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name); - final int res = AudioSystem.setDeviceConnectionState(ada, - AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); + final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + device, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( "APM failed to make available LE Audio device addr=" + address @@ -1899,13 +1359,12 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( "LE Audio device addr=" + address + " now available").printLog(TAG)); } + // Reset LEA suspend state each time a new sink is connected mDeviceBroker.clearLeAudioSuspended(); - UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), - new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT, - sensorUuid)); + new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false); } @@ -1921,9 +1380,6 @@ public class AudioDeviceInventory { final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType); mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType); mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable"); - - updateBluetoothPreferredModes_l(); - mDeviceBroker.postNotifyPreferredAudioProfileApplied(btInfo.mDevice); } @GuardedBy("mDevicesLock") @@ -1948,9 +1404,6 @@ public class AudioDeviceInventory { } setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/); - - updateBluetoothPreferredModes_l(); - purgeDevicesRoles_l(); } @GuardedBy("mDevicesLock") @@ -2286,6 +1739,18 @@ public class AudioDeviceInventory { } } + /* package */ AudioDeviceAttributes getDeviceOfType(int type) { + synchronized (mDevicesLock) { + for (DeviceInfo di : mConnectedDevices.values()) { + if (di.mDeviceType == type) { + return new AudioDeviceAttributes( + di.mDeviceType, di.mDeviceAddress, di.mDeviceName); + } + } + } + return null; + } + //---------------------------------------------------------- // For tests only @@ -2296,12 +1761,10 @@ public class AudioDeviceInventory { */ @VisibleForTesting public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) { - for (DeviceInfo di : getConnectedDevicesOfTypes( - Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) { - if (di.mDeviceAddress.equals(device.getAddress())) { - return true; - } + final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + device.getAddress()); + synchronized (mDevicesLock) { + return (mConnectedDevices.get(key) != null); } - return false; } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 1142a8d64304c..7af7ed5fff652 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -435,7 +435,7 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, } /** - * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, List)} + * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, int[], String[])} * @param capturePreset * @param role * @param devicesToRemove @@ -447,19 +447,6 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove); } - /** - * Same as {@link AudioSystem#addDevicesRoleForCapturePreset(int, int, List)} - * @param capturePreset the capture preset to configure - * @param role the role of the devices - * @param devices the list of devices to be added as role for the given capture preset - * @return {@link #SUCCESS} if successfully add - */ - public int addDevicesRoleForCapturePreset( - int capturePreset, int role, @NonNull List devices) { - invalidateRoutingCache(); - return AudioSystem.addDevicesRoleForCapturePreset(capturePreset, role, devices); - } - /** * Same as {@link AudioSystem#} * @param capturePreset diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index e46c3cc4a2853..8c27c3ecfd872 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -33,7 +33,6 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.BluetoothProfileConnectionInfo; import android.os.Binder; -import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -151,12 +150,60 @@ public class BtHelper { } } + //---------------------------------------------------------------------- + /*package*/ static class BluetoothA2dpDeviceInfo { + private final @NonNull BluetoothDevice mBtDevice; + private final int mVolume; + private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; + + BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) { + this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT); + } + + BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) { + mBtDevice = btDevice; + mVolume = volume; + mCodec = codec; + } + + public @NonNull BluetoothDevice getBtDevice() { + return mBtDevice; + } + + public int getVolume() { + return mVolume; + } + + public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() { + return mCodec; + } + + // redefine equality op so we can match messages intended for this device + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (this == o) { + return true; + } + if (o instanceof BluetoothA2dpDeviceInfo) { + return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice()); + } + return false; + } + + + } + // A2DP device events /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0; + /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1; - /*package*/ static String deviceEventToString(int event) { + /*package*/ static String a2dpDeviceEventToString(int event) { switch (event) { case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE"; + case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE"; default: return new String("invalid event:" + event); } @@ -573,12 +620,11 @@ public class BtHelper { return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice); } - private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) { + private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) { if (btDevice == null) { return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""); } String address = btDevice.getAddress(); - String name = getName(btDevice); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } @@ -600,7 +646,7 @@ public class BtHelper { + " btClass: " + (btClass == null ? "Unknown" : btClass) + " nativeType: " + nativeType + " address: " + address); } - return new AudioDeviceAttributes(nativeType, address, name); + return new AudioDeviceAttributes(nativeType, address); } private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { @@ -609,9 +655,12 @@ public class BtHelper { } int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice); + String btDeviceName = getName(btDevice); boolean result = false; if (isActive) { - result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName), + isActive); } else { int[] outDeviceTypes = { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, @@ -620,14 +669,14 @@ public class BtHelper { }; for (int outDeviceType : outDeviceTypes) { result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( - outDeviceType, audioDevice.getAddress(), audioDevice.getName()), - isActive, btDevice); + outDeviceType, audioDevice.getAddress(), btDeviceName), + isActive); } } // handleDeviceConnection() && result to make sure the method get executed result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( - inDevice, audioDevice.getAddress(), audioDevice.getName()), - isActive, btDevice) && result; + inDevice, audioDevice.getAddress(), btDeviceName), + isActive) && result; return result; } @@ -924,30 +973,6 @@ public class BtHelper { } } - /*package */ static int getProfileFromType(int deviceType) { - if (AudioSystem.isBluetoothA2dpOutDevice(deviceType)) { - return BluetoothProfile.A2DP; - } else if (AudioSystem.isBluetoothScoDevice(deviceType)) { - return BluetoothProfile.HEADSET; - } else if (AudioSystem.isBluetoothLeDevice(deviceType)) { - return BluetoothProfile.LE_AUDIO; - } - return 0; // 0 is not a valid profile - } - - /*package */ static Bundle getPreferredAudioProfiles(String address) { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address)); - } - - /** - * Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices - * have been applied. - */ - public static void onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) { - BluetoothAdapter.getDefaultAdapter().notifyActiveDeviceChangeApplied(btDevice); - } - /** * Returns the string equivalent for the btDeviceClass class. */