summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Eric Laurent <elaurent@google.com> 2024-06-28 12:07:41 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-06-28 12:07:41 +0000
commitd094bc7c907c1b42cc86050f773fe90789197735 (patch)
tree3e6896a91763f3ea2d735296a10b1a39bd6290bb
parent0088e07ba4d431a84679d50ccca01c5e617a1b53 (diff)
parent429defcc96f7e698220aa30a87b803fb506f9797 (diff)
Merge "AudioService: handle SCO audio activaton for regular VoIP apps" into main
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java110
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java103
2 files changed, 137 insertions, 76 deletions
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index a8c269da6f14..cc4f7d9b2ddb 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -51,6 +51,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -374,6 +375,16 @@ public class AudioDeviceBroker {
deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, deviceInfo.mEventSource);
}
+ /**
+ * Indicates if a Bluetooth SCO activation request owner is controlling
+ * the SCO audio state itself or not.
+ * @param uid the UI of the SOC request owner app
+ * @return true if we should control SCO audio state, false otherwise
+ */
+ private boolean shouldStartScoForUid(int uid) {
+ return !(uid == Process.BLUETOOTH_UID || uid == Process.PHONE_UID);
+ }
+
@GuardedBy("mDeviceStateLock")
/*package*/ void setCommunicationRouteForClient(
IBinder cb, int uid, AudioDeviceAttributes device,
@@ -388,7 +399,7 @@ public class AudioDeviceBroker {
+ " device: " + device + " isPrivileged: " + isPrivileged
+ " from API: " + eventSource)).printLog(TAG));
- final boolean wasBtScoRequested = isBluetoothScoRequested();
+ final int previousBtScoRequesterUid = bluetoothScoRequestOwnerUid();
CommunicationRouteClient client;
// Save previous client route in case of failure to start BT SCO audio
@@ -412,8 +423,40 @@ public class AudioDeviceBroker {
if (client == null) {
return;
}
- if (!mScoManagedByAudio) {
- boolean isBtScoRequested = isBluetoothScoRequested();
+ final int btScoRequesterUid = bluetoothScoRequestOwnerUid();
+ final boolean isBtScoRequested = btScoRequesterUid != -1;
+ final boolean wasBtScoRequested = previousBtScoRequesterUid != -1;
+
+ if (mScoManagedByAudio) {
+ if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive()
+ || !mBtHelper.isBluetoothScoRequestedInternally())) {
+ boolean scoStarted = false;
+ if (shouldStartScoForUid(btScoRequesterUid)) {
+ scoStarted = mBtHelper.startBluetoothSco(scoAudioMode, eventSource);
+ if (!scoStarted) {
+ 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);
+ }
+ } else {
+ scoStarted = true;
+ }
+ if (scoStarted) {
+ setBluetoothScoOn(true, "setCommunicationRouteForClient");
+ }
+ } else if (!isBtScoRequested && wasBtScoRequested) {
+ if (shouldStartScoForUid(previousBtScoRequesterUid)) {
+ mBtHelper.stopBluetoothSco(eventSource);
+ }
+ setBluetoothScoOn(false, "setCommunicationRouteForClient");
+ }
+ } else {
if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive()
|| !mBtHelper.isBluetoothScoRequestedInternally())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
@@ -575,12 +618,12 @@ public class AudioDeviceBroker {
@GuardedBy("mDeviceStateLock")
/*package*/ void updateCommunicationRouteClientState(
CommunicationRouteClient client, boolean wasActive) {
- boolean wasBtScoRequested = isBluetoothScoRequested();
+ int btScoRequesterUid = bluetoothScoRequestOwnerUid();
client.setPlaybackActive(mAudioService.isPlaybackActiveForUid(client.getUid()));
client.setRecordingActive(mAudioService.isRecordingActiveForUid(client.getUid()));
if (wasActive != client.isActive()) {
postUpdateCommunicationRouteClient(
- wasBtScoRequested, "updateCommunicationRouteClientState");
+ btScoRequesterUid, "updateCommunicationRouteClientState");
}
}
@@ -763,6 +806,22 @@ public class AudioDeviceBroker {
}
/**
+ * Helper method on top of isBluetoothScoRequested() returning the UID of the
+ * BT SCO route request owner of -1 if SCO is not requested.
+ * @return the UID of the BT SCO route request owner of -1 if SCO is not requested.
+ */
+ @GuardedBy("mDeviceStateLock")
+ /*package*/ int bluetoothScoRequestOwnerUid() {
+ if (!isBluetoothScoRequested()) {
+ return -1;
+ }
+ CommunicationRouteClient crc = topCommunicationRouteClient();
+ if (crc == null) {
+ return -1;
+ }
+ return crc.getUid();
+ }
+ /**
* Helper method on top of isDeviceRequestedForCommunication() indicating if
* Bluetooth LE Audio communication device is currently requested or not.
* @return true if Bluetooth LE Audio device is requested, false otherwise.
@@ -1148,15 +1207,18 @@ public class AudioDeviceBroker {
}
}
+ @GuardedBy("mDeviceStateLock")
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
synchronized (mBluetoothAudioStateLock) {
- boolean isBtScoRequested = isBluetoothScoRequested();
+ int btScoRequesterUId = bluetoothScoRequestOwnerUid();
Log.i(TAG, "setBluetoothScoOn: " + on + ", mBluetoothScoOn: "
- + mBluetoothScoOn + ", isBtScoRequested: " + isBtScoRequested
+ + mBluetoothScoOn + ", btScoRequesterUId: " + btScoRequesterUId
+ ", from: " + eventSource);
mBluetoothScoOn = on;
updateAudioHalBluetoothState();
- postUpdateCommunicationRouteClient(isBtScoRequested, eventSource);
+ if (!mScoManagedByAudio) {
+ postUpdateCommunicationRouteClient(btScoRequesterUId, eventSource);
+ }
}
}
@@ -1510,9 +1572,9 @@ public class AudioDeviceBroker {
}
/*package*/ void postUpdateCommunicationRouteClient(
- boolean wasBtScoRequested, String eventSource) {
+ int btScoRequesterUid, String eventSource) {
sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE,
- wasBtScoRequested ? 1 : 0, eventSource);
+ btScoRequesterUid, eventSource);
}
/*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) {
@@ -1865,7 +1927,7 @@ public class AudioDeviceBroker {
|| btInfo.mProfile == BluetoothProfile.HEARING_AID
|| (mScoManagedByAudio
&& btInfo.mProfile == BluetoothProfile.HEADSET)) {
- onUpdateCommunicationRouteClient(isBluetoothScoRequested(),
+ onUpdateCommunicationRouteClient(bluetoothScoRequestOwnerUid(),
"setBluetoothActiveDevice");
}
}
@@ -1927,11 +1989,11 @@ public class AudioDeviceBroker {
case MSG_I_SET_MODE_OWNER:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- boolean wasBtScoRequested = isBluetoothScoRequested();
+ int btScoRequesterUid = bluetoothScoRequestOwnerUid();
mAudioModeOwner = (AudioModeInfo) msg.obj;
if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) {
onUpdateCommunicationRouteClient(
- wasBtScoRequested, "setNewModeOwner");
+ btScoRequesterUid, "setNewModeOwner");
}
}
}
@@ -1958,7 +2020,7 @@ public class AudioDeviceBroker {
case MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- onUpdateCommunicationRouteClient(msg.arg1 == 1, (String) msg.obj);
+ onUpdateCommunicationRouteClient(msg.arg1, (String) msg.obj);
}
}
break;
@@ -2457,7 +2519,7 @@ public class AudioDeviceBroker {
@Nullable private AudioDeviceAttributes preferredCommunicationDevice() {
boolean btSCoOn = mBtHelper.isBluetoothScoOn();
synchronized (mBluetoothAudioStateLock) {
- btSCoOn = btSCoOn && mBluetoothScoOn;
+ btSCoOn = (btSCoOn || mScoManagedByAudio) && mBluetoothScoOn;
}
if (btSCoOn) {
@@ -2522,18 +2584,28 @@ public class AudioDeviceBroker {
*/
// @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
- private void onUpdateCommunicationRouteClient(boolean wasBtScoRequested, String eventSource) {
+ private void onUpdateCommunicationRouteClient(
+ int previousBtScoRequesterUid, String eventSource) {
CommunicationRouteClient crc = topCommunicationRouteClient();
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " + crc
- + " wasBtScoRequested: " + wasBtScoRequested + " eventSource: " + eventSource);
+ + " previousBtScoRequesterUid: " + previousBtScoRequesterUid
+ + " eventSource: " + eventSource);
}
if (crc != null) {
setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
} else {
- if (!mScoManagedByAudio && !isBluetoothScoRequested() && wasBtScoRequested) {
- mBtHelper.stopBluetoothSco(eventSource);
+ boolean wasScoRequested = previousBtScoRequesterUid != -1;
+ if (!isBluetoothScoRequested() && wasScoRequested) {
+ if (mScoManagedByAudio) {
+ if (shouldStartScoForUid(previousBtScoRequesterUid)) {
+ mBtHelper.stopBluetoothSco(eventSource);
+ }
+ setBluetoothScoOn(false, eventSource);
+ } else {
+ mBtHelper.stopBluetoothSco(eventSource);
+ }
}
updateCommunicationRoute(eventSource);
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index b6125cc7a467..0de342807df3 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -401,70 +401,59 @@ public class BtHelper {
private void onScoAudioStateChanged(int state) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
- Log.i(TAG, "onScoAudioStateChanged state: " + state + " mScoAudioState: " + mScoAudioState);
- if (mDeviceBroker.isScoManagedByAudio()) {
- switch (state) {
- case BluetoothHeadset.STATE_AUDIO_CONNECTED:
- mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
- scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ Log.i(TAG, "onScoAudioStateChanged state: " + state
+ + ", mScoAudioState: " + mScoAudioState);
+ 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;
- 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;
- }
+ }
+ if (!mDeviceBroker.isScoManagedByAudio()) {
mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
- break;
- case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ }
+ break;
+ case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ if (!mDeviceBroker.isScoManagedByAudio()) {
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) {
+ }
+ 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;
}
- 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 (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) {
+ Log.i(TAG, "onScoAudioStateChanged broadcasting state: " + scoAudioState);
broadcastScoConnectionState(scoAudioState);
//FIXME: this is to maintain compatibility with deprecated intent
// AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.