目录
- 前言
- 正文
- MediaSession.java
- setCallback()
- CallbackMessageHandler.handleMessage()
- dispatchMediaButton()
- MediaSession.CallbackStub
- CallbackStub.onMediaButton()
- MediaSession()
- MediaSessionManager.java
- MediaSessionService.java
- MediaSessionRecord.java
- MediaSessionService.java
- MediaSessionStack.java
- MediaSessionService.java
- KeyEventHandler.handleKeyEventLocked()
- KeyEventHandler.handleMediaKeyEventLocked()
- SessionManagerImpl.dispatchMediaKeyEvent()
- MediaSessionManager.java
- PhoneFallbackEventHandler.java
- 参考文章
前言
本文监听MediaButton是如何分发,以及分发到对应的监听者中。
本想改个名字,后面想都跟MediaSession有关系就延用。
个人流水账,随便跟一下。
正文
接之前《》,我们设置过MediaSession.Callback的监听,今天我们介绍按键上下曲的监听。
- //MediaSession.Callback的实现
- private final MediaSession.Callback mediaSessionCallback = new MediaSession.Callback() {
- @Override
- public boolean onMediaButtonEvent(@NonNull Intent intent) {
- String action = intent.getAction();
- //判断是否有焦点
- if (action.equals(Intent.ACTION_MEDIA_BUTTON) && !isAudioFocusLoss()) {
- //获取keyevent事件
- KeyEvent keyevent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
- int keyCode = keyevent.getKeyCode();
- int keyAction = keyevent.getAction();
- //弹起响应
- if (keyAction == KeyEvent.ACTION_UP) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- //下一曲
- onSkipToNext();
- return true;
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- //上一曲
- onSkipToPrevious();
- return true;
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- //暂停
- return true;
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- //播放
- return true;
- }
- }
- }
- return super.onMediaButtonEvent(intent);
- }
- };
这里我们跟一下上一曲KeyEvent.KEYCODE_MEDIA_PREVIOUS,键值为88。
之前分析说过。
- MediaSession mMediaSession = new MediaSession(MusicApp.getContext(), TAG);
- //设置MediaSession.Callback监听
- mMediaSession.setCallback(mediaSessionCallback);
MediaSession.java
- frameworks\base\media\java\android\media\session\MediaSession.java
setCallback()
- public void setCallback(@Nullable Callback callback) {
- setCallback(callback, null);
- }
-
- public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
- synchronized (mLock) {
- if (mCallback != null) {
- mCallback.mCallback.mSession = null;
- mCallback.removeCallbacksAndMessages(null);
- }
- if (callback == null) {
- mCallback = null;
- return;
- }
- if (handler == null) {
- handler = new Handler();
- }
- callback.mSession = this;
- CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),callback);
- mCallback = msgHandler;
- }
- }
注意这里的mCallback是CallbackMessageHandler(重写的Handler)对象。同时把callback (MediaSession.Callback对象)传入到mCallback中。
既然传入到Handler中,那自然看handleMessage中的消息处理。
CallbackMessageHandler.handleMessage()
- @Override
- public void handleMessage(Message msg) {
- //略
- switch (msg.what) {
- case MSG_COMMAND:
- Command cmd = (Command) obj;
- mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
- break;
- case MSG_MEDIA_BUTTON:
- mCallback.onMediaButtonEvent((Intent) obj);
- break;
- //略
- }
- }
找到MSG_MEDIA_BUTTON,也就是这里回调了mCallback。
注意这里的mCallback是CallbackMessageHandler中的遍历,也就是上面传入的MediaSession.Callback。
既然知道发送的MSG_MEDIA_BUTTON消息,那就是找到发送的地方即可。
dispatchMediaButton()
- void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
- postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null);
- }
经过跟踪,定位到CallbackStub.onMediaButton()中。
MediaSession.CallbackStub
在看CallbackStub.onMediaButton()之前,先看一下CallbackStub类。
CallbackStub比较重要,很多方法都是这里回调的。
- public static class CallbackStub extends ISessionCallback.Stub {
- private WeakReference<MediaSession> mMediaSession;
-
- public CallbackStub(MediaSession session) {
- mMediaSession = new WeakReference<>(session);
- }
- //略
- }
记住这里传入的mMediaSession。
CallbackStub.onMediaButton()
- @Override
- public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
- int sequenceNumber, ResultReceiver cb) {
- //获取构造函数中传入的MediaSession
- MediaSession session = mMediaSession.get();
- try {
- if (session != null) {
- //分发Media Button
- session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid),
- mediaButtonIntent);
- }
- } finally {
- if (cb != null) {
- cb.send(sequenceNumber, null);
- }
- }
- }
看何时创建CallbackStub,也就是mMediaSession的初始化。
CallbackStub初始化是在MediaSession构造函数中。
MediaSession()
- public MediaSession(@NonNull Context context, @NonNull String tag,
- @Nullable Bundle sessionInfo) {
- //略
- //创建CallbackStub对象
- mCbStub = new CallbackStub(this);
- //创建MediaSessionManager对象
- MediaSessionManager manager = (MediaSessionManager) context
- .getSystemService(Context.MEDIA_SESSION_SERVICE);
- try {
- //这里传入了mCbStub
- mBinder = manager.createSession(mCbStub, tag, sessionInfo);
- mSessionToken = new Token(Process.myUid(), mBinder.getController());
- //创建MediaController对象
- mController = new MediaController(context, mSessionToken);
- } catch (RemoteException e) {
- throw new RuntimeException("Remote error creating session.", e);
- }
- }
也就是传入CallbackStub的当前MediaSession对象,也就是上面的mMediaSession。
在创建mBinder时,传入的第一个参数为mCbStub(CallbackStub对象),跟上,这个是重点。
- mBinder = manager.createSession(mCbStub, tag, sessionInfo);
MediaSessionManager.java
先看MediaSessionManager中的createSession()
createSession()
- @NonNull
- public ISession createSession(@NonNull MediaSession.CallbackStub cbStub, @NonNull String tag,
- @Nullable Bundle sessionInfo) {
- //略
- try {
- return mService.createSession(mContext.getPackageName(), cbStub, tag, sessionInfo,
- UserHandle.myUserId());
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
之前介绍过,mService是SessionManagerImpl在MediaSessionManager的代理对象。
SessionManagerImpl在MediaSessionService.java中,其实就是MediaSessionService跟MediaSessionManager沟通的桥梁。
MediaSessionService.java
- frameworks\base\services\core\java\com\android\server\media\MediaSessionService.java
我们看SessionManagerImpl中的createSession()
SessionManagerImpl.createSession()
紧跟上面,传入的mCbStub是第二个参数,也就是ISessionCallback。
我们上面也说了CallbackStub是实现于ISessionCallback。
- @Override
- public ISession createSession(String packageName, ISessionCallback cb, String tag,
- Bundle sessionInfo, int userId) throws RemoteException {
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- try {
- enforcePackageName(packageName, uid);
- int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
- if (cb == null) {
- throw new IllegalArgumentException("Controller callback cannot be null");
- }
- //创建MediaSessionRecordImpl
- //第5个参数为cb
- MediaSessionRecord session = createSessionInternal(
- pid, uid, resolvedUserId, packageName, cb, tag, sessionInfo);
- if (session == null) {
- throw new IllegalStateException("Failed to create a new session record");
- }
- ISession sessionBinder = session.getSessionBinder();
- if (sessionBinder == null) {
- throw new IllegalStateException("Invalid session record");
- }
- return sessionBinder;
- } catch (Exception e) {
- throw e;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
上面通过createSessionInternal创建了MediaSessionRecord,然后传回了MediaSessionRecord的Binder。
重点关注createSessionInternal(),注意,这里也传入了cb。
createSessionInternal()
- private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
- String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
- synchronized (mLock) {
- int policies = 0;
- if (mCustomMediaSessionPolicyProvider != null) {
- policies = mCustomMediaSessionPolicyProvider.getSessionPoliciesForApplication(
- callerUid, callerPackageName);
- }
- FullUserRecord user = getFullUserRecordLocked(userId);
- if (user == null) {
- throw new RuntimeException("Session request from invalid user.");
- }
- final int sessionCount = user.mUidToSessionCount.get(callerUid, 0);
- if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
- && !hasMediaControlPermission(callerPid, callerUid)) {
- throw new RuntimeException("Created too many sessions. count="
- + sessionCount + ")");
- }
- final MediaSessionRecord session;
- try {
- //cb传入了MediaSessionRecord
- session = new MediaSessionRecord(callerPid, callerUid, userId,
- callerPackageName, cb, tag, sessionInfo, this,
- mRecordThread.getLooper(), policies);
- } catch (RemoteException e) {
- throw new RuntimeException("Media Session owner died prematurely.", e);
- }
- user.mUidToSessionCount.put(callerUid, sessionCount + 1);
- user.mPriorityStack.addSession(session);
- mHandler.postSessionsChanged(session);
- return session;
- }
- }
忽略其他的,暂时关注ISessionCallback对象,传入到MediaSessionRecord中。
MediaSessionRecord.java
MediaSessionRecord()
- public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
- ISessionCallback cb, String tag, Bundle sessionInfo,
- MediaSessionService service, Looper handlerLooper, int policies)
- throws RemoteException {
- //略
- //ISessionCallback传入到SessionCb
- mSessionCb = new SessionCb(cb);
- //略
- }
SessionCb定义在MediaSessionRecord()中。
SessionCb()
- class SessionCb {
- private final ISessionCallback mCb;
-
- SessionCb(ISessionCallback cb) {
- mCb = cb;
- }
- public boolean sendMediaButton(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
- //略
- }
- public void play(String packageName, int pid, int uid) {
- //略
- }
- //略
- }
SessionCb中很多方法,都是跟ISessionCallback接口中定义的方法一样。
CallBackStub的的回调是在这里的。当前我们这里重点关注sendMediaButton()。
这里定义了两个sendMediaButton(),经过但两个最终回调的方法不一样。
看第一个即可。
sendMediaButton()
- public boolean sendMediaButton(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
- try {
- if (KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
- final String reason = "action=" + KeyEvent.actionToString(keyEvent.getAction())
- + ";code=" + KeyEvent.keyCodeToString(keyEvent.getKeyCode());
- mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
- pid, uid, packageName, reason);
- }
- //asSystemService为true,传入的就是true
- if (asSystemService) {
- //走这里
- mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
- Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
- } else {
- mCb.onMediaButton(packageName, pid, uid,
- createMediaButtonIntent(keyEvent), sequenceId, cb);
- }
- return true;
- } catch (RemoteException e) {
- }
- return false;
- }
MediaSessionService.java
最后发现sendMediaButton()是在MediaSessionService中的SessionManagerImpl.dispatchMediaKeyEventLocked()调用的。
SessionManagerImpl.dispatchMediaKeyEventLocked()
- private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
- //略
- MediaSessionRecord session = null;
- MediaButtonReceiverHolder mediaButtonReceiverHolder = null;
- //略
- //上面创建的临时对象,都为null
- if (session == null && mediaButtonReceiverHolder == null) {
- //获取当前MediaSessionRecord
- //这里重点关注
- session = (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
- //已经不为null了
- if (session == null) {
- mediaButtonReceiverHolder =
- mCurrentFullUserRecord.mLastMediaButtonReceiverHolder;
- }
- }
- //不为null咯
- if (session != null) {
- //为true
- if (needWakeLock) {
- mKeyEventReceiver.acquireWakeLockLocked();
- }
- //调用sendMediaButton发送,这就是调用上面的了
- session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
- needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
- mKeyEventReceiver);
- } else if (mediaButtonReceiverHolder != null) {
- //略
- }
- }
调用分发的已经知道,但到底分发给谁,就需要看获取MediaSessionRecord的方法。
- session = (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
mCurrentFullUserRecord是FullUserRecord对象。
FullUserRecord.getMediaButtonSessionLocked()
- private MediaSessionRecordImpl getMediaButtonSessionLocked() {
- boolean gloobal = isGlobalPriorityActiveLocked();
- return isGlobalPriorityActiveLocked()
- ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
- }
这里要判断是否有全局Session,这个优先级高于其他的。
isGlobalPriorityActiveLocked()判断之前有介绍过,当前没有注册全局MediaSession,跳过。
因此返回的是
- mPriorityStack.getMediaButtonSession()
mPriorityStack是MediaSessionStack对象。
MediaSessionStack.java
- frameworks\base\services\core\java\com\android\server\media\MediaSessionStack.java
getMediaButtonSession()
- public MediaSessionRecordImpl getMediaButtonSession() {
- return mMediaButtonSession;
- }
这里返回的mMediaButtonSession是当前能收到Media Button的Session。
所以之前分析最新的mMediaButtonSession就是可以接收到Media Button的回应。
MediaSessionService.java
继续回到上面dispatchMediaKeyEventLocked(),看一下哪里分发按键的。
dispatchMediaKeyEventLocked()是在KeyEventHandler.handleKeyEventLocked()中调用的。
跟上。
KeyEventHandler.handleKeyEventLocked()
- void handleKeyEventLocked(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
- String opPackageName, int stream, boolean musicOnly) {
- if (keyEvent.isCanceled()) {
- return;
- }
- int overriddenKeyEvents = 0;
- if (mCustomMediaKeyDispatcher != null
- && mCustomMediaKeyDispatcher.getOverriddenKeyEvents() != null) {
- overriddenKeyEvents = mCustomMediaKeyDispatcher.getOverriddenKeyEvents()
- .get(keyEvent.getKeyCode());
- }
- cancelTrackingIfNeeded(packageName, pid, uid, asSystemService, keyEvent,
- needWakeLock, opPackageName, stream, musicOnly, overriddenKeyEvents);
- //判断是否需要Tracking,
- if (!needTracking(keyEvent, overriddenKeyEvents)) {
- //我们暂时只分析down事件,因此进入这里
- //不是音量类型
- if (mKeyType == KEY_TYPE_VOLUME) {
- dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
- asSystemService, keyEvent, stream, musicOnly);
- } else {
- //走这里
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- }
- return;
- }
- //下面的暂时不看,略
- }
当前我这只分析down事件,up事件一样流程。
KeyEventHandler.handleMediaKeyEventLocked()
这里调用了上面的handleKeyEventLocked()
- void handleMediaKeyEventLocked(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
- handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, needWakeLock,
- null, 0, false);
- }
接下来看哪里调用了handleMediaKeyEventLocked()。
回调的地方在SessionManagerImpl.dispatchMediaKeyEvent()中。
SessionManagerImpl.dispatchMediaKeyEvent()
- @Override
- public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
- KeyEvent keyEvent, boolean needWakeLock) {
- //略
- try {
- //开机引导是否完成
- //之前写过过上下曲收不到就是这个原因
- if (!isUserSetupComplete()) {
- return;
- }
- synchronized (mLock) {
- boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
- //没有全局的Session,而且全局的都是系统进程
- if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
- return;
- }
- //略
- if (isGlobalPriorityActive) {
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- } else {
- //走这里
- //mMediaKeyEventHandler是KeyEventHandler对象
- mMediaKeyEventHandler.handleMediaKeyEventLocked(packageName, pid, uid,
- asSystemService, keyEvent, needWakeLock);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
我们知道MediaSessionManager中的mSerivce就是SessionManagerImpl的代理。
MediaSessionManager.java
dispatchMediaKeyEventInternal()
这里调用了SessionManagerImpl中的dispatchMediaKeyEvent()
- private void dispatchMediaKeyEventInternal(KeyEvent keyEvent, boolean asSystemService,
- boolean needWakeLock) {
- try {
- mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,needWakeLock);
- } catch (RemoteException e) {
- }
- }
在dispatchMediaKeyEventAsSystemService中有调用了dispatchMediaKeyEventInternal()。
dispatchMediaKeyEventAsSystemService()
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public void dispatchMediaKeyEventAsSystemService(@NonNull KeyEvent keyEvent) {
- dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/true, /*needWakeLock=*/true);
- }
asSystemService这里默认传入的asSystemService就是为true
dispatchMediaKeyEventAsSystemService()的调用是在PhoneFallbackEventHandler()中。
PhoneFallbackEventHandler.java
- frameworks\base\core\java\com\android\internal\policy\PhoneFallbackEventHandler.java
handleMediaKeyEvent()
- private void handleMediaKeyEvent(KeyEvent keyEvent) {
- getMediaSessionManager().dispatchMediaKeyEventAsSystemService(keyEvent);
- }
handleMediaKeyEvent()的调用分down和up,我们这里暂时看down。
onKeyUp()
- @UnsupportedAppUsage
- boolean onKeyDown(int keyCode, KeyEvent event) {
- final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
- switch (keyCode) {
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_MUTE: {
- //处理音量
- handleVolumeKeyEvent(event);
- return true;
- }
- 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:
- case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
- //这里处理大部分媒体按键
- handleMediaKeyEvent(event);
- return true;
- }
- //略
- }
- return false;
- }
dispatchKeyEvent()
- public boolean dispatchKeyEvent(KeyEvent event) {
- final int action = event.getAction();
- final int keyCode = event.getKeyCode();
- if (action == KeyEvent.ACTION_DOWN) {
- return onKeyDown(keyCode, event);
- } else {
- return onKeyUp(keyCode, event);
- }
- }
dispatchKeyEvent()的调用是在ViewRootImpl.java中,暂时跟到这里。