Merge "Call listeners whenever players are removed"
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index be223f1..d71c3b0 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -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.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 @@
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 @@
}
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 @@
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);
+ final int pii = config.getPlayerInterfaceId();
+ boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;
+ if (wasActive != config.isActive()) {
+ sendAudioPlayerActiveStateChangedMessageLocked(
+ config, /* isRemoved */ false);
}
}
- 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;
- }
- }
- 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 @@
}
/**
- * 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 @@
}
/**
- * Unregisters OnAudioPlayerStateChangedListener.
+ * Unregisters OnAudioPlayerActiveStateChangedListener.
*/
- public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
+ public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {
synchronized (mLock) {
mListenerMap.remove(listener);
}
@@ -239,16 +224,7 @@
*/
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 @@
}
}
- 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);
- }
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3c9e1d4..3e51252 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -19,7 +19,7 @@
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 @@
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 @@
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 @@
};
@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");
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f6a81d0..06f4f5e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -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,23 +137,19 @@
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()
- == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
- return;
- }
- synchronized (mLock) {
- FullUserRecord user =
- getFullUserRecordLocked(UserHandle.getUserId(uid));
- if (user != null) {
- user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+ (config, isRemoved) -> {
+ if (isRemoved || !config.isActive() || config.getPlayerType()
+ == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ return;
}
- }
- }
- }, null /* handler */);
+ synchronized (mLock) {
+ FullUserRecord user = getFullUserRecordLocked(
+ UserHandle.getUserId(config.getClientUid()));
+ if (user != null) {
+ user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+ }
+ }
+ }, null /* handler */);
mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
mContentResolver = getContext().getContentResolver();
mSettingsObserver = new SettingsObserver();