Merge "AudioDeviceBroker: strengthen setcommunicationDevice implementation"
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index bbffc89..2fe06094 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -286,22 +286,9 @@
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
}
-
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = null;
- if (on) {
- device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
- } else {
- CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
- if (client == null || !client.requestsSpeakerphone()) {
- return;
- }
- }
- postSetCommunicationRouteForClient(new CommunicationClientInfo(
- cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource));
- }
- }
+ postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
+ cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
+ on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
}
/**
@@ -311,6 +298,9 @@
* @param device Device selected or null to unselect.
* @param eventSource for logging purposes
*/
+
+ private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
+
/*package*/ boolean setCommunicationDevice(
IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {
@@ -318,21 +308,53 @@
Log.v(TAG, "setCommunicationDevice, device: " + device + ", pid: " + pid);
}
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes deviceAttr = null;
- if (device != null) {
- deviceAttr = new AudioDeviceAttributes(device);
- } else {
- CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
- if (client == null) {
- return false;
+ AudioDeviceAttributes deviceAttr =
+ (device != null) ? new AudioDeviceAttributes(device) : null;
+ CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, pid, deviceAttr,
+ device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true);
+ postSetCommunicationDeviceForClient(deviceInfo);
+ boolean status;
+ synchronized (deviceInfo) {
+ final long start = System.currentTimeMillis();
+ long elapsed = 0;
+ while (deviceInfo.mWaitForStatus) {
+ try {
+ deviceInfo.wait(SET_COMMUNICATION_DEVICE_TIMEOUT_MS - elapsed);
+ } catch (InterruptedException e) {
+ elapsed = System.currentTimeMillis() - start;
+ if (elapsed >= SET_COMMUNICATION_DEVICE_TIMEOUT_MS) {
+ deviceInfo.mStatus = false;
+ deviceInfo.mWaitForStatus = false;
}
}
- postSetCommunicationRouteForClient(new CommunicationClientInfo(
- cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource));
+ }
+ status = deviceInfo.mStatus;
+ }
+ return status;
+ }
+
+ /**
+ * Sets or resets the communication device for matching client. If no client matches and the
+ * request is to reset for a given device (deviceInfo.mOn == false), the method is a noop.
+ * @param deviceInfo information on the device and requester {@link #CommunicationDeviceInfo}
+ * @return true if the communication device is set or reset
+ */
+ @GuardedBy("mDeviceStateLock")
+ /*package*/ boolean onSetCommunicationDeviceForClient(CommunicationDeviceInfo deviceInfo) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo);
+ }
+ if (!deviceInfo.mOn) {
+ CommunicationRouteClient client = getCommunicationRouteClientForPid(deviceInfo.mPid);
+ if (client == null || (deviceInfo.mDevice != null
+ && !deviceInfo.mDevice.equals(client.getDevice()))) {
+ return false;
}
}
+
+ AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null;
+ setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mPid, device,
+ deviceInfo.mScoAudioMode, deviceInfo.mEventSource);
return true;
}
@@ -390,7 +412,7 @@
mBtHelper.stopBluetoothSco(eventSource);
}
- sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+ updateCommunicationRoute(eventSource);
}
/**
@@ -424,7 +446,7 @@
CommunicationRouteClient crc = topCommunicationRouteClient();
AudioDeviceAttributes device = crc != null ? crc.getDevice() : null;
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "requestedCommunicationDevice, device: "
+ Log.v(TAG, "requestedCommunicationDevice: "
+ device + " mAudioModeOwner: " + mAudioModeOwner.toString());
}
return device;
@@ -822,37 +844,22 @@
@NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
+ Log.v(TAG, "startBluetoothScoForClient, pid: " + pid);
}
-
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device =
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
-
- postSetCommunicationRouteForClient(new CommunicationClientInfo(
- cb, pid, device, scoAudioMode, eventSource));
- }
- }
+ postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
+ cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
+ true, scoAudioMode, eventSource, false));
}
/*package*/ void stopBluetoothScoForClient(
IBinder cb, int pid, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
+ Log.v(TAG, "stopBluetoothScoForClient, pid: " + pid);
}
-
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
- if (client == null || !client.requestsBluetoothSco()) {
- return;
- }
- postSetCommunicationRouteForClient(new CommunicationClientInfo(
- cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource));
- }
- }
+ postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
+ cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
+ false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
}
/*package*/ int setPreferredDevicesForStrategySync(int strategy,
@@ -990,7 +997,8 @@
}
//---------------------------------------------------------------------
- // Message handling on behalf of helper classes
+ // Message handling on behalf of helper classes.
+ // Each of these methods posts a message to mBrokerHandler message queue.
/*package*/ void postBroadcastScoConnectionState(int state) {
sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
}
@@ -1046,28 +1054,34 @@
sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, eventSource);
}
- /*package*/ void postSetCommunicationRouteForClient(CommunicationClientInfo info) {
- sendLMsgNoDelay(MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT, SENDMSG_QUEUE, info);
+ /*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) {
+ sendLMsgNoDelay(MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT, SENDMSG_QUEUE, info);
}
/*package*/ void postScoAudioStateChanged(int state) {
sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state);
}
- /*package*/ static final class CommunicationClientInfo {
- final @NonNull IBinder mCb;
- final int mPid;
- final @NonNull AudioDeviceAttributes mDevice;
- final int mScoAudioMode;
- final @NonNull String mEventSource;
+ /*package*/ static final class CommunicationDeviceInfo {
+ final @NonNull IBinder mCb; // Identifies the requesting client for death handler
+ final int mPid; // Requester process ID
+ final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset.
+ final boolean mOn; // true if setting, false if resetting
+ final int mScoAudioMode; // only used for SCO: requested audio mode
+ final @NonNull String mEventSource; // caller identifier for logging
+ boolean mWaitForStatus; // true if the caller waits for a completion status (API dependent)
+ boolean mStatus = false; // completion status only used if mWaitForStatus is true
- CommunicationClientInfo(@NonNull IBinder cb, int pid, @NonNull AudioDeviceAttributes device,
- int scoAudioMode, @NonNull String eventSource) {
+ CommunicationDeviceInfo(@NonNull IBinder cb, int pid,
+ @Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode,
+ @NonNull String eventSource, boolean waitForStatus) {
mCb = cb;
mPid = pid;
mDevice = device;
+ mOn = on;
mScoAudioMode = scoAudioMode;
mEventSource = eventSource;
+ mWaitForStatus = waitForStatus;
}
// redefine equality op so we can match messages intended for this client
@@ -1079,21 +1093,24 @@
if (this == o) {
return true;
}
- if (!(o instanceof CommunicationClientInfo)) {
+ if (!(o instanceof CommunicationDeviceInfo)) {
return false;
}
- return mCb.equals(((CommunicationClientInfo) o).mCb)
- && mPid == ((CommunicationClientInfo) o).mPid;
+ return mCb.equals(((CommunicationDeviceInfo) o).mCb)
+ && mPid == ((CommunicationDeviceInfo) o).mPid;
}
@Override
public String toString() {
- return "CommunicationClientInfo mCb=" + mCb.toString()
- +"mPid=" + mPid
- +"mDevice=" + mDevice.toString()
- +"mScoAudioMode=" + mScoAudioMode
- +"mEventSource=" + mEventSource;
+ return "CommunicationDeviceInfo mCb=" + mCb.toString()
+ + " mPid=" + mPid
+ + " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]"
+ + " mOn=" + mOn
+ + " mScoAudioMode=" + mScoAudioMode
+ + " mEventSource=" + mEventSource
+ + " mWaitForStatus=" + mWaitForStatus
+ + " mStatus=" + mStatus;
}
}
@@ -1297,7 +1314,7 @@
updateActiveCommunicationDevice();
mDeviceInventory.onRestoreDevices();
mBtHelper.onAudioServerDiedRestoreA2dp();
- onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
+ updateCommunicationRoute("MSG_RESTORE_DEVICES");
}
}
break;
@@ -1392,12 +1409,19 @@
}
break;
- case MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT:
+ case MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT:
+ CommunicationDeviceInfo deviceInfo = (CommunicationDeviceInfo) msg.obj;
+ boolean status;
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- CommunicationClientInfo info = (CommunicationClientInfo) msg.obj;
- setCommunicationRouteForClient(info.mCb, info.mPid, info.mDevice,
- info.mScoAudioMode, info.mEventSource);
+ status = onSetCommunicationDeviceForClient(deviceInfo);
+ }
+ }
+ synchronized (deviceInfo) {
+ if (deviceInfo.mWaitForStatus) {
+ deviceInfo.mStatus = status;
+ deviceInfo.mWaitForStatus = false;
+ deviceInfo.notify();
}
}
break;
@@ -1410,14 +1434,6 @@
}
break;
- case MSG_L_UPDATE_COMMUNICATION_ROUTE:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- onUpdateCommunicationRoute((String) msg.obj);
- }
- }
- break;
-
case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
@@ -1568,8 +1584,7 @@
private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
- private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
- private static final int MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT = 42;
+ private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42;
private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44;
@@ -1793,18 +1808,6 @@
AudioDeviceAttributes getDevice() {
return mDevice;
}
-
- boolean requestsBluetoothSco() {
- return mDevice != null
- && mDevice.getType()
- == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
- }
-
- boolean requestsSpeakerphone() {
- return mDevice != null
- && mDevice.getType()
- == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
- }
}
// @GuardedBy("mSetModeLock")
@@ -1852,14 +1855,14 @@
*/
// @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
- private void onUpdateCommunicationRoute(String eventSource) {
+ private void updateCommunicationRoute(String eventSource) {
AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice();
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ Log.v(TAG, "updateCommunicationRoute, preferredCommunicationDevice: "
+ preferredCommunicationDevice + " eventSource: " + eventSource);
}
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ "updateCommunicationRoute, preferredCommunicationDevice: "
+ preferredCommunicationDevice + " eventSource: " + eventSource)));
if (preferredCommunicationDevice == null
@@ -1895,7 +1898,7 @@
// @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
private void onUpdateCommunicationRouteClient(String eventSource) {
- onUpdateCommunicationRoute(eventSource);
+ updateCommunicationRoute(eventSource);
CommunicationRouteClient crc = topCommunicationRouteClient();
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "onUpdateCommunicationRouteClient, crc: "