Loading apex/media/aidl/private/android/media/IMediaCommunicationService.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.media; import android.media.Session2Token; import android.media.IMediaCommunicationServiceCallback; import android.media.MediaParceledListSlice; import android.view.KeyEvent; /** {@hide} */ interface IMediaCommunicationService { Loading @@ -25,6 +26,8 @@ interface IMediaCommunicationService { boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); MediaParceledListSlice getSession2Tokens(int userId); void dispatchMediaKeyEvent(String packageName, in KeyEvent keyEvent, boolean asSystemService); void registerCallback(IMediaCommunicationServiceCallback callback, String packageName); void unregisterCallback(IMediaCommunicationServiceCallback callback); } Loading apex/media/framework/api/module-lib-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package android.media { public class MediaCommunicationManager { method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback); method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback); } Loading apex/media/framework/java/android/media/MediaCommunicationManager.java +37 −10 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.media.MediaBrowserService; import android.util.Log; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.modules.annotation.MinSdk; Loading Loading @@ -63,7 +64,8 @@ public class MediaCommunicationManager { private static final int CURRENT_VERSION = VERSION_1; private final Context mContext; private final IMediaCommunicationService mService; // Do not access directly use getService(). private IMediaCommunicationService mService; private final Object mLock = new Object(); private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords = Loading @@ -80,10 +82,6 @@ public class MediaCommunicationManager { throw new UnsupportedOperationException("Android version must be S or greater."); } mContext = context; mService = IMediaCommunicationService.Stub.asInterface( MediaFrameworkInitializer.getMediaServiceManager() .getMediaCommunicationServiceRegisterer() .get()); } /** Loading @@ -105,7 +103,7 @@ public class MediaCommunicationManager { throw new IllegalArgumentException("token's type should be TYPE_SESSION"); } try { mService.notifySession2Created(token); getService().notifySession2Created(token); } catch (RemoteException e) { e.rethrowFromSystemServer(); } Loading @@ -130,7 +128,7 @@ public class MediaCommunicationManager { return false; } try { return mService.isTrusted( return getService().isTrusted( userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid()); } catch (RemoteException e) { Log.w(TAG, "Cannot communicate with the service.", e); Loading Loading @@ -182,7 +180,7 @@ public class MediaCommunicationManager { MediaCommunicationServiceCallbackStub callbackStub = new MediaCommunicationServiceCallbackStub(); try { mService.registerCallback(callbackStub, mContext.getPackageName()); getService().registerCallback(callbackStub, mContext.getPackageName()); mCallbackStub = callbackStub; } catch (RemoteException ex) { Log.e(TAG, "Failed to register callback.", ex); Loading @@ -205,7 +203,7 @@ public class MediaCommunicationManager { synchronized (mLock) { if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) { try { mService.unregisterCallback(mCallbackStub); getService().unregisterCallback(mCallbackStub); } catch (RemoteException ex) { Log.e(TAG, "Failed to unregister callback.", ex); } Loading @@ -214,9 +212,19 @@ public class MediaCommunicationManager { } } private IMediaCommunicationService getService() { if (mService == null) { mService = IMediaCommunicationService.Stub.asInterface( MediaFrameworkInitializer.getMediaServiceManager() .getMediaCommunicationServiceRegisterer() .get()); } return mService; } private List<Session2Token> getSession2Tokens(int userId) { try { MediaParceledListSlice slice = mService.getSession2Tokens(userId); MediaParceledListSlice slice = getService().getSession2Tokens(userId); return slice == null ? Collections.emptyList() : slice.getList(); } catch (RemoteException e) { Log.e(TAG, "Failed to get session tokens", e); Loading @@ -224,6 +232,25 @@ public class MediaCommunicationManager { return Collections.emptyList(); } /** * Sends a media key event. The receiver will be selected automatically. * * @param keyEvent the key event to send * @param asSystemService if {@code true}, the event sent to the session as if it was come from * the system service instead of the app process. * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) { Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null"); try { getService().dispatchMediaKeyEvent(mContext.getPackageName(), keyEvent, asSystemService); } catch (RemoteException e) { Log.e(TAG, "Failed to send key event.", e); } } /** * Callback for listening to changes to the sessions. * @see #registerSessionCallback(Executor, SessionCallback) Loading apex/media/service/java/com/android/server/media/MediaCommunicationService.java +56 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.media.MediaController2; import android.media.MediaParceledListSlice; import android.media.Session2CommandGroup; import android.media.Session2Token; import android.media.session.MediaSessionManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; Loading @@ -42,6 +43,7 @@ import android.os.UserManager; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; Loading @@ -60,7 +62,7 @@ import java.util.stream.Collectors; * @hide */ public class MediaCommunicationService extends SystemService { private static final String TAG = "MediaCommunicationService"; private static final String TAG = "MediaCommunicationSrv"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); final Context mContext; Loading @@ -77,6 +79,7 @@ public class MediaCommunicationService extends SystemService { @GuardedBy("mLock") final List<CallbackRecord> mCallbackRecords = new ArrayList<>(); final NotificationManager mNotificationManager; MediaSessionManager mSessionManager; public MediaCommunicationService(Context context) { super(context); Loading @@ -90,6 +93,17 @@ public class MediaCommunicationService extends SystemService { updateUser(); } @Override public void onBootPhase(int phase) { super.onBootPhase(phase); switch (phase) { // This ensures MediaSessionService is started case PHASE_BOOT_COMPLETED: mSessionManager = mContext.getSystemService(MediaSessionManager.class); break; } } @Override public void onUserStarting(@NonNull TargetUser user) { if (DEBUG) Log.d(TAG, "onUserStarting: " + user); Loading Loading @@ -267,6 +281,24 @@ public class MediaCommunicationService extends SystemService { session.close(); } static boolean isMediaSessionKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_STOP: case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: return true; } return false; } private class Stub extends IMediaCommunicationService.Stub { @Override public void notifySession2Created(Session2Token sessionToken) { Loading Loading @@ -350,6 +382,29 @@ public class MediaCommunicationService extends SystemService { } } @Override public void dispatchMediaKeyEvent(String packageName, KeyEvent keyEvent, boolean asSystemService) { if (keyEvent == null || !isMediaSessionKey(keyEvent.getKeyCode())) { Log.w(TAG, "Attempted to dispatch null or non-media key event."); return; } final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { //TODO: Dispatch key event to media session 2 if required if (asSystemService) { mSessionManager.dispatchMediaKeyEventAsSystemService(keyEvent); } else { mSessionManager.dispatchMediaKeyEvent(keyEvent, false); } } finally { Binder.restoreCallingIdentity(token); } } @Override public void registerCallback(IMediaCommunicationServiceCallback callback, String packageName) throws RemoteException { Loading media/java/android/media/session/MediaSessionLegacyHelper.java +4 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.media.MediaCommunicationManager; import android.media.MediaMetadata; import android.media.MediaMetadataEditor; import android.media.MediaMetadataRetriever; Loading Loading @@ -53,6 +54,7 @@ public class MediaSessionLegacyHelper { private Context mContext; private MediaSessionManager mSessionManager; private MediaCommunicationManager mCommunicationManager; private Handler mHandler = new Handler(Looper.getMainLooper()); // The legacy APIs use PendingIntents to register/unregister media button // receivers and these are associated with RCC. Loading @@ -63,6 +65,7 @@ public class MediaSessionLegacyHelper { mContext = context; mSessionManager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); mCommunicationManager = context.getSystemService(MediaCommunicationManager.class); } @UnsupportedAppUsage Loading Loading @@ -171,7 +174,7 @@ public class MediaSessionLegacyHelper { Log.w(TAG, "Tried to send a null key event. Ignoring."); return; } mSessionManager.dispatchMediaKeyEvent(keyEvent, needWakeLock); mCommunicationManager.dispatchMediaKeyEvent(keyEvent, needWakeLock); if (DEBUG) { Log.d(TAG, "dispatched media key " + keyEvent); } Loading Loading
apex/media/aidl/private/android/media/IMediaCommunicationService.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.media; import android.media.Session2Token; import android.media.IMediaCommunicationServiceCallback; import android.media.MediaParceledListSlice; import android.view.KeyEvent; /** {@hide} */ interface IMediaCommunicationService { Loading @@ -25,6 +26,8 @@ interface IMediaCommunicationService { boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); MediaParceledListSlice getSession2Tokens(int userId); void dispatchMediaKeyEvent(String packageName, in KeyEvent keyEvent, boolean asSystemService); void registerCallback(IMediaCommunicationServiceCallback callback, String packageName); void unregisterCallback(IMediaCommunicationServiceCallback callback); } Loading
apex/media/framework/api/module-lib-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package android.media { public class MediaCommunicationManager { method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback); method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback); } Loading
apex/media/framework/java/android/media/MediaCommunicationManager.java +37 −10 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.media.MediaBrowserService; import android.util.Log; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.modules.annotation.MinSdk; Loading Loading @@ -63,7 +64,8 @@ public class MediaCommunicationManager { private static final int CURRENT_VERSION = VERSION_1; private final Context mContext; private final IMediaCommunicationService mService; // Do not access directly use getService(). private IMediaCommunicationService mService; private final Object mLock = new Object(); private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords = Loading @@ -80,10 +82,6 @@ public class MediaCommunicationManager { throw new UnsupportedOperationException("Android version must be S or greater."); } mContext = context; mService = IMediaCommunicationService.Stub.asInterface( MediaFrameworkInitializer.getMediaServiceManager() .getMediaCommunicationServiceRegisterer() .get()); } /** Loading @@ -105,7 +103,7 @@ public class MediaCommunicationManager { throw new IllegalArgumentException("token's type should be TYPE_SESSION"); } try { mService.notifySession2Created(token); getService().notifySession2Created(token); } catch (RemoteException e) { e.rethrowFromSystemServer(); } Loading @@ -130,7 +128,7 @@ public class MediaCommunicationManager { return false; } try { return mService.isTrusted( return getService().isTrusted( userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid()); } catch (RemoteException e) { Log.w(TAG, "Cannot communicate with the service.", e); Loading Loading @@ -182,7 +180,7 @@ public class MediaCommunicationManager { MediaCommunicationServiceCallbackStub callbackStub = new MediaCommunicationServiceCallbackStub(); try { mService.registerCallback(callbackStub, mContext.getPackageName()); getService().registerCallback(callbackStub, mContext.getPackageName()); mCallbackStub = callbackStub; } catch (RemoteException ex) { Log.e(TAG, "Failed to register callback.", ex); Loading @@ -205,7 +203,7 @@ public class MediaCommunicationManager { synchronized (mLock) { if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) { try { mService.unregisterCallback(mCallbackStub); getService().unregisterCallback(mCallbackStub); } catch (RemoteException ex) { Log.e(TAG, "Failed to unregister callback.", ex); } Loading @@ -214,9 +212,19 @@ public class MediaCommunicationManager { } } private IMediaCommunicationService getService() { if (mService == null) { mService = IMediaCommunicationService.Stub.asInterface( MediaFrameworkInitializer.getMediaServiceManager() .getMediaCommunicationServiceRegisterer() .get()); } return mService; } private List<Session2Token> getSession2Tokens(int userId) { try { MediaParceledListSlice slice = mService.getSession2Tokens(userId); MediaParceledListSlice slice = getService().getSession2Tokens(userId); return slice == null ? Collections.emptyList() : slice.getList(); } catch (RemoteException e) { Log.e(TAG, "Failed to get session tokens", e); Loading @@ -224,6 +232,25 @@ public class MediaCommunicationManager { return Collections.emptyList(); } /** * Sends a media key event. The receiver will be selected automatically. * * @param keyEvent the key event to send * @param asSystemService if {@code true}, the event sent to the session as if it was come from * the system service instead of the app process. * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) { Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null"); try { getService().dispatchMediaKeyEvent(mContext.getPackageName(), keyEvent, asSystemService); } catch (RemoteException e) { Log.e(TAG, "Failed to send key event.", e); } } /** * Callback for listening to changes to the sessions. * @see #registerSessionCallback(Executor, SessionCallback) Loading
apex/media/service/java/com/android/server/media/MediaCommunicationService.java +56 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.media.MediaController2; import android.media.MediaParceledListSlice; import android.media.Session2CommandGroup; import android.media.Session2Token; import android.media.session.MediaSessionManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; Loading @@ -42,6 +43,7 @@ import android.os.UserManager; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; Loading @@ -60,7 +62,7 @@ import java.util.stream.Collectors; * @hide */ public class MediaCommunicationService extends SystemService { private static final String TAG = "MediaCommunicationService"; private static final String TAG = "MediaCommunicationSrv"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); final Context mContext; Loading @@ -77,6 +79,7 @@ public class MediaCommunicationService extends SystemService { @GuardedBy("mLock") final List<CallbackRecord> mCallbackRecords = new ArrayList<>(); final NotificationManager mNotificationManager; MediaSessionManager mSessionManager; public MediaCommunicationService(Context context) { super(context); Loading @@ -90,6 +93,17 @@ public class MediaCommunicationService extends SystemService { updateUser(); } @Override public void onBootPhase(int phase) { super.onBootPhase(phase); switch (phase) { // This ensures MediaSessionService is started case PHASE_BOOT_COMPLETED: mSessionManager = mContext.getSystemService(MediaSessionManager.class); break; } } @Override public void onUserStarting(@NonNull TargetUser user) { if (DEBUG) Log.d(TAG, "onUserStarting: " + user); Loading Loading @@ -267,6 +281,24 @@ public class MediaCommunicationService extends SystemService { session.close(); } static boolean isMediaSessionKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_STOP: case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: return true; } return false; } private class Stub extends IMediaCommunicationService.Stub { @Override public void notifySession2Created(Session2Token sessionToken) { Loading Loading @@ -350,6 +382,29 @@ public class MediaCommunicationService extends SystemService { } } @Override public void dispatchMediaKeyEvent(String packageName, KeyEvent keyEvent, boolean asSystemService) { if (keyEvent == null || !isMediaSessionKey(keyEvent.getKeyCode())) { Log.w(TAG, "Attempted to dispatch null or non-media key event."); return; } final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { //TODO: Dispatch key event to media session 2 if required if (asSystemService) { mSessionManager.dispatchMediaKeyEventAsSystemService(keyEvent); } else { mSessionManager.dispatchMediaKeyEvent(keyEvent, false); } } finally { Binder.restoreCallingIdentity(token); } } @Override public void registerCallback(IMediaCommunicationServiceCallback callback, String packageName) throws RemoteException { Loading
media/java/android/media/session/MediaSessionLegacyHelper.java +4 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.media.MediaCommunicationManager; import android.media.MediaMetadata; import android.media.MediaMetadataEditor; import android.media.MediaMetadataRetriever; Loading Loading @@ -53,6 +54,7 @@ public class MediaSessionLegacyHelper { private Context mContext; private MediaSessionManager mSessionManager; private MediaCommunicationManager mCommunicationManager; private Handler mHandler = new Handler(Looper.getMainLooper()); // The legacy APIs use PendingIntents to register/unregister media button // receivers and these are associated with RCC. Loading @@ -63,6 +65,7 @@ public class MediaSessionLegacyHelper { mContext = context; mSessionManager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); mCommunicationManager = context.getSystemService(MediaCommunicationManager.class); } @UnsupportedAppUsage Loading Loading @@ -171,7 +174,7 @@ public class MediaSessionLegacyHelper { Log.w(TAG, "Tried to send a null key event. Ignoring."); return; } mSessionManager.dispatchMediaKeyEvent(keyEvent, needWakeLock); mCommunicationManager.dispatchMediaKeyEvent(keyEvent, needWakeLock); if (DEBUG) { Log.d(TAG, "dispatched media key " + keyEvent); } Loading