目录
- 前言
- 正文
- 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中,暂时跟到这里。