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

Commit b4168380 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

Audio focus doesn't affect RemoteController

The top of the stack of PlayerRecord instances is the "promoted"
 instance whose RemoteControlClient information is available
 to RemoteController objects, only when it also has audio focus.

This change removes the condition on audio focus ownership:
- whenever the player record stack ordering changes, no need to
  check if it also has audio focus
- since the player record and audio focus stacks operate separately,
  there is no need to synchronize access to both stacks.

Change-Id: I668f2aa2950f19f8c2a30bc59c7352246edcc56e
parent 560d1d9c
Loading
Loading
Loading
Loading
+101 −164
Original line number Diff line number Diff line
@@ -471,10 +471,6 @@ public class MediaFocusControl implements OnFinished {
                final FocusRequester exFocusOwner = mFocusStack.pop();
                exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS);
                exFocusOwner.release();
                // clear RCD
                synchronized(mPRStack) {
                    clearRemoteControlDisplay_syncAfRcs();
                }
            }
        }
    }
@@ -535,10 +531,6 @@ public class MediaFocusControl implements OnFinished {
            if (signal) {
                // notify the new top of the stack it gained focus
                notifyTopOfAudioFocusStack();
                // there's a new top of the stack, let the remote control know
                synchronized(mPRStack) {
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
            }
        } else {
            // focus is abandoned by a client that's not at the top of the stack,
@@ -582,10 +574,6 @@ public class MediaFocusControl implements OnFinished {
            // we removed an entry at the top of the stack:
            //  notify the new top of the stack it gained focus.
            notifyTopOfAudioFocusStack();
            // there's a new top of the stack, let the remote control know
            synchronized(mPRStack) {
                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
            }
        }
    }

@@ -694,10 +682,6 @@ public class MediaFocusControl implements OnFinished {
            mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb,
                    clientId, afdh, callingPackageName, Binder.getCallingUid()));

            // there's a new top of the stack, let the remote control know
            synchronized(mPRStack) {
                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
            }
        }//synchronized(mAudioFocusLock)

        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@@ -1237,11 +1221,11 @@ public class MediaFocusControl implements OnFinished {
    /**
     * Helper function:
     * Set the new remote control receiver at the top of the RC focus stack.
     * Called synchronized on mAudioFocusLock, then mPRStack
     * Called synchronized on mPRStack
     * precondition: mediaIntent != null
     * @return true if mPRStack was changed, false otherwise
     */
    private boolean pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent,
    private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent,
            ComponentName target, IBinder token) {
        // already at top of stack?
        if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
@@ -1285,10 +1269,10 @@ public class MediaFocusControl implements OnFinished {
    /**
     * Helper function:
     * Remove the remote control receiver from the RC focus stack.
     * Called synchronized on mAudioFocusLock, then mPRStack
     * Called synchronized on mPRStack
     * precondition: pi != null
     */
    private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
    private void removeMediaButtonReceiver_syncPrs(PendingIntent pi) {
        try {
            for (int index = mPRStack.size()-1; index >= 0; index--) {
                final PlayerRecord prse = mPRStack.elementAt(index);
@@ -1470,7 +1454,7 @@ public class MediaFocusControl implements OnFinished {
     * Helper function:
     * Called synchronized on mPRStack
     */
    private void clearRemoteControlDisplay_syncAfRcs() {
    private void clearRemoteControlDisplay_syncPrs() {
        synchronized(mCurrentRcLock) {
            mCurrentRcClient = null;
        }
@@ -1480,20 +1464,20 @@ public class MediaFocusControl implements OnFinished {

    /**
     * Helper function for code readability: only to be called from
     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
     *    checkUpdateRemoteControlDisplay_syncPrs() which checks the preconditions for
     *    this method.
     * Preconditions:
     *    - called synchronized mAudioFocusLock then on mPRStack
     *    - called synchronized on mPRStack
     *    - mPRStack.isEmpty() is false
     */
    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
    private void updateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
        PlayerRecord prse = mPRStack.peek();
        int infoFlagsAboutToBeUsed = infoChangedFlags;
        // this is where we enforce opt-in for information display on the remote controls
        //   with the new AudioManager.registerRemoteControlClient() API
        if (prse.getRcc() == null) {
            //Log.w(TAG, "Can't update remote control display with null remote control client");
            clearRemoteControlDisplay_syncAfRcs();
            clearRemoteControlDisplay_syncPrs();
            return;
        }
        synchronized(mCurrentRcLock) {
@@ -1511,62 +1495,25 @@ public class MediaFocusControl implements OnFinished {

    /**
     * Helper function:
     * Called synchronized on mAudioFocusLock, then mPRStack
     * Called synchronized on mPRStack
     * Check whether the remote control display should be updated, triggers the update if required
     * @param infoChangedFlags the flags corresponding to the remote control client information
     *     that has changed, if applicable (checking for the update conditions might trigger a
     *     clear, rather than an update event).
     */
    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
    private void checkUpdateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
        // determine whether the remote control display should be refreshed
        // if either stack is empty, there is a mismatch, so clear the RC display
        if (mPRStack.isEmpty() || mFocusStack.isEmpty()) {
            clearRemoteControlDisplay_syncAfRcs();
            return;
        }

        // determine which entry in the AudioFocus stack to consider, and compare against the
        // top of the stack for the media button event receivers : simply using the top of the
        // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
        // notifications playing during music playback.
        // Crawl the AudioFocus stack from the top until an entry is found with the following
        // characteristics:
        // - focus gain on STREAM_MUSIC stream
        // - non-transient focus gain on a stream other than music
        FocusRequester af = null;
        try {
            for (int index = mFocusStack.size()-1; index >= 0; index--) {
                FocusRequester fr = mFocusStack.elementAt(index);
                if ((fr.getStreamType() == AudioManager.STREAM_MUSIC)
                        || (fr.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN)) {
                    af = fr;
                    break;
                }
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
            af = null;
        }
        if (af == null) {
            clearRemoteControlDisplay_syncAfRcs();
        // if the player record stack is empty, there is nothing to display, so clear the RC display
        if (mPRStack.isEmpty()) {
            clearRemoteControlDisplay_syncPrs();
            return;
        }

        // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
        if (!af.hasSamePackage(mPRStack.peek().getCallingPackageName())) {
            clearRemoteControlDisplay_syncAfRcs();
            return;
        }
        // if the audio focus didn't originate from the same Uid as the one in which the remote
        //   control information will be retrieved, clear
        if (!af.hasSameUid(mPRStack.peek().getCallingUid())) {
            clearRemoteControlDisplay_syncAfRcs();
            return;
        }
        // this is where more rules for refresh go

        // refresh conditions were verified: update the remote controls
        // ok to call: synchronized mAudioFocusLock then on mPRStack, mPRStack is not empty
        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
        // ok to call: synchronized on mPRStack, mPRStack is not empty
        updateRemoteControlDisplay_syncPrs(infoChangedFlags);
    }

    /**
@@ -1582,7 +1529,6 @@ public class MediaFocusControl implements OnFinished {

    private void onPromoteRcc(int rccId) {
        if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
        synchronized(mAudioFocusLock) {
        synchronized(mPRStack) {
            // ignore if given RCC ID is already at top of remote control stack
            if (!mPRStack.isEmpty() && (mPRStack.peek().getRccId() == rccId)) {
@@ -1603,14 +1549,13 @@ public class MediaFocusControl implements OnFinished {
                    final PlayerRecord prse = mPRStack.remove(indexToPromote);
                    mPRStack.push(prse);
                    // the RC stack changed, reevaluate the display
                        checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                    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)
        }//synchronized(mAudioFocusLock)
    }

    /**
@@ -1621,12 +1566,10 @@ public class MediaFocusControl implements OnFinished {
            IBinder token) {
        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);

        synchronized(mAudioFocusLock) {
        synchronized(mPRStack) {
                if (pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token)) {
            if (pushMediaButtonReceiver_syncPrs(mediaIntent, eventReceiver, token)) {
                // new RC client, assume every type of information shall be queried
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
            }
        }
    }
@@ -1639,14 +1582,12 @@ public class MediaFocusControl implements OnFinished {
    {
        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);

        synchronized(mAudioFocusLock) {
        synchronized(mPRStack) {
            boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
                removeMediaButtonReceiver_syncAfRcs(mediaIntent);
            removeMediaButtonReceiver_syncPrs(mediaIntent);
            if (topOfStackWillChange) {
                // current RC client will change, assume every type of info needs to be queried
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
            }
        }
    }
@@ -1697,7 +1638,6 @@ public class MediaFocusControl implements OnFinished {
            IRemoteControlClient rcClient, String callingPackageName) {
        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
        synchronized(mAudioFocusLock) {
        synchronized(mPRStack) {
            // store the new display information
            try {
@@ -1716,7 +1656,7 @@ public class MediaFocusControl implements OnFinished {
                        // there is a new (non-null) client:
                        //     give the new client the displays (if any)
                        if (mRcDisplays.size() > 0) {
                                plugRemoteControlDisplaysIntoClient_syncRcStack(prse.getRcc());
                            plugRemoteControlDisplaysIntoClient_syncPrs(prse.getRcc());
                        }
                        break;
                    }
@@ -1729,10 +1669,9 @@ public class MediaFocusControl implements OnFinished {
            // if the eventReceiver is at the top of the stack
            // then check for potential refresh of the remote controls
            if (isCurrentRcController(mediaIntent)) {
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
            }
        }//synchronized(mPRStack)
        }//synchronized(mAudioFocusLock)
        return rccId;
    }

@@ -1743,7 +1682,6 @@ public class MediaFocusControl implements OnFinished {
    protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
            IRemoteControlClient rcClient) {
        if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
        synchronized(mAudioFocusLock) {
        synchronized(mPRStack) {
            boolean topRccChange = false;
            try {
@@ -1764,8 +1702,7 @@ public class MediaFocusControl implements OnFinished {
            }
            if (topRccChange) {
                // no more RCC for the RCD, check for potential refresh of the remote controls
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
            }
        }
    }
@@ -1843,7 +1780,7 @@ public class MediaFocusControl implements OnFinished {
     * Plug each registered display into the specified client
     * @param rcc, guaranteed non null
     */
    private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
    private void plugRemoteControlDisplaysIntoClient_syncPrs(IRemoteControlClient rcc) {
        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
        while (displayIterator.hasNext()) {
            final DisplayInfoForServer di = displayIterator.next();