diff options
| author | 2024-06-28 12:07:41 +0000 | |
|---|---|---|
| committer | 2024-06-28 12:07:41 +0000 | |
| commit | d094bc7c907c1b42cc86050f773fe90789197735 (patch) | |
| tree | 3e6896a91763f3ea2d735296a10b1a39bd6290bb | |
| parent | 0088e07ba4d431a84679d50ccca01c5e617a1b53 (diff) | |
| parent | 429defcc96f7e698220aa30a87b803fb506f9797 (diff) | |
Merge "AudioService: handle SCO audio activaton for regular VoIP apps" into main
| -rw-r--r-- | services/core/java/com/android/server/audio/AudioDeviceBroker.java | 110 | ||||
| -rw-r--r-- | services/core/java/com/android/server/audio/BtHelper.java | 103 |
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. |