Merge "AudioService: BT dual mode support" into udc-dev am: b44e83134d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22887817 Change-Id: I1f20170d79c10fc6fdd66c13952019edce244c96 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -1237,6 +1237,9 @@ public class AudioSystem
|
|||||||
public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
|
public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
|
public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
|
||||||
|
/** @hide */
|
||||||
|
public static final Set<Integer> DEVICE_IN_ALL_BLE_SET;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DEVICE_IN_ALL_SET = new HashSet<>();
|
DEVICE_IN_ALL_SET = new HashSet<>();
|
||||||
DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION);
|
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_ACCESSORY);
|
||||||
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE);
|
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE);
|
||||||
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
|
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 */
|
/** @hide */
|
||||||
|
|||||||
@@ -88,14 +88,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
private final @NonNull AudioSystemAdapter mAudioSystem;
|
private final @NonNull AudioSystemAdapter mAudioSystem;
|
||||||
|
|
||||||
/** ID for Communication strategy retrieved form audio policy manager */
|
/** 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 */
|
/** ID for Accessibility strategy retrieved form audio policy manager */
|
||||||
private int mAccessibilityStrategyId = -1;
|
private int mAccessibilityStrategyId = -1;
|
||||||
|
|
||||||
|
|
||||||
/** Active communication device reported by audio policy manager */
|
/** Active communication device reported by audio policy manager */
|
||||||
private AudioDeviceInfo mActiveCommunicationDevice;
|
/*package*/ AudioDeviceInfo mActiveCommunicationDevice;
|
||||||
/** Last preferred device set for communication strategy */
|
/** Last preferred device set for communication strategy */
|
||||||
private AudioDeviceAttributes mPreferredCommunicationDevice;
|
private AudioDeviceAttributes mPreferredCommunicationDevice;
|
||||||
|
|
||||||
@@ -755,6 +755,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
mIsLeOutput = false;
|
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
|
// redefine equality op so we can match messages intended for this device
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
@@ -821,7 +834,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
* @param info struct with the (dis)connection information
|
* @param info struct with the (dis)connection information
|
||||||
*/
|
*/
|
||||||
/*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
|
/*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
|
||||||
if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
|
if (data.mPreviousDevice != null
|
||||||
&& data.mPreviousDevice.equals(data.mNewDevice)) {
|
&& data.mPreviousDevice.equals(data.mNewDevice)) {
|
||||||
final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
|
final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
|
||||||
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
|
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())
|
.set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
|
||||||
.record();
|
.record();
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mDeviceStateLock) {
|
||||||
postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
|
postBluetoothDeviceConfigChange(createBtDeviceInfo(data, data.mNewDevice,
|
||||||
|
BluetoothProfile.STATE_CONNECTED));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mDeviceStateLock) {
|
||||||
@@ -1064,8 +1078,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
new AudioModeInfo(mode, pid, uid));
|
new AudioModeInfo(mode, pid, uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
|
/*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
|
||||||
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
|
sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
|
/*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);
|
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 {
|
/*package*/ static final class CommunicationDeviceInfo {
|
||||||
final @NonNull IBinder mCb; // Identifies the requesting client for death handler
|
final @NonNull IBinder mCb; // Identifies the requesting client for death handler
|
||||||
final int mPid; // Requester process ID
|
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) {
|
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);
|
(String) msg.obj, msg.arg1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
|
case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
|
||||||
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
|
|
||||||
synchronized (mDeviceStateLock) {
|
synchronized (mDeviceStateLock) {
|
||||||
final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
|
mDeviceInventory.onBluetoothDeviceConfigChange(
|
||||||
mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
|
(BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
|
||||||
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
|
|
||||||
BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
|
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
|
||||||
@@ -1810,6 +1827,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
case MSG_IL_SET_LEAUDIO_SUSPENDED: {
|
case MSG_IL_SET_LEAUDIO_SUSPENDED: {
|
||||||
setLeAudioSuspended((msg.arg1 == 1), false /*internal*/, (String) msg.obj);
|
setLeAudioSuspended((msg.arg1 == 1), false /*internal*/, (String) msg.obj);
|
||||||
} break;
|
} break;
|
||||||
|
case MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED: {
|
||||||
|
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
|
||||||
|
BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
Log.wtf(TAG, "Invalid message " + msg.what);
|
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;
|
private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
|
||||||
|
|
||||||
// process change of A2DP device configuration, obj is BluetoothDevice
|
// 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_BROADCAST_AUDIO_BECOMING_NOISY = 12;
|
||||||
private static final int MSG_REPORT_NEW_ROUTES = 13;
|
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_A2DP_SUSPENDED = 50;
|
||||||
private static final int MSG_IL_SET_LEAUDIO_SUSPENDED = 51;
|
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) {
|
private static boolean isMessageHandledUnderWakelock(int msgId) {
|
||||||
switch(msgId) {
|
switch(msgId) {
|
||||||
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
|
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
|
||||||
case MSG_L_SET_BT_ACTIVE_DEVICE:
|
case MSG_L_SET_BT_ACTIVE_DEVICE:
|
||||||
case MSG_IL_BTA2DP_TIMEOUT:
|
case MSG_IL_BTA2DP_TIMEOUT:
|
||||||
case MSG_IL_BTLEAUDIO_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_TOGGLE_HDMI:
|
||||||
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
|
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
|
||||||
case MSG_L_HEARING_AID_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_L_SET_WIRED_DEVICE_CONNECTION_STATE:
|
||||||
case MSG_IL_BTA2DP_TIMEOUT:
|
case MSG_IL_BTA2DP_TIMEOUT:
|
||||||
case MSG_IL_BTLEAUDIO_TIMEOUT:
|
case MSG_IL_BTLEAUDIO_TIMEOUT:
|
||||||
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
|
case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
|
||||||
if (sLastDeviceConnectMsgTime >= time) {
|
if (sLastDeviceConnectMsgTime >= time) {
|
||||||
// add a little delay to make sure messages are ordered as expected
|
// add a little delay to make sure messages are ordered as expected
|
||||||
time = sLastDeviceConnectMsgTime + 30;
|
time = sLastDeviceConnectMsgTime + 30;
|
||||||
@@ -2005,7 +2028,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
static {
|
static {
|
||||||
MESSAGES_MUTE_MUSIC = new HashSet<>();
|
MESSAGES_MUTE_MUSIC = new HashSet<>();
|
||||||
MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE);
|
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_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT);
|
||||||
MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE);
|
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.
|
// Do not mute on bluetooth event if music is playing on a wired headset.
|
||||||
if ((message == MSG_L_SET_BT_ACTIVE_DEVICE
|
if ((message == MSG_L_SET_BT_ACTIVE_DEVICE
|
||||||
|| message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT
|
|| 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)
|
&& AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
|
||||||
&& hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
|
&& hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
|
||||||
mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
|
mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
|
||||||
@@ -2173,6 +2196,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId);
|
mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId);
|
||||||
mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId);
|
mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId);
|
||||||
}
|
}
|
||||||
|
mDeviceInventory.applyConnectedDevicesRoles();
|
||||||
} else {
|
} else {
|
||||||
mDeviceInventory.setPreferredDevicesForStrategy(
|
mDeviceInventory.setPreferredDevicesForStrategy(
|
||||||
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
|
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
|
||||||
|
|||||||
@@ -34,11 +34,15 @@ import android.media.ICapturePresetDevicesRoleDispatcher;
|
|||||||
import android.media.IStrategyNonDefaultDevicesDispatcher;
|
import android.media.IStrategyNonDefaultDevicesDispatcher;
|
||||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||||
import android.media.MediaMetrics;
|
import android.media.MediaMetrics;
|
||||||
|
import android.media.MediaRecorder.AudioSource;
|
||||||
|
import android.media.audiopolicy.AudioProductStrategy;
|
||||||
import android.media.permission.ClearCallingIdentityContext;
|
import android.media.permission.ClearCallingIdentityContext;
|
||||||
import android.media.permission.SafeCloseable;
|
import android.media.permission.SafeCloseable;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.RemoteCallbackList;
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
@@ -50,6 +54,8 @@ import com.android.internal.annotations.GuardedBy;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.utils.EventLogger;
|
import com.android.server.utils.EventLogger;
|
||||||
|
|
||||||
|
import com.google.android.collect.Sets;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -179,6 +185,8 @@ public class AudioDeviceInventory {
|
|||||||
final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
|
final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
|
||||||
new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
|
new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
|
||||||
|
|
||||||
|
final List<AudioProductStrategy> mStrategies;
|
||||||
|
|
||||||
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
|
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
|
||||||
this(broker, AudioSystemAdapter.getDefaultAdapter());
|
this(broker, AudioSystemAdapter.getDefaultAdapter());
|
||||||
}
|
}
|
||||||
@@ -193,8 +201,10 @@ public class AudioDeviceInventory {
|
|||||||
@Nullable AudioSystemAdapter audioSystem) {
|
@Nullable AudioSystemAdapter audioSystem) {
|
||||||
mDeviceBroker = broker;
|
mDeviceBroker = broker;
|
||||||
mAudioSystem = audioSystem;
|
mAudioSystem = audioSystem;
|
||||||
|
mStrategies = AudioProductStrategy.getAudioProductStrategies();
|
||||||
|
mBluetoothDualModeEnabled = SystemProperties.getBoolean(
|
||||||
|
"persist.bluetooth.enable_dual_mode_audio", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) {
|
/*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) {
|
||||||
mDeviceBroker = broker;
|
mDeviceBroker = broker;
|
||||||
}
|
}
|
||||||
@@ -211,8 +221,13 @@ public class AudioDeviceInventory {
|
|||||||
int mDeviceCodecFormat;
|
int mDeviceCodecFormat;
|
||||||
final UUID mSensorUuid;
|
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,
|
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
|
||||||
int deviceCodecFormat, UUID sensorUuid) {
|
int deviceCodecFormat, @Nullable UUID sensorUuid) {
|
||||||
mDeviceType = deviceType;
|
mDeviceType = deviceType;
|
||||||
mDeviceName = deviceName == null ? "" : deviceName;
|
mDeviceName = deviceName == null ? "" : deviceName;
|
||||||
mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
|
mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
|
||||||
@@ -220,11 +235,31 @@ public class AudioDeviceInventory {
|
|||||||
mSensorUuid = sensorUuid;
|
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,
|
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
|
||||||
int deviceCodecFormat) {
|
int deviceCodecFormat) {
|
||||||
this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
|
this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
|
||||||
|
this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
|
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
|
||||||
@@ -232,7 +267,8 @@ public class AudioDeviceInventory {
|
|||||||
+ ") name:" + mDeviceName
|
+ ") name:" + mDeviceName
|
||||||
+ " addr:" + mDeviceAddress
|
+ " addr:" + mDeviceAddress
|
||||||
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
|
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
|
||||||
+ " sensorUuid: " + Objects.toString(mSensorUuid) + "]";
|
+ " sensorUuid: " + Objects.toString(mSensorUuid)
|
||||||
|
+ " disabled modes: " + mDisabledModes + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull String getKey() {
|
@NonNull String getKey() {
|
||||||
@@ -317,6 +353,7 @@ public class AudioDeviceInventory {
|
|||||||
di.mDeviceCodecFormat);
|
di.mDeviceCodecFormat);
|
||||||
}
|
}
|
||||||
mAppliedStrategyRoles.clear();
|
mAppliedStrategyRoles.clear();
|
||||||
|
applyConnectedDevicesRoles_l();
|
||||||
}
|
}
|
||||||
synchronized (mPreferredDevices) {
|
synchronized (mPreferredDevices) {
|
||||||
mPreferredDevices.forEach((strategy, devices) -> {
|
mPreferredDevices.forEach((strategy, devices) -> {
|
||||||
@@ -397,8 +434,7 @@ public class AudioDeviceInventory {
|
|||||||
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
|
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
|
||||||
"onSetBtActiveDevice");
|
"onSetBtActiveDevice");
|
||||||
}
|
}
|
||||||
makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
|
makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice");
|
||||||
"onSetBtActiveDevice", btInfo.mCodec);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BluetoothProfile.HEARING_AID:
|
case BluetoothProfile.HEARING_AID:
|
||||||
@@ -414,10 +450,7 @@ public class AudioDeviceInventory {
|
|||||||
if (switchToUnavailable) {
|
if (switchToUnavailable) {
|
||||||
makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
|
makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
|
||||||
} else if (switchToAvailable) {
|
} else if (switchToAvailable) {
|
||||||
makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
|
makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice");
|
||||||
streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
|
|
||||||
btInfo.mAudioSystemDevice,
|
|
||||||
"onSetBtActiveDevice");
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: throw new IllegalArgumentException("Invalid profile "
|
default: throw new IllegalArgumentException("Invalid profile "
|
||||||
@@ -428,30 +461,30 @@ public class AudioDeviceInventory {
|
|||||||
|
|
||||||
|
|
||||||
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
|
||||||
/*package*/ void onBluetoothA2dpDeviceConfigChange(
|
/*package*/ void onBluetoothDeviceConfigChange(
|
||||||
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
|
@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) {
|
||||||
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
|
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
|
||||||
+ "onBluetoothA2dpDeviceConfigChange")
|
+ "onBluetoothDeviceConfigChange")
|
||||||
.set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event));
|
.set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
|
||||||
|
|
||||||
final BluetoothDevice btDevice = btInfo.getBtDevice();
|
final BluetoothDevice btDevice = btInfo.mDevice;
|
||||||
if (btDevice == null) {
|
if (btDevice == null) {
|
||||||
mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record();
|
mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (AudioService.DEBUG_DEVICES) {
|
if (AudioService.DEBUG_DEVICES) {
|
||||||
Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
|
Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
|
||||||
}
|
}
|
||||||
int a2dpVolume = btInfo.getVolume();
|
int volume = btInfo.mVolume;
|
||||||
@AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec();
|
@AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec;
|
||||||
|
|
||||||
String address = btDevice.getAddress();
|
String address = btDevice.getAddress();
|
||||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||||
address = "";
|
address = "";
|
||||||
}
|
}
|
||||||
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
||||||
"onBluetoothA2dpDeviceConfigChange addr=" + address
|
"onBluetoothDeviceConfigChange addr=" + address
|
||||||
+ " event=" + BtHelper.a2dpDeviceEventToString(event)));
|
+ " event=" + BtHelper.deviceEventToString(event)));
|
||||||
|
|
||||||
synchronized (mDevicesLock) {
|
synchronized (mDevicesLock) {
|
||||||
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
|
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
|
||||||
@@ -466,53 +499,53 @@ public class AudioDeviceInventory {
|
|||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
|
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
|
||||||
final DeviceInfo di = mConnectedDevices.get(key);
|
final DeviceInfo di = mConnectedDevices.get(key);
|
||||||
if (di == null) {
|
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();
|
mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmi.set(MediaMetrics.Property.ADDRESS, address)
|
mmi.set(MediaMetrics.Property.ADDRESS, address)
|
||||||
.set(MediaMetrics.Property.ENCODING,
|
.set(MediaMetrics.Property.ENCODING,
|
||||||
AudioSystem.audioFormatToString(a2dpCodec))
|
AudioSystem.audioFormatToString(audioCodec))
|
||||||
.set(MediaMetrics.Property.INDEX, a2dpVolume)
|
.set(MediaMetrics.Property.INDEX, volume)
|
||||||
.set(MediaMetrics.Property.NAME, di.mDeviceName);
|
.set(MediaMetrics.Property.NAME, di.mDeviceName);
|
||||||
|
|
||||||
if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) {
|
|
||||||
// Device is connected
|
if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
|
||||||
if (a2dpVolume != -1) {
|
boolean a2dpCodecChange = false;
|
||||||
mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
|
if (btInfo.mProfile == BluetoothProfile.A2DP) {
|
||||||
// convert index to internal representation in VolumeStreamState
|
if (di.mDeviceCodecFormat != audioCodec) {
|
||||||
a2dpVolume * 10,
|
di.mDeviceCodecFormat = audioCodec;
|
||||||
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);
|
mConnectedDevices.replace(key, di);
|
||||||
}
|
a2dpCodecChange = true;
|
||||||
}
|
}
|
||||||
final int res = mAudioSystem.handleDeviceConfigChange(
|
final int res = mAudioSystem.handleDeviceConfigChange(
|
||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address,
|
btInfo.mAudioSystemDevice, address,
|
||||||
BtHelper.getName(btDevice), a2dpCodec);
|
BtHelper.getName(btDevice), audioCodec);
|
||||||
|
|
||||||
if (res != AudioSystem.AUDIO_STATUS_OK) {
|
if (res != AudioSystem.AUDIO_STATUS_OK) {
|
||||||
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
||||||
"APM handleDeviceConfigChange failed for A2DP device addr=" + address
|
"APM handleDeviceConfigChange failed for A2DP device addr="
|
||||||
+ " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
|
+ address + " codec="
|
||||||
|
+ AudioSystem.audioFormatToString(audioCodec))
|
||||||
.printLog(TAG));
|
.printLog(TAG));
|
||||||
|
|
||||||
int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
|
// force A2DP device disconnection in case of error so that AudioService
|
||||||
// force A2DP device disconnection in case of error so that AudioService state is
|
// state is consistent with audio policy manager state
|
||||||
// consistent with audio policy manager state
|
setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo,
|
||||||
setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice,
|
BluetoothProfile.STATE_DISCONNECTED));
|
||||||
BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
|
|
||||||
musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
|
|
||||||
} else {
|
} else {
|
||||||
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
||||||
"APM handleDeviceConfigChange success for A2DP device addr=" + address
|
"APM handleDeviceConfigChange success for A2DP device addr="
|
||||||
+ " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
|
+ address
|
||||||
|
+ " codec=" + AudioSystem.audioFormatToString(audioCodec))
|
||||||
.printLog(TAG));
|
.printLog(TAG));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!a2dpCodecChange) {
|
||||||
|
updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mmi.record();
|
mmi.record();
|
||||||
@@ -595,7 +628,7 @@ public class AudioDeviceInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!handleDeviceConnection(wdcs.mAttributes,
|
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
|
// change of connection state failed, bailout
|
||||||
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
|
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
|
||||||
.record();
|
.record();
|
||||||
@@ -1134,10 +1167,11 @@ public class AudioDeviceInventory {
|
|||||||
* @param connect true if connection
|
* @param connect true if connection
|
||||||
* @param isForTesting if true, not calling AudioSystem for the connection as this is
|
* @param isForTesting if true, not calling AudioSystem for the connection as this is
|
||||||
* just for testing
|
* just for testing
|
||||||
|
* @param btDevice the corresponding Bluetooth device when relevant.
|
||||||
* @return false if an error was reported by AudioSystem
|
* @return false if an error was reported by AudioSystem
|
||||||
*/
|
*/
|
||||||
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
|
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
|
||||||
boolean isForTesting) {
|
boolean isForTesting, @Nullable BluetoothDevice btDevice) {
|
||||||
int device = attributes.getInternalType();
|
int device = attributes.getInternalType();
|
||||||
String address = attributes.getAddress();
|
String address = attributes.getAddress();
|
||||||
String deviceName = attributes.getName();
|
String deviceName = attributes.getName();
|
||||||
@@ -1152,6 +1186,7 @@ public class AudioDeviceInventory {
|
|||||||
.set(MediaMetrics.Property.MODE, connect
|
.set(MediaMetrics.Property.MODE, connect
|
||||||
? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT)
|
? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT)
|
||||||
.set(MediaMetrics.Property.NAME, deviceName);
|
.set(MediaMetrics.Property.NAME, deviceName);
|
||||||
|
boolean status = false;
|
||||||
synchronized (mDevicesLock) {
|
synchronized (mDevicesLock) {
|
||||||
final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
|
final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
|
||||||
if (AudioService.DEBUG_DEVICES) {
|
if (AudioService.DEBUG_DEVICES) {
|
||||||
@@ -1179,25 +1214,31 @@ public class AudioDeviceInventory {
|
|||||||
.record();
|
.record();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mConnectedDevices.put(deviceKey, new DeviceInfo(
|
mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
|
||||||
device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
|
|
||||||
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
|
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
|
||||||
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
|
status = true;
|
||||||
return true;
|
|
||||||
} else if (!connect && isConnected) {
|
} else if (!connect && isConnected) {
|
||||||
mAudioSystem.setDeviceConnectionState(attributes,
|
mAudioSystem.setDeviceConnectionState(attributes,
|
||||||
AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
|
AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
|
||||||
// always remove even if disconnection failed
|
// always remove even if disconnection failed
|
||||||
mConnectedDevices.remove(deviceKey);
|
mConnectedDevices.remove(deviceKey);
|
||||||
purgeDevicesRoles_l();
|
status = true;
|
||||||
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
|
|
||||||
return 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
|
Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
|
||||||
+ ", deviceSpec=" + di + ", connect=" + connect);
|
+ ", deviceSpec=" + di + ", connect=" + connect);
|
||||||
}
|
|
||||||
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
|
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
|
||||||
return false;
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1406,15 +1447,20 @@ public class AudioDeviceInventory {
|
|||||||
// Internal utilities
|
// Internal utilities
|
||||||
|
|
||||||
@GuardedBy("mDevicesLock")
|
@GuardedBy("mDevicesLock")
|
||||||
private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
|
private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo,
|
||||||
int a2dpCodec) {
|
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
|
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
|
||||||
// audio policy manager
|
// audio policy manager
|
||||||
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
|
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
|
||||||
// at this point there could be another A2DP device already connected in APM, but it
|
// 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
|
// doesn't matter as this new one will overwrite the previous one
|
||||||
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
|
AudioDeviceAttributes ada = new AudioDeviceAttributes(
|
||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
|
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
|
||||||
|
final int res = mAudioSystem.setDeviceConnectionState(ada,
|
||||||
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
|
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
|
||||||
|
|
||||||
// TODO: log in MediaMetrics once distinction between connection failure and
|
// 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
|
// The convention for head tracking sensors associated with A2DP devices is to
|
||||||
// use a UUID derived from the MAC address as follows:
|
// 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
|
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
|
||||||
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
|
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
|
||||||
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
|
|
||||||
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
|
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
|
||||||
address, a2dpCodec, sensorUuid);
|
address, a2dpCodec, sensorUuid);
|
||||||
final String diKey = di.getKey();
|
final String diKey = di.getKey();
|
||||||
@@ -1448,6 +1493,208 @@ public class AudioDeviceInventory {
|
|||||||
|
|
||||||
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
|
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
|
||||||
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
|
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")
|
@GuardedBy("mDevicesLock")
|
||||||
@@ -1495,6 +1742,7 @@ public class AudioDeviceInventory {
|
|||||||
// Remove A2DP routes as well
|
// Remove A2DP routes as well
|
||||||
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
|
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
|
||||||
mmi.record();
|
mmi.record();
|
||||||
|
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
|
||||||
purgeDevicesRoles_l();
|
purgeDevicesRoles_l();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1525,8 +1773,7 @@ public class AudioDeviceInventory {
|
|||||||
AudioSystem.AUDIO_FORMAT_DEFAULT);
|
AudioSystem.AUDIO_FORMAT_DEFAULT);
|
||||||
mConnectedDevices.put(
|
mConnectedDevices.put(
|
||||||
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
|
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
|
||||||
new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "",
|
new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
|
||||||
address, AudioSystem.AUDIO_FORMAT_DEFAULT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mDevicesLock")
|
@GuardedBy("mDevicesLock")
|
||||||
@@ -1552,8 +1799,7 @@ public class AudioDeviceInventory {
|
|||||||
AudioSystem.AUDIO_FORMAT_DEFAULT);
|
AudioSystem.AUDIO_FORMAT_DEFAULT);
|
||||||
mConnectedDevices.put(
|
mConnectedDevices.put(
|
||||||
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
|
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
|
||||||
new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name,
|
new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address));
|
||||||
address, AudioSystem.AUDIO_FORMAT_DEFAULT));
|
|
||||||
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
|
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
|
||||||
mDeviceBroker.postApplyVolumeOnDevice(streamType,
|
mDeviceBroker.postApplyVolumeOnDevice(streamType,
|
||||||
AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
|
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.
|
* @return true if a DEVICE_OUT_HEARING_AID is connected, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean isHearingAidConnected() {
|
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) {
|
synchronized (mDevicesLock) {
|
||||||
for (DeviceInfo di : mConnectedDevices.values()) {
|
for (DeviceInfo di : mConnectedDevices.values()) {
|
||||||
if (di.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
|
if (internalTypes.contains(di.mDeviceType)) {
|
||||||
return true;
|
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")
|
@GuardedBy("mDevicesLock")
|
||||||
private void makeLeAudioDeviceAvailable(String address, String name, int streamType,
|
private void makeLeAudioDeviceAvailable(
|
||||||
int volumeIndex, int device, String eventSource) {
|
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) {
|
if (device != AudioSystem.DEVICE_NONE) {
|
||||||
/* Audio Policy sees Le Audio similar to A2DP. Let's make sure
|
/* Audio Policy sees Le Audio similar to A2DP. Let's make sure
|
||||||
* AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
|
* AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
|
||||||
*/
|
*/
|
||||||
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
|
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
|
||||||
|
|
||||||
final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
|
AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
|
||||||
device, address, name),
|
final int res = AudioSystem.setDeviceConnectionState(ada,
|
||||||
AudioSystem.DEVICE_STATE_AVAILABLE,
|
AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
|
||||||
AudioSystem.AUDIO_FORMAT_DEFAULT);
|
|
||||||
if (res != AudioSystem.AUDIO_STATUS_OK) {
|
if (res != AudioSystem.AUDIO_STATUS_OK) {
|
||||||
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
||||||
"APM failed to make available LE Audio device addr=" + address
|
"APM failed to make available LE Audio device addr=" + address
|
||||||
@@ -1624,12 +1897,13 @@ public class AudioDeviceInventory {
|
|||||||
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
|
||||||
"LE Audio device addr=" + address + " now available").printLog(TAG));
|
"LE Audio device addr=" + address + " now available").printLog(TAG));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset LEA suspend state each time a new sink is connected
|
// Reset LEA suspend state each time a new sink is connected
|
||||||
mDeviceBroker.clearLeAudioSuspended();
|
mDeviceBroker.clearLeAudioSuspended();
|
||||||
|
|
||||||
|
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
|
||||||
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
|
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);
|
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
|
||||||
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
|
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
|
||||||
}
|
}
|
||||||
@@ -1645,6 +1919,8 @@ public class AudioDeviceInventory {
|
|||||||
final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
|
final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
|
||||||
mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
|
mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
|
||||||
mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
|
mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
|
||||||
|
|
||||||
|
updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mDevicesLock")
|
@GuardedBy("mDevicesLock")
|
||||||
@@ -1669,6 +1945,7 @@ public class AudioDeviceInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
|
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
|
||||||
|
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
|
||||||
purgeDevicesRoles_l();
|
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
|
// For tests only
|
||||||
|
|
||||||
@@ -2027,10 +2292,12 @@ public class AudioDeviceInventory {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
|
public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
|
||||||
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
|
for (DeviceInfo di : getConnectedDevicesOfTypes(
|
||||||
device.getAddress());
|
Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
|
||||||
synchronized (mDevicesLock) {
|
if (di.mDeviceAddress.equals(device.getAddress())) {
|
||||||
return (mConnectedDevices.get(key) != null);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import android.media.AudioManager;
|
|||||||
import android.media.AudioSystem;
|
import android.media.AudioSystem;
|
||||||
import android.media.BluetoothProfileConnectionInfo;
|
import android.media.BluetoothProfileConnectionInfo;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
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
|
// A2DP device events
|
||||||
/*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0;
|
/*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) {
|
switch (event) {
|
||||||
case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE";
|
case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE";
|
||||||
case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE";
|
|
||||||
default:
|
default:
|
||||||
return new String("invalid event:" + event);
|
return new String("invalid event:" + event);
|
||||||
}
|
}
|
||||||
@@ -620,11 +573,12 @@ public class BtHelper {
|
|||||||
return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
|
return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
|
private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
|
||||||
if (btDevice == null) {
|
if (btDevice == null) {
|
||||||
return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
|
return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
|
||||||
}
|
}
|
||||||
String address = btDevice.getAddress();
|
String address = btDevice.getAddress();
|
||||||
|
String name = getName(btDevice);
|
||||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||||
address = "";
|
address = "";
|
||||||
}
|
}
|
||||||
@@ -646,7 +600,7 @@ public class BtHelper {
|
|||||||
+ " btClass: " + (btClass == null ? "Unknown" : btClass)
|
+ " btClass: " + (btClass == null ? "Unknown" : btClass)
|
||||||
+ " nativeType: " + nativeType + " address: " + address);
|
+ " nativeType: " + nativeType + " address: " + address);
|
||||||
}
|
}
|
||||||
return new AudioDeviceAttributes(nativeType, address);
|
return new AudioDeviceAttributes(nativeType, address, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
|
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
|
||||||
@@ -655,12 +609,9 @@ public class BtHelper {
|
|||||||
}
|
}
|
||||||
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
|
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
|
||||||
AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
|
AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
|
||||||
String btDeviceName = getName(btDevice);
|
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
|
result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
|
||||||
audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName),
|
|
||||||
isActive);
|
|
||||||
} else {
|
} else {
|
||||||
int[] outDeviceTypes = {
|
int[] outDeviceTypes = {
|
||||||
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
|
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
|
||||||
@@ -669,14 +620,14 @@ public class BtHelper {
|
|||||||
};
|
};
|
||||||
for (int outDeviceType : outDeviceTypes) {
|
for (int outDeviceType : outDeviceTypes) {
|
||||||
result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
|
result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
|
||||||
outDeviceType, audioDevice.getAddress(), btDeviceName),
|
outDeviceType, audioDevice.getAddress(), audioDevice.getName()),
|
||||||
isActive);
|
isActive, btDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handleDeviceConnection() && result to make sure the method get executed
|
// handleDeviceConnection() && result to make sure the method get executed
|
||||||
result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
|
result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
|
||||||
inDevice, audioDevice.getAddress(), btDeviceName),
|
inDevice, audioDevice.getAddress(), audioDevice.getName()),
|
||||||
isActive) && result;
|
isActive, btDevice) && result;
|
||||||
return 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.
|
* Returns the string equivalent for the btDeviceClass class.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user