Merge changes from topic "presubmit-am-ff7699f855c14d1ca2c8a3438a9402f0" into sc-v2-dev-plus-aosp
* changes:
[conflict] audioservice: improve SCO audio control logic 2p: a0b2ee74b2
audioservice: improve SCO audio control logic
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 406ff9b..f42d274 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -251,8 +251,8 @@
return;
}
}
- setCommunicationRouteForClient(
- cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource));
}
}
}
@@ -282,8 +282,8 @@
return false;
}
}
- setCommunicationRouteForClient(
- cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource));
}
}
return true;
@@ -347,26 +347,35 @@
}
/**
- * Returns the device currently requested for communication use case.
- * If the current audio mode owner is in the communication route client list,
- * use this preference.
- * Otherwise use first client's preference (first client corresponds to latest request).
- * null is returned if no client is in the list.
- * @return AudioDeviceAttributes the requested device for communication.
+ * Returns the communication client with the highest priority:
+ * - 1) the client which is currently also controlling the audio mode
+ * - 2) the first client in the stack if there is no audio mode owner
+ * - 3) no client otherwise
+ * @return CommunicationRouteClient the client driving the communication use case routing.
*/
-
@GuardedBy("mDeviceStateLock")
- private AudioDeviceAttributes requestedCommunicationDevice() {
- AudioDeviceAttributes device = null;
- for (CommunicationRouteClient cl : mCommunicationRouteClients) {
- if (cl.getPid() == mModeOwnerPid) {
- device = cl.getDevice();
+ private CommunicationRouteClient topCommunicationRouteClient() {
+ for (CommunicationRouteClient crc : mCommunicationRouteClients) {
+ if (crc.getPid() == mModeOwnerPid) {
+ return crc;
}
}
if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
- device = mCommunicationRouteClients.get(0).getDevice();
+ return mCommunicationRouteClients.get(0);
}
+ return null;
+ }
+ /**
+ * Returns the device currently requested for communication use case.
+ * Use the device requested by the communication route client selected by
+ * {@link #topCommunicationRouteClient()} if any or none otherwise.
+ * @return AudioDeviceAttributes the requested device for communication.
+ */
+ @GuardedBy("mDeviceStateLock")
+ private AudioDeviceAttributes requestedCommunicationDevice() {
+ CommunicationRouteClient crc = topCommunicationRouteClient();
+ AudioDeviceAttributes device = crc != null ? crc.getDevice() : null;
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "requestedCommunicationDevice, device: "
+ device + " mode owner pid: " + mModeOwnerPid);
@@ -696,7 +705,7 @@
}
synchronized (mDeviceStateLock) {
mBluetoothScoOn = on;
- sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+ postUpdateCommunicationRouteClient(eventSource);
}
}
@@ -756,7 +765,9 @@
synchronized (mDeviceStateLock) {
AudioDeviceAttributes device =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
- setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, device, scoAudioMode, eventSource));
}
}
}
@@ -774,8 +785,8 @@
if (client == null || !client.requestsBluetoothSco()) {
return;
}
- setCommunicationRouteForClient(
- cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource));
}
}
}
@@ -976,6 +987,61 @@
MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
}
+ /*package*/ void postUpdateCommunicationRouteClient(String eventSource) {
+ 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 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;
+
+ CommunicationClientInfo(@NonNull IBinder cb, int pid, @NonNull AudioDeviceAttributes device,
+ int scoAudioMode, @NonNull String eventSource) {
+ mCb = cb;
+ mPid = pid;
+ mDevice = device;
+ mScoAudioMode = scoAudioMode;
+ mEventSource = eventSource;
+ }
+
+ // redefine equality op so we can match messages intended for this client
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CommunicationClientInfo)) {
+ return false;
+ }
+
+ return mCb.equals(((CommunicationClientInfo) o).mCb)
+ && mPid == ((CommunicationClientInfo) o).mPid;
+ }
+
+ @Override
+ public String toString() {
+ return "CommunicationClientInfo mCb=" + mCb.toString()
+ +"mPid=" + mPid
+ +"mDevice=" + mDevice.toString()
+ +"mScoAudioMode=" + mScoAudioMode
+ +"mEventSource=" + mEventSource;
+ }
+ }
+
//---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method
@@ -1252,18 +1318,30 @@
synchronized (mDeviceStateLock) {
mModeOwnerPid = msg.arg1;
if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
- onUpdateCommunicationRoute("setNewModeOwner");
+ onUpdateCommunicationRouteClient("setNewModeOwner");
}
}
}
break;
- case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
+
+ case MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
+ CommunicationClientInfo info = (CommunicationClientInfo) msg.obj;
+ setCommunicationRouteForClient(info.mCb, info.mPid, info.mDevice,
+ info.mScoAudioMode, info.mEventSource);
}
}
break;
+
+ case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onUpdateCommunicationRouteClient((String) msg.obj);
+ }
+ }
+ break;
+
case MSG_L_UPDATE_COMMUNICATION_ROUTE:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
@@ -1271,6 +1349,23 @@
}
}
break;
+
+ case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
+ }
+ }
+ break;
+
+ case MSG_I_SCO_AUDIO_STATE_CHANGED:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onScoAudioStateChanged(msg.arg1);
+ }
+ }
+ break;
+
case MSG_TOGGLE_HDMI:
synchronized (mDeviceStateLock) {
mDeviceInventory.onToggleHdmi();
@@ -1424,6 +1519,9 @@
private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;
+ private static final int MSG_L_SET_COMMUNICATION_ROUTE_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;
private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
//
@@ -1642,9 +1740,8 @@
return;
}
Log.w(TAG, "Communication client died");
- setCommunicationRouteForClient(
- client.getBinder(), client.getPid(), null, BtHelper.SCO_MODE_UNDEFINED,
- "onCommunicationRouteClientDied");
+ removeCommunicationRouteClient(client.getBinder(), true);
+ onUpdateCommunicationRouteClient("onCommunicationRouteClientDied");
}
/**
@@ -1698,11 +1795,31 @@
AudioSystem.setParameters("BT_SCO=on");
}
if (preferredCommunicationDevice == null) {
- postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
+ removePreferredDevicesForStrategySync(mCommunicationStrategyId);
} else {
- postSetPreferredDevicesForStrategy(
+ setPreferredDevicesForStrategySync(
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
}
+ onUpdatePhoneStrategyDevice(preferredCommunicationDevice);
+ }
+
+ /**
+ * Select new communication device from communication route client at the top of the stack
+ * and restore communication route including restarting SCO audio if needed.
+ */
+ // @GuardedBy("mSetModeLock")
+ @GuardedBy("mDeviceStateLock")
+ private void onUpdateCommunicationRouteClient(String eventSource) {
+ onUpdateCommunicationRoute(eventSource);
+ CommunicationRouteClient crc = topCommunicationRouteClient();
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onUpdateCommunicationRouteClient, crc: "
+ + crc + " eventSource: " + eventSource);
+ }
+ if (crc != null) {
+ setCommunicationRouteForClient(crc.getBinder(), crc.getPid(), crc.getDevice(),
+ BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ }
}
private void onUpdatePhoneStrategyDevice(AudioDeviceAttributes device) {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 4485c5b..7d9173d 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -103,7 +103,7 @@
// SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
/*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
// SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
- private static final int SCO_MODE_VR = 2;
+ private static final int SCO_MODE_VR = 2;
// max valid SCO audio mode values
private static final int SCO_MODE_MAX = 2;
@@ -304,69 +304,77 @@
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
setBtScoActiveDevice(btDevice);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
- boolean broadcast = false;
- int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
- switch (btState) {
- 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;
- }
- mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
- break;
- case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
- mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
- 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
- || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
- && mDeviceBroker.isBluetoothScoRequested())) {
- 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) {
- 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) {
- broadcastScoConnectionState(scoAudioState);
- //FIXME: this is to maintain compatibility with deprecated intent
- // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
- Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
- newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
- sendStickyBroadcastToAll(newIntent);
- }
+ Log.i(TAG,"receiveBtEvent ACTION_AUDIO_STATE_CHANGED: "+btState);
+ mDeviceBroker.postScoAudioStateChanged(btState);
}
}
/**
+ * Exclusively called from AudioDeviceBroker when handling MSG_I_SCO_AUDIO_STATE_CHANGED
+ * as part of the serialization of the communication route selection
+ */
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ void onScoAudioStateChanged(int state) {
+ boolean broadcast = false;
+ int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ 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;
+ }
+ mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
+ break;
+ case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
+ 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) {
+ 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) {
+ broadcastScoConnectionState(scoAudioState);
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
+ Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
+ sendStickyBroadcastToAll(newIntent);
+ }
+
+ }
+ /**
*
* @return false if SCO isn't connected
*/
@@ -744,6 +752,15 @@
case SCO_STATE_ACTIVE_INTERNAL:
Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
break;
+ case SCO_STATE_ACTIVE_EXTERNAL:
+ /* Confirm SCO Audio connection to requesting app as it is already connected
+ * externally (i.e. through SCO APIs by Telecom service).
+ * Once SCO Audio is disconnected by the external owner, we will reconnect it
+ * automatically on behalf of the requesting app and the state will move to
+ * SCO_STATE_ACTIVE_INTERNAL.
+ */
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ break;
default:
Log.w(TAG, "requestScoState: failed to connect in state "
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);