summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java62
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java9
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java11
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java100
4 files changed, 114 insertions, 68 deletions
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1dc1846fbb96..1d21ccb62b8c 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -15,6 +15,8 @@
*/
package com.android.server.audio;
+import static android.media.audio.Flags.scoManagedByAudio;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
@@ -54,6 +56,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.sysprop.BluetoothProperties;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -74,7 +77,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
-
/**
* @hide
* (non final for mocking/spying)
@@ -167,6 +169,15 @@ public class AudioDeviceBroker {
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
public static final long USE_SET_COMMUNICATION_DEVICE = 243827847L;
+ /** Indicates if headset profile connection and SCO audio control use the new implementation
+ * aligned with other BT profiles. True if both the feature flag Flags.scoManagedByAudio() and
+ * the system property audio.sco.managed.by.audio are true.
+ */
+ private final boolean mScoManagedByAudio;
+ /*package*/ boolean isScoManagedByAudio() {
+ return mScoManagedByAudio;
+ }
+
//-------------------------------------------------------------------
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service,
@NonNull AudioSystemAdapter audioSystem) {
@@ -176,7 +187,8 @@ public class AudioDeviceBroker {
mDeviceInventory = new AudioDeviceInventory(this);
mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext);
mAudioSystem = audioSystem;
-
+ mScoManagedByAudio = scoManagedByAudio()
+ && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false);
init();
}
@@ -192,7 +204,8 @@ public class AudioDeviceBroker {
mDeviceInventory = mockDeviceInventory;
mSystemServer = mockSystemServer;
mAudioSystem = audioSystem;
-
+ mScoManagedByAudio = scoManagedByAudio()
+ && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false);
init();
}
@@ -400,24 +413,24 @@ public class AudioDeviceBroker {
if (client == null) {
return;
}
-
- boolean isBtScoRequested = isBluetoothScoRequested();
- if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
- if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
- Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
- + uid);
- // clean up or restore previous client selection
- if (prevClientDevice != null) {
- addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
- } else {
- removeCommunicationRouteClient(cb, true);
+ if (!mScoManagedByAudio) {
+ boolean isBtScoRequested = isBluetoothScoRequested();
+ if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
+ if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+ Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
+ + uid);
+ // clean up or restore previous client selection
+ if (prevClientDevice != null) {
+ addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
+ } else {
+ removeCommunicationRouteClient(cb, true);
+ }
+ postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
- postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ } else if (!isBtScoRequested && wasBtScoRequested) {
+ mBtHelper.stopBluetoothSco(eventSource);
}
- } else if (!isBtScoRequested && wasBtScoRequested) {
- mBtHelper.stopBluetoothSco(eventSource);
}
-
// In BT classic for communication, the device changes from a2dp to sco device, but for
// LE Audio it stays the same and we must trigger the proper stream volume alignment, if
// LE Audio communication device is activated after the audio system has already switched to
@@ -1685,6 +1698,8 @@ public class AudioDeviceBroker {
pw.println("\n" + prefix + "mAudioModeOwner: " + mAudioModeOwner);
+ pw.println("\n" + prefix + "mScoManagedByAudio: " + mScoManagedByAudio);
+
mBtHelper.dump(pw, prefix);
}
@@ -1837,10 +1852,10 @@ public class AudioDeviceBroker {
? mAudioService.getBluetoothContextualVolumeStream()
: AudioSystem.STREAM_DEFAULT);
if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
- || btInfo.mProfile
- == BluetoothProfile.HEARING_AID) {
- onUpdateCommunicationRouteClient(
- isBluetoothScoRequested(),
+ || btInfo.mProfile == BluetoothProfile.HEARING_AID
+ || (mScoManagedByAudio
+ && btInfo.mProfile == BluetoothProfile.HEADSET)) {
+ onUpdateCommunicationRouteClient(isBluetoothScoRequested(),
"setBluetoothActiveDevice");
}
}
@@ -2511,7 +2526,7 @@ public class AudioDeviceBroker {
setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
} else {
- if (!isBluetoothScoRequested() && wasBtScoRequested) {
+ if (!mScoManagedByAudio && !isBluetoothScoRequested() && wasBtScoRequested) {
mBtHelper.stopBluetoothSco(eventSource);
}
updateCommunicationRoute(eventSource);
@@ -2815,4 +2830,5 @@ public class AudioDeviceBroker {
void clearDeviceInventory() {
mDeviceInventory.clearDeviceInventory();
}
+
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e0790da7cd09..287c92f86f0f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -859,6 +859,15 @@ public class AudioDeviceInventory {
btInfo, streamType, codec, "onSetBtActiveDevice");
}
break;
+ case BluetoothProfile.HEADSET:
+ if (mDeviceBroker.isScoManagedByAudio()) {
+ if (switchToUnavailable) {
+ mDeviceBroker.onSetBtScoActiveDevice(null);
+ } else if (switchToAvailable) {
+ mDeviceBroker.onSetBtScoActiveDevice(btInfo.mDevice);
+ }
+ }
+ break;
default: throw new IllegalArgumentException("Invalid profile "
+ BluetoothProfile.getProfileName(btInfo.mProfile));
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cb0ad78ce51c..2a23b9ca522e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -48,6 +48,7 @@ import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
import static android.media.audio.Flags.focusFreezeTestApi;
import static android.media.audio.Flags.roForegroundAudioControl;
+import static android.media.audio.Flags.scoManagedByAudio;
import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
@@ -1503,7 +1504,9 @@ public class AudioService extends IAudioService.Stub
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
- intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+ if (!mDeviceBroker.isScoManagedByAudio()) {
+ intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+ }
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
if (mDisplayManager == null) {
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
@@ -4529,11 +4532,12 @@ public class AudioService extends IAudioService.Stub
+ focusFreezeTestApi());
pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:"
+ disablePrescaleAbsoluteVolume());
-
pw.println("\tcom.android.media.audio.setStreamVolumeOrder:"
+ setStreamVolumeOrder());
pw.println("\tandroid.media.audio.roForegroundAudioControl:"
+ roForegroundAudioControl());
+ pw.println("\tandroid.media.audio.scoManagedByAudio:"
+ + scoManagedByAudio());
pw.println("\tcom.android.media.audio.vgsVssSyncMuteOrder:"
+ vgsVssSyncMuteOrder());
}
@@ -7859,7 +7863,8 @@ public class AudioService extends IAudioService.Stub
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
&& profile != BluetoothProfile.LE_AUDIO
&& profile != BluetoothProfile.LE_AUDIO_BROADCAST
- && profile != BluetoothProfile.HEARING_AID) {
+ && profile != BluetoothProfile.HEARING_AID
+ && !(mDeviceBroker.isScoManagedByAudio() && profile == BluetoothProfile.HEADSET)) {
throw new IllegalArgumentException("Illegal BluetoothProfile profile for device "
+ previousDevice + " -> " + newDevice + ". Got: " + profile);
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index b1ea85cf3fd3..6bb3eb1c3078 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -401,50 +401,67 @@ public class BtHelper {
private void onScoAudioStateChanged(int state) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
- switch (state) {
- case BluetoothHeadset.STATE_AUDIO_CONNECTED:
- scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
- && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
- } else if (mDeviceBroker.isBluetoothScoRequested()) {
- // broadcast intent if the connection was initated by AudioService
+ if (mDeviceBroker.isScoManagedByAudio()) {
+ switch (state) {
+ case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+ mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
broadcast = true;
- }
- mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
- break;
- case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
- mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
- scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
- // There are two cases where we want to immediately reconnect audio:
- // 1) If a new start request was received while disconnecting: this was
- // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
- // 2) If audio was connected then disconnected via Bluetooth APIs and
- // we still have pending activation requests by apps: this is indicated by
- // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
- if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
- if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
- && connectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+ break;
+ case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ broadcast = true;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (state) {
+ case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
+ && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ } else if (mDeviceBroker.isBluetoothScoRequested()) {
+ // broadcast intent if the connection was initated by AudioService
broadcast = true;
- break;
}
- }
- if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
- broadcast = true;
- }
- mScoAudioState = SCO_STATE_INACTIVE;
- break;
- case BluetoothHeadset.STATE_AUDIO_CONNECTING:
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
- && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
- }
- break;
- default:
- break;
+ mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
+ break;
+ case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ // There are two cases where we want to immediately reconnect audio:
+ // 1) If a new start request was received while disconnecting: this was
+ // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
+ // 2) If audio was connected then disconnected via Bluetooth APIs and
+ // we still have pending activation requests by apps: this is indicated by
+ // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
+ if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
+ && connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+ broadcast = true;
+ break;
+ }
+ }
+ if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+ broadcast = true;
+ }
+ mScoAudioState = SCO_STATE_INACTIVE;
+ break;
+ case BluetoothHeadset.STATE_AUDIO_CONNECTING:
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
+ && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ }
+ break;
+ default:
+ break;
+ }
}
if (broadcast) {
broadcastScoConnectionState(scoAudioState);
@@ -454,7 +471,6 @@ public class BtHelper {
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
sendStickyBroadcastToAll(newIntent);
}
-
}
/**
*