Add MCM#dispatchMediaKeyEvent
Make a room for MediaCommunicationService to handle key events. Bug:193757214 Test: atest CtsMediaTestCases Change-Id: I5236ab549f9f06b98d8229b45050df791c26c309
This commit is contained in:
@@ -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 {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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 =
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -63,6 +65,7 @@ public class MediaSessionLegacyHelper {
|
||||
mContext = context;
|
||||
mSessionManager = (MediaSessionManager) context
|
||||
.getSystemService(Context.MEDIA_SESSION_SERVICE);
|
||||
mCommunicationManager = context.getSystemService(MediaCommunicationManager.class);
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user