Loading services/core/java/com/android/server/audio/AudioDeviceBroker.java +314 −175 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/audio/AudioService.java +25 −24 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -3765,7 +3768,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(); Loading Loading @@ -4406,10 +4409,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) Loading @@ -4417,24 +4420,21 @@ 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 { mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource); Binder.restoreCallingIdentity(ident); } } } /** @see AudioManager#isSpeakerphoneOn() */ public boolean isSpeakerphoneOn() { 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()")) { Loading @@ -4443,7 +4443,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; } Loading @@ -4469,7 +4469,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 Loading Loading @@ -4516,7 +4516,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); } Loading @@ -4535,10 +4535,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, Loading @@ -4549,9 +4549,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(); } Loading @@ -4566,9 +4566,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) Loading Loading @@ -7848,6 +7848,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)); Loading services/core/java/com/android/server/audio/BtHelper.java +112 −309 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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; Loading Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading @@ -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) { Loading @@ -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); return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); } // @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); } // @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) { Loading Loading @@ -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"); Loading Loading @@ -740,77 +664,16 @@ 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; Loading Loading @@ -919,19 +782,6 @@ public class BtHelper { 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); } mScoClients.remove(this); } } //----------------------------------------------------- // Utilities private void sendStickyBroadcastToAll(Intent intent) { Loading Loading @@ -982,49 +832,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(); Loading Loading @@ -1070,10 +877,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); Loading Loading
services/core/java/com/android/server/audio/AudioDeviceBroker.java +314 −175 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/audio/AudioService.java +25 −24 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -3765,7 +3768,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(); Loading Loading @@ -4406,10 +4409,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) Loading @@ -4417,24 +4420,21 @@ 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 { mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource); Binder.restoreCallingIdentity(ident); } } } /** @see AudioManager#isSpeakerphoneOn() */ public boolean isSpeakerphoneOn() { 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()")) { Loading @@ -4443,7 +4443,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; } Loading @@ -4469,7 +4469,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 Loading Loading @@ -4516,7 +4516,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); } Loading @@ -4535,10 +4535,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, Loading @@ -4549,9 +4549,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(); } Loading @@ -4566,9 +4566,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) Loading Loading @@ -7848,6 +7848,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)); Loading
services/core/java/com/android/server/audio/BtHelper.java +112 −309 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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; Loading Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading @@ -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) { Loading @@ -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); return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); } // @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); } // @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) { Loading Loading @@ -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"); Loading Loading @@ -740,77 +664,16 @@ 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; Loading Loading @@ -919,19 +782,6 @@ public class BtHelper { 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); } mScoClients.remove(this); } } //----------------------------------------------------- // Utilities private void sendStickyBroadcastToAll(Intent intent) { Loading Loading @@ -982,49 +832,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(); Loading Loading @@ -1070,10 +877,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); Loading