summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Eric Laurent <elaurent@google.com> 2020-12-23 13:31:15 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2020-12-23 13:31:15 +0000
commitb1733f14bedc4cef9a43cffb5cf6460afadb7c52 (patch)
tree1deaf3256ab197e6f52d6676c593bfe5b1511403
parent54e6b9c79b86ab1c8407c13a3c3d9be231d6a028 (diff)
parentad87098233b42a2afd18ad52b3da67d20a14c189 (diff)
AudioService: refactor communication route control am: ec51aa8207 am: ad87098233
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1534431 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Id01d7af0da83c4acc44303cb77e1858914a25687
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java489
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java49
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java420
3 files changed, 451 insertions, 507 deletions
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1615998f7787..3b407f14297e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -25,6 +25,8 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
@@ -39,6 +41,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -46,8 +49,8 @@ import android.util.PrintWriterPrinter;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -73,10 +76,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** Forced device usage for communications sent to AudioSystem */
private int mForcedUseForComm;
- /**
- * Externally reported force device usage state returned by getters: always consistent
- * with requests by setters */
- private int mForcedUseForCommExt;
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
@@ -136,7 +135,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
setupMessaging(mContext);
mForcedUseForComm = AudioSystem.FORCE_NONE;
- mForcedUseForCommExt = mForcedUseForComm;
}
/*package*/ Context getContext() {
@@ -159,15 +157,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
/*package*/ void onAudioServerDied() {
- // Restore forced usage for communications and record
- synchronized (mDeviceStateLock) {
- AudioSystem.setParameters(
- "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
- onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm,
- false /*fromA2dp*/, "onAudioServerDied");
- onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm,
- false /*fromA2dp*/, "onAudioServerDied");
- }
// restore devices
sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
@@ -219,85 +208,156 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Turns speakerphone on/off
* @param on
* @param eventSource for logging purposes
- * @return true if speakerphone state changed
*/
- /*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
- synchronized (mDeviceStateLock) {
- if (!addSpeakerphoneClient(cb, pid, on)) {
- return false;
- }
- if (on) {
- // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
- // are mutually exclusive.
- // See symmetrical operation for startBluetoothScoForClient_Sync().
- mBtHelper.stopBluetoothScoForPid(pid);
+ /*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
+
+ 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(AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+ } else {
+ CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+ if (client == null || !client.requestsSpeakerphone()) {
+ return;
+ }
+ }
+ setCommunicationRouteForClient(
+ cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
}
- final boolean wasOn = isSpeakerphoneOn();
- updateSpeakerphoneOn(eventSource);
- return (wasOn != isSpeakerphoneOn());
}
}
- /**
- * Turns speakerphone off for a given pid and update speakerphone state.
- * @param pid
- */
@GuardedBy("mDeviceStateLock")
- private void setSpeakerphoneOffForPid(int pid) {
- SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+ /*package*/ void setCommunicationRouteForClient(
+ IBinder cb, int pid, AudioDeviceAttributes device,
+ int scoAudioMode, String eventSource) {
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
+ }
+
+ final boolean wasBtScoRequested = isBluetoothScoRequested();
+ final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
+ CommunicationRouteClient client;
+
+
+ // Save previous client route in case of failure to start BT SCO audio
+ AudioDeviceAttributes prevClientDevice = null;
+ client = getCommunicationRouteClientForPid(pid);
+ if (client != null) {
+ prevClientDevice = client.getDevice();
+ }
+
+ if (device != null) {
+ client = addCommunicationRouteClient(cb, pid, device);
+ if (client == null) {
+ Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
+ + pid + " and device: " + device);
+ }
+ } else {
+ client = removeCommunicationRouteClient(cb, true);
+ }
if (client == null) {
return;
}
- client.unregisterDeathRecipient();
- mSpeakerphoneClients.remove(client);
- final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
- .append(pid).append(")").toString();
- updateSpeakerphoneOn(eventSource);
- }
- @GuardedBy("mDeviceStateLock")
- private void updateSpeakerphoneOn(String eventSource) {
- if (isSpeakerphoneOnRequested()) {
- if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
- setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+ boolean isBtScoRequested = isBluetoothScoRequested();
+ if (isBtScoRequested && !wasBtScoRequested) {
+ if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+ Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+ + pid);
+ // clean up or restore previous client selection
+ if (prevClientDevice != null) {
+ addCommunicationRouteClient(cb, pid, prevClientDevice);
+ } else {
+ removeCommunicationRouteClient(cb, true);
+ }
}
- mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
- } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
- if (mBtHelper.isBluetoothScoOn()) {
- mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
- setForceUse_Async(
- AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
- } else {
- mForcedUseForComm = AudioSystem.FORCE_NONE;
+ } else if (!isBtScoRequested && wasBtScoRequested) {
+ mBtHelper.stopBluetoothSco(eventSource);
+ }
+
+ if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
+ try {
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+ } catch (Exception e) {
+ Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
}
}
- mForcedUseForCommExt = mForcedUseForComm;
- setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
+
+ sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
}
/**
- * Returns if speakerphone is requested ON or OFF.
- * If the current audio mode owner is in the speakerphone client list, use this preference.
+ * 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).
- * Speakerphone is requested OFF if no client is in the list.
- * @return true if speakerphone is requested ON, false otherwise
+ * null is returned if no client is in the list.
+ * @return AudioDeviceAttributes the requested device for communication.
*/
+
@GuardedBy("mDeviceStateLock")
- private boolean isSpeakerphoneOnRequested() {
- if (mSpeakerphoneClients.isEmpty()) {
- return false;
- }
- for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+ private AudioDeviceAttributes requestedCommunicationDevice() {
+ AudioDeviceAttributes device = null;
+ for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getPid() == mModeOwnerPid) {
- return cl.isOn();
+ device = cl.getDevice();
}
}
- return mSpeakerphoneClients.get(0).isOn();
+ if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
+ device = mCommunicationRouteClients.get(0).getDevice();
+ }
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "requestedCommunicationDevice, device: "
+ + device + " mode owner pid: " + mModeOwnerPid);
+ }
+ return device;
+ }
+
+ /**
+ * Helper method on top of requestedCommunicationDevice() indicating if
+ * speakerphone ON is currently requested or not.
+ * @return true if speakerphone ON requested, false otherwise.
+ */
+
+ private boolean isSpeakerphoneRequested() {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ return device != null
+ && device.getType()
+ == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ }
}
+ /**
+ * Indicates if active route selection for communication is speakerphone.
+ * @return true if speakerphone is active, false otherwise.
+ */
/*package*/ boolean isSpeakerphoneOn() {
+ return getForcedUseForComm() == AudioSystem.FORCE_SPEAKER;
+ }
+
+ /**
+ * Helper method on top of requestedCommunicationDevice() indicating if
+ * Bluetooth SCO ON is currently requested or not.
+ * @return true if Bluetooth SCO ON is requested, false otherwise.
+ */
+ /*package*/ boolean isBluetoothScoRequested() {
synchronized (mDeviceStateLock) {
- return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ return device != null
+ && device.getType()
+ == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
}
}
@@ -348,7 +408,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
-
/*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
@@ -431,42 +490,28 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
}
- // never called by system components
- /*package*/ void setBluetoothScoOnByApp(boolean on) {
- synchronized (mDeviceStateLock) {
- mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
- }
- }
- /*package*/ boolean isBluetoothScoOnForApp() {
+ /**
+ * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
+ */
+ private boolean mBluetoothScoOn;
+
+ /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
+ }
synchronized (mDeviceStateLock) {
- return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
+ mBluetoothScoOn = on;
+ sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
}
}
- /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
- //Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
- synchronized (mDeviceStateLock) {
- if (on) {
- // do not accept SCO ON if SCO audio is not connected
- if (!mBtHelper.isBluetoothScoOn()) {
- mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
- return;
- }
- mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
- } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
- mForcedUseForComm = isSpeakerphoneOnRequested()
- ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
- }
- mForcedUseForCommExt = mForcedUseForComm;
- AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
- }
- // Un-mute ringtone stream volume
- mAudioService.postUpdateRingerModeServiceInt();
+ /**
+ * Indicates if active route selection for communication is Bluetooth SCO.
+ * @return true if Bluetooth SCO is active , false otherwise.
+ */
+ /*package*/ boolean isBluetoothScoOn() {
+ return getForcedUseForComm() == AudioSystem.FORCE_BT_SCO;
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
@@ -509,22 +554,43 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
}
- @GuardedBy("mSetModeLock")
- /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+ /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
@NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
- // are mutually exclusive.
- // See symmetrical operation for setSpeakerphoneOn(true).
- setSpeakerphoneOffForPid(Binder.getCallingPid());
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
+ }
+
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "");
+ setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+ if (!isBluetoothScoRequested()) {
+ Log.w(TAG, "startBluetoothScoForClient_Sync: rejected for pid: "
+ + pid + " mode owner pid: " + mModeOwnerPid);
+ postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ }
}
}
- @GuardedBy("mSetModeLock")
- /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ /*package*/ void stopBluetoothScoForClient(
+ IBinder cb, int pid, @NonNull String eventSource) {
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
+ }
+
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+ if (client == null || !client.requestsBluetoothSco()) {
+ return;
+ }
+ setCommunicationRouteForClient(
+ cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ }
}
}
@@ -696,12 +762,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
hearingAidProfile);
}
- /*package*/ void postScoClientDied(Object obj) {
- sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
- }
-
- /*package*/ void postSpeakerphoneClientDied(Object obj) {
- sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
+ /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
+ sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
}
/*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
@@ -821,15 +883,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.dump(pw, prefix);
+ pw.println("\n" + prefix + "Communication route clients:");
+ mCommunicationRouteClients.forEach((cl) -> {
+ pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
+ + cl.getDevice() + " cb: " + cl.getBinder()); });
+
pw.println("\n" + prefix + "mForcedUseForComm: "
+ AudioSystem.forceUseConfigToString(mForcedUseForComm));
- pw.println(prefix + "mForcedUseForCommExt: "
- + AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
- pw.println(prefix + "Speakerphone clients:");
- mSpeakerphoneClients.forEach((cl) -> {
- pw.println(" " + prefix + "pid: " + cl.getPid() + " on: "
- + cl.isOn() + " cb: " + cl.getBinder()); });
mBtHelper.dump(pw, prefix);
}
@@ -852,6 +913,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
.set(MediaMetrics.Property.FORCE_USE_MODE,
AudioSystem.forceUseConfigToString(config))
.record();
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
+ + fromA2dp + ">, eventSource<" + eventSource + ">)");
+ }
AudioSystem.setForceUse(useCase, config);
}
@@ -917,9 +983,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onRestoreDevices();
- mBtHelper.onAudioServerDiedRestoreA2dp();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onRestoreDevices();
+ mBtHelper.onAudioServerDiedRestoreA2dp();
+ onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
+ }
}
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -1009,25 +1078,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (mModeOwnerPid != msg.arg1) {
mModeOwnerPid = msg.arg1;
if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
- updateSpeakerphoneOn("setNewModeOwner");
- }
- if (mModeOwnerPid != 0) {
- mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
+ onUpdateCommunicationRoute("setNewModeOwner");
}
}
}
}
break;
- case MSG_L_SCOCLIENT_DIED:
+ case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mBtHelper.scoClientDied(msg.obj);
+ onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
}
}
break;
- case MSG_L_SPEAKERPHONE_CLIENT_DIED:
- synchronized (mDeviceStateLock) {
- speakerphoneClientDied(msg.obj);
+ case MSG_L_UPDATE_COMMUNICATION_ROUTE:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onUpdateCommunicationRoute((String) msg.obj);
+ }
}
break;
case MSG_TOGGLE_HDMI:
@@ -1207,17 +1275,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
// process external command to (dis)connect a hearing aid device
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
- // a ScoClient died in BtHelper
- private static final int MSG_L_SCOCLIENT_DIED = 32;
- private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33;
- private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34;
+ private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
+ private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
+
+ private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
+ private static final int MSG_CHECK_MUTE_MUSIC = 35;
+ private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
- private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
- private static final int MSG_CHECK_MUTE_MUSIC = 36;
- private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
+ 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_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
- private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
+ private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -1372,14 +1440,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- private class SpeakerphoneClient implements IBinder.DeathRecipient {
+ // List of applications requesting a specific route for communication.
+ @GuardedBy("mDeviceStateLock")
+ private final @NonNull LinkedList<CommunicationRouteClient> mCommunicationRouteClients =
+ new LinkedList<CommunicationRouteClient>();
+
+ private class CommunicationRouteClient implements IBinder.DeathRecipient {
private final IBinder mCb;
private final int mPid;
- private final boolean mOn;
- SpeakerphoneClient(IBinder cb, int pid, boolean on) {
+ private AudioDeviceAttributes mDevice;
+
+ CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
mCb = cb;
mPid = pid;
- mOn = on;
+ mDevice = device;
}
public boolean registerDeathRecipient() {
@@ -1388,7 +1462,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
mCb.linkToDeath(this, 0);
status = true;
} catch (RemoteException e) {
- Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
+ Log.w(TAG, "CommunicationRouteClient could not link to " + mCb + " binder death");
}
return status;
}
@@ -1397,13 +1471,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
try {
mCb.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
- Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
+ Log.w(TAG, "CommunicationRouteClient could not not unregistered to binder");
}
}
@Override
public void binderDied() {
- postSpeakerphoneClientDied(this);
+ postCommunicationRouteClientDied(this);
}
IBinder getBinder() {
@@ -1414,29 +1488,99 @@ import java.util.concurrent.atomic.AtomicBoolean;
return mPid;
}
- boolean isOn() {
- return mOn;
+ 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")
@GuardedBy("mDeviceStateLock")
- private void speakerphoneClientDied(Object obj) {
- if (obj == null) {
+ private void onCommunicationRouteClientDied(CommunicationRouteClient client) {
+ if (client == null) {
return;
}
Log.w(TAG, "Speaker client died");
- if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
- updateSpeakerphoneOn("speakerphoneClientDied");
+ if (removeCommunicationRouteClient(client.getBinder(), false)
+ != null) {
+ onUpdateCommunicationRoute("onCommunicationRouteClientDied");
+ }
+ }
+
+ /**
+ * Determines which forced usage for communication should be sent to audio policy manager
+ * as a function of current SCO audio activation state and active communication route requests.
+ * SCO audio state has the highest priority as it can result from external activation by
+ * telephony service.
+ * @return selected forced usage for communication.
+ */
+ @GuardedBy("mDeviceStateLock")
+ private int getForcedUseForComm() {
+ boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
+
+ if (btSCoOn) {
+ return AudioSystem.FORCE_BT_SCO;
+ }
+ if (isSpeakerphoneRequested()) {
+ return AudioSystem.FORCE_SPEAKER;
+ }
+ return AudioSystem.FORCE_NONE;
+ }
+
+ /**
+ * Configures audio policy manager and audio HAL according to active communication route.
+ * Always called from message Handler.
+ */
+ // @GuardedBy("mSetModeLock")
+ @GuardedBy("mDeviceStateLock")
+ private void onUpdateCommunicationRoute(String eventSource) {
+ mForcedUseForComm = getForcedUseForComm();
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onUpdateCommunicationRoute, mForcedUseForComm: " + mForcedUseForComm
+ + " eventSource: " + eventSource);
}
+
+ if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
+ AudioSystem.setParameters("BT_SCO=on");
+ setForceUse_Async(
+ AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO, eventSource);
+ setForceUse_Async(
+ AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
+ } else {
+ AudioSystem.setParameters("BT_SCO=off");
+ setForceUse_Async(
+ AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+ if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
+ setForceUse_Async(
+ AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER, eventSource);
+ } else {
+ setForceUse_Async(
+ AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE, eventSource);
+ }
+ }
+ mAudioService.postUpdateRingerModeServiceInt();
}
- private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
- for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+ private CommunicationRouteClient removeCommunicationRouteClient(
+ IBinder cb, boolean unregister) {
+ for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getBinder() == cb) {
if (unregister) {
cl.unregisterDeathRecipient();
}
- mSpeakerphoneClients.remove(cl);
+ mCommunicationRouteClients.remove(cl);
return cl;
}
}
@@ -1444,30 +1588,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
@GuardedBy("mDeviceStateLock")
- private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
+ private CommunicationRouteClient addCommunicationRouteClient(
+ IBinder cb, int pid, AudioDeviceAttributes device) {
// always insert new request at first position
- removeSpeakerphoneClient(cb, true);
- SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
+ removeCommunicationRouteClient(cb, true);
+ CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
if (client.registerDeathRecipient()) {
- mSpeakerphoneClients.add(0, client);
- return true;
+ mCommunicationRouteClients.add(0, client);
+ return client;
}
- return false;
+ return null;
}
@GuardedBy("mDeviceStateLock")
- private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
- for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+ private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
+ for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getPid() == pid) {
return cl;
}
}
return null;
}
-
- // List of clients requesting speakerPhone ON
- @GuardedBy("mDeviceStateLock")
- private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
- new ArrayList<SpeakerphoneClient>();
-
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 83350ed492eb..98c78172431e 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -207,6 +207,9 @@ public class AudioService extends IAudioService.Stub
/** debug calls to devices APIs */
protected static final boolean DEBUG_DEVICES = false;
+ /** Debug communication route */
+ protected static final boolean DEBUG_COMM_RTE = false;
+
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
@@ -3689,7 +3692,7 @@ public class AudioService extends IAudioService.Stub
final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|| ringerMode == AudioManager.RINGER_MODE_SILENT;
final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
- && isBluetoothScoOn();
+ && mDeviceBroker.isBluetoothScoOn();
// Ask audio policy engine to force use Bluetooth SCO channel if needed
final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+ "/" + Binder.getCallingPid();
@@ -4274,10 +4277,10 @@ public class AudioService extends IAudioService.Stub
// for logging only
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
+
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
- final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
+ MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
.setUid(uid)
@@ -4285,17 +4288,9 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.STATE, on
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
.record();
-
- if (stateChanged) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
+ final long ident = Binder.clearCallingIdentity();
+ mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+ Binder.restoreCallingIdentity(ident);
}
/** @see AudioManager#isSpeakerphoneOn() */
@@ -4303,6 +4298,11 @@ public class AudioService extends IAudioService.Stub
return mDeviceBroker.isSpeakerphoneOn();
}
+
+ /** BT SCO audio state seen by apps using the deprecated API setBluetoothScoOn().
+ * @see isBluetoothScoOn() */
+ private boolean mBtScoOnByApp;
+
/** @see AudioManager#setBluetoothScoOn(boolean) */
public void setBluetoothScoOn(boolean on) {
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
@@ -4311,7 +4311,7 @@ public class AudioService extends IAudioService.Stub
// Only enable calls from system components
if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
- mDeviceBroker.setBluetoothScoOnByApp(on);
+ mBtScoOnByApp = on;
return;
}
@@ -4337,7 +4337,7 @@ public class AudioService extends IAudioService.Stub
* Note that it doesn't report internal state, but state seen by apps (which may have
* called setBluetoothScoOn() */
public boolean isBluetoothScoOn() {
- return mDeviceBroker.isBluetoothScoOnForApp();
+ return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
}
// TODO investigate internal users due to deprecation of SDK API
@@ -4384,7 +4384,7 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(scoAudioMode))
.record();
- startBluetoothScoInt(cb, scoAudioMode, eventSource);
+ startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
}
@@ -4403,10 +4403,10 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
.record();
- startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+ startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
}
- void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
+ void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -4417,9 +4417,9 @@ public class AudioService extends IAudioService.Stub
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
return;
}
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
- }
+ final long ident = Binder.clearCallingIdentity();
+ mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+ Binder.restoreCallingIdentity(ident);
mmi.record();
}
@@ -4434,9 +4434,9 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
- }
+ final long ident = Binder.clearCallingIdentity();
+ mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+ Binder.restoreCallingIdentity(ident);
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.setUid(uid)
.setPid(pid)
@@ -7670,6 +7670,7 @@ public class AudioService extends IAudioService.Stub
pw.print(" mHasVibrator="); pw.println(mHasVibrator);
pw.print(" mVolumePolicy="); pw.println(mVolumePolicy);
pw.print(" mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
+ pw.print(" mBtScoOnByApp="); pw.println(mBtScoOnByApp);
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 7616557ac80f..42820bba164f 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -30,8 +30,6 @@ import android.content.Intent;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -39,9 +37,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.List;
-import java.util.NoSuchElementException;
import java.util.Objects;
/**
@@ -58,10 +54,6 @@ public class BtHelper {
mDeviceBroker = broker;
}
- // List of clients having issued a SCO start request
- @GuardedBy("BtHelper.this")
- private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
-
// BluetoothHeadset API to control SCO connection
private @Nullable BluetoothHeadset mBluetoothHeadset;
@@ -301,6 +293,8 @@ public class BtHelper {
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
+
+ Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
setBtScoActiveDevice(btDevice);
@@ -308,20 +302,16 @@ public class BtHelper {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- // broadcast intent if the connection was initated by AudioService
- if (!mScoClients.isEmpty()
- && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
- || mScoAudioState == SCO_STATE_ACTIVATE_REQ
- || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
- || mScoAudioState == SCO_STATE_DEACTIVATING)) {
- broadcast = true;
- }
+ 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;
@@ -333,21 +323,21 @@ public class BtHelper {
// 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 the mScoClients list not empty.
+ // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
|| (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
- && !mScoClients.isEmpty())) {
+ && mDeviceBroker.isBluetoothScoRequested())) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- broadcast = false;
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+ broadcast = true;
break;
}
}
- // Tear down SCO if disconnected from external
- if (mScoAudioState == SCO_STATE_DEACTIVATING) {
- clearAllScoClients(0, false);
+ if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+ broadcast = true;
}
mScoAudioState = SCO_STATE_INACTIVE;
break;
@@ -356,11 +346,8 @@ public class BtHelper {
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
- broadcast = false;
break;
default:
- // do not broadcast CONNECTING or invalid state
- broadcast = false;
break;
}
if (broadcast) {
@@ -386,81 +373,19 @@ public class BtHelper {
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
}
- /**
- * Disconnect all SCO connections started by {@link AudioManager} except those started by
- * {@param exceptPid}
- *
- * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
- */
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
- checkScoAudioState();
- if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
- return;
- }
- clearAllScoClients(exceptPid, true);
- }
-
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
+ /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
@NonNull String eventSource) {
- ScoClient client = getScoClient(cb, true);
- // The calling identity must be cleared before calling ScoClient.incCount().
- // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
- // and this must be done on behalf of system server to make sure permissions are granted.
- // The caller identity must be cleared after getScoClient() because it is needed if a new
- // client is created.
- final long ident = Binder.clearCallingIdentity();
- try {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
- client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
- } catch (NullPointerException e) {
- Log.e(TAG, "Null ScoClient", e);
- }
- Binder.restoreCallingIdentity(ident);
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
- @NonNull String eventSource) {
- ScoClient client = getScoClient(cb, false);
- // The calling identity must be cleared before calling ScoClient.decCount().
- // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
- // and this must be done on behalf of system server to make sure permissions are granted.
- final long ident = Binder.clearCallingIdentity();
- if (client != null) {
- stopAndRemoveClient(client, eventSource);
- }
- Binder.restoreCallingIdentity(ident);
+ AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+ return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
}
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
- ScoClient client = getScoClientForPid(pid);
- if (client == null) {
- return;
- }
- final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
- .append(pid).append(")").toString();
- stopAndRemoveClient(client, eventSource);
- }
-
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- // @GuardedBy("BtHelper.this")
- private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
+ /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
- client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
- SCO_MODE_VIRTUAL_CALL);
- // If a disconnection is pending, the client will be removed when clearAllScoClients()
- // is called form receiveBtEvent()
- if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
- && mScoAudioState != SCO_STATE_DEACTIVATING) {
- client.remove(false /*stop */, true /*unregister*/);
- }
+ return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
}
/*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
@@ -507,7 +432,6 @@ public class BtHelper {
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
- clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
AudioSystem.setParameters("A2dpSuspended=false");
@@ -733,195 +657,122 @@ public class BtHelper {
};
//----------------------------------------------------------------------
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void scoClientDied(Object obj) {
- final ScoClient client = (ScoClient) obj;
- client.remove(true /*stop*/, false /*unregister*/);
- Log.w(TAG, "SCO client died");
- }
-
- private class ScoClient implements IBinder.DeathRecipient {
- private IBinder mCb; // To be notified of client's death
- private int mCreatorPid;
-
- ScoClient(IBinder cb) {
- mCb = cb;
- mCreatorPid = Binder.getCallingPid();
- }
-
- public void registerDeathRecipient() {
- try {
- mCb.linkToDeath(this, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
- }
- }
-
- public void unregisterDeathRecipient() {
- try {
- mCb.unlinkToDeath(this, 0);
- } catch (NoSuchElementException e) {
- Log.w(TAG, "ScoClient could not not unregistered to binder");
- }
- }
-
- @Override
- public void binderDied() {
- // process this from DeviceBroker's message queue to take the right locks since
- // this event can impact SCO mode and requires querying audio mode stack
- mDeviceBroker.postScoClientDied(this);
- }
-
- IBinder getBinder() {
- return mCb;
- }
-
- int getPid() {
- return mCreatorPid;
- }
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @GuardedBy("BtHelper.this")
- private boolean requestScoState(int state, int scoAudioMode) {
- checkScoAudioState();
- if (mScoClients.size() != 1) {
- Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
- + ", num SCO clients=" + mScoClients.size());
- return true;
- }
- if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
- // Make sure that the state transitions to CONNECTING even if we cannot initiate
- // the connection.
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
- // Accept SCO audio activation only in NORMAL audio mode or if the mode is
- // currently controlled by the same client process.
- final int modeOwnerPid = mDeviceBroker.getModeOwnerPid();
- if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
- Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
- + modeOwnerPid + " != creatorPid " + mCreatorPid);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- switch (mScoAudioState) {
- case SCO_STATE_INACTIVE:
- mScoAudioMode = scoAudioMode;
- if (scoAudioMode == SCO_MODE_UNDEFINED) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- if (mBluetoothHeadsetDevice != null) {
- mScoAudioMode = Settings.Global.getInt(
- mDeviceBroker.getContentResolver(),
- "bluetooth_sco_channel_"
- + mBluetoothHeadsetDevice.getAddress(),
- SCO_MODE_VIRTUAL_CALL);
- if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- }
- }
- }
- if (mBluetoothHeadset == null) {
- if (getBluetoothHeadset()) {
- mScoAudioState = SCO_STATE_ACTIVATE_REQ;
- } else {
- Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
- + " connection, mScoAudioMode=" + mScoAudioMode);
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
+ private boolean requestScoState(int state, int scoAudioMode) {
+ checkScoAudioState();
+ if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ // Make sure that the state transitions to CONNECTING even if we cannot initiate
+ // the connection.
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+ switch (mScoAudioState) {
+ case SCO_STATE_INACTIVE:
+ mScoAudioMode = scoAudioMode;
+ if (scoAudioMode == SCO_MODE_UNDEFINED) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+ if (mBluetoothHeadsetDevice != null) {
+ mScoAudioMode = Settings.Global.getInt(
+ mDeviceBroker.getContentResolver(),
+ "bluetooth_sco_channel_"
+ + mBluetoothHeadsetDevice.getAddress(),
+ SCO_MODE_VIRTUAL_CALL);
+ if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
}
- break;
}
- if (mBluetoothHeadsetDevice == null) {
- Log.w(TAG, "requestScoState: no active device while connecting,"
- + " mScoAudioMode=" + mScoAudioMode);
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ }
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
} else {
- Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
- + " failed, mScoAudioMode=" + mScoAudioMode);
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " connection, mScoAudioMode=" + mScoAudioMode);
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
return false;
}
break;
- case SCO_STATE_DEACTIVATING:
- mScoAudioState = SCO_STATE_ACTIVATE_REQ;
- break;
- case SCO_STATE_DEACTIVATE_REQ:
+ }
+ if (mBluetoothHeadsetDevice == null) {
+ Log.w(TAG, "requestScoState: no active device while connecting,"
+ + " mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
+ }
+ if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
- break;
- case SCO_STATE_ACTIVE_INTERNAL:
- Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
- break;
- default:
- Log.w(TAG, "requestScoState: failed to connect in state "
- + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ } else {
+ Log.w(TAG, "requestScoState: connect to "
+ + getAnonymizedAddress(mBluetoothHeadsetDevice)
+ + " failed, mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
return false;
- }
- } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
- switch (mScoAudioState) {
- case SCO_STATE_ACTIVE_INTERNAL:
- if (mBluetoothHeadset == null) {
- if (getBluetoothHeadset()) {
- mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
- } else {
- Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
- + " disconnection, mScoAudioMode=" + mScoAudioMode);
- mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- break;
- }
- if (mBluetoothHeadsetDevice == null) {
- mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- break;
- }
- if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_DEACTIVATING;
+ }
+ break;
+ case SCO_STATE_DEACTIVATING:
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ break;
+ case SCO_STATE_ACTIVE_INTERNAL:
+ Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to connect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
+ }
+ } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVE_INTERNAL:
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
} else {
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " disconnection, mScoAudioMode=" + mScoAudioMode);
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
}
break;
- case SCO_STATE_ACTIVATE_REQ:
+ }
+ if (mBluetoothHeadsetDevice == null) {
mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
break;
- default:
- Log.w(TAG, "requestScoState: failed to disconnect in state "
- + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- }
- return true;
- }
-
- @GuardedBy("BtHelper.this")
- void remove(boolean stop, boolean unregister) {
- if (unregister) {
- unregisterDeathRecipient();
- }
- if (stop) {
- requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
- SCO_MODE_VIRTUAL_CALL);
+ }
+ if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_DEACTIVATING;
+ } else {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ break;
+ case SCO_STATE_ACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to disconnect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
}
- mScoClients.remove(this);
}
+ return true;
}
//-----------------------------------------------------
@@ -974,49 +825,6 @@ public class BtHelper {
}
}
-
- @GuardedBy("BtHelper.this")
- private ScoClient getScoClient(IBinder cb, boolean create) {
- for (ScoClient existingClient : mScoClients) {
- if (existingClient.getBinder() == cb) {
- return existingClient;
- }
- }
- if (create) {
- ScoClient newClient = new ScoClient(cb);
- newClient.registerDeathRecipient();
- mScoClients.add(newClient);
- return newClient;
- }
- return null;
- }
-
- @GuardedBy("BtHelper.this")
- private ScoClient getScoClientForPid(int pid) {
- for (ScoClient cl : mScoClients) {
- if (cl.getPid() == pid) {
- return cl;
- }
- }
- return null;
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @GuardedBy("BtHelper.this")
- private void clearAllScoClients(int exceptPid, boolean stopSco) {
- final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
- for (ScoClient cl : mScoClients) {
- if (cl.getPid() != exceptPid) {
- clients.add(cl);
- }
- }
- for (ScoClient cl : clients) {
- cl.remove(stopSco, true /*unregister*/);
- }
-
- }
-
private boolean getBluetoothHeadset() {
boolean result = false;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1062,10 +870,6 @@ public class BtHelper {
pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
- pw.println(prefix + "Sco clients:");
- mScoClients.forEach((cl) -> {
- pw.println(" " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
-
pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
pw.println(prefix + "mA2dp: " + mA2dp);
pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);