audio系列之MediaButton--基于Android7.0

来源:互联网 发布:caffe 图片预处理 编辑:程序博客网 时间:2024/06/06 06:41

AudioManager#registreMediaButtonBroadcastReceiver(PendingIntent);

/**     * @hide     * no-op if (pi == null) or (eventReceiver == null)     */    public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {        if (pi == null) {            Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");            return;        }        MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());        helper.addMediaButtonListener(pi, eventReceiver, getContext());    }

下面看下MediaSessionLegacyHelper#addMediaButtonListener():

public void addMediaButtonListener(PendingIntent pi, ComponentName mbrComponent,            Context context) {        if (pi == null) {            Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");            return;        }        SessionHolder holder = getHolder(pi, true);        if (holder == null) {            return;        }        if (holder.mMediaButtonListener != null) {            // Already have this listener registered            if (DEBUG) {                Log.d(TAG, "addMediaButtonListener already added " + pi);            }        }        holder.mMediaButtonListener = new MediaButtonListener(pi, context);        // TODO determine if handling transport performer commands should also        // set this flag        holder.mFlags |= MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;        holder.mSession.setFlags(holder.mFlags);        holder.mSession.setMediaButtonReceiver(pi);        holder.update();        if (DEBUG) {            Log.d(TAG, "addMediaButtonListener added " + pi);        }    }

其中在getHolder()中用到了mSessions,是一个ArrayMap。

holder.mSession.setMediaButtonReceiver(pi);分析这句:其中mSession是MediaSession类型

/**     * Set a pending intent for your media button receiver to allow restarting     * playback after the session has been stopped. If your app is started in     * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via     * the pending intent.     *     * @param mbr The {@link PendingIntent} to send the media button event to.     */    public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {        try {            mBinder.setMediaButtonReceiver(mbr);        } catch (RemoteException e) {            Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);        }    }   

其中用到了mBinder(ISession),这是一个IInterface,就肯定是把Receiver注册到一个远程对象中了,就是一个注册到一个系统服务中,而这个系统服务持有这个ISession实例。MediaSession应该是媒体会话的意思

它的实现者是private final class SessionStub extends ISession.Stub,而SessionStub是MediaSessionRecord的内部类。

SessionStub#setMediaButtonReceiver()

@Override        public void setMediaButtonReceiver(PendingIntent pi) {            mMediaButtonReceiver = pi;        }
由上可知SessionStub和一个媒体会话,即一个MediaButtonReceiver对应。MediaSession和MediaSessionRecord也是。


那在AudioManager注册完Receiver后,AudioManager在截取了ACTION_MEDIA_BUTTONG广播后是如果分配给这些Receiver的,下面接着分析:

MediaSessionLegacyHelper#sendMediaButtonEvent(KeyEvent keyEvent, boolean needWakeLock)是入口:

public void sendMediaButtonEvent(KeyEvent keyEvent, boolean needWakeLock) {        if (keyEvent == null) {            Log.w(TAG, "Tried to send a null key event. Ignoring.");            return;        }        mSessionManager.dispatchMediaKeyEvent(keyEvent, needWakeLock);        if (DEBUG) {            Log.d(TAG, "dispatched media key " + keyEvent);        }    }

最终去到class SessionManagerImpl extends ISessionManager.Stub #public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock),

而SessionManagerImpl是MediaSessionService的内部类。

/**         * Handles the dispatching of the media button events to one of the         * registered listeners, or if there was none, broadcast an         * ACTION_MEDIA_BUTTON intent to the rest of the system.         *         * @param keyEvent a non-null KeyEvent whose key code is one of the         *            supported media buttons         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held         *            while this key event is dispatched.         */        @Override        public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {            if (keyEvent == null || !KeyEvent.isMediaKey(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 {                if (DEBUG) {                    Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="                            + keyEvent);                }                if (!isUserSetupComplete()) {                    // Global media key handling can have the side-effect of starting new                    // activities which is undesirable while setup is in progress.                    Slog.i(TAG, "Not dispatching media key event because user "                            + "setup is in progress.");                    return;                }                synchronized (mLock) {                    // If we don't have a media button receiver to fall back on                    // include non-playing sessions for dispatching                    UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());                    boolean useNotPlayingSessions = (ur == null) ||                            (ur.mLastMediaButtonReceiver == null                                && ur.mRestoredMediaButtonReceiver == null);                    MediaSessionRecord session = mPriorityStack                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);                    if (isVoiceKey(keyEvent.getKeyCode())) {                        handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);                    } else {                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);                    }                }            } finally {                Binder.restoreCallingIdentity(token);            }        }
重点分析这个两句:

MediaSessionRecord session = mPriorityStack                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);                    if (isVoiceKey(keyEvent.getKeyCode())) {                        handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);                    } else {                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);                    }                    
mPriorityStack是MediaSessionStack类型,看getDefaultMEdiaButtonSession()源码,这部分非常重要,就是按怎样的规则去选择栈中record(MediaSessionRecord)的:

/**     * Get the highest priority session that can handle media buttons.     *     * @param userId The user to check.     * @param includeNotPlaying Return a non-playing session if nothing else is     *            available     * @return The default media button session or null.     */    public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) {        if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {            return mGlobalPrioritySession;        }        if (mCachedButtonReceiver != null) {            return mCachedButtonReceiver;        }        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);        if (records.size() > 0) {            //在records列表中扫描            MediaSessionRecord record = records.get(0);           //判断给session(会话)是否正在播放,如果是则把事件给它。            if (record.isPlaybackActive(false)) {                // Since we're going to send a button event to this record make                // it the last interesting one.                mLastInterestingRecord = record;                mCachedButtonReceiver = record;            } else if (mLastInterestingRecord != null) {                if (records.contains(mLastInterestingRecord)) {                    mCachedButtonReceiver = mLastInterestingRecord;                } else {                    // That record is no longer used. Clear its reference.                    mLastInterestingRecord = null;                }            }        //如果includeNotPlaying标志位true,且cache为空,则record不是正在播放也分给它。             if (includeNotPlaying && mCachedButtonReceiver == null) {                // If we really want a record and we didn't find one yet use the                // highest priority session even if it's not playing.                mCachedButtonReceiver = record;            }        }        return mCachedButtonReceiver;    }

主要分析一下:

ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
下面是MediaSessionStack#getPriorityLocked()源码:


/**     * Get a priority sorted list of sessions. Can filter to only return active     * sessions or sessions with specific flags.     *     * @param activeOnly True to only return active sessions, false to return     *            all sessions.     * @param withFlags Only return sessions with all the specified flags set. 0     *            returns all sessions.     * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}     *            will return sessions for all users.     * @return The priority sorted list of sessions.     */    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,            int userId) {        ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();        int lastLocalIndex = 0;        int lastActiveIndex = 0;        int lastPublishedIndex = 0;        int size = mSessions.size();        for (int i = 0; i < size; i++) {            final MediaSessionRecord session = mSessions.get(i);            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {                // Filter out sessions for the wrong user                continue;            }            if ((session.getFlags() & withFlags) != withFlags) {                // Filter out sessions with the wrong flags                continue;            }            if (!session.isActive()) {                if (!activeOnly) {                    // If we're getting unpublished as well always put them at                    // the end                    result.add(session);                }                continue;            }            if (session.isSystemPriority()) {                // System priority sessions are special and always go at the                // front. We expect there to only be one of these at a time.                result.add(0, session);                lastLocalIndex++;                lastActiveIndex++;                lastPublishedIndex++;            } else if (session.isPlaybackActive(true)) {                // TODO this with real local route check                if (true) {                    // Active local sessions get top priority                    result.add(lastLocalIndex, session);                    lastLocalIndex++;                    lastActiveIndex++;                    lastPublishedIndex++;                } else {                    // Then active remote sessions                    result.add(lastActiveIndex, session);                    lastActiveIndex++;                    lastPublishedIndex++;                }            } else {                // inactive sessions go at the end in order of whoever last did                // something.                result.add(lastPublishedIndex, session);                lastPublishedIndex++;            }        }        return result;    }
看上面的源码加载arraylist的index为位置的record优先级是最高的。

isSystemPriority()      isActive()    isPlayBackActive()  然后是其他的,只要是Record的userId对的话都会加入arraylist中,

只是优先级不一样。



其中 dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);,看源码:

 private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,                MediaSessionRecord session) {            if (session != null) {                if (DEBUG) {                    Log.d(TAG, "Sending media key to " + session.toString());                }                if (needWakeLock) {                    mKeyEventReceiver.aquireWakeLockLocked();                }                // If we don't need a wakelock use -1 as the id so we                // won't release it later                session.sendMediaButton(keyEvent,                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,                        mKeyEventReceiver);            } else {                // Launch the last PendingIntent we had with priority                int userId = ActivityManager.getCurrentUser();                UserRecord user = mUserRecords.get(userId);                if (user.mLastMediaButtonReceiver != null                        || user.mRestoredMediaButtonReceiver != null) {                    if (DEBUG) {                        Log.d(TAG, "Sending media key to last known PendingIntent "                                + user.mLastMediaButtonReceiver + " or restored Intent "                                + user.mRestoredMediaButtonReceiver);                    }                    if (needWakeLock) {                        mKeyEventReceiver.aquireWakeLockLocked();                    }                    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);                    mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);                    try {                        if (user.mLastMediaButtonReceiver != null) {                            user.mLastMediaButtonReceiver.send(getContext(),                                    needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,                                    mediaButtonIntent, mKeyEventReceiver, null);                        } else {                            mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);                            getContext().sendBroadcastAsUser(mediaButtonIntent,                                    new UserHandle(userId));                        }                    } catch (CanceledException e) {                        Log.i(TAG, "Error sending key event to media button receiver "                                + user.mLastMediaButtonReceiver, e);                    }                } else {                    if (DEBUG) {                        Log.d(TAG, "Sending media key ordered broadcast");                    }                    if (needWakeLock) {                        mMediaEventWakeLock.acquire();                    }                    // Fallback to legacy behavior                    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);                    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);                    if (needWakeLock) {                        keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,                                WAKELOCK_RELEASE_ON_FINISHED);                    }                    getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,                            null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);                }            }        }



























0 0