Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0456e3aa authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

Merge "MediaFocusControl: priority to playing players for media button"

parents 98ae02c5 8be88d11
Loading
Loading
Loading
Loading
+118 −94
Original line number Diff line number Diff line
@@ -327,13 +327,12 @@ public class MediaFocusControl implements OnFinished {
    private static final int MSG_REEVALUATE_REMOTE = 3;
    private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4;
    private static final int MSG_RCC_NEW_VOLUME_OBS = 5;
    private static final int MSG_PROMOTE_RCC = 6;
    private static final int MSG_RCC_NEW_PLAYBACK_STATE = 7;
    private static final int MSG_RCC_SEEK_REQUEST = 8;
    private static final int MSG_RCC_UPDATE_METADATA = 9;
    private static final int MSG_RCDISPLAY_INIT_INFO = 10;
    private static final int MSG_REEVALUATE_RCD = 11;
    private static final int MSG_UNREGISTER_MEDIABUTTONINTENT = 12;
    private static final int MSG_RCC_NEW_PLAYBACK_STATE = 6;
    private static final int MSG_RCC_SEEK_REQUEST = 7;
    private static final int MSG_RCC_UPDATE_METADATA = 8;
    private static final int MSG_RCDISPLAY_INIT_INFO = 9;
    private static final int MSG_REEVALUATE_RCD = 10;
    private static final int MSG_UNREGISTER_MEDIABUTTONINTENT = 11;

    // sendMsg() flags
    /** If the msg is already queued, replace it with this one. */
@@ -406,10 +405,6 @@ public class MediaFocusControl implements OnFinished {
                            (Rating) msg.obj /* value */);
                    break;

                case MSG_PROMOTE_RCC:
                    onPromoteRcc(msg.arg1);
                    break;

                case MSG_RCDISPLAY_INIT_INFO:
                    // msg.obj is guaranteed to be non null
                    onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
@@ -1212,56 +1207,91 @@ public class MediaFocusControl implements OnFinished {
            mediaButtonIntent.setComponent(eventReceiver);
            PendingIntent pi = PendingIntent.getBroadcast(mContext,
                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
            registerMediaButtonIntent(pi, eventReceiver, null);
            registerMediaButtonIntent(pi, eventReceiver, null /*token*/);
        }
    }

    /**
     * Helper function:
     * Set the new remote control receiver at the top of the RC focus stack.
     * Push the new media button receiver "near" the top of the PlayerRecord stack.
     * "Near the top" is defined as:
     *   - at the top if the current PlayerRecord at the top is not playing
     *   - below the entries at the top of the stack that correspond to the playing PlayerRecord
     *     otherwise
     * Called synchronized on mPRStack
     * precondition: mediaIntent != null
     * @return true if mPRStack was changed, false otherwise
     * @return true if the top of mPRStack was changed, false otherwise
     */
    private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent,
            ComponentName target, IBinder token) {
        // already at top of stack?
        if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
        if (mPRStack.empty()) {
            mPRStack.push(new PlayerRecord(mediaIntent, target, token));
            return true;
        } else if (mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
            // already at top of stack
            return false;
        }
        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
                mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
            return false;
        }
        PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
        boolean topChanged = false;
        PlayerRecord prse = null;
        boolean wasInsideStack = false;
        int lastPlayingIndex = mPRStack.size();
        int inStackIndex = -1;
        try {
            // go through the stack from the top to figure out who's playing, and the position
            // of this media button receiver (note that it may not be in the stack)
            for (int index = mPRStack.size()-1; index >= 0; index--) {
                prse = mPRStack.elementAt(index);
                if (prse.isPlaybackActive()) {
                    lastPlayingIndex = index;
                }
                if (prse.hasMatchingMediaButtonIntent(mediaIntent)) {
                    // ok to remove element while traversing the stack since we're leaving the loop
                    mPRStack.removeElementAt(index);
                    wasInsideStack = true;
                    inStackIndex = index;
                    // found it, ok to stop here
                    break;
                }
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            // not expected to happen, indicates improper concurrent modification
            Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
        }
        if (!wasInsideStack) {

            if (inStackIndex == -1) {
                // is not in stack
                prse = new PlayerRecord(mediaIntent, target, token);
                // it's new so it's not playing (no RemoteControlClient to give a playstate),
                // therefore it goes after the ones with active playback
                mPRStack.add(lastPlayingIndex, prse);
            } else {
                // is in the stack
                if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
                    prse = mPRStack.elementAt(inStackIndex);
                    // remove it from its old location in the stack
                    mPRStack.removeElementAt(inStackIndex);
                    if (prse.isPlaybackActive()) {
                        // and put it at the top
                        mPRStack.push(prse);
                    } else {
                        // and put it after the ones with active playback
                        mPRStack.add(lastPlayingIndex, prse);
                    }
                }
            }
        mPRStack.push(prse); // prse is never null

            topChanged = (oldTopPrse != mPRStack.lastElement());
            // post message to persist the default media button receiver
        if (target != null) {
            if (topChanged && (target != null)) {
                mEventHandler.sendMessage( mEventHandler.obtainMessage(
                        MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
            }

        // RC stack was modified
        return true;
        } catch (ArrayIndexOutOfBoundsException e) {
            // not expected to happen, indicates improper concurrent modification or bad index
            Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
                    + " size=" + mPRStack.size()
                    + " accessing media button stack", e);
        }

        return (topChanged);
    }

    /**
@@ -1514,48 +1544,6 @@ public class MediaFocusControl implements OnFinished {
        updateRemoteControlDisplay_syncPrs(infoChangedFlags);
    }

    /**
     * Helper function:
     * Post a message to asynchronously move the media button event receiver associated with the
     * given remote control client ID to the top of the remote control stack
     * @param rccId
     */
    private void postPromoteRcc(int rccId) {
        sendMsg(mEventHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
                rccId /*arg1*/, 0, null, 0/*delay*/);
    }

    private void onPromoteRcc(int rccId) {
        if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
        synchronized(mPRStack) {
            // ignore if given RCC ID is already at top of remote control stack
            if (!mPRStack.isEmpty() && (mPRStack.peek().getRccId() == rccId)) {
                return;
            }
            int indexToPromote = -1;
            try {
                for (int index = mPRStack.size()-1; index >= 0; index--) {
                    final PlayerRecord prse = mPRStack.elementAt(index);
                    if (prse.getRccId() == rccId) {
                        indexToPromote = index;
                        break;
                    }
                }
                if (indexToPromote >= 0) {
                    if (DEBUG_RC) { Log.d(TAG, "  moving RCC from index " + indexToPromote
                            + " to " + (mPRStack.size()-1)); }
                    final PlayerRecord prse = mPRStack.remove(indexToPromote);
                    mPRStack.push(prse);
                    // the RC stack changed, reevaluate the display
                    checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                // not expected to happen, indicates improper concurrent modification
                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
            }
        }//synchronized(mPRStack)
    }

    /**
     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
     * precondition: mediaIntent != null
@@ -2151,30 +2139,66 @@ public class MediaFocusControl implements OnFinished {
        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
                + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
        synchronized(mPRStack) {
            // iterating from top of stack as playback information changes are more likely
            //   on entries at the top of the remote control stack
            if (mPRStack.empty()) {
                return;
            }
            PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
            PlayerRecord prse = null;
            int lastPlayingIndex = mPRStack.size();
            int inStackIndex = -1;
            try {
                // go through the stack from the top to figure out who's playing, and the position
                // of this RemoteControlClient (note that it may not be in the stack)
                for (int index = mPRStack.size()-1; index >= 0; index--) {
                    final PlayerRecord prse = mPRStack.elementAt(index);
                    prse = mPRStack.elementAt(index);
                    if (prse.isPlaybackActive()) {
                        lastPlayingIndex = index;
                    }
                    if (prse.getRccId() == rccId) {
                        inStackIndex = index;
                        prse.mPlaybackState = newState;
                    }
                }

                if (inStackIndex != -1) {
                    // is in the stack
                    prse = mPRStack.elementAt(inStackIndex);
                    synchronized (mMainRemote) {
                        if (rccId == mMainRemote.mRccId) {
                            mMainRemoteIsActive = isPlaystateActive(state);
                            postReevaluateRemote();
                        }
                    }
                        // an RCC moving to a "playing" state should become the media button
                        //   event receiver so it can be controlled, without requiring the
                        //   app to re-register its receiver
                        if (isPlaystateActive(state)) {
                            postPromoteRcc(rccId);
                    if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
                        // remove it from its old location in the stack
                        mPRStack.removeElementAt(inStackIndex);
                        if (prse.isPlaybackActive()) {
                            // and put it at the top
                            mPRStack.push(prse);
                        } else {
                            // and put it after the ones with active playback
                            mPRStack.add(lastPlayingIndex, prse);
                        }
                    }

                    if (oldTopPrse != mPRStack.lastElement()) {
                        // the top of the stack changed:
                        final ComponentName target =
                                mPRStack.lastElement().getMediaButtonReceiver();
                        if (target != null) {
                            // post message to persist the default media button receiver
                            mEventHandler.sendMessage( mEventHandler.obtainMessage(
                                    MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
                        }
                        // reevaluate the display
                        checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
                    }
                }
                }//for
            } catch (ArrayIndexOutOfBoundsException e) {
                // not expected to happen, indicates improper concurrent modification
                Log.e(TAG, "Wrong index on mPRStack in onNewPlaybackStateForRcc, lock error? ", e);
                // not expected to happen, indicates improper concurrent modification or bad index
                Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
                        + " size=" + mPRStack.size()
                        + "accessing PlayerRecord stack in onNewPlaybackStateForRcc", e);
            }
        }
    }
@@ -2249,7 +2273,7 @@ public class MediaFocusControl implements OnFinished {
     * @param playState the playback state to evaluate
     * @return true if active, false otherwise (inactive or unknown)
     */
    private static boolean isPlaystateActive(int playState) {
    protected static boolean isPlaystateActive(int playState) {
        switch (playState) {
            case RemoteControlClient.PLAYSTATE_PLAYING:
            case RemoteControlClient.PLAYSTATE_BUFFERING:
+16 −1
Original line number Diff line number Diff line
@@ -58,6 +58,9 @@ class PlayerRecord implements DeathRecipient {

    private int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;

    /**
     * A non-null token implies this record tracks a "live" player whose death is being monitored.
     */
    private IBinder mToken;
    private String mCallingPackageName;
    private int mCallingUid;
@@ -267,7 +270,19 @@ class PlayerRecord implements DeathRecipient {
    }

    protected boolean hasMatchingMediaButtonIntent(PendingIntent pi) {
        if (mToken != null) {
            return mMediaIntent.equals(pi);
        } else {
            if (mReceiverComponent != null) {
                return mReceiverComponent.equals(pi.getIntent().getComponent());
            } else {
                return false;
            }
        }
    }

    protected boolean isPlaybackActive() {
        return MediaFocusControl.isPlaystateActive(mPlaybackState.mState);
    }

    //---------------------------------------------