Merge "AudioService: BT dual mode support" into udc-dev

This commit is contained in:
Eric Laurent
2023-05-02 15:06:55 +00:00
committed by Android (Google) Code Review
4 changed files with 513 additions and 184 deletions

View File

@@ -1237,6 +1237,9 @@ public class AudioSystem
public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
/** @hide */
public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
/** @hide */
public static final Set<Integer> DEVICE_IN_ALL_BLE_SET;
static {
DEVICE_IN_ALL_SET = new HashSet<>();
DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION);
@@ -1276,6 +1279,66 @@ 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 */

View File

@@ -88,14 +88,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
private final @NonNull AudioSystemAdapter mAudioSystem;
/** ID for Communication strategy retrieved form audio policy manager */
private int mCommunicationStrategyId = -1;
/*package*/ int mCommunicationStrategyId = -1;
/** ID for Accessibility strategy retrieved form audio policy manager */
private int mAccessibilityStrategyId = -1;
/** Active communication device reported by audio policy manager */
private AudioDeviceInfo mActiveCommunicationDevice;
/*package*/ AudioDeviceInfo mActiveCommunicationDevice;
/** Last preferred device set for communication strategy */
private AudioDeviceAttributes mPreferredCommunicationDevice;
@@ -755,6 +755,19 @@ 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) {
@@ -821,7 +834,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @param info struct with the (dis)connection information
*/
/*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
if (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
@@ -830,7 +843,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
.set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
.record();
synchronized (mDeviceStateLock) {
postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
postBluetoothDeviceConfigChange(createBtDeviceInfo(data, data.mNewDevice,
BluetoothProfile.STATE_CONNECTED));
}
} else {
synchronized (mDeviceStateLock) {
@@ -1064,8 +1078,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
new AudioModeInfo(mode, pid, uid));
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
/*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
}
/*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
@@ -1322,6 +1336,10 @@ 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
@@ -1397,9 +1415,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) {
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes,
boolean connect, @Nullable BluetoothDevice btDevice) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/);
return mDeviceInventory.handleDeviceConnection(
attributes, connect, false /*for test*/, btDevice);
}
}
@@ -1640,13 +1660,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
(String) msg.obj, msg.arg1);
}
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
synchronized (mDeviceStateLock) {
final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
mDeviceInventory.onBluetoothDeviceConfigChange(
(BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
}
break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
@@ -1810,6 +1827,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_IL_SET_LEAUDIO_SUSPENDED: {
setLeAudioSuspended((msg.arg1 == 1), false /*internal*/, (String) msg.obj);
} 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);
}
@@ -1845,7 +1866,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_A2DP_DEVICE_CONFIG_CHANGE = 11;
private static final int MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE = 11;
private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12;
private static final int MSG_REPORT_NEW_ROUTES = 13;
@@ -1887,13 +1908,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_IL_SET_A2DP_SUSPENDED = 50;
private static final int MSG_IL_SET_LEAUDIO_SUSPENDED = 51;
private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52;
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_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1985,7 +2008,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_A2DP_DEVICE_CONFIG_CHANGE:
case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
if (sLastDeviceConnectMsgTime >= time) {
// add a little delay to make sure messages are ordered as expected
time = sLastDeviceConnectMsgTime + 30;
@@ -2005,7 +2028,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_A2DP_DEVICE_CONFIG_CHANGE);
MESSAGES_MUTE_MUSIC.add(MSG_L_BLUETOOTH_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);
}
@@ -2026,7 +2049,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_A2DP_DEVICE_CONFIG_CHANGE)
|| message == MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE)
&& AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
&& hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
@@ -2173,6 +2196,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId);
mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId);
}
mDeviceInventory.applyConnectedDevicesRoles();
} else {
mDeviceInventory.setPreferredDevicesForStrategy(
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));

View File

@@ -34,11 +34,15 @@ 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;
@@ -50,6 +54,8 @@ 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;
@@ -179,6 +185,8 @@ public class AudioDeviceInventory {
final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
final List<AudioProductStrategy> mStrategies;
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
this(broker, AudioSystemAdapter.getDefaultAdapter());
}
@@ -193,8 +201,10 @@ public class AudioDeviceInventory {
@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;
}
@@ -211,8 +221,13 @@ 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<String> mDisabledModes = new ArraySet(0);
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
int deviceCodecFormat, UUID sensorUuid) {
int deviceCodecFormat, @Nullable UUID sensorUuid) {
mDeviceType = deviceType;
mDeviceName = deviceName == null ? "" : deviceName;
mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
@@ -220,11 +235,31 @@ 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)
@@ -232,7 +267,8 @@ public class AudioDeviceInventory {
+ ") name:" + mDeviceName
+ " addr:" + mDeviceAddress
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
+ " sensorUuid: " + Objects.toString(mSensorUuid) + "]";
+ " sensorUuid: " + Objects.toString(mSensorUuid)
+ " disabled modes: " + mDisabledModes + "]";
}
@NonNull String getKey() {
@@ -317,6 +353,7 @@ public class AudioDeviceInventory {
di.mDeviceCodecFormat);
}
mAppliedStrategyRoles.clear();
applyConnectedDevicesRoles_l();
}
synchronized (mPreferredDevices) {
mPreferredDevices.forEach((strategy, devices) -> {
@@ -397,8 +434,7 @@ public class AudioDeviceInventory {
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
"onSetBtActiveDevice");
}
makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
"onSetBtActiveDevice", btInfo.mCodec);
makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice");
}
break;
case BluetoothProfile.HEARING_AID:
@@ -414,10 +450,7 @@ public class AudioDeviceInventory {
if (switchToUnavailable) {
makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
} else if (switchToAvailable) {
makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
btInfo.mAudioSystemDevice,
"onSetBtActiveDevice");
makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice");
}
break;
default: throw new IllegalArgumentException("Invalid profile "
@@ -428,30 +461,30 @@ public class AudioDeviceInventory {
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void onBluetoothA2dpDeviceConfigChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
/*package*/ void onBluetoothDeviceConfigChange(
@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
+ "onBluetoothA2dpDeviceConfigChange")
.set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event));
+ "onBluetoothDeviceConfigChange")
.set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
final BluetoothDevice btDevice = btInfo.getBtDevice();
final BluetoothDevice btDevice = btInfo.mDevice;
if (btDevice == null) {
mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record();
return;
}
if (AudioService.DEBUG_DEVICES) {
Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
}
int a2dpVolume = btInfo.getVolume();
@AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec();
int volume = btInfo.mVolume;
@AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec;
String address = btDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"onBluetoothA2dpDeviceConfigChange addr=" + address
+ " event=" + BtHelper.a2dpDeviceEventToString(event)));
"onBluetoothDeviceConfigChange addr=" + address
+ " event=" + BtHelper.deviceEventToString(event)));
synchronized (mDevicesLock) {
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
@@ -466,53 +499,53 @@ 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 onBluetoothA2dpDeviceConfigChange");
Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange");
mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
return;
}
mmi.set(MediaMetrics.Property.ADDRESS, address)
.set(MediaMetrics.Property.ENCODING,
AudioSystem.audioFormatToString(a2dpCodec))
.set(MediaMetrics.Property.INDEX, a2dpVolume)
AudioSystem.audioFormatToString(audioCodec))
.set(MediaMetrics.Property.INDEX, volume)
.set(MediaMetrics.Property.NAME, di.mDeviceName);
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");
}
} 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));
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);
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));
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 (!a2dpCodecChange) {
updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/);
}
}
}
mmi.record();
@@ -595,7 +628,7 @@ public class AudioDeviceInventory {
}
if (!handleDeviceConnection(wdcs.mAttributes,
wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) {
wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) {
// change of connection state failed, bailout
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
.record();
@@ -1134,10 +1167,11 @@ 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) {
boolean isForTesting, @Nullable BluetoothDevice btDevice) {
int device = attributes.getInternalType();
String address = attributes.getAddress();
String deviceName = attributes.getName();
@@ -1152,6 +1186,7 @@ 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) {
@@ -1179,25 +1214,31 @@ public class AudioDeviceInventory {
.record();
return false;
}
mConnectedDevices.put(deviceKey, new DeviceInfo(
device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
return true;
status = 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);
purgeDevicesRoles_l();
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
return true;
status = true;
}
if (status) {
if (AudioSystem.isBluetoothScoDevice(device)) {
updateBluetoothPreferredModes_l(connect ? btDevice : null /*connectedDevice*/);
if (!connect) {
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();
}
Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
+ ", deviceSpec=" + di + ", connect=" + connect);
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
return false;
return status;
}
@@ -1406,15 +1447,20 @@ public class AudioDeviceInventory {
// Internal utilities
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
int a2dpCodec) {
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;
// 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
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
// TODO: log in MediaMetrics once distinction between connection failure and
@@ -1436,8 +1482,7 @@ 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(
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
address, a2dpCodec, sensorUuid);
final String diKey = di.getKey();
@@ -1448,6 +1493,208 @@ public class AudioDeviceInventory {
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
}
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(BluetoothDevice connectedDevice) {
if (!mBluetoothDualModeEnabled) {
return;
}
HashSet<String> 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();
if (connectedDevice != null) {
mDeviceBroker.postNotifyPreferredAudioProfileApplied(connectedDevice);
}
}
@GuardedBy("mDevicesLock")
@@ -1495,6 +1742,7 @@ public class AudioDeviceInventory {
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
mmi.record();
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
purgeDevicesRoles_l();
}
@@ -1525,8 +1773,7 @@ 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, AudioSystem.AUDIO_FORMAT_DEFAULT));
new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
}
@GuardedBy("mDevicesLock")
@@ -1552,8 +1799,7 @@ 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, AudioSystem.AUDIO_FORMAT_DEFAULT));
new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address));
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postApplyVolumeOnDevice(streamType,
AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
@@ -1591,29 +1837,56 @@ 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 first connected device matching one of the supplied types
*/
private DeviceInfo getFirstConnectedDeviceOfTypes(Set<Integer> internalTypes) {
List<DeviceInfo> devices = getConnectedDevicesOfTypes(internalTypes);
return devices.isEmpty() ? null : devices.get(0);
}
/**
* Returns a list of connected devices matching one of the supplied types
*/
private List<DeviceInfo> getConnectedDevicesOfTypes(Set<Integer> internalTypes) {
ArrayList<DeviceInfo> devices = new ArrayList<>();
synchronized (mDevicesLock) {
for (DeviceInfo di : mConnectedDevices.values()) {
if (di.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
return true;
if (internalTypes.contains(di.mDeviceType)) {
devices.add(di);
}
}
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(String address, String name, int streamType,
int volumeIndex, int device, String eventSource) {
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;
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);
final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
device, address, name),
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
final int res = AudioSystem.setDeviceConnectionState(ada,
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
@@ -1624,12 +1897,13 @@ 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));
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT,
sensorUuid));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
}
@@ -1645,6 +1919,8 @@ public class AudioDeviceInventory {
final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
}
@GuardedBy("mDevicesLock")
@@ -1669,6 +1945,7 @@ public class AudioDeviceInventory {
}
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
purgeDevicesRoles_l();
}
@@ -2005,18 +2282,6 @@ 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
@@ -2027,10 +2292,12 @@ public class AudioDeviceInventory {
*/
@VisibleForTesting
public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
device.getAddress());
synchronized (mDevicesLock) {
return (mConnectedDevices.get(key) != null);
for (DeviceInfo di : getConnectedDevicesOfTypes(
Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
if (di.mDeviceAddress.equals(device.getAddress())) {
return true;
}
}
return false;
}
}

View File

@@ -33,6 +33,7 @@ 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;
@@ -150,60 +151,12 @@ 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 a2dpDeviceEventToString(int event) {
/*package*/ static String deviceEventToString(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);
}
@@ -620,11 +573,12 @@ public class BtHelper {
return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
}
private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
private static 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 = "";
}
@@ -646,7 +600,7 @@ public class BtHelper {
+ " btClass: " + (btClass == null ? "Unknown" : btClass)
+ " nativeType: " + nativeType + " address: " + address);
}
return new AudioDeviceAttributes(nativeType, address);
return new AudioDeviceAttributes(nativeType, address, name);
}
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
@@ -655,12 +609,9 @@ 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(new AudioDeviceAttributes(
audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName),
isActive);
result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
} else {
int[] outDeviceTypes = {
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
@@ -669,14 +620,14 @@ public class BtHelper {
};
for (int outDeviceType : outDeviceTypes) {
result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
outDeviceType, audioDevice.getAddress(), btDeviceName),
isActive);
outDeviceType, audioDevice.getAddress(), audioDevice.getName()),
isActive, btDevice);
}
}
// handleDeviceConnection() && result to make sure the method get executed
result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
inDevice, audioDevice.getAddress(), btDeviceName),
isActive) && result;
inDevice, audioDevice.getAddress(), audioDevice.getName()),
isActive, btDevice) && result;
return result;
}
@@ -973,6 +924,30 @@ 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.
*/