Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad870982 authored by Eric Laurent's avatar Eric Laurent Committed by Automerger Merge Worker
Browse files

AudioService: refactor communication route control am: ec51aa82

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1534431

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I3749a0d5ace4abccefdf156159a320f774a3abb2
parents db951726 ec51aa82
Loading
Loading
Loading
Loading
+314 −175

File changed.

Preview size limit exceeded, changes collapsed.

+25 −24
Original line number Original line Diff line number Diff line
@@ -207,6 +207,9 @@ public class AudioService extends IAudioService.Stub
    /** debug calls to devices APIs */
    /** debug calls to devices APIs */
    protected static final boolean DEBUG_DEVICES = false;
    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. */
    /** How long to delay before persisting a change in volume/ringer mode. */
    private static final int PERSIST_DELAY = 500;
    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
        final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                || ringerMode == AudioManager.RINGER_MODE_SILENT;
                || ringerMode == AudioManager.RINGER_MODE_SILENT;
        final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
        final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                && isBluetoothScoOn();
                && mDeviceBroker.isBluetoothScoOn();
        // Ask audio policy engine to force use Bluetooth SCO channel if needed
        // Ask audio policy engine to force use Bluetooth SCO channel if needed
        final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
        final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                + "/" + Binder.getCallingPid();
                + "/" + Binder.getCallingPid();
@@ -4274,10 +4277,10 @@ public class AudioService extends IAudioService.Stub
        // for logging only
        // for logging only
        final int uid = Binder.getCallingUid();
        final int uid = Binder.getCallingUid();
        final int pid = Binder.getCallingPid();
        final int pid = Binder.getCallingPid();

        final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
        final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                .append(") from u/pid:").append(uid).append("/")
                .append(") from u/pid:").append(uid).append("/")
                .append(pid).toString();
                .append(pid).toString();
        final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
        new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
        new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
                + MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
                + MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
                .setUid(uid)
                .setUid(uid)
@@ -4285,24 +4288,21 @@ public class AudioService extends IAudioService.Stub
                .set(MediaMetrics.Property.STATE, on
                .set(MediaMetrics.Property.STATE, on
                        ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
                        ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
                .record();
                .record();

        if (stateChanged) {
        final long ident = Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
            try {
        mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
                mContext.sendBroadcastAsUser(
                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
            } finally {
        Binder.restoreCallingIdentity(ident);
        Binder.restoreCallingIdentity(ident);
    }
    }
        }
    }


    /** @see AudioManager#isSpeakerphoneOn() */
    /** @see AudioManager#isSpeakerphoneOn() */
    public boolean isSpeakerphoneOn() {
    public boolean isSpeakerphoneOn() {
        return mDeviceBroker.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) */
    /** @see AudioManager#setBluetoothScoOn(boolean) */
    public void setBluetoothScoOn(boolean on) {
    public void setBluetoothScoOn(boolean on) {
        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
@@ -4311,7 +4311,7 @@ public class AudioService extends IAudioService.Stub


        // Only enable calls from system components
        // Only enable calls from system components
        if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
        if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
            mDeviceBroker.setBluetoothScoOnByApp(on);
            mBtScoOnByApp = on;
            return;
            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
     * Note that it doesn't report internal state, but state seen by apps (which may have
     * called setBluetoothScoOn() */
     * called setBluetoothScoOn() */
    public boolean isBluetoothScoOn() {
    public boolean isBluetoothScoOn() {
        return mDeviceBroker.isBluetoothScoOnForApp();
        return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
    }
    }


    // TODO investigate internal users due to deprecation of SDK API
    // 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,
                .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                        BtHelper.scoAudioModeToString(scoAudioMode))
                        BtHelper.scoAudioModeToString(scoAudioMode))
                .record();
                .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,
                .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                        BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
                        BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
                .record();
                .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)
        MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
                .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
                .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                .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();
            mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
            return;
            return;
        }
        }
        synchronized (mDeviceBroker.mSetModeLock) {
        final long ident = Binder.clearCallingIdentity();
            mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
        mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
        }
        Binder.restoreCallingIdentity(ident);
        mmi.record();
        mmi.record();
    }
    }


@@ -4434,9 +4434,9 @@ public class AudioService extends IAudioService.Stub
        final String eventSource =  new StringBuilder("stopBluetoothSco()")
        final String eventSource =  new StringBuilder("stopBluetoothSco()")
                .append(") from u/pid:").append(uid).append("/")
                .append(") from u/pid:").append(uid).append("/")
                .append(pid).toString();
                .append(pid).toString();
        synchronized (mDeviceBroker.mSetModeLock) {
        final long ident = Binder.clearCallingIdentity();
            mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
        mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
        }
        Binder.restoreCallingIdentity(ident);
        new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
        new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                .setUid(uid)
                .setUid(uid)
                .setPid(pid)
                .setPid(pid)
@@ -7670,6 +7670,7 @@ public class AudioService extends IAudioService.Stub
        pw.print("  mHasVibrator="); pw.println(mHasVibrator);
        pw.print("  mHasVibrator="); pw.println(mHasVibrator);
        pw.print("  mVolumePolicy="); pw.println(mVolumePolicy);
        pw.print("  mVolumePolicy="); pw.println(mVolumePolicy);
        pw.print("  mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
        pw.print("  mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
        pw.print("  mBtScoOnByApp="); pw.println(mBtScoOnByApp);
        pw.print("  mIsSingleVolume="); pw.println(mIsSingleVolume);
        pw.print("  mIsSingleVolume="); pw.println(mIsSingleVolume);
        pw.print("  mUseFixedVolume="); pw.println(mUseFixedVolume);
        pw.print("  mUseFixedVolume="); pw.println(mUseFixedVolume);
        pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
        pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
+112 −308
Original line number Original line Diff line number Diff line
@@ -30,8 +30,6 @@ import android.content.Intent;
import android.media.AudioManager;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.AudioSystem;
import android.os.Binder;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings;
import android.util.Log;
import android.util.Log;
@@ -39,9 +37,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Objects;


/**
/**
@@ -58,10 +54,6 @@ public class BtHelper {
        mDeviceBroker = broker;
        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
    // BluetoothHeadset API to control SCO connection
    private @Nullable BluetoothHeadset mBluetoothHeadset;
    private @Nullable BluetoothHeadset mBluetoothHeadset;


@@ -301,6 +293,8 @@ public class BtHelper {
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    /*package*/ synchronized void receiveBtEvent(Intent intent) {
    /*package*/ synchronized void receiveBtEvent(Intent intent) {
        final String action = intent.getAction();
        final String action = intent.getAction();

        Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
        if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
        if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
            BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            setBtScoActiveDevice(btDevice);
            setBtScoActiveDevice(btDevice);
@@ -308,20 +302,16 @@ public class BtHelper {
            boolean broadcast = false;
            boolean broadcast = false;
            int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
            int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
            int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
            int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
            // broadcast intent if the connection was initated by AudioService
            Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
            if (!mScoClients.isEmpty()
                    && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
                    || mScoAudioState == SCO_STATE_ACTIVATE_REQ
                    || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
                    || mScoAudioState == SCO_STATE_DEACTIVATING)) {
                broadcast = true;
            }
            switch (btState) {
            switch (btState) {
                case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                    scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                    scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                    if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
                    if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
                            && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                            && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                        mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                        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");
                    mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
                    break;
                    break;
@@ -333,21 +323,21 @@ public class BtHelper {
                    // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
                    // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
                    // 2) If audio was connected then disconnected via Bluetooth APIs and
                    // 2) If audio was connected then disconnected via Bluetooth APIs and
                    // we still have pending activation requests by apps: this is indicated by
                    // 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
                    if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
                            || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
                            || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
                                    && !mScoClients.isEmpty())) {
                                    && mDeviceBroker.isBluetoothScoRequested())) {
                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
                                && connectBluetoothScoAudioHelper(mBluetoothHeadset,
                                && connectBluetoothScoAudioHelper(mBluetoothHeadset,
                                mBluetoothHeadsetDevice, mScoAudioMode)) {
                                mBluetoothHeadsetDevice, mScoAudioMode)) {
                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
                            broadcast = false;
                            scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
                            broadcast = true;
                            break;
                            break;
                        }
                        }
                    }
                    }
                    // Tear down SCO if disconnected from external
                    if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
                    if (mScoAudioState == SCO_STATE_DEACTIVATING) {
                        broadcast = true;
                        clearAllScoClients(0, false);
                    }
                    }
                    mScoAudioState = SCO_STATE_INACTIVE;
                    mScoAudioState = SCO_STATE_INACTIVE;
                    break;
                    break;
@@ -356,11 +346,8 @@ public class BtHelper {
                            && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                            && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                        mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                        mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                    }
                    }
                    broadcast = false;
                    break;
                    break;
                default:
                default:
                    // do not broadcast CONNECTING or invalid state
                    broadcast = false;
                    break;
                    break;
            }
            }
            if (broadcast) {
            if (broadcast) {
@@ -386,81 +373,19 @@ public class BtHelper {
                == BluetoothHeadset.STATE_AUDIO_CONNECTED;
                == 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.mSetModeLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
    /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
                @NonNull String eventSource) {
                @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));
        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
            client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
        return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
        } catch (NullPointerException e) {
            Log.e(TAG, "Null ScoClient", e);
        }
        Binder.restoreCallingIdentity(ident);
    }
    }


    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
    /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
            @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) {
        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
        return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
                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*/);
        }
    }
    }


    /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
    /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
@@ -507,7 +432,6 @@ public class BtHelper {
    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    /*package*/ synchronized void resetBluetoothSco() {
    /*package*/ synchronized void resetBluetoothSco() {
        clearAllScoClients(0, false);
        mScoAudioState = SCO_STATE_INACTIVE;
        mScoAudioState = SCO_STATE_INACTIVE;
        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
        AudioSystem.setParameters("A2dpSuspended=false");
        AudioSystem.setParameters("A2dpSuspended=false");
@@ -733,77 +657,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.mSetModeLock")
    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    @GuardedBy("BtHelper.this")
    @GuardedBy("BtHelper.this")
    private boolean requestScoState(int state, int scoAudioMode) {
    private boolean requestScoState(int state, int scoAudioMode) {
        checkScoAudioState();
        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) {
        if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
            // Make sure that the state transitions to CONNECTING even if we cannot initiate
            // Make sure that the state transitions to CONNECTING even if we cannot initiate
            // the connection.
            // the connection.
            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
            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) {
            switch (mScoAudioState) {
                case SCO_STATE_INACTIVE:
                case SCO_STATE_INACTIVE:
                    mScoAudioMode = scoAudioMode;
                    mScoAudioMode = scoAudioMode;
@@ -843,7 +706,8 @@ public class BtHelper {
                            mBluetoothHeadsetDevice, mScoAudioMode)) {
                            mBluetoothHeadsetDevice, mScoAudioMode)) {
                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
                    } else {
                    } else {
                            Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
                        Log.w(TAG, "requestScoState: connect to "
                                + getAnonymizedAddress(mBluetoothHeadsetDevice)
                                + " failed, mScoAudioMode=" + mScoAudioMode);
                                + " failed, mScoAudioMode=" + mScoAudioMode);
                        broadcastScoConnectionState(
                        broadcastScoConnectionState(
                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -911,19 +775,6 @@ public class BtHelper {
        return true;
        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
    // Utilities
    private void sendStickyBroadcastToAll(Intent intent) {
    private void sendStickyBroadcastToAll(Intent intent) {
@@ -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() {
    private boolean getBluetoothHeadset() {
        boolean result = false;
        boolean result = false;
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1062,10 +870,6 @@ public class BtHelper {
        pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
        pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
        pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
        pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
        pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
        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("\n" + prefix + "mHearingAid: " + mHearingAid);
        pw.println(prefix + "mA2dp: " + mA2dp);
        pw.println(prefix + "mA2dp: " + mA2dp);
        pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
        pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);