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

Commit 53b542cb authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Revert "Fix overactive media routing""

parents a754bf8f 0aec178b
Loading
Loading
Loading
Loading
+289 −0
Original line number Diff line number Diff line
@@ -16,23 +16,20 @@

package com.android.server.media;

import android.annotation.Nullable;
import android.content.Context;
import android.media.AudioManager.AudioPlaybackCallback;
import android.media.AudioPlaybackConfiguration;
import android.media.IAudioService;
import android.media.IPlaybackConfigDispatcher;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import android.util.SparseArray;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
@@ -40,70 +37,58 @@ import java.util.Map;
import java.util.Set;

/**
 * Monitors the state changes of audio players.
 * Monitors changes in audio playback, and notify the newly started audio playback through the
 * {@link OnAudioPlaybackStartedListener} and the activeness change through the
 * {@link OnAudioPlaybackActiveStateListener}.
 */
class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
class AudioPlaybackMonitor extends IPlaybackConfigDispatcher.Stub {
    private static boolean DEBUG = MediaSessionService.DEBUG;
    private static String TAG = "AudioPlayerStateMonitor";
    private static String TAG = "AudioPlaybackMonitor";

    private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor();
    private static AudioPlaybackMonitor sInstance;

    /**
     * Called when the state of audio player is changed.
     * Called when audio playback is started for a given UID.
     */
    interface OnAudioPlayerStateChangedListener {
        void onAudioPlayerStateChanged(
                int uid, int prevState, @Nullable AudioPlaybackConfiguration config);
    }

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

        private final OnAudioPlayerStateChangedListener mListsner;

        public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) {
            super(looper);
            mListsner = 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);
                    break;
            }
    interface OnAudioPlaybackStartedListener {
        void onAudioPlaybackStarted(int uid);
    }

        public void sendAudioPlayerStateChangedMessage(int uid, int prevState,
                AudioPlaybackConfiguration config) {
            obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget();
        }
    /**
     * Called when audio player state is changed.
     */
    interface OnAudioPlayerActiveStateChangedListener {
        void onAudioPlayerActiveStateChanged(int uid, boolean active);
    }

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap =
            new HashMap<>();
    @GuardedBy("mLock")
    private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>();
    @GuardedBy("mLock")
    private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>();
    private final Context mContext;
    private final List<OnAudioPlaybackStartedListener> mAudioPlaybackStartedListeners
            = new ArrayList<>();
    private final List<OnAudioPlayerActiveStateChangedListener>
            mAudioPlayerActiveStateChangedListeners = new ArrayList<>();
    private final Map<Integer, Integer> mAudioPlaybackStates = new HashMap<>();
    private final Set<Integer> mActiveAudioPlaybackClientUids = new HashSet<>();

    // 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.
    @GuardedBy("mLock")
    private final IntArray mSortedAudioPlaybackClientUids = new IntArray();

    @GuardedBy("mLock")
    private boolean mRegisteredToAudioService;

    static AudioPlayerStateMonitor getInstance() {
    static AudioPlaybackMonitor getInstance(Context context, IAudioService audioService) {
        if (sInstance == null) {
            sInstance = new AudioPlaybackMonitor(context, audioService);
        }
        return sInstance;
    }

    private AudioPlayerStateMonitor() {
    private AudioPlaybackMonitor(Context context, IAudioService audioService) {
        mContext = context;
        try {
            audioService.registerPlaybackCallback(this);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Failed to register playback callback", e);
        }
    }

    /**
@@ -122,78 +107,69 @@ 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);
            List<Integer> newActiveAudioPlaybackClientUids = new ArrayList<>();
            List<OnAudioPlayerActiveStateChangedListener> audioPlayerActiveStateChangedListeners;
            List<OnAudioPlaybackStartedListener> audioPlaybackStartedListeners;
            synchronized (mLock) {
                mAudioPlayerStates.clear();
                mAudioPlayersForUid.clear();
                // Update mActiveAudioPlaybackClientUids and mSortedAudioPlaybackClientUids,
                // and find newly activated audio playbacks.
                mActiveAudioPlaybackClientUids.clear();
                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);
                    }
                }
                for (AudioPlaybackConfiguration config : configs) {
                    if (!config.isActive()) {
                    // Ignore inactive (i.e. not playing) or PLAYER_TYPE_JAM_SOUNDPOOL
                    // (i.e. playback from the SoundPool class which is only for sound effects)
                    // playback.
                    // Note that we shouldn't ignore PLAYER_TYPE_UNKNOWN because it might be OEM
                    // specific audio/video players.
                    if (!config.isActive() || config.getPlayerType()
                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
                        continue;
                    }

                    int uid = config.getClientUid();
                    if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) {
                    mActiveAudioPlaybackClientUids.add(config.getClientUid());
                    Integer oldState = mAudioPlaybackStates.get(config.getPlayerInterfaceId());
                    if (!isActiveState(oldState)) {
                        if (DEBUG) {
                            Log.d(TAG, "Found a new active media playback. " +
                                    AudioPlaybackConfiguration.toLogFriendlyString(config));
                        }
                        // New active audio playback.
                        int index = mSortedAudioPlaybackClientUids.indexOf(uid);
                        newActiveAudioPlaybackClientUids.add(config.getClientUid());
                        int index = mSortedAudioPlaybackClientUids.indexOf(config.getClientUid());
                        if (index == 0) {
                            // It's the lastly played music app already. Skip updating.
                            continue;
                        } else if (index > 0) {
                            mSortedAudioPlaybackClientUids.remove(index);
                        }
                        mSortedAudioPlaybackClientUids.add(0, uid);
                        mSortedAudioPlaybackClientUids.add(0, config.getClientUid());
                    }
                }
                audioPlayerActiveStateChangedListeners = new ArrayList<>(
                        mAudioPlayerActiveStateChangedListeners);
                audioPlaybackStartedListeners = new ArrayList<>(mAudioPlaybackStartedListeners);
            }
                // Notify the change of audio player states.
            // Notify the change of audio playback states.
            for (AudioPlaybackConfiguration config : configs) {
                    Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId());
                    if (prevState == null || prevState != config.getPlayerState()) {
                        sendAudioPlayerStateChangedMessageLocked(
                                config.getClientUid(), prevState, 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> players = mAudioPlayersForUid.get(prevUid);
                        int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN;
                        for (int pii : players) {
                            Integer state = prevAudioPlayerStates.get(pii);
                            if (state == null) {
                                continue;
                boolean wasActive = isActiveState(
                        mAudioPlaybackStates.get(config.getPlayerInterfaceId()));
                boolean isActive = config.isActive();
                if (wasActive != isActive) {
                    for (OnAudioPlayerActiveStateChangedListener listener
                            : audioPlayerActiveStateChangedListeners) {
                        listener.onAudioPlayerActiveStateChanged(config.getClientUid(),
                                isActive);
                    }
                            if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                                prevState = state;
                                break;
                            } else if (prevState
                                    == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) {
                                prevState = state;
                }
            }
                        sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null);
            // Notify the start of audio playback
            for (int uid : newActiveAudioPlaybackClientUids) {
                for (OnAudioPlaybackStartedListener listener : audioPlaybackStartedListeners) {
                    listener.onAudioPlaybackStarted(uid);
                }
            }
            mAudioPlaybackStates.clear();
            for (AudioPlaybackConfiguration config : configs) {
                mAudioPlaybackStates.put(config.getPlayerInterfaceId(), config.getPlayerState());
            }
        } finally {
            Binder.restoreCallingIdentity(token);
@@ -201,21 +177,40 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
    }

    /**
     * Registers OnAudioPlayerStateChangedListener.
     * Registers OnAudioPlaybackStartedListener.
     */
    public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) {
    public void registerOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) {
        synchronized (mLock) {
            mListenerMap.put(listener, new MessageHandler((handler == null) ?
                    Looper.myLooper() : handler.getLooper(), listener));
            mAudioPlaybackStartedListeners.add(listener);
        }
    }

    /**
     * Unregisters OnAudioPlayerStateChangedListener.
     * Unregisters OnAudioPlaybackStartedListener.
     */
    public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
    public void unregisterOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) {
        synchronized (mLock) {
            mListenerMap.remove(listener);
            mAudioPlaybackStartedListeners.remove(listener);
        }
    }

    /**
     * Registers OnAudioPlayerActiveStateChangedListener.
     */
    public void registerOnAudioPlayerActiveStateChangedListener(
            OnAudioPlayerActiveStateChangedListener listener) {
        synchronized (mLock) {
            mAudioPlayerActiveStateChangedListeners.add(listener);
        }
    }

    /**
     * Unregisters OnAudioPlayerActiveStateChangedListener.
     */
    public void unregisterOnAudioPlayerActiveStateChangedListener(
            OnAudioPlayerActiveStateChangedListener listener) {
        synchronized (mLock) {
            mAudioPlayerActiveStateChangedListeners.remove(listener);
        }
    }

@@ -236,24 +231,15 @@ 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 mActiveAudioPlaybackClientUids.contains(uid);
        }
    }

    /**
     * Cleans up the sorted list of audio playback client UIDs with given {@param
     * mediaButtonSessionUid}.
     * <p>UIDs whose audio playback are inactive and have started before the media button session's
     * audio playback cannot be the lastly played media app. So they won't needed anymore.
     * <p>UIDs whose audio playback started after the media button session's audio playback
     * cannot be the lastly played media app. So they won't needed anymore.
     *
     * @param mediaButtonSessionUid UID of the media button session.
     */
@@ -277,16 +263,16 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
    }

    /**
     * Dumps {@link AudioPlayerStateMonitor}.
     * Dumps {@link AudioPlaybackMonitor}.
     */
    public void dump(Context context, PrintWriter pw, String prefix) {
    public void dump(PrintWriter pw, String prefix) {
        synchronized (mLock) {
            pw.println(prefix + "Audio playback (lastly played comes first)");
            String indent = prefix + "  ";
            for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) {
                int uid = mSortedAudioPlaybackClientUids.get(i);
                pw.print(indent + "uid=" + uid + " packages=");
                String[] packages = context.getPackageManager().getPackagesForUid(uid);
                String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
                if (packages != null && packages.length > 0) {
                    for (int j = 0; j < packages.length; j++) {
                        pw.print(packages[j] + " ");
@@ -297,28 +283,7 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
        }
    }

    public void registerSelfIntoAudioServiceIfNeeded(IAudioService audioService) {
        synchronized (mLock) {
            try {
                if (!mRegisteredToAudioService) {
                    audioService.registerPlaybackCallback(this);
                    mRegisteredToAudioService = true;
                }
            } catch (RemoteException e) {
                Log.wtf(TAG, "Failed to register playback callback", e);
                mRegisteredToAudioService = false;
            }
        }
    }

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

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

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
@@ -98,8 +96,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
    private int mCurrentUserId = -1;
    private boolean mGlobalBluetoothA2dpOn = false;
    private final IAudioService mAudioService;
    private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
    private final Handler mHandler = new Handler();
    private final AudioPlaybackMonitor mAudioPlaybackMonitor;
    private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();

    public MediaRouterService(Context context) {
@@ -109,57 +106,31 @@ public final class MediaRouterService extends IMediaRouterService.Stub
        mAudioService = IAudioService.Stub.asInterface(
                ServiceManager.getService(Context.AUDIO_SERVICE));

        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
        mAudioPlayerStateMonitor.registerListener(
                new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
            static final long WAIT_MS = 500;
            final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() {
        mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(context, mAudioService);
        mAudioPlaybackMonitor.registerOnAudioPlayerActiveStateChangedListener(
                new AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener() {
            @Override
                public void run() {
                    restoreBluetoothA2dp();
                }
            };

            @Override
            public void onAudioPlayerStateChanged(
                    int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
                int restoreUid = -1;
                boolean active = config == null ? false : config.isActive();
            public void onAudioPlayerActiveStateChanged(int uid, boolean active) {
                if (active) {
                    restoreUid = uid;
                } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                    // Noting to do if the prev state is not an active state.
                    return;
                    restoreRoute(uid);
                } else {
                    IntArray sortedAudioPlaybackClientUids =
                            mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
                    for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) {
                        if (mAudioPlayerStateMonitor.isPlaybackActive(
                            mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids();
                    boolean restored = false;
                    for (int i = 0; i < sortedAudioPlaybackClientUids.size(); i++) {
                        if (mAudioPlaybackMonitor.isPlaybackActive(
                                sortedAudioPlaybackClientUids.get(i))) {
                            restoreUid = sortedAudioPlaybackClientUids.get(i);
                            restoreRoute(sortedAudioPlaybackClientUids.get(i));
                            restored = true;
                            break;
                        }
                    }
                }

                mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
                if (restoreUid >= 0) {
                    restoreRoute(restoreUid);
                    if (DEBUG) {
                        Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
                                + " active " + active + " restoring " + restoreUid);
                    }
                } else {
                    mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
                    if (DEBUG) {
                        Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
                                + " active " + active + " delaying");
                    if (!restored) {
                        restoreBluetoothA2dp();
                    }
                }
            }
        }, mHandler);
        mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);

        });
        AudioRoutesInfo audioRoutes = null;
        try {
            audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() {
@@ -290,14 +261,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub

        final long token = Binder.clearCallingIdentity();
        try {
            ClientRecord clientRecord;
            synchronized (mLock) {
                clientRecord = mAllClientRecords.get(client.asBinder());
                return isPlaybackActiveLocked(client);
            }
            if (clientRecord != null) {
                return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid);
            }
            return false;
        } finally {
            Binder.restoreCallingIdentity(token);
        }
@@ -514,6 +480,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub
        return null;
    }

    private boolean isPlaybackActiveLocked(IMediaRouterClient client) {
        ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
        if (clientRecord != null) {
            return mAudioPlaybackMonitor.isPlaybackActive(clientRecord.mUid);
        }
        return false;
    }

    private void setDiscoveryRequestLocked(IMediaRouterClient client,
            int routeTypes, boolean activeScan) {
        final IBinder binder = client.asBinder();
+12 −16

File changed.

Preview size limit exceeded, changes collapsed.

+8 −6
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ class MediaSessionStack {
     */
    private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();

    private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
    private final AudioPlaybackMonitor mAudioPlaybackMonitor;
    private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener;

    /**
@@ -84,6 +84,7 @@ class MediaSessionStack {
     */
    private MediaSessionRecord mMediaButtonSession;

    private MediaSessionRecord mCachedDefault;
    private MediaSessionRecord mCachedVolumeDefault;

    /**
@@ -92,8 +93,8 @@ class MediaSessionStack {
    private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists =
            new SparseArray<>();

    MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) {
        mAudioPlayerStateMonitor = monitor;
    MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) {
        mAudioPlaybackMonitor = monitor;
        mOnMediaButtonSessionChangedListener = listener;
    }

@@ -186,13 +187,13 @@ class MediaSessionStack {
        if (DEBUG) {
            Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
        }
        IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
        IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids();
        for (int i = 0; i < audioPlaybackUids.size(); i++) {
            MediaSessionRecord mediaButtonSession =
                    findMediaButtonSession(audioPlaybackUids.get(i));
            if (mediaButtonSession != null) {
                // Found the media button session.
                mAudioPlayerStateMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid());
                mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid());
                if (mMediaButtonSession != mediaButtonSession) {
                    updateMediaButtonSession(mediaButtonSession);
                }
@@ -215,7 +216,7 @@ class MediaSessionStack {
        for (MediaSessionRecord session : mSessions) {
            if (uid == session.getUid()) {
                if (session.getPlaybackState() != null && session.isPlaybackActive() ==
                        mAudioPlayerStateMonitor.isPlaybackActive(session.getUid())) {
                        mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) {
                    // If there's a media session whose PlaybackState matches
                    // the audio playback state, return it immediately.
                    return session;
@@ -375,6 +376,7 @@ class MediaSessionStack {
    }

    private void clearCache(int userId) {
        mCachedDefault = null;
        mCachedVolumeDefault = null;
        mCachedActiveLists.remove(userId);
        // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL,