diff options
| author | 2020-12-23 13:31:15 +0000 | |
|---|---|---|
| committer | 2020-12-23 13:31:15 +0000 | |
| commit | b1733f14bedc4cef9a43cffb5cf6460afadb7c52 (patch) | |
| tree | 1deaf3256ab197e6f52d6676c593bfe5b1511403 | |
| parent | 54e6b9c79b86ab1c8407c13a3c3d9be231d6a028 (diff) | |
| parent | ad87098233b42a2afd18ad52b3da67d20a14c189 (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
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); |