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

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

Fix 5243349 RemoteControlDisplay incorrectly updated

This fixes a case where the RCD would display transport control
 for a RemoteControlClient that didn't have audio focus.
This was happening because registering an RCD was directly calling
 the updateRemoteControlDisplay method, without first calling
 the checkUpdateRemoteControlDisplay method which verifies the
 conditions before updating the display. One of those conditions
 is that the audio focus stack shouldn't be empty.

To verify this fix, several functions were also rename to clearly
 indicate the lock order and verify we properly synchronize on
 the right objects. In doing so, a missing synchronization on
 audio focus was found.

Change-Id: If1baaac224ea676aeb83ac0aefcc53f87461c32e
parent 528e382f
Loading
Loading
Loading
Loading
+47 −43
Original line number Diff line number Diff line
@@ -2637,7 +2637,7 @@ public class AudioService extends IAudioService.Stub {
                notifyTopOfAudioFocusStack();
                // there's a new top of the stack, let the remote control know
                synchronized(mRCStack) {
                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
            }
        } else {
@@ -2680,7 +2680,7 @@ public class AudioService extends IAudioService.Stub {
            notifyTopOfAudioFocusStack();
            // there's a new top of the stack, let the remote control know
            synchronized(mRCStack) {
                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
            }
        }
    }
@@ -2784,7 +2784,7 @@ public class AudioService extends IAudioService.Stub {

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

@@ -3182,7 +3182,7 @@ public class AudioService extends IAudioService.Stub {
     * Helper function:
     * Called synchronized on mRCStack
     */
    private void clearRemoteControlDisplay_syncRcs() {
    private void clearRemoteControlDisplay_syncAfRcs() {
        synchronized(mCurrentRcLock) {
            mCurrentRcClient = null;
        }
@@ -3191,18 +3191,21 @@ public class AudioService extends IAudioService.Stub {
    }

    /**
     * Helper function:
     * Called synchronized on mRCStack
     * mRCStack.isEmpty() is false
     * Helper function for code readability: only to be called from
     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
     *    this method.
     * Preconditions:
     *    - called synchronized mAudioFocusLock then on mRCStack
     *    - mRCStack.isEmpty() is false
     */
    private void updateRemoteControlDisplay_syncRcs(int infoChangedFlags) {
    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
        RemoteControlStackEntry rcse = mRCStack.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 (rcse.mRcClient == null) {
            //Log.w(TAG, "Can't update remote control display with null remote control client");
            clearRemoteControlDisplay_syncRcs();
            clearRemoteControlDisplay_syncAfRcs();
            return;
        }
        synchronized(mCurrentRcLock) {
@@ -3219,17 +3222,17 @@ public class AudioService extends IAudioService.Stub {

    /**
     * Helper function:
     * Called synchronized on mFocusLock, then mRCStack
     * Called synchronized on mAudioFocusLock, then mRCStack
     * 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_syncRcs(int infoChangedFlags) {
    private void checkUpdateRemoteControlDisplay_syncAfRcs(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 (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
            clearRemoteControlDisplay_syncRcs();
            clearRemoteControlDisplay_syncAfRcs();
            return;
        }
        // if the top of the two stacks belong to different packages, there is a mismatch, clear
@@ -3237,18 +3240,18 @@ public class AudioService extends IAudioService.Stub {
                && (mFocusStack.peek().mPackageName != null)
                && !(mRCStack.peek().mCallingPackageName.compareTo(
                        mFocusStack.peek().mPackageName) == 0)) {
            clearRemoteControlDisplay_syncRcs();
            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 (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
            clearRemoteControlDisplay_syncRcs();
            clearRemoteControlDisplay_syncAfRcs();
            return;
        }
        // refresh conditions were verified: update the remote controls
        // ok to call, mRCStack is not empty
        updateRemoteControlDisplay_syncRcs(infoChangedFlags);
        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
    }

    /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
@@ -3259,7 +3262,7 @@ public class AudioService extends IAudioService.Stub {
            synchronized(mRCStack) {
                pushMediaButtonReceiver(eventReceiver);
                // new RC client, assume every type of information shall be queried
                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
            }
        }
    }
@@ -3274,7 +3277,7 @@ public class AudioService extends IAudioService.Stub {
                removeMediaButtonReceiver(eventReceiver);
                if (topOfStackWillChange) {
                    // current RC client will change, assume every type of info needs to be queried
                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
            }
        }
@@ -3283,6 +3286,7 @@ public class AudioService extends IAudioService.Stub {
    /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
    public void registerRemoteControlClient(ComponentName eventReceiver,
            IRemoteControlClient rcClient, String clientName, String callingPackageName) {
        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
        synchronized(mAudioFocusLock) {
            synchronized(mRCStack) {
                // store the new display information
@@ -3330,7 +3334,7 @@ public class AudioService extends IAudioService.Stub {
                // if the eventReceiver is at the top of the stack
                // then check for potential refresh of the remote controls
                if (isCurrentRcController(eventReceiver)) {
                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                }
            }
        }
@@ -3435,6 +3439,7 @@ public class AudioService extends IAudioService.Stub {
     */
    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
        synchronized(mAudioFocusLock) {
            synchronized(mRCStack) {
                if ((mRcDisplay == rcd) || (rcd == null)) {
                    return;
@@ -3461,9 +3466,8 @@ public class AudioService extends IAudioService.Stub {
                    }
                }

            if (!mRCStack.isEmpty()) {
                // we have a new display, of which all the clients are now aware: have it be updated
                updateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
            }
        }
    }