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

Commit 77bfa66d authored by Sungsoo Lim's avatar Sungsoo Lim Committed by Android (Google) Code Review
Browse files

Merge "Call listeners whenever players are removed"

parents 3d8600a0 2afdbc4c
Loading
Loading
Loading
Loading
+65 −93
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package com.android.server.media;

import android.annotation.Nullable;
import android.annotation.NonNull;
import android.content.Context;
import android.media.AudioPlaybackConfiguration;
import android.media.IAudioService;
@@ -27,14 +27,15 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;
import java.util.HashSet;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -49,47 +50,57 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
    private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor();

    /**
     * Called when the state of audio player is changed.
     * Listener for handling the active state changes of audio players.
     */
    interface OnAudioPlayerStateChangedListener {
        void onAudioPlayerStateChanged(
                int uid, int prevState, @Nullable AudioPlaybackConfiguration config);
    interface OnAudioPlayerActiveStateChangedListener {
        /**
         * Called when the active state of audio player is changed.
         *
         * @param config The audio playback configuration for the audio player of which active state
         *              was changed. If {@param isRemoved} is {@code true}, this hold outdated
         *              information.
         * @param isRemoved {@code true} if the audio player is removed.
         */
        void onAudioPlayerActiveStateChanged(
                @NonNull AudioPlaybackConfiguration config, boolean isRemoved);
    }

    private final static class MessageHandler extends Handler {
        private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1;
        private static final int MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED = 1;

        private final OnAudioPlayerStateChangedListener mListsner;
        private final OnAudioPlayerActiveStateChangedListener mListener;

        public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) {
        public MessageHandler(Looper looper, OnAudioPlayerActiveStateChangedListener listener) {
            super(looper);
            mListsner = listener;
            mListener = listener;
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_AUDIO_PLAYER_STATE_CHANGED:
                    mListsner.onAudioPlayerStateChanged(
                            msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj);
                case MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED:
                    mListener.onAudioPlayerActiveStateChanged((AudioPlaybackConfiguration) msg.obj,
                            msg.arg1 != 0);
                    break;
            }
        }

        public void sendAudioPlayerStateChangedMessage(int uid, int prevState,
                AudioPlaybackConfiguration config) {
            obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget();
        public void sendAudioPlayerActiveStateChangedMessage(
                final AudioPlaybackConfiguration config, final boolean isRemoved) {
            obtainMessage(MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED,
                    isRemoved ? 1 : 0, 0 /* unused */, config).sendToTarget();
        }
    }

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap =
            new HashMap<>();
    private final Map<OnAudioPlayerActiveStateChangedListener, MessageHandler> mListenerMap =
            new ArrayMap<>();
    @GuardedBy("mLock")
    private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>();
    private final Set<Integer> mActiveAudioUids = new ArraySet();
    @GuardedBy("mLock")
    private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>();
    private ArrayMap<Integer, AudioPlaybackConfiguration> mPrevActiveAudioPlaybackConfigs =
            new ArrayMap<>();
    // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
    // The UID whose audio playback becomes active at the last comes first.
    // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@@ -122,32 +133,24 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
        }
        final long token = Binder.clearCallingIdentity();
        try {
            final Map<Integer, Integer> prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates);
            final Map<Integer, HashSet<Integer>> prevAudioPlayersForUid =
                    new HashMap<>(mAudioPlayersForUid);
            synchronized (mLock) {
                mAudioPlayerStates.clear();
                mAudioPlayersForUid.clear();
                // Update mActiveAudioUids
                mActiveAudioUids.clear();
                ArrayMap<Integer, AudioPlaybackConfiguration> activeAudioPlaybackConfigs =
                        new ArrayMap<>();
                for (AudioPlaybackConfiguration config : configs) {
                    int pii = config.getPlayerInterfaceId();
                    int uid = config.getClientUid();
                    mAudioPlayerStates.put(pii, config.getPlayerState());
                    HashSet<Integer> players = mAudioPlayersForUid.get(uid);
                    if (players == null) {
                        players = new HashSet<Integer>();
                        players.add(pii);
                        mAudioPlayersForUid.put(uid, players);
                    } else {
                        players.add(pii);
                    }
                    if (config.isActive()) {
                        mActiveAudioUids.add(config.getClientUid());
                        activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);
                    }
                for (AudioPlaybackConfiguration config : configs) {
                    if (!config.isActive()) {
                        continue;
                }

                    int uid = config.getClientUid();
                    if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) {
                // Update mSortedAuioPlaybackClientUids.
                for (int i = 0; i < activeAudioPlaybackConfigs.size(); ++i) {
                    AudioPlaybackConfiguration config = activeAudioPlaybackConfigs.valueAt(i);
                    final int uid = config.getClientUid();
                    if (!mPrevActiveAudioPlaybackConfigs.containsKey(
                            config.getPlayerInterfaceId())) {
                        if (DEBUG) {
                            Log.d(TAG, "Found a new active media playback. " +
                                    AudioPlaybackConfiguration.toLogFriendlyString(config));
@@ -163,40 +166,21 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
                        mSortedAudioPlaybackClientUids.add(0, uid);
                    }
                }
                // Notify the change of audio player states.
                // Notify the active state change of audio players.
                for (AudioPlaybackConfiguration config : configs) {
                    final Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId());
                    final int prevStateInt =
                            (prevState == null) ? AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN :
                                prevState.intValue();
                    if (prevStateInt != config.getPlayerState()) {
                        sendAudioPlayerStateChangedMessageLocked(
                                config.getClientUid(), prevStateInt, config);
                    }
                }
                for (Integer prevUid : prevAudioPlayersForUid.keySet()) {
                    // If all players for prevUid is removed, notify the prev state was
                    // PLAYER_STATE_STARTED only when there were a player whose state was
                    // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify.
                    if (!mAudioPlayersForUid.containsKey(prevUid)) {
                        Set<Integer> prevPlayers = prevAudioPlayersForUid.get(prevUid);
                        int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN;
                        for (int pii : prevPlayers) {
                            Integer state = prevAudioPlayerStates.get(pii);
                            if (state == null) {
                                continue;
                            }
                            if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                                prevState = state;
                                break;
                            } else if (prevState
                                    == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) {
                                prevState = state;
                            }
                    final int pii = config.getPlayerInterfaceId();
                    boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;
                    if (wasActive != config.isActive()) {
                        sendAudioPlayerActiveStateChangedMessageLocked(
                                config, /* isRemoved */ false);
                    }
                        sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null);
                }
                for (AudioPlaybackConfiguration config : mPrevActiveAudioPlaybackConfigs.values()) {
                    sendAudioPlayerActiveStateChangedMessageLocked(config, /* isRemoved */ true);
                }

                // Update mPrevActiveAudioPlaybackConfigs
                mPrevActiveAudioPlaybackConfigs = activeAudioPlaybackConfigs;
            }
        } finally {
            Binder.restoreCallingIdentity(token);
@@ -204,9 +188,10 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
    }

    /**
     * Registers OnAudioPlayerStateChangedListener.
     * Registers OnAudioPlayerActiveStateChangedListener.
     */
    public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) {
    public void registerListener(
            OnAudioPlayerActiveStateChangedListener listener, Handler handler) {
        synchronized (mLock) {
            mListenerMap.put(listener, new MessageHandler((handler == null) ?
                    Looper.myLooper() : handler.getLooper(), listener));
@@ -214,9 +199,9 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
    }

    /**
     * Unregisters OnAudioPlayerStateChangedListener.
     * Unregisters OnAudioPlayerActiveStateChangedListener.
     */
    public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
    public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {
        synchronized (mLock) {
            mListenerMap.remove(listener);
        }
@@ -239,16 +224,7 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
     */
    public boolean isPlaybackActive(int uid) {
        synchronized (mLock) {
            Set<Integer> players = mAudioPlayersForUid.get(uid);
            if (players == null) {
                return false;
            }
            for (Integer pii : players) {
                if (isActiveState(mAudioPlayerStates.get(pii))) {
                    return true;
                }
            }
            return false;
            return mActiveAudioUids.contains(uid);
        }
    }

@@ -314,14 +290,10 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
        }
    }

    private void sendAudioPlayerStateChangedMessageLocked(
            final int uid, final int prevState, final AudioPlaybackConfiguration config) {
    private void sendAudioPlayerActiveStateChangedMessageLocked(
            final AudioPlaybackConfiguration config, final boolean isRemoved) {
        for (MessageHandler messageHandler : mListenerMap.values()) {
            messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config);
            messageHandler.sendAudioPlayerActiveStateChangedMessage(config, isRemoved);
        }
    }

    private static boolean isActiveState(Integer state) {
        return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
    }
}
+26 −22
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.server.media;
import com.android.internal.util.DumpUtils;
import com.android.server.Watchdog;

import android.annotation.Nullable;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -101,6 +101,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
    private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
    private final Handler mHandler = new Handler();
    private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
    private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
    private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();

    public MediaRouterService(Context context) {
        mContext = context;
@@ -111,7 +113,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub

        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
        mAudioPlayerStateMonitor.registerListener(
                new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
                new AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener() {
            static final long WAIT_MS = 500;
            final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() {
                @Override
@@ -121,39 +123,41 @@ public final class MediaRouterService extends IMediaRouterService.Stub
            };

            @Override
            public void onAudioPlayerStateChanged(
                    int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
            public void onAudioPlayerActiveStateChanged(
                    @NonNull AudioPlaybackConfiguration config, boolean isRemoved) {
                final boolean active = !isRemoved && config.isActive();
                final int pii = config.getPlayerInterfaceId();
                final int uid = config.getClientUid();

                final int idx = mActivePlayerMinPriorityQueue.indexOf(pii);
                // Keep the latest active player and its uid at the end of the queue.
                if (idx >= 0) {
                    mActivePlayerMinPriorityQueue.remove(idx);
                    mActivePlayerUidMinPriorityQueue.remove(idx);
                }

                int restoreUid = -1;
                boolean active = config == null ? false : config.isActive();
                if (active) {
                    mActivePlayerMinPriorityQueue.add(config.getPlayerInterfaceId());
                    mActivePlayerUidMinPriorityQueue.add(uid);
                    restoreUid = uid;
                } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                    // Noting to do if the prev state is not an active state.
                    return;
                } else {
                    IntArray sortedAudioPlaybackClientUids =
                            mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
                    for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) {
                        if (mAudioPlayerStateMonitor.isPlaybackActive(
                                sortedAudioPlaybackClientUids.get(i))) {
                            restoreUid = sortedAudioPlaybackClientUids.get(i);
                            break;
                        }
                    }
                } else if (mActivePlayerUidMinPriorityQueue.size() > 0) {
                    restoreUid = mActivePlayerUidMinPriorityQueue.get(
                            mActivePlayerUidMinPriorityQueue.size() - 1);
                }

                mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
                if (restoreUid >= 0) {
                    restoreRoute(restoreUid);
                    if (DEBUG) {
                        Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
                                + " active " + active + " restoring " + restoreUid);
                        Slog.d(TAG, "onAudioPlayerActiveStateChanged: " + "uid=" + uid
                                + ", active=" + active + ", restoreUid=" + restoreUid);
                    }
                } else {
                    mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
                    if (DEBUG) {
                        Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
                                + " active " + active + " delaying");
                        Slog.d(TAG, "onAudioPlayerACTIVEStateChanged: " + "uid=" + uid
                                + ", active=" + active + ", delaying");
                    }
                }
            }
+12 −17
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.media;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.KeyguardManager;
@@ -138,22 +137,18 @@ public class MediaSessionService extends SystemService implements Monitor {
        mAudioService = getAudioService();
        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
        mAudioPlayerStateMonitor.registerListener(
                new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
            @Override
            public void onAudioPlayerStateChanged(
                    int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
                if (config == null || !config.isActive() || config.getPlayerType()
                (config, isRemoved) -> {
                    if (isRemoved || !config.isActive() || config.getPlayerType()
                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
                        return;
                    }
                    synchronized (mLock) {
                    FullUserRecord user =
                            getFullUserRecordLocked(UserHandle.getUserId(uid));
                        FullUserRecord user = getFullUserRecordLocked(
                                UserHandle.getUserId(config.getClientUid()));
                        if (user != null) {
                            user.mPriorityStack.updateMediaButtonSessionIfNeeded();
                        }
                    }
            }
                }, null /* handler */);
        mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
        mContentResolver = getContext().getContentResolver();