Adds listeners for changes to the list of active sessions
The listeners get notified when sessions are added, removed, or
reprioritized.
Change-Id: I7f3bfc84049719c3b9c19016c6bac92e1a5c3179
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 87665e1..4d1829d 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -29,9 +29,11 @@
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.routeprovider.RouteRequest;
+import android.media.session.IActiveSessionsListener;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
+import android.media.session.MediaSessionToken;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
import android.media.session.MediaSession;
@@ -39,6 +41,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -75,10 +78,12 @@
private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
+ private final ArrayList<SessionsListenerRecord> mSessionsListeners
+ = new ArrayList<SessionsListenerRecord>();
// private final ArrayList<MediaRouteProviderProxy> mProviders
// = new ArrayList<MediaRouteProviderProxy>();
private final Object mLock = new Object();
- private final Handler mHandler = new Handler();
+ private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
private KeyguardManager mKeyguardManager;
@@ -200,15 +205,20 @@
}
}
}
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
}
public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
+ boolean updateSessions = false;
synchronized (mLock) {
if (!mAllSessions.contains(record)) {
Log.d(TAG, "Unknown session changed playback state. Ignoring.");
return;
}
- mPriorityStack.onPlaystateChange(record, oldState, newState);
+ updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
+ }
+ if (updateSessions) {
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
}
}
@@ -315,6 +325,8 @@
// ignore exceptions while destroying a session.
}
session.onDestroy();
+
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
}
private void enforcePackageName(String packageName, int uid) {
@@ -428,6 +440,8 @@
UserRecord user = getOrCreateUser(userId);
user.addSessionLocked(session);
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
+
if (DEBUG) {
Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
}
@@ -453,11 +467,43 @@
return -1;
}
+ private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
+ for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+ if (mSessionsListeners.get(i).mListener == listener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
private boolean isSessionDiscoverable(MediaSessionRecord record) {
// TODO probably want to check more than if it's active.
return record.isActive();
}
+ private void pushSessionsChanged(int userId) {
+ synchronized (mLock) {
+ List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
+ int size = records.size();
+ ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>();
+ for (int i = 0; i < size; i++) {
+ tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
+ }
+ for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+ SessionsListenerRecord record = mSessionsListeners.get(i);
+ if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
+ try {
+ record.mListener.onActiveSessionsChanged(tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
+ e);
+ mSessionsListeners.remove(i);
+ }
+ }
+ }
+ }
+ }
+
private MediaRouteProviderProxy.RoutesListener mRoutesCallback
= new MediaRouteProviderProxy.RoutesListener() {
@Override
@@ -613,6 +659,23 @@
};
}
+ final class SessionsListenerRecord implements IBinder.DeathRecipient {
+ private final IActiveSessionsListener mListener;
+ private final int mUserId;
+
+ public SessionsListenerRecord(IActiveSessionsListener listener, int userId) {
+ mListener = listener;
+ mUserId = userId;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mSessionsListeners.remove(this);
+ }
+ }
+ }
+
class SessionManagerImpl extends ISessionManager.Stub {
private static final String EXTRA_WAKELOCK_ACQUIRED =
"android.media.AudioService.WAKELOCK_ACQUIRED";
@@ -648,20 +711,7 @@
final long token = Binder.clearCallingIdentity();
try {
- String packageName = null;
- if (componentName != null) {
- // If they gave us a component name verify they own the
- // package
- packageName = componentName.getPackageName();
- enforcePackageName(packageName, uid);
- }
- // Check that they can make calls on behalf of the user and
- // get the final user id
- int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
- true /* allowAll */, true /* requireFull */, "getSessions", packageName);
- // Check if they have the permissions or their component is
- // enabled for the user they're calling from.
- enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+ int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
ArrayList<IBinder> binders = new ArrayList<IBinder>();
synchronized (mLock) {
ArrayList<MediaSessionRecord> records = mPriorityStack
@@ -677,6 +727,52 @@
}
}
+ @Override
+ public void addSessionsListener(IActiveSessionsListener listener,
+ ComponentName componentName, int userId) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
+ synchronized (mLock) {
+ int index = findIndexOfSessionsListenerLocked(listener);
+ if (index != -1) {
+ Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
+ return;
+ }
+ SessionsListenerRecord record = new SessionsListenerRecord(listener,
+ resolvedUserId);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
+ return;
+ }
+ mSessionsListeners.add(record);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeSessionsListener(IActiveSessionsListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ int index = findIndexOfSessionsListenerLocked(listener);
+ if (index != -1) {
+ SessionsListenerRecord record = mSessionsListeners.remove(index);
+ try {
+ record.mListener.asBinder().unlinkToDeath(record, 0);
+ } catch (Exception e) {
+ // ignore exceptions, the record is being removed
+ }
+ }
+ }
+ }
+
/**
* Handles the dispatching of the media button events to one of the
* registered listeners, or if there was none, broadcast an
@@ -764,6 +860,25 @@
}
}
+ private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
+ final int uid) {
+ String packageName = null;
+ if (componentName != null) {
+ // If they gave us a component name verify they own the
+ // package
+ packageName = componentName.getPackageName();
+ enforcePackageName(packageName, uid);
+ }
+ // Check that they can make calls on behalf of the user and
+ // get the final user id
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+ // Check if they have the permissions or their component is
+ // enabled for the user they're calling from.
+ enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+ return resolvedUserId;
+ }
+
private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
MediaSessionRecord session) {
int direction = 0;
@@ -994,4 +1109,20 @@
};
}
+ final class MessageHandler extends Handler {
+ private static final int MSG_SESSIONS_CHANGED = 1;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SESSIONS_CHANGED:
+ pushSessionsChanged(msg.arg1);
+ break;
+ }
+ }
+
+ public void post(int what, int arg1, int arg2) {
+ obtainMessage(what, arg1, arg2).sendToTarget();
+ }
+ }
}