Protect audio communication device set/clear operations with a lock.
Currently, when telecom audio stack handle series messages relate to
communication device set/clear operations, these operations will be
completed in random order which will cause unexpected device set as the
communication device. Protect these operations with a lock to avoid race
conditions.
Bug: b/303001133
Test: atest CallAudioRouteStateMachineTest
Change-Id: Ib9fcbc712aaa3583ceea17288fa85ac33c0e7d3c
diff --git a/flags/telecom_callaudioroutestatemachine_flags.aconfig b/flags/telecom_callaudioroutestatemachine_flags.aconfig
index b0f14a4..ab7ec88 100644
--- a/flags/telecom_callaudioroutestatemachine_flags.aconfig
+++ b/flags/telecom_callaudioroutestatemachine_flags.aconfig
@@ -40,4 +40,11 @@
namespace: "telecom"
description: "Refactor call audio set/clear communication device and include unsupported routes."
bug: "308968392"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "communication_device_protected_by_lock"
+ namespace: "telecom"
+ description: "Protect set/clear communication device operation with lock to avoid race condition."
+ bug: "303001133"
+}
diff --git a/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java b/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
index 38a96eb..4af60f0 100644
--- a/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
+++ b/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
@@ -24,10 +24,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.flags.Flags;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Semaphore;
/**
* Helper class used to keep track of the requested communication device within Telecom for audio
@@ -50,6 +52,7 @@
private int mAudioDeviceType = sAUDIO_DEVICE_TYPE_INVALID;
// Keep track of the locally requested BT audio device if set
private String mBtAudioDevice = null;
+ private final Semaphore mLock = new Semaphore(1);
public CallAudioCommunicationDeviceTracker(Context context) {
mAudioManager = context.getSystemService(AudioManager.class);
@@ -94,6 +97,9 @@
*/
public boolean setCommunicationDevice(int audioDeviceType,
BluetoothDevice btDevice) {
+ if (Flags.communicationDeviceProtectedByLock()) {
+ mLock.tryAcquire();
+ }
// There is only one audio device type associated with each type of BT device.
boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType);
Log.i(this, "setCommunicationDevice: type = %s, isBtDevice = %s, btDevice = %s",
@@ -155,6 +161,9 @@
}
}
}
+ if (Flags.communicationDeviceProtectedByLock()) {
+ mLock.release();
+ }
return result;
}
@@ -164,6 +173,9 @@
* @param audioDeviceTypes The supported audio device types for the device.
*/
public void clearCommunicationDevice(int audioDeviceType) {
+ if (Flags.communicationDeviceProtectedByLock()) {
+ mLock.tryAcquire();
+ }
// There is only one audio device type associated with each type of BT device.
boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType);
Log.i(this, "clearCommunicationDevice: type = %s, isBtDevice = %s",
@@ -195,6 +207,9 @@
mBluetoothRouteManager.onAudioLost(mBtAudioDevice);
mBtAudioDevice = null;
}
+ if (Flags.communicationDeviceProtectedByLock()) {
+ mLock.release();
+ }
}
private boolean isUsbHeadsetType(int audioDeviceType, int sourceType) {