Loading services/core/java/com/android/server/media/AudioPlayerStateMonitor.java→services/core/java/com/android/server/media/AudioPlaybackMonitor.java +289 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } } /** Loading @@ -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); Loading @@ -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); } } Loading @@ -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. */ Loading @@ -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] + " "); Loading @@ -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); } } services/core/java/com/android/server/media/MediaRouterService.java +24 −50 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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() { Loading Loading @@ -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); } Loading Loading @@ -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(); Loading services/core/java/com/android/server/media/MediaSessionService.java +12 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/media/MediaSessionStack.java +8 −6 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -84,6 +84,7 @@ class MediaSessionStack { */ private MediaSessionRecord mMediaButtonSession; private MediaSessionRecord mCachedDefault; private MediaSessionRecord mCachedVolumeDefault; /** Loading @@ -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; } Loading Loading @@ -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); } Loading @@ -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; Loading Loading @@ -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, Loading Loading
services/core/java/com/android/server/media/AudioPlayerStateMonitor.java→services/core/java/com/android/server/media/AudioPlaybackMonitor.java +289 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } } /** Loading @@ -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); Loading @@ -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); } } Loading @@ -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. */ Loading @@ -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] + " "); Loading @@ -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); } }
services/core/java/com/android/server/media/MediaRouterService.java +24 −50 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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() { Loading Loading @@ -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); } Loading Loading @@ -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(); Loading
services/core/java/com/android/server/media/MediaSessionService.java +12 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/media/MediaSessionStack.java +8 −6 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -84,6 +84,7 @@ class MediaSessionStack { */ private MediaSessionRecord mMediaButtonSession; private MediaSessionRecord mCachedDefault; private MediaSessionRecord mCachedVolumeDefault; /** Loading @@ -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; } Loading Loading @@ -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); } Loading @@ -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; Loading Loading @@ -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, Loading