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

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

AudioService: prevent ignoring already connected devices on restore

AudioDeviceInventory.onRestoreDevices is called after audioserver
restarted. In case a device had been reconnected after
audioserver died, and before onRestoreDevices had run, we're
calling AudioSystem.setDeviceConnectionState(AVAILABLE) to
reconnect the device. But when the device is already connected
it doesn't return OK, which was then considered an error, and
the device was ignored.
The fix consists in checking whether a device is already connected
before attempting to reconnect it, but also in keeping track of
devices that were attempted to be connected while audioserver
was restarting.
The new code also changes the role of mApmConnectedDevices:
it contains all the devices, regardless of their type, which
have been made available to AudioPolicy.
While AudioService handles an audio server restart, don't
attempt to connect the device to AudioPolicy. Instead keep
track of them by adding them to mConnectedDevices.
mApmConnectedDevices will be rebuilt in
AudioDeviceInventory.onRestoreDevices() which is
called only after audioserver is back online.

Bug: 393821141
Flag: EXEMPT bug fix
Test: kill audio server and connect HFP/AVRCP headset before device
restore, verify media goes to A2DP

Change-Id: I0eda2c02d4a92db72ee086dca79363730c0ab985
parent 951c20e0
Loading
Loading
Loading
Loading
+63 −9
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.server.audio.AudioService.BtCommDeviceActiveType;
@@ -127,6 +128,10 @@ public class AudioDeviceBroker {
    // Delay before unmuting call after HFP device switch
    private static final int HFP_SWITCH_CALL_UNMUTE_DELAY_MS = 2000;

    // Delay before attempting to restore devices again after audioserver died and a previous
    // restore attempt failed
    private static final int DEVICE_RESTORE_TRY_AGAIN_DELAY_MS = 500;

    private final @NonNull AudioService mAudioService;
    private final @NonNull Context mContext;
    private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -150,7 +155,6 @@ public class AudioDeviceBroker {
    // Adapter for system_server-reserved operations
    private final SystemServerAdapter mSystemServer;


    //-------------------------------------------------------------------
    // we use a different lock than mDeviceStateLock so as not to create
    // lock contention between enqueueing a message and handling them
@@ -161,6 +165,8 @@ public class AudioDeviceBroker {
    // General lock to be taken whenever the state of the audio devices is to be checked or changed
    private final Object mDeviceStateLock = new Object();

    private final AtomicBoolean mWaitingForDeviceRestore = new AtomicBoolean(false);

    // Request to override default use of A2DP for media.
    private AtomicBoolean mBluetoothA2dpEnabled = new AtomicBoolean(false);

@@ -242,6 +248,48 @@ public class AudioDeviceBroker {
        init();
    }

    /**
     * Signals that device restoration is done or not after an audioserver restart.
     * Important: if modifying or adding a codepath using this API, be careful about the call
     * site as this is currently called from {@link AudioDeviceInventory#onRestoreDevices()} with
     * {@link AudioDeviceInventory#mDevicesLock} held, which can present a cross deadlock if
     * {@link #mDeviceStateLock} was not already held when calling onRestoreDevices.
     * @param waiting
     */
    /*package*/ void setWaitingForDeviceRestore(boolean waiting) {
        Slog.i(TAG, "setWaitingForDeviceRestore " + waiting);
        synchronized (mDeviceStateLock) {
            if (!waiting) {
                // only allow to end the wait
                //  - if there are no more server died messages in flight
                if (mAudioService.isHandlingAudioServerDeath()) {
                    Slog.i(TAG, "not ready to stop waiting for device restore");
                    return;
                }
                //  - if there are no more attempts to restore devices
                if (mBrokerHandler.hasMessages(MSG_RESTORE_DEVICES)) {
                    return;
                }
            }
            mWaitingForDeviceRestore.set(waiting);
        }
    }

    /**
     * Returns whether audioserver has died and devices haven't been restored yet.
     * When true, new device connections (to APM) will not be attempted, and the connection will
     * be delayed until the device restore operation is started.
     * It is used inside
     * {@link AudioDeviceInventory#setApmDeviceConnectionAvailable(AudioDeviceAttributes, int, boolean)}
     * to check if a device can be made available to APM.
     * @return true if audioserver has died and the devices haven't been restored yet. False when
     *     audioserver is up and running
     */
    /*package*/ boolean isWaitingForDeviceRestore() {
        // lock-free reader relies on atomicity of update
        return mWaitingForDeviceRestore.get();
    }

    private void initRoutingStrategyIds() {
        List<AudioProductStrategy> strategies = AudioProductStrategy.getAudioProductStrategies();
        mCommunicationStrategyId = -1;
@@ -1912,15 +1960,21 @@ public class AudioDeviceBroker {
                        synchronized (mDeviceStateLock) {
                            initRoutingStrategyIds();
                            updateActiveCommunicationDevice();
                            mDeviceInventory.onRestoreDevices();
                            if (mDeviceInventory.onRestoreDevices()) {
                                synchronized (mBluetoothAudioStateLock) {
                                    reapplyAudioHalBluetoothState();
                                }
                                final int forceForMedia = getBluetoothA2dpEnabled()
                                        ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP;
                                setForceUse_Async(
                                    AudioSystem.FOR_MEDIA, forceForMedia, "MSG_RESTORE_DEVICES");
                                        AudioSystem.FOR_MEDIA, forceForMedia,
                                        "MSG_RESTORE_DEVICES");
                                updateCommunicationRoute("MSG_RESTORE_DEVICES");
                            } else {
                                // device restoration failed and needs to be attempted again later
                                sendMsg(MSG_RESTORE_DEVICES, SENDMSG_REPLACE,
                                        DEVICE_RESTORE_TRY_AGAIN_DELAY_MS);
                            }
                        }
                    }
                    break;
+257 −117

File changed.

Preview size limit exceeded, changes collapsed.

+20 −1
Original line number Diff line number Diff line
@@ -769,6 +769,10 @@ public class AudioService extends IAudioService.Stub
        public void onError(int error) {
            switch (error) {
                case AudioSystem.AUDIO_STATUS_SERVER_DIED:
                    // do not handle device connections right now, tell AudioDeviceBroker
                    // to just make note of connections, and wait for the device restoration
                    // to complete after audioserver is back online
                    mDeviceBroker.setWaitingForDeviceRestore(true);
                    // check for null in case error callback is called during instance creation
                    if (mRecordMonitor != null) {
                        mRecordMonitor.onAudioServerDied();
@@ -2077,10 +2081,25 @@ public class AudioService extends IAudioService.Stub
                INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
    }
    /**
     * Returns whether there are pending audioserver death messages
     * @return true if
     */
    protected boolean isHandlingAudioServerDeath() {
        if (mAudioHandler.hasMessages(MSG_AUDIO_SERVER_DIED)) {
            return true;
        }
        if (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK) {
            return true;
        }
        return false;
    }
    public void onAudioServerDied() {
        if (!mSystemReady ||
                (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) {
            Log.e(TAG, "Audioserver died.");
            sDeviceLogger.enqueueAndSlog("AudioService.onAudioServerDied",
                    EventLogger.Event.ALOGE, TAG);
            sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                    "onAudioServerDied() audioserver died"));
            sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0,