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

Commit c4b9c96d authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

Refactor locks in "audio" service for device management

Do not use shared locks across AudioDeviceBroker, BtHelper
  and AudioDeviceInventory.
Synchronize package private  tmethods on BtHelper and avoid
  dealing with external locks.
Clean up code ordering, more nullability annotations and
  annotation for connection state.

Bug: 112863932
Test: regression test on BT usecases
Change-Id: Id9e19e3c5e5241af913d06275f325fe505c1b509
parent f2bfd385
Loading
Loading
Loading
Loading
+170 −105
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
package com.android.server.audio;

import static com.android.server.audio.AudioService.CONNECTION_STATE_CONNECTED;
import static com.android.server.audio.AudioService.CONNECTION_STATE_DISCONNECTED;

import android.annotation.NonNull;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
@@ -43,7 +46,7 @@ import java.util.ArrayList;
/** @hide */
/*package*/ final class AudioDeviceBroker {

    private static final String TAG = "AudioDeviceBroker";
    private static final String TAG = "AS.AudioDeviceBroker";

    private static final long BROKER_WAKELOCK_TIMEOUT_MS = 5000; //5s

@@ -62,27 +65,27 @@ import java.util.ArrayList;
    private int mForcedUseForCommExt;

    // Manages all connected devices, only ever accessed on the message loop
    //### or make it synchronized
    private final AudioDeviceInventory mDeviceInventory;
    // Manages notifications to BT service
    private final BtHelper mBtHelper;


    //-------------------------------------------------------------------
    // we use a different lock than mDeviceStateLock so as not to create
    // lock contention between enqueueing a message and handling them
    private static final Object sLastDeviceConnectionMsgTimeLock = new Object();
    @GuardedBy("sLastDeviceConnectionMsgTimeLock")
    private static long sLastDeviceConnectMsgTime = 0;

    private final Object mBluetoothA2dpEnabledLock = new Object();
    // General lock to be taken whenever the state of the audio devices is to be checked or changed
    private final Object mDeviceStateLock = new Object();

    // Request to override default use of A2DP for media.
    @GuardedBy("mBluetoothA2dpEnabledLock")
    @GuardedBy("mDeviceStateLock")
    private boolean mBluetoothA2dpEnabled;

    // lock always taken synchronized on mConnectedDevices
    /*package*/  final Object mA2dpAvrcpLock = new Object();
    // lock always taken synchronized on mConnectedDevices
    /*package*/  final Object mHearingAidLock = new Object();

    // lock always taken when accessing AudioService.mSetModeDeathHandlers
    // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
    /*package*/ final Object mSetModeLock = new Object();

    //-------------------------------------------------------------------
@@ -109,13 +112,17 @@ import java.util.ArrayList;
    // All post* methods are asynchronous

    /*package*/ void onSystemReady() {
        synchronized (mDeviceStateLock) {
            mBtHelper.onSystemReady();
        }
    }

    /*package*/ void onAudioServerDied() {
        // Restore forced usage for communications and record
        synchronized (mDeviceStateLock) {
            onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
            onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
        }
        // restore devices
        sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
    }
@@ -130,8 +137,10 @@ import java.util.ArrayList;
    }

    /*package*/ void disconnectAllBluetoothProfiles() {
        synchronized (mDeviceStateLock) {
            mBtHelper.disconnectAllBluetoothProfiles();
        }
    }

    /**
     * Handle BluetoothHeadset intents where the action is one of
@@ -140,11 +149,13 @@ import java.util.ArrayList;
     * @param intent
     */
    /*package*/ void receiveBtEvent(@NonNull Intent intent) {
        synchronized (mDeviceStateLock) {
            mBtHelper.receiveBtEvent(intent);
        }
    }

    /*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
        synchronized (mBluetoothA2dpEnabledLock) {
        synchronized (mDeviceStateLock) {
            if (mBluetoothA2dpEnabled == on) {
                return;
            }
@@ -158,6 +169,7 @@ import java.util.ArrayList;
    }

    /*package*/ void setSpeakerphoneOn(boolean on, String eventSource) {
        synchronized (mDeviceStateLock) {
            if (on) {
                if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
                    setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
@@ -170,17 +182,22 @@ import java.util.ArrayList;
            mForcedUseForCommExt = mForcedUseForComm;
            setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
        }
    }

    /*package*/ boolean isSpeakerphoneOn() {
        synchronized (mDeviceStateLock) {
            return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
        }
    }

    /*package*/ void setWiredDeviceConnectionState(int type,
            @AudioService.ConnectionState int state, String address, String name,
            String caller) {
        //TODO move logging here just like in setBluetooth* methods
        synchronized (mDeviceStateLock) {
            mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
        }
    }

    /*package*/ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
@@ -192,6 +209,7 @@ import java.util.ArrayList;
                        + " addr=" + device.getAddress()
                        + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
                        + " vol=" + a2dpVolume)).printLog(TAG));
        synchronized (mDeviceStateLock) {
            if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
                    new BtHelper.BluetoothA2dpDeviceInfo(device))) {
                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
@@ -199,16 +217,20 @@ import java.util.ArrayList;
                return 0;
            }
            return mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
                device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
                    device, state, profile, suppressNoisyIntent,
                    AudioSystem.DEVICE_NONE, a2dpVolume);
        }
    }

    /*package*/ int handleBluetoothA2dpActiveDeviceChange(
            @NonNull BluetoothDevice device,
            @AudioService.BtProfileConnectionState int state, int profile,
            boolean suppressNoisyIntent, int a2dpVolume) {
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile,
                    suppressNoisyIntent, a2dpVolume);
        }
    }

    /*package*/ int setBluetoothHearingAidDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
@@ -218,21 +240,28 @@ import java.util.ArrayList;
                        + " addr=" + device.getAddress()
                        + " supprNoisy=" + suppressNoisyIntent
                        + " src=" + eventSource)).printLog(TAG));
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
                    device, state, suppressNoisyIntent, musicDevice);
        }
    }

    // never called by system components
    /*package*/ void setBluetoothScoOnByApp(boolean on) {
        synchronized (mDeviceStateLock) {
            mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
        }
    }

    /*package*/ boolean isBluetoothScoOnForApp() {
        synchronized (mDeviceStateLock) {
            return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
        }
    }

    /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
        //Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
        synchronized (mDeviceStateLock) {
            if (on) {
                // do not accept SCO ON if SCO audio is not connected
                if (!mBtHelper.isBluetoothScoOn()) {
@@ -252,23 +281,28 @@ import java.util.ArrayList;
            // Un-mute ringtone stream volume
            mAudioService.setUpdateRingerModeServiceInt();
        }
    }

    /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.startWatchingRoutes(observer);
        }
    }

    /*package*/ AudioRoutesInfo getCurAudioRoutes() {
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.getCurAudioRoutes();
        }
    }

    /*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
        synchronized (mA2dpAvrcpLock) {
        synchronized (mDeviceStateLock) {
            return mBtHelper.isAvrcpAbsoluteVolumeSupported();
        }
    }

    /*package*/ boolean isBluetoothA2dpOn() {
        synchronized (mBluetoothA2dpEnabledLock) {
        synchronized (mDeviceStateLock) {
            return mBluetoothA2dpEnabled;
        }
    }
@@ -355,14 +389,12 @@ import java.util.ArrayList;
        sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
    }

    //###TODO unify with handleSetA2dpSinkConnectionState
    /*package*/ void postA2dpSinkConnection(int state,
            @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
        sendILMsg(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE, SENDMSG_QUEUE,
                state, btDeviceInfo, delay);
    }

    //###TODO unify with handleSetA2dpSourceConnectionState
    /*package*/ void postA2dpSourceConnection(int state,
            @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
        sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
@@ -395,7 +427,7 @@ import java.util.ArrayList;
                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
                .append(Binder.getCallingPid()).append(" src:").append(source).toString();

        synchronized (mBluetoothA2dpEnabledLock) {
        synchronized (mDeviceStateLock) {
            mBluetoothA2dpEnabled = on;
            mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE);
            onSetForceUse(
@@ -407,25 +439,38 @@ import java.util.ArrayList;

    /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
                                                       String deviceName) {
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
        }
    }

    /*package*/ void handleDisconnectA2dp() {
        synchronized (mDeviceStateLock) {
            mDeviceInventory.disconnectA2dp();
        }
    }
    /*package*/ void handleDisconnectA2dpSink() {
        synchronized (mDeviceStateLock) {
            mDeviceInventory.disconnectA2dpSink();
        }
    }

    /*package*/ void handleDisconnectHearingAid() {
        synchronized (mDeviceStateLock) {
            mDeviceInventory.disconnectHearingAid();
        }
    }

    /*package*/ void handleSetA2dpSinkConnectionState(@BluetoothProfile.BtProfileState int state,
                @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
        final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
        //### DOESN'T HONOR SYNC ON DEVICES -> make a synchronized version?
        // might be ok here because called on BT thread? + sync happening in
        //  checkSendBecomingNoisyIntent
        final int delay = mDeviceInventory.checkSendBecomingNoisyIntent(
        final int intState = (state == BluetoothA2dp.STATE_CONNECTED)
                ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED;
        final int delay;
        synchronized (mDeviceStateLock) {
            delay = mDeviceInventory.checkSendBecomingNoisyIntent(
                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState,
                    AudioSystem.DEVICE_NONE);
        }
        final String addr = btDeviceInfo == null ? "null" : btDeviceInfo.getBtDevice().getAddress();

        if (AudioService.DEBUG_DEVICES) {
@@ -437,10 +482,6 @@ import java.util.ArrayList;
                state, btDeviceInfo, delay);
    }

    /*package*/ void handleDisconnectHearingAid() {
        mDeviceInventory.disconnectHearingAid();
    }

    /*package*/ void handleSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
            @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
        final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
@@ -468,8 +509,6 @@ import java.util.ArrayList;
        sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
    }

    //###
    // must be called synchronized on mConnectedDevices
    /*package*/ boolean hasScheduledA2dpDockTimeout() {
        return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
    }
@@ -486,19 +525,19 @@ import java.util.ArrayList;
    }

    /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
        synchronized (mA2dpAvrcpLock) {
        synchronized (mDeviceStateLock) {
            mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
        }
    }

    /*package*/ boolean getBluetoothA2dpEnabled() {
        synchronized (mBluetoothA2dpEnabledLock) {
        synchronized (mDeviceStateLock) {
            return mBluetoothA2dpEnabled;
        }
    }

    /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
        synchronized (mA2dpAvrcpLock) {
        synchronized (mDeviceStateLock) {
            return mBtHelper.getA2dpCodec(device);
        }
    }
@@ -579,71 +618,97 @@ import java.util.ArrayList;
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RESTORE_DEVICES:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onRestoreDevices();
                    synchronized (mBluetoothA2dpEnabledLock) {
                        mBtHelper.onAudioServerDiedRestoreA2dp();
                    }
                    break;
                case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetWiredDeviceConnectionState(
                                (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
                    }
                    break;
                case MSG_I_BROADCAST_BT_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mBtHelper.onBroadcastScoConnectionState(msg.arg1);
                    }
                    break;
                case MSG_IIL_SET_FORCE_USE: // intented fall-through
                case MSG_IIL_SET_FORCE_USE: // intended fall-through
                case MSG_IIL_SET_FORCE_BT_A2DP_USE:
                    onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);
                    break;
                case MSG_REPORT_NEW_ROUTES:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onReportNewRoutes();
                    }
                    break;
                case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetA2dpSinkConnectionState(
                                (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
                    }
                    break;
                case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetA2dpSourceConnectionState(
                                (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
                    }
                    break;
                case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetHearingAidConnectionState(
                                (BluetoothDevice) msg.obj, msg.arg1);
                    }
                    break;
                case MSG_BT_HEADSET_CNCT_FAILED:
                    synchronized (mDeviceStateLock) {
                        mBtHelper.resetBluetoothSco();
                    }
                    break;
                case MSG_IL_BTA2DP_DOCK_TIMEOUT:
                    // msg.obj  == address of BTA2DP device
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
                    }
                    break;
                case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                    final int a2dpCodec;
                    final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
                    synchronized (mA2dpAvrcpLock) {
                    synchronized (mDeviceStateLock) {
                        a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
                    }
                        mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
                                new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec));
                    }
                    break;
                case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
                    onSendBecomingNoisyIntent();
                    break;
                case MSG_II_SET_HEARING_AID_VOLUME:
                    synchronized (mDeviceStateLock) {
                        mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
                    }
                    break;
                case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
                    synchronized (mDeviceStateLock) {
                        mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
                    }
                    break;
                case MSG_I_DISCONNECT_BT_SCO:
                    synchronized (mDeviceStateLock) {
                        mBtHelper.disconnectBluetoothSco(msg.arg1);
                    }
                    break;
                case MSG_TOGGLE_HDMI:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onToggleHdmi();
                    }
                    break;
                case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
                                (BtHelper.BluetoothA2dpDeviceInfo) msg.obj);
                    }
                    break;
                default:
                    Log.wtf(TAG, "Invalid message " + msg.what);
+32 −37
Original line number Diff line number Diff line
@@ -162,10 +162,7 @@ public final class AudioDeviceInventory {
                "A2DP sink connected: device addr=" + address + " state=" + state
                        + " vol=" + a2dpVolume));

        final int a2dpCodec;
        synchronized (mDeviceBroker.mA2dpAvrcpLock) {
            a2dpCodec = btInfo.getCodec();
        }
        final int a2dpCodec = btInfo.getCodec();

        synchronized (mConnectedDevices) {
            final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
@@ -508,7 +505,6 @@ public final class AudioDeviceInventory {

    /*package*/ void disconnectA2dp() {
        synchronized (mConnectedDevices) {
            synchronized (mDeviceBroker.mA2dpAvrcpLock) {
            final ArraySet<String> toRemove = new ArraySet<>();
            // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
            mConnectedDevices.values().forEach(deviceInfo -> {
@@ -519,14 +515,13 @@ public final class AudioDeviceInventory {
            if (toRemove.size() > 0) {
                final int delay = checkSendBecomingNoisyIntentInt(
                        AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                            0, AudioSystem.DEVICE_NONE);
                        AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
                toRemove.stream().forEach(deviceAddress ->
                        makeA2dpDeviceUnavailableLater(deviceAddress, delay)
                );
            }
        }
    }
    }

    /*package*/ void disconnectA2dpSink() {
        synchronized (mConnectedDevices) {
@@ -543,7 +538,6 @@ public final class AudioDeviceInventory {

    /*package*/ void disconnectHearingAid() {
        synchronized (mConnectedDevices) {
            synchronized (mDeviceBroker.mHearingAidLock) {
            final ArraySet<String> toRemove = new ArraySet<>();
            // Disconnect ALL DEVICE_OUT_HEARING_AID devices
            mConnectedDevices.values().forEach(deviceInfo -> {
@@ -561,12 +555,12 @@ public final class AudioDeviceInventory {
            }
        }
    }
    }

    // must be called before removing the device from mConnectedDevices
    // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
    // from AudioSystem
    /*package*/ int checkSendBecomingNoisyIntent(int device, int state, int musicDevice) {
    /*package*/ int checkSendBecomingNoisyIntent(int device,
            @AudioService.ConnectionState int state, int musicDevice) {
        synchronized (mConnectedDevices) {
            return checkSendBecomingNoisyIntentInt(device, state, musicDevice);
        }
@@ -840,8 +834,9 @@ public final class AudioDeviceInventory {
    // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
    // from AudioSystem
    @GuardedBy("mConnectedDevices")
    private int checkSendBecomingNoisyIntentInt(int device, int state, int musicDevice) {
        if (state != 0) {
    private int checkSendBecomingNoisyIntentInt(int device,
            @AudioService.ConnectionState int state, int musicDevice) {
        if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
            return 0;
        }
        if ((device & mBecomingNoisyIntentDevices) == 0) {
+0 −2
Original line number Diff line number Diff line
@@ -786,7 +786,6 @@ public class AudioService extends IAudioService.Stub
                mPrescaleAbsoluteVolume[i] = preScale[i];
            }
        }

    }

    public void systemReady() {
@@ -3821,7 +3820,6 @@ public class AudioService extends IAudioService.Stub

    private static void sendMsg(Handler handler, int msg,
            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {

        if (existingMsgPolicy == SENDMSG_REPLACE) {
            handler.removeMessages(msg);
        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
+245 −274

File changed.

Preview size limit exceeded, changes collapsed.