diff options
6 files changed, 209 insertions, 19 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index b1d2e33df3f7..4759689335e9 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3730,7 +3730,12 @@ public class AudioManager { @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(Manifest.permission.BLUETOOTH_STACK) public void setA2dpSuspended(boolean enable) { - AudioSystem.setParameters("A2dpSuspended=" + enable); + final IAudioService service = getService(); + try { + service.setA2dpSuspended(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -3743,7 +3748,12 @@ public class AudioManager { @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(Manifest.permission.BLUETOOTH_STACK) public void setLeAudioSuspended(boolean enable) { - AudioSystem.setParameters("LeAudioSuspended=" + enable); + final IAudioService service = getService(); + try { + service.setLeAudioSuspended(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index fe5afc5a717e..7ce189ba85d5 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -231,6 +231,12 @@ interface IAudioService { void setBluetoothScoOn(boolean on); + @EnforcePermission("BLUETOOTH_STACK") + void setA2dpSuspended(boolean on); + + @EnforcePermission("BLUETOOTH_STACK") + void setLeAudioSuspended(boolean enable); + boolean isBluetoothScoOn(); void setBluetoothA2dpOn(boolean on); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 71401f437232..f4c9d05ee0ef 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -209,6 +209,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private void init() { setupMessaging(mContext); + initAudioHalBluetoothState(); initRoutingStrategyIds(); mPreferredCommunicationDevice = null; updateActiveCommunicationDevice(); @@ -864,21 +865,174 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /** - * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn(). - */ + // Lock protecting state variable related to Bluetooth audio state + private final Object mBluetoothAudioStateLock = new Object(); + + // Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn(). + @GuardedBy("mBluetoothAudioStateLock") private boolean mBluetoothScoOn; + // value of BT_SCO parameter currently applied to audio HAL. + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothScoOnApplied; + + // A2DP suspend state requested by AudioManager.setA2dpSuspended() API. + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothA2dpSuspendedExt; + // A2DP suspend state requested by AudioDeviceInventory. + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothA2dpSuspendedInt; + // value of BT_A2dpSuspendedSCO parameter currently applied to audio HAL. + + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothA2dpSuspendedApplied; + + // LE Audio suspend state requested by AudioManager.setLeAudioSuspended() API. + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothLeSuspendedExt; + // LE Audio suspend state requested by AudioDeviceInventory. + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothLeSuspendedInt; + // value of LeAudioSuspended parameter currently applied to audio HAL. + @GuardedBy("mBluetoothAudioStateLock") + private boolean mBluetoothLeSuspendedApplied; + + private void initAudioHalBluetoothState() { + synchronized (mBluetoothAudioStateLock) { + mBluetoothScoOnApplied = false; + AudioSystem.setParameters("BT_SCO=off"); + mBluetoothA2dpSuspendedApplied = false; + AudioSystem.setParameters("A2dpSuspended=false"); + mBluetoothLeSuspendedApplied = false; + AudioSystem.setParameters("LeAudioSuspended=false"); + } + } + + @GuardedBy("mBluetoothAudioStateLock") + private void updateAudioHalBluetoothState() { + if (mBluetoothScoOn != mBluetoothScoOnApplied) { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "updateAudioHalBluetoothState() mBluetoothScoOn: " + + mBluetoothScoOn + ", mBluetoothScoOnApplied: " + mBluetoothScoOnApplied); + } + if (mBluetoothScoOn) { + if (!mBluetoothA2dpSuspendedApplied) { + AudioSystem.setParameters("A2dpSuspended=true"); + mBluetoothA2dpSuspendedApplied = true; + } + if (!mBluetoothLeSuspendedApplied) { + AudioSystem.setParameters("LeAudioSuspended=true"); + mBluetoothLeSuspendedApplied = true; + } + AudioSystem.setParameters("BT_SCO=on"); + } else { + AudioSystem.setParameters("BT_SCO=off"); + } + mBluetoothScoOnApplied = mBluetoothScoOn; + } + if (!mBluetoothScoOnApplied) { + if ((mBluetoothA2dpSuspendedExt || mBluetoothA2dpSuspendedInt) + != mBluetoothA2dpSuspendedApplied) { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "updateAudioHalBluetoothState() mBluetoothA2dpSuspendedExt: " + + mBluetoothA2dpSuspendedExt + + ", mBluetoothA2dpSuspendedInt: " + mBluetoothA2dpSuspendedInt + + ", mBluetoothA2dpSuspendedApplied: " + + mBluetoothA2dpSuspendedApplied); + } + mBluetoothA2dpSuspendedApplied = + mBluetoothA2dpSuspendedExt || mBluetoothA2dpSuspendedInt; + if (mBluetoothA2dpSuspendedApplied) { + AudioSystem.setParameters("A2dpSuspended=true"); + } else { + AudioSystem.setParameters("A2dpSuspended=false"); + } + } + if ((mBluetoothLeSuspendedExt || mBluetoothLeSuspendedInt) + != mBluetoothLeSuspendedApplied) { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "updateAudioHalBluetoothState() mBluetoothLeSuspendedExt: " + + mBluetoothLeSuspendedExt + + ", mBluetoothLeSuspendedInt: " + mBluetoothLeSuspendedInt + + ", mBluetoothLeSuspendedApplied: " + mBluetoothLeSuspendedApplied); + } + mBluetoothLeSuspendedApplied = + mBluetoothLeSuspendedExt || mBluetoothLeSuspendedInt; + if (mBluetoothLeSuspendedApplied) { + AudioSystem.setParameters("LeAudioSuspended=true"); + } else { + AudioSystem.setParameters("LeAudioSuspended=false"); + } + } + } + } /*package*/ void setBluetoothScoOn(boolean on, String eventSource) { if (AudioService.DEBUG_COMM_RTE) { Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource); } - synchronized (mDeviceStateLock) { + synchronized (mBluetoothAudioStateLock) { mBluetoothScoOn = on; + updateAudioHalBluetoothState(); postUpdateCommunicationRouteClient(eventSource); } } + /*package*/ void setA2dpSuspended(boolean enable, boolean internal, String eventSource) { + synchronized (mBluetoothAudioStateLock) { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "setA2dpSuspended source: " + eventSource + ", enable: " + + enable + ", internal: " + internal + + ", mBluetoothA2dpSuspendedInt: " + mBluetoothA2dpSuspendedInt + + ", mBluetoothA2dpSuspendedExt: " + mBluetoothA2dpSuspendedExt); + } + if (internal) { + mBluetoothA2dpSuspendedInt = enable; + } else { + mBluetoothA2dpSuspendedExt = enable; + } + updateAudioHalBluetoothState(); + } + } + + /*package*/ void clearA2dpSuspended() { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "clearA2dpSuspended"); + } + synchronized (mBluetoothAudioStateLock) { + mBluetoothA2dpSuspendedInt = false; + mBluetoothA2dpSuspendedExt = false; + updateAudioHalBluetoothState(); + } + } + + /*package*/ void setLeAudioSuspended(boolean enable, boolean internal, String eventSource) { + synchronized (mBluetoothAudioStateLock) { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "setLeAudioSuspended source: " + eventSource + ", enable: " + + enable + ", internal: " + internal + + ", mBluetoothLeSuspendedInt: " + mBluetoothA2dpSuspendedInt + + ", mBluetoothLeSuspendedExt: " + mBluetoothA2dpSuspendedExt); + } + if (internal) { + mBluetoothLeSuspendedInt = enable; + } else { + mBluetoothLeSuspendedExt = enable; + } + updateAudioHalBluetoothState(); + } + } + + /*package*/ void clearLeAudioSuspended() { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "clearLeAudioSuspended"); + } + synchronized (mBluetoothAudioStateLock) { + mBluetoothLeSuspendedInt = false; + mBluetoothLeSuspendedExt = false; + updateAudioHalBluetoothState(); + } + } + /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { synchronized (mDeviceStateLock) { return mDeviceInventory.startWatchingRoutes(observer); @@ -1985,7 +2139,11 @@ import java.util.concurrent.atomic.AtomicBoolean; */ @GuardedBy("mDeviceStateLock") @Nullable private AudioDeviceAttributes preferredCommunicationDevice() { - boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn(); + boolean btSCoOn = mBtHelper.isBluetoothScoOn(); + synchronized (mBluetoothAudioStateLock) { + btSCoOn = btSCoOn && mBluetoothScoOn; + } + if (btSCoOn) { // Use the SCO device known to BtHelper so that it matches exactly // what has been communicated to audio policy manager. The device @@ -2020,12 +2178,6 @@ import java.util.concurrent.atomic.AtomicBoolean; "updateCommunicationRoute, preferredCommunicationDevice: " + preferredCommunicationDevice + " eventSource: " + eventSource))); - if (preferredCommunicationDevice == null - || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) { - AudioSystem.setParameters("BT_SCO=off"); - } else { - AudioSystem.setParameters("BT_SCO=on"); - } if (preferredCommunicationDevice == null) { AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice(); if (defaultDevice != null) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index b70c3e48b6e6..1d8bef1c6732 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -1478,7 +1478,7 @@ public class AudioDeviceInventory { } // Reset A2DP suspend state each time a new sink is connected - mAudioSystem.setParameters("A2dpSuspended=false"); + mDeviceBroker.clearA2dpSuspended(); // The convention for head tracking sensors associated with A2DP devices is to // use a UUID derived from the MAC address as follows: @@ -1751,7 +1751,8 @@ public class AudioDeviceInventory { private void makeA2dpDeviceUnavailableLater(String address, int delayMs) { // prevent any activity on the A2DP audio output to avoid unwanted // reconnection of the sink. - mAudioSystem.setParameters("A2dpSuspended=true"); + mDeviceBroker.setA2dpSuspended( + true /*enable*/, true /*internal*/, "makeA2dpDeviceUnavailableLater"); // retrieve DeviceInfo before removing device final String deviceKey = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); @@ -1898,7 +1899,7 @@ public class AudioDeviceInventory { "LE Audio device addr=" + address + " now available").printLog(TAG)); } // Reset LEA suspend state each time a new sink is connected - mAudioSystem.setParameters("LeAudioSuspended=false"); + mDeviceBroker.clearLeAudioSuspended(); UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), @@ -1953,7 +1954,8 @@ public class AudioDeviceInventory { private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) { // prevent any activity on the LEA output to avoid unwanted // reconnection of the sink. - mAudioSystem.setParameters("LeAudioSuspended=true"); + mDeviceBroker.setLeAudioSuspended( + true /*enable*/, true /*internal*/, "makeLeAudioDeviceUnavailableLater"); // the device will be made unavailable later, so consider it disconnected right away mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); // send the delayed message to make the device unavailable later diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 24eba76ccf66..7ebc68612e4d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6412,6 +6412,26 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.setBluetoothScoOn(on, eventSource); } + /** @see AudioManager#setA2dpSuspended(boolean) */ + @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setA2dpSuspended(boolean enable) { + super.setA2dpSuspended_enforcePermission(); + final String eventSource = new StringBuilder("setA2dpSuspended(").append(enable) + .append(") from u/pid:").append(Binder.getCallingUid()).append("/") + .append(Binder.getCallingPid()).toString(); + mDeviceBroker.setA2dpSuspended(enable, false /*internal*/, eventSource); + } + + /** @see AudioManager#setA2dpSuspended(boolean) */ + @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setLeAudioSuspended(boolean enable) { + super.setLeAudioSuspended_enforcePermission(); + final String eventSource = new StringBuilder("setLeAudioSuspended(").append(enable) + .append(") from u/pid:").append(Binder.getCallingUid()).append("/") + .append(Binder.getCallingPid()).toString(); + mDeviceBroker.setLeAudioSuspended(enable, false /*internal*/, eventSource); + } + /** @see AudioManager#isBluetoothScoOn() * Note that it doesn't report internal state, but state seen by apps (which may have * called setBluetoothScoOn() */ diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index bfa6c36e1c57..e46c3cc4a285 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -445,8 +445,8 @@ public class BtHelper { /*package*/ synchronized void resetBluetoothSco() { mScoAudioState = SCO_STATE_INACTIVE; broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - AudioSystem.setParameters("A2dpSuspended=false"); - AudioSystem.setParameters("LeAudioSuspended=false"); + mDeviceBroker.clearA2dpSuspended(); + mDeviceBroker.clearLeAudioSuspended(); mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } |