Loading Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -502,6 +502,7 @@ java_defaults { "media/java/android/media/session/IOnMediaKeyListener.aidl", "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl", "media/java/android/media/session/ISession.aidl", "media/java/android/media/session/ISession2TokensListener.aidl", "media/java/android/media/session/ISessionCallback.aidl", "media/java/android/media/session/ISessionController.aidl", "media/java/android/media/session/ISessionControllerCallback.aidl", Loading media/java/android/media/session/ISession2TokensListener.aidl 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.session; import android.media.Session2Token; /** * Listens for changes to the list of session2 tokens. * @hide */ oneway interface ISession2TokensListener { void onSession2TokensChanged(in List<Session2Token> tokens); } media/java/android/media/session/ISessionManager.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession; import android.media.session.ISession2TokensListener; import android.media.session.SessionCallbackLink; import android.os.Bundle; import android.view.KeyEvent; Loading @@ -45,6 +46,8 @@ interface ISessionManager { void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, int userId); void removeSessionsListener(in IActiveSessionsListener listener); void addSession2TokensListener(in ISession2TokensListener listener, int userId); void removeSession2TokensListener(in ISession2TokensListener listener); // This is for the system volume UI only void setRemoteVolumeController(in IRemoteVolumeController rvc); Loading @@ -56,6 +59,5 @@ interface ISessionManager { void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); void setOnMediaKeyListener(in IOnMediaKeyListener listener); // MediaSession2 boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); } media/java/android/media/session/MediaSessionManager.java +129 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ import android.util.ArrayMap; import android.util.Log; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.Objects; Loading Loading @@ -69,9 +71,13 @@ public final class MediaSessionManager { */ public static final int RESULT_MEDIA_KEY_HANDLED = 1; private final Object mLock = new Object(); @GuardedBy("mLock") private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); private final Object mLock = new Object(); @GuardedBy("mLock") private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper> mSession2TokensListeners = new ArrayMap<>(); private final ISessionManager mService; private Context mContext; Loading Loading @@ -323,6 +329,87 @@ public final class MediaSessionManager { } } /** * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. * * @param listener The listener to add * @param handler The handler to call listener on. If {@code null}, calling thread's looper will * be used. * @hide */ // TODO(jaewan): Unhide public void addOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) { addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler); } /** * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. * * @param userId The userId to listen for changes on * @param listener The listener to add * @param handler The handler to call listener on. If {@code null}, calling thread's looper will * be used. * @hide */ public void addOnSession2TokensChangedListener(int userId, @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) { if (listener == null) { throw new IllegalArgumentException("listener shouldn't be null"); } synchronized (mLock) { if (mSession2TokensListeners.get(listener) != null) { Log.w(TAG, "Attempted to add session listener twice, ignoring."); return; } Session2TokensChangedWrapper wrapper = new Session2TokensChangedWrapper(listener, handler); try { mService.addSession2TokensListener(wrapper.getStub(), userId); mSession2TokensListeners.put(listener, wrapper); } catch (RemoteException e) { Log.e(TAG, "Error in addSessionTokensListener.", e); e.rethrowFromSystemServer(); } } } /** * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates. * * @param listener The listener to remove. * @hide */ // TODO(jaewan): Unhide public void removeOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener) { if (listener == null) { throw new IllegalArgumentException("listener may not be null"); } final Session2TokensChangedWrapper wrapper; synchronized (mLock) { wrapper = mSession2TokensListeners.remove(listener); } if (wrapper != null) { try { mService.removeSession2TokensListener(wrapper.getStub()); } catch (RemoteException e) { Log.e(TAG, "Error in removeSessionTokensListener.", e); e.rethrowFromSystemServer(); } } } /** * Set the remote volume controller to receive volume updates on. Only for * use by system UI. Loading Loading @@ -589,6 +676,26 @@ public final class MediaSessionManager { public void onActiveSessionsChanged(@Nullable List<MediaController> controllers); } /** * Listens for changes to the {@link #getSession2Tokens()}. This can be added * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. * * @hide */ public interface OnSession2TokensChangedListener { /** * Called when the {@link #getSession2Tokens()} is changed. * * @param tokens list of {@link Session2Token} */ void onSession2TokensChanged(@NonNull List<Session2Token> tokens); } /** * Listens the volume key long-presses. * @hide Loading Loading @@ -807,6 +914,27 @@ public final class MediaSessionManager { } } private static final class Session2TokensChangedWrapper { private final OnSession2TokensChangedListener mListener; private final Handler mHandler; private final ISession2TokensListener.Stub mStub = new ISession2TokensListener.Stub() { @Override public void onSession2TokensChanged(final List<Session2Token> tokens) { mHandler.post(() -> mListener.onSession2TokensChanged(tokens)); } }; Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) { mListener = listener; mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper()); } public ISession2TokensListener.Stub getStub() { return mStub; } } private static final class OnVolumeKeyLongPressListenerImpl extends IOnVolumeKeyLongPressListener.Stub { private OnVolumeKeyLongPressListener mListener; Loading services/core/java/com/android/server/media/MediaSessionService.java +141 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.media; import static android.os.UserHandle.USER_ALL; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.INotificationManager; Loading Loading @@ -48,6 +50,7 @@ import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession; import android.media.session.ISession2TokensListener; import android.media.session.ISessionManager; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; Loading Loading @@ -119,6 +122,9 @@ public class MediaSessionService extends SystemService implements Monitor { // one place. @GuardedBy("mLock") private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>(); @GuardedBy("mLock") private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords = new ArrayList<>(); private KeyguardManager mKeyguardManager; private IAudioService mAudioService; Loading Loading @@ -235,7 +241,7 @@ public class MediaSessionService extends SystemService implements Monitor { private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { List<MediaSessionRecord> records = new ArrayList<>(); if (userId == UserHandle.USER_ALL) { if (userId == USER_ALL) { int size = mUserRecords.size(); for (int i = 0; i < size; i++) { records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); Loading @@ -251,13 +257,24 @@ public class MediaSessionService extends SystemService implements Monitor { // Return global priority session at the first whenever it's asked. if (isGlobalPriorityActiveLocked() && (userId == UserHandle.USER_ALL || userId == mGlobalPrioritySession.getUserId())) { && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) { records.add(0, mGlobalPrioritySession); } return records; } List<Session2Token> getSession2TokensLocked(int userId) { List<Session2Token> list = new ArrayList<>(); if (userId == USER_ALL) { for (int i = 0; i < mSession2TokensPerUser.size(); i++) { list.addAll(mSession2TokensPerUser.valueAt(i)); } } else { list.addAll(mSession2TokensPerUser.get(userId)); } return list; } /** * Tells the system UI that volume has changed on an active remote session. */ Loading Loading @@ -316,7 +333,7 @@ public class MediaSessionService extends SystemService implements Monitor { FullUserRecord user = getFullUserRecordLocked(userId); if (user != null) { if (user.mFullUserId == userId) { user.destroySessionsForUserLocked(UserHandle.USER_ALL); user.destroySessionsForUserLocked(USER_ALL); mUserRecords.remove(userId); } else { user.destroySessionsForUserLocked(userId); Loading Loading @@ -393,14 +410,14 @@ public class MediaSessionService extends SystemService implements Monitor { for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { SessionsListenerRecord listener = mSessionsListeners.get(i); try { enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid, listener.mUserId); enforceMediaPermissions(listener.componentName, listener.pid, listener.uid, listener.userId); } catch (SecurityException e) { Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName Log.i(TAG, "ActiveSessionsListener " + listener.componentName + " is no longer authorized. Disconnecting."); mSessionsListeners.remove(i); try { listener.mListener listener.listener .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); } catch (Exception e1) { // ignore Loading Loading @@ -562,13 +579,23 @@ public class MediaSessionService extends SystemService implements Monitor { private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) { if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) { return i; } } return -1; } private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) { for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) { return i; } } return -1; } private void pushSessionsChanged(int userId) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(userId); Loading @@ -585,9 +612,9 @@ public class MediaSessionService extends SystemService implements Monitor { pushRemoteVolumeUpdateLocked(userId); for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { SessionsListenerRecord record = mSessionsListeners.get(i); if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { if (record.userId == USER_ALL || record.userId == userId) { try { record.mListener.onActiveSessionsChanged(tokens); record.listener.onActiveSessionsChanged(tokens); } catch (RemoteException e) { Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", e); Loading @@ -614,6 +641,25 @@ public class MediaSessionService extends SystemService implements Monitor { } } void pushSession2TokensChangedLocked(int userId) { List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL); List<Session2Token> session2Tokens = getSession2TokensLocked(userId); for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); try { if (listenerRecord.userId == USER_ALL) { listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); } else if (listenerRecord.userId == userId) { listenerRecord.listener.onSession2TokensChanged(session2Tokens); } } catch (RemoteException e) { Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); mSession2TokensListenerRecords.remove(i); } } } /** * Called when the media button receiver for the {@param record} is changed. * Loading Loading @@ -855,20 +901,20 @@ public class MediaSessionService extends SystemService implements Monitor { } final class SessionsListenerRecord implements IBinder.DeathRecipient { private final IActiveSessionsListener mListener; private final ComponentName mComponentName; private final int mUserId; private final int mPid; private final int mUid; public final IActiveSessionsListener listener; public final ComponentName componentName; public final int userId; public final int pid; public final int uid; public SessionsListenerRecord(IActiveSessionsListener listener, ComponentName componentName, int userId, int pid, int uid) { mListener = listener; mComponentName = componentName; mUserId = userId; mPid = pid; mUid = uid; this.listener = listener; this.componentName = componentName; this.userId = userId; this.pid = pid; this.uid = uid; } @Override Loading @@ -879,6 +925,24 @@ public class MediaSessionService extends SystemService implements Monitor { } } final class Session2TokensListenerRecord implements IBinder.DeathRecipient { public final ISession2TokensListener listener; public final int userId; Session2TokensListenerRecord(ISession2TokensListener listener, int userId) { this.listener = listener; this.userId = userId; } @Override public void binderDied() { synchronized (mLock) { mSession2TokensListenerRecords.remove(this); } } } final class SettingsObserver extends ContentObserver { private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); Loading @@ -889,7 +953,7 @@ public class MediaSessionService extends SystemService implements Monitor { private void observe() { mContentResolver.registerContentObserver(mSecureSettingsUri, false, this, UserHandle.USER_ALL); false, this, USER_ALL); } @Override Loading Loading @@ -984,15 +1048,9 @@ public class MediaSessionService extends SystemService implements Monitor { int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, true /* allowAll */, true /* requireFull */, "getSession2Tokens", null /* optional packageName */); List<Session2Token> result = new ArrayList<>(); List<Session2Token> result; synchronized (mLock) { if (resolvedUserId == UserHandle.USER_ALL) { for (int i = 0; i < mSession2TokensPerUser.size(); i++) { result.addAll(mSession2TokensPerUser.valueAt(i)); } } else { result.addAll(mSession2TokensPerUser.get(userId)); } result = getSession2TokensLocked(resolvedUserId); } return result; } finally { Loading Loading @@ -1038,7 +1096,7 @@ public class MediaSessionService extends SystemService implements Monitor { if (index != -1) { SessionsListenerRecord record = mSessionsListeners.remove(index); try { record.mListener.asBinder().unlinkToDeath(record, 0); record.listener.asBinder().unlinkToDeath(record, 0); } catch (Exception e) { // ignore exceptions, the record is being removed } Loading @@ -1046,6 +1104,56 @@ public class MediaSessionService extends SystemService implements Monitor { } } @Override public void addSession2TokensListener(ISession2TokensListener listener, int userId) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { // 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 */, "addSession2TokensListener", null /* optional packageName */); synchronized (mLock) { int index = findIndexOfSession2TokensListenerLocked(listener); if (index >= 0) { Log.w(TAG, "addSession2TokensListener is already added, ignoring"); return; } mSession2TokensListenerRecords.add( new Session2TokensListenerRecord(listener, resolvedUserId)); } } finally { Binder.restoreCallingIdentity(token); } } @Override public void removeSession2TokensListener(ISession2TokensListener listener) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { int index = findIndexOfSession2TokensListenerLocked(listener); if (index >= 0) { Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.remove(index); try { listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0); } catch (Exception e) { // Ignore exception. } } } } finally { Binder.restoreCallingIdentity(token); } } /** * Handles the dispatching of the media button events to one of the * registered listeners, or if there was none, broadcast an Loading Loading @@ -2012,6 +2120,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int userId = UserHandle.getUserId(mToken.getUid()); mSession2TokensPerUser.get(userId).add(mToken); pushSession2TokensChangedLocked(userId); } } Loading @@ -2020,6 +2129,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int userId = UserHandle.getUserId(mToken.getUid()); mSession2TokensPerUser.get(userId).remove(mToken); pushSession2TokensChangedLocked(userId); } } } Loading Loading
Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -502,6 +502,7 @@ java_defaults { "media/java/android/media/session/IOnMediaKeyListener.aidl", "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl", "media/java/android/media/session/ISession.aidl", "media/java/android/media/session/ISession2TokensListener.aidl", "media/java/android/media/session/ISessionCallback.aidl", "media/java/android/media/session/ISessionController.aidl", "media/java/android/media/session/ISessionControllerCallback.aidl", Loading
media/java/android/media/session/ISession2TokensListener.aidl 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.session; import android.media.Session2Token; /** * Listens for changes to the list of session2 tokens. * @hide */ oneway interface ISession2TokensListener { void onSession2TokensChanged(in List<Session2Token> tokens); }
media/java/android/media/session/ISessionManager.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession; import android.media.session.ISession2TokensListener; import android.media.session.SessionCallbackLink; import android.os.Bundle; import android.view.KeyEvent; Loading @@ -45,6 +46,8 @@ interface ISessionManager { void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, int userId); void removeSessionsListener(in IActiveSessionsListener listener); void addSession2TokensListener(in ISession2TokensListener listener, int userId); void removeSession2TokensListener(in ISession2TokensListener listener); // This is for the system volume UI only void setRemoteVolumeController(in IRemoteVolumeController rvc); Loading @@ -56,6 +59,5 @@ interface ISessionManager { void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); void setOnMediaKeyListener(in IOnMediaKeyListener listener); // MediaSession2 boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); }
media/java/android/media/session/MediaSessionManager.java +129 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ import android.util.ArrayMap; import android.util.Log; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.Objects; Loading Loading @@ -69,9 +71,13 @@ public final class MediaSessionManager { */ public static final int RESULT_MEDIA_KEY_HANDLED = 1; private final Object mLock = new Object(); @GuardedBy("mLock") private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>(); private final Object mLock = new Object(); @GuardedBy("mLock") private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper> mSession2TokensListeners = new ArrayMap<>(); private final ISessionManager mService; private Context mContext; Loading Loading @@ -323,6 +329,87 @@ public final class MediaSessionManager { } } /** * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. * * @param listener The listener to add * @param handler The handler to call listener on. If {@code null}, calling thread's looper will * be used. * @hide */ // TODO(jaewan): Unhide public void addOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) { addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler); } /** * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. * * @param userId The userId to listen for changes on * @param listener The listener to add * @param handler The handler to call listener on. If {@code null}, calling thread's looper will * be used. * @hide */ public void addOnSession2TokensChangedListener(int userId, @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) { if (listener == null) { throw new IllegalArgumentException("listener shouldn't be null"); } synchronized (mLock) { if (mSession2TokensListeners.get(listener) != null) { Log.w(TAG, "Attempted to add session listener twice, ignoring."); return; } Session2TokensChangedWrapper wrapper = new Session2TokensChangedWrapper(listener, handler); try { mService.addSession2TokensListener(wrapper.getStub(), userId); mSession2TokensListeners.put(listener, wrapper); } catch (RemoteException e) { Log.e(TAG, "Error in addSessionTokensListener.", e); e.rethrowFromSystemServer(); } } } /** * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates. * * @param listener The listener to remove. * @hide */ // TODO(jaewan): Unhide public void removeOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener) { if (listener == null) { throw new IllegalArgumentException("listener may not be null"); } final Session2TokensChangedWrapper wrapper; synchronized (mLock) { wrapper = mSession2TokensListeners.remove(listener); } if (wrapper != null) { try { mService.removeSession2TokensListener(wrapper.getStub()); } catch (RemoteException e) { Log.e(TAG, "Error in removeSessionTokensListener.", e); e.rethrowFromSystemServer(); } } } /** * Set the remote volume controller to receive volume updates on. Only for * use by system UI. Loading Loading @@ -589,6 +676,26 @@ public final class MediaSessionManager { public void onActiveSessionsChanged(@Nullable List<MediaController> controllers); } /** * Listens for changes to the {@link #getSession2Tokens()}. This can be added * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}. * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> * for consistent behavior across all devices. * * @hide */ public interface OnSession2TokensChangedListener { /** * Called when the {@link #getSession2Tokens()} is changed. * * @param tokens list of {@link Session2Token} */ void onSession2TokensChanged(@NonNull List<Session2Token> tokens); } /** * Listens the volume key long-presses. * @hide Loading Loading @@ -807,6 +914,27 @@ public final class MediaSessionManager { } } private static final class Session2TokensChangedWrapper { private final OnSession2TokensChangedListener mListener; private final Handler mHandler; private final ISession2TokensListener.Stub mStub = new ISession2TokensListener.Stub() { @Override public void onSession2TokensChanged(final List<Session2Token> tokens) { mHandler.post(() -> mListener.onSession2TokensChanged(tokens)); } }; Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) { mListener = listener; mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper()); } public ISession2TokensListener.Stub getStub() { return mStub; } } private static final class OnVolumeKeyLongPressListenerImpl extends IOnVolumeKeyLongPressListener.Stub { private OnVolumeKeyLongPressListener mListener; Loading
services/core/java/com/android/server/media/MediaSessionService.java +141 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.media; import static android.os.UserHandle.USER_ALL; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.INotificationManager; Loading Loading @@ -48,6 +50,7 @@ import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; import android.media.session.IOnVolumeKeyLongPressListener; import android.media.session.ISession; import android.media.session.ISession2TokensListener; import android.media.session.ISessionManager; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; Loading Loading @@ -119,6 +122,9 @@ public class MediaSessionService extends SystemService implements Monitor { // one place. @GuardedBy("mLock") private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>(); @GuardedBy("mLock") private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords = new ArrayList<>(); private KeyguardManager mKeyguardManager; private IAudioService mAudioService; Loading Loading @@ -235,7 +241,7 @@ public class MediaSessionService extends SystemService implements Monitor { private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { List<MediaSessionRecord> records = new ArrayList<>(); if (userId == UserHandle.USER_ALL) { if (userId == USER_ALL) { int size = mUserRecords.size(); for (int i = 0; i < size; i++) { records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); Loading @@ -251,13 +257,24 @@ public class MediaSessionService extends SystemService implements Monitor { // Return global priority session at the first whenever it's asked. if (isGlobalPriorityActiveLocked() && (userId == UserHandle.USER_ALL || userId == mGlobalPrioritySession.getUserId())) { && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) { records.add(0, mGlobalPrioritySession); } return records; } List<Session2Token> getSession2TokensLocked(int userId) { List<Session2Token> list = new ArrayList<>(); if (userId == USER_ALL) { for (int i = 0; i < mSession2TokensPerUser.size(); i++) { list.addAll(mSession2TokensPerUser.valueAt(i)); } } else { list.addAll(mSession2TokensPerUser.get(userId)); } return list; } /** * Tells the system UI that volume has changed on an active remote session. */ Loading Loading @@ -316,7 +333,7 @@ public class MediaSessionService extends SystemService implements Monitor { FullUserRecord user = getFullUserRecordLocked(userId); if (user != null) { if (user.mFullUserId == userId) { user.destroySessionsForUserLocked(UserHandle.USER_ALL); user.destroySessionsForUserLocked(USER_ALL); mUserRecords.remove(userId); } else { user.destroySessionsForUserLocked(userId); Loading Loading @@ -393,14 +410,14 @@ public class MediaSessionService extends SystemService implements Monitor { for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { SessionsListenerRecord listener = mSessionsListeners.get(i); try { enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid, listener.mUserId); enforceMediaPermissions(listener.componentName, listener.pid, listener.uid, listener.userId); } catch (SecurityException e) { Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName Log.i(TAG, "ActiveSessionsListener " + listener.componentName + " is no longer authorized. Disconnecting."); mSessionsListeners.remove(i); try { listener.mListener listener.listener .onActiveSessionsChanged(new ArrayList<MediaSession.Token>()); } catch (Exception e1) { // ignore Loading Loading @@ -562,13 +579,23 @@ public class MediaSessionService extends SystemService implements Monitor { private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) { if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) { return i; } } return -1; } private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) { for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) { return i; } } return -1; } private void pushSessionsChanged(int userId) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(userId); Loading @@ -585,9 +612,9 @@ public class MediaSessionService extends SystemService implements Monitor { pushRemoteVolumeUpdateLocked(userId); for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { SessionsListenerRecord record = mSessionsListeners.get(i); if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { if (record.userId == USER_ALL || record.userId == userId) { try { record.mListener.onActiveSessionsChanged(tokens); record.listener.onActiveSessionsChanged(tokens); } catch (RemoteException e) { Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", e); Loading @@ -614,6 +641,25 @@ public class MediaSessionService extends SystemService implements Monitor { } } void pushSession2TokensChangedLocked(int userId) { List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL); List<Session2Token> session2Tokens = getSession2TokensLocked(userId); for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); try { if (listenerRecord.userId == USER_ALL) { listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); } else if (listenerRecord.userId == userId) { listenerRecord.listener.onSession2TokensChanged(session2Tokens); } } catch (RemoteException e) { Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); mSession2TokensListenerRecords.remove(i); } } } /** * Called when the media button receiver for the {@param record} is changed. * Loading Loading @@ -855,20 +901,20 @@ public class MediaSessionService extends SystemService implements Monitor { } final class SessionsListenerRecord implements IBinder.DeathRecipient { private final IActiveSessionsListener mListener; private final ComponentName mComponentName; private final int mUserId; private final int mPid; private final int mUid; public final IActiveSessionsListener listener; public final ComponentName componentName; public final int userId; public final int pid; public final int uid; public SessionsListenerRecord(IActiveSessionsListener listener, ComponentName componentName, int userId, int pid, int uid) { mListener = listener; mComponentName = componentName; mUserId = userId; mPid = pid; mUid = uid; this.listener = listener; this.componentName = componentName; this.userId = userId; this.pid = pid; this.uid = uid; } @Override Loading @@ -879,6 +925,24 @@ public class MediaSessionService extends SystemService implements Monitor { } } final class Session2TokensListenerRecord implements IBinder.DeathRecipient { public final ISession2TokensListener listener; public final int userId; Session2TokensListenerRecord(ISession2TokensListener listener, int userId) { this.listener = listener; this.userId = userId; } @Override public void binderDied() { synchronized (mLock) { mSession2TokensListenerRecords.remove(this); } } } final class SettingsObserver extends ContentObserver { private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); Loading @@ -889,7 +953,7 @@ public class MediaSessionService extends SystemService implements Monitor { private void observe() { mContentResolver.registerContentObserver(mSecureSettingsUri, false, this, UserHandle.USER_ALL); false, this, USER_ALL); } @Override Loading Loading @@ -984,15 +1048,9 @@ public class MediaSessionService extends SystemService implements Monitor { int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, true /* allowAll */, true /* requireFull */, "getSession2Tokens", null /* optional packageName */); List<Session2Token> result = new ArrayList<>(); List<Session2Token> result; synchronized (mLock) { if (resolvedUserId == UserHandle.USER_ALL) { for (int i = 0; i < mSession2TokensPerUser.size(); i++) { result.addAll(mSession2TokensPerUser.valueAt(i)); } } else { result.addAll(mSession2TokensPerUser.get(userId)); } result = getSession2TokensLocked(resolvedUserId); } return result; } finally { Loading Loading @@ -1038,7 +1096,7 @@ public class MediaSessionService extends SystemService implements Monitor { if (index != -1) { SessionsListenerRecord record = mSessionsListeners.remove(index); try { record.mListener.asBinder().unlinkToDeath(record, 0); record.listener.asBinder().unlinkToDeath(record, 0); } catch (Exception e) { // ignore exceptions, the record is being removed } Loading @@ -1046,6 +1104,56 @@ public class MediaSessionService extends SystemService implements Monitor { } } @Override public void addSession2TokensListener(ISession2TokensListener listener, int userId) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { // 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 */, "addSession2TokensListener", null /* optional packageName */); synchronized (mLock) { int index = findIndexOfSession2TokensListenerLocked(listener); if (index >= 0) { Log.w(TAG, "addSession2TokensListener is already added, ignoring"); return; } mSession2TokensListenerRecords.add( new Session2TokensListenerRecord(listener, resolvedUserId)); } } finally { Binder.restoreCallingIdentity(token); } } @Override public void removeSession2TokensListener(ISession2TokensListener listener) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { int index = findIndexOfSession2TokensListenerLocked(listener); if (index >= 0) { Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.remove(index); try { listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0); } catch (Exception e) { // Ignore exception. } } } } finally { Binder.restoreCallingIdentity(token); } } /** * Handles the dispatching of the media button events to one of the * registered listeners, or if there was none, broadcast an Loading Loading @@ -2012,6 +2120,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int userId = UserHandle.getUserId(mToken.getUid()); mSession2TokensPerUser.get(userId).add(mToken); pushSession2TokensChangedLocked(userId); } } Loading @@ -2020,6 +2129,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int userId = UserHandle.getUserId(mToken.getUid()); mSession2TokensPerUser.get(userId).remove(mToken); pushSession2TokensChangedLocked(userId); } } } Loading