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

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

Merge "Opt-in mechanism for RemoteControlClient position anti-drift check" into jb-mr2-dev

parents a4629b0b c3c4babf
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -2281,6 +2281,32 @@ public class AudioManager {
        }
    }

    /**
     * @hide
     * Controls whether a remote control display needs periodic checks of the RemoteControlClient
     * playback position to verify that the estimated position has not drifted from the actual
     * position. By default the check is not performed.
     * The IRemoteControlDisplay must have been previously registered for this to have any effect.
     * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
     *     or disabled. No effect is null.
     * @param wantsSync if true, RemoteControlClient instances which expose their playback position
     *     to the framework will regularly compare the estimated playback position with the actual
     *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
     *     detected.
     */
    public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
            boolean wantsSync) {
        if (rcd == null) {
            return;
        }
        IAudioService service = getService();
        try {
            service.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
        } catch (RemoteException e) {
            Log.e(TAG, "Dead object in remoteControlDisplayWantsPlaybackPositionSync " + e);
        }
    }

    /**
     * @hide
     * Request the user of a RemoteControlClient to seek to the given playback position.
+52 −1
Original line number Diff line number Diff line
@@ -5085,7 +5085,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
                pw.println("  IRCD: " + di.mRcDisplay +
                        "  -- w:" + di.mArtworkExpectedWidth +
                        "  -- h:" + di.mArtworkExpectedHeight);
                        "  -- h:" + di.mArtworkExpectedHeight+
                        "  -- wantsPosSync:" + di.mWantsPositionSync);
            }
        }
    }
@@ -5689,6 +5690,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
        private IBinder mRcDisplayBinder;
        private int mArtworkExpectedWidth = -1;
        private int mArtworkExpectedHeight = -1;
        private boolean mWantsPositionSync = false;

        public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
            if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
@@ -5752,6 +5754,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
            try {
                rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
                        di.mArtworkExpectedHeight);
                if (di.mWantsPositionSync) {
                    rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
            }
@@ -5905,6 +5910,52 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
        }
    }

    /**
     * Controls whether a remote control display needs periodic checks of the RemoteControlClient
     * playback position to verify that the estimated position has not drifted from the actual
     * position. By default the check is not performed.
     * The IRemoteControlDisplay must have been previously registered for this to have any effect.
     * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
     *     or disabled. Not null.
     * @param wantsSync if true, RemoteControlClient instances which expose their playback position
     *     to the framework will regularly compare the estimated playback position with the actual
     *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
     *     detected.
     */
    public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
            boolean wantsSync) {
        synchronized(mRCStack) {
            boolean rcdRegistered = false;
            // store the information about this display
            // (display stack traversal order doesn't matter).
            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
            while (displayIterator.hasNext()) {
                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
                    di.mWantsPositionSync = wantsSync;
                    rcdRegistered = true;
                    break;
                }
            }
            if (!rcdRegistered) {
                return;
            }
            // notify all current RemoteControlClients
            // (stack traversal order doesn't matter as we notify all RCCs)
            final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
            while (stackIterator.hasNext()) {
                final RemoteControlStackEntry rcse = stackIterator.next();
                if (rcse.mRcClient != null) {
                    try {
                        rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
                    }
                }
            }
        }
    }

    public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
        // ignore position change requests if invalid generation ID
        synchronized(mRCStack) {
+14 −0
Original line number Diff line number Diff line
@@ -152,6 +152,20 @@ interface IAudioService {
     *   display doesn't need to receive artwork.
     */
    oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
    /**
     * Controls whether a remote control display needs periodic checks of the RemoteControlClient
     * playback position to verify that the estimated position has not drifted from the actual
     * position. By default the check is not performed.
     * The IRemoteControlDisplay must have been previously registered for this to have any effect.
     * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
     *     or disabled. Not null.
     * @param wantsSync if true, RemoteControlClient instances which expose their playback position
     *     to the framework will regularly compare the estimated playback position with the actual
     *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
     *     detected.
     */
    oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
            boolean wantsSync);
    /**
     * Request the user of a RemoteControlClient to seek to the given playback position.
     * @param generationId the RemoteControlClient generation counter for which this request is
+1 −0
Original line number Diff line number Diff line
@@ -47,5 +47,6 @@ oneway interface IRemoteControlClient
    void   plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h);
    void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
    void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
    void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync);
    void seekTo(int clientGeneration, long timeMs);
}
 No newline at end of file
+89 −22
Original line number Diff line number Diff line
@@ -645,12 +645,21 @@ public class RemoteControlClient
                sendAudioServiceNewPlaybackState_syncCacheLock();

                // handle automatic playback position refreshes
                initiateCheckForDrift_syncCacheLock();
            }
        }
    }

    private void initiateCheckForDrift_syncCacheLock() {
        if (mEventHandler == null) {
            return;
        }
        mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
                if (timeInMs == PLAYBACK_POSITION_INVALID) {
                    // this playback state refresh has no known playback position, it's no use
        if (!mNeedsPositionSync) {
            return;
        }
        if (mPlaybackPositionMs < 0) {
            // the current playback state has no known playback position, it's no use
            // trying to see if there is any drift at this point
            // (this also bypasses this mechanism for older apps that use the old
            //  setPlaybackState(int) API)
@@ -660,20 +669,18 @@ public class RemoteControlClient
            // playback position moving, schedule next position drift check
            mEventHandler.sendMessageDelayed(
                    mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
                            getCheckPeriodFromSpeed(playbackSpeed));
                }
            }
                    getCheckPeriodFromSpeed(mPlaybackSpeed));
        }
    }

    private void onPositionDriftCheck() {
        if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
        synchronized(mCacheLock) {
            if ((mEventHandler == null) || (mPositionProvider == null)) {
            if ((mEventHandler == null) || (mPositionProvider == null) || !mNeedsPositionSync) {
                return;
            }
            if ((mPlaybackPositionMs == PLAYBACK_POSITION_INVALID) || (mPlaybackSpeed == 0.0f)) {
                if (DEBUG) { Log.d(TAG, " no position or 0 speed, no check needed"); }
            if ((mPlaybackPositionMs < 0) || (mPlaybackSpeed == 0.0f)) {
                if (DEBUG) { Log.d(TAG, " no valid position or 0 speed, no check needed"); }
                return;
            }
            long estPos = mPlaybackPositionMs + (long)
@@ -1011,6 +1018,12 @@ public class RemoteControlClient
     */
    private final PendingIntent mRcMediaIntent;

    /**
     * Reflects whether any "plugged in" IRemoteControlDisplay has mWantsPositonSync set to true.
     */
    // TODO consider using a ref count for IRemoteControlDisplay requiring sync instead
    private boolean mNeedsPositionSync = false;

    /**
     * A class to encapsulate all the information about a remote control display.
     * A RemoteControlClient's metadata and state may be displayed on multiple IRemoteControlDisplay
@@ -1020,6 +1033,7 @@ public class RemoteControlClient
        private IRemoteControlDisplay mRcDisplay;
        private int mArtworkExpectedWidth;
        private int mArtworkExpectedHeight;
        private boolean mWantsPositionSync = false;

        DisplayInfoForClient(IRemoteControlDisplay rcd, int w, int h) {
            mRcDisplay = rcd;
@@ -1109,6 +1123,14 @@ public class RemoteControlClient
            }
        }

        public void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync) {
            // only post messages, we can't block here
            if ((mEventHandler != null) && (rcd != null)) {
                mEventHandler.sendMessage(mEventHandler.obtainMessage(
                        MSG_DISPLAY_WANTS_POS_SYNC, wantsSync ? 1 : 0, 0/*arg2 ignored*/, rcd));
            }
        }

        public void seekTo(int generationId, long timeMs) {
            // only post messages, we can't block here
            if (mEventHandler != null) {
@@ -1160,6 +1182,7 @@ public class RemoteControlClient
    private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9;
    private final static int MSG_SEEK_TO = 10;
    private final static int MSG_POSITION_DRIFT_CHECK = 11;
    private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;

    private class EventHandler extends Handler {
        public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1210,6 +1233,9 @@ public class RemoteControlClient
                case MSG_POSITION_DRIFT_CHECK:
                    onPositionDriftCheck();
                    break;
                case MSG_DISPLAY_WANTS_POS_SYNC:
                    onDisplayWantsSync((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
                    break;
                default:
                    Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
            }
@@ -1410,13 +1436,29 @@ public class RemoteControlClient
    /** pre-condition rcd != null */
    private void onUnplugDisplay(IRemoteControlDisplay rcd) {
        synchronized(mCacheLock) {
            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
            Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
            while (displayIterator.hasNext()) {
                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
                    displayIterator.remove();
                    return;
                    break;
                }
            }
            // list of RCDs has changed, reevaluate whether position check is still needed
            boolean oldNeedsPositionSync = mNeedsPositionSync;
            boolean newNeedsPositionSync = false;
            displayIterator = mRcDisplays.iterator();
            while (displayIterator.hasNext()) {
                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
                if (di.mWantsPositionSync) {
                    newNeedsPositionSync = true;
                    break;
                }
            }
            mNeedsPositionSync = newNeedsPositionSync;
            if (oldNeedsPositionSync != mNeedsPositionSync) {
                // update needed?
                initiateCheckForDrift_syncCacheLock();
            }
        }
    }
@@ -1440,6 +1482,31 @@ public class RemoteControlClient
        }
    }

    /** pre-condition rcd != null */
    private void onDisplayWantsSync(IRemoteControlDisplay rcd, boolean wantsSync) {
        synchronized(mCacheLock) {
            boolean oldNeedsPositionSync = mNeedsPositionSync;
            boolean newNeedsPositionSync = false;
            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
            // go through the list of RCDs and for each entry, check both whether this is the RCD
            //  that gets upated, and whether the list has one entry that wants position sync
            while (displayIterator.hasNext()) {
                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
                    di.mWantsPositionSync = wantsSync;
                }
                if (di.mWantsPositionSync) {
                    newNeedsPositionSync = true;
                }
            }
            mNeedsPositionSync = newNeedsPositionSync;
            if (oldNeedsPositionSync != mNeedsPositionSync) {
                // update needed?
                initiateCheckForDrift_syncCacheLock();
            }
        }
    }

    private void onSeekTo(int generationId, long timeMs) {
        synchronized (mCacheLock) {
            if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) {