Let RemoteControlDisplay know more about playback position

The BT stack needs to differentiate between applications that use
 the new RemoteControlClient APIs to pass a playback position but
 don't have one yet, and applications that use the legacy API and
 will never pass a position.

Bug 9294855

Change-Id: I05cba82a073e6e0aaea1d8bbf9cc8c99da715f58
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index c70889c..583f436 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -40,6 +40,33 @@
     void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
             boolean clearing);
 
+    /**
+     * Sets the playback information (state, position and speed) of a client.
+     * @param generationId the current generation ID as known by this client
+     * @param state the current playback state, one of the following values:
+     *       {@link RemoteControlClient#PLAYSTATE_STOPPED},
+     *       {@link RemoteControlClient#PLAYSTATE_PAUSED},
+     *       {@link RemoteControlClient#PLAYSTATE_PLAYING},
+     *       {@link RemoteControlClient#PLAYSTATE_FAST_FORWARDING},
+     *       {@link RemoteControlClient#PLAYSTATE_REWINDING},
+     *       {@link RemoteControlClient#PLAYSTATE_SKIPPING_FORWARDS},
+     *       {@link RemoteControlClient#PLAYSTATE_SKIPPING_BACKWARDS},
+     *       {@link RemoteControlClient#PLAYSTATE_BUFFERING},
+     *       {@link RemoteControlClient#PLAYSTATE_ERROR}.
+     * @param stateChangeTimeMs the time at which the client reported the playback information
+     * @param currentPosMs a 0 or positive value for the current media position expressed in ms
+     *    Strictly negative values imply that position is not known:
+     *    a value of {@link RemoteControlClient#PLAYBACK_POSITION_INVALID} is intended to express
+     *    that an application doesn't know the position (e.g. listening to a live stream of a radio)
+     *    or that the position information is not applicable (e.g. when state
+     *    is {@link RemoteControlClient#PLAYSTATE_BUFFERING} and nothing had played yet);
+     *    a value of {@link RemoteControlClient#PLAYBACK_POSITION_ALWAYS_UNKNOWN} implies that the
+     *    application uses {@link RemoteControlClient#setPlaybackState(int)} (legacy API) and will
+     *    never pass a playback position.
+     * @param speed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
+     *    2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
+     *    playing (e.g. when state is {@link RemoteControlClient#PLAYSTATE_ERROR}).
+     */
     void setPlaybackState(int generationId, int state, long stateChangeTimeMs, long currentPosMs,
             float speed);
 
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index c6ae9aa..7379438 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -180,6 +180,12 @@
     public final static long PLAYBACK_POSITION_INVALID = -1;
     /**
      * @hide
+     * An invalid playback position value associated with the use of {@link #setPlaybackState(int)}
+     * used to indicate that playback position will remain unknown.
+     */
+    public final static long PLAYBACK_POSITION_ALWAYS_UNKNOWN = 0x8019771980198300L;
+    /**
+     * @hide
      * The default playback speed, 1x.
      */
     public final static float PLAYBACK_SPEED_1X = 1.0f;
@@ -602,7 +608,8 @@
      *       {@link #PLAYSTATE_ERROR}.
      */
     public void setPlaybackState(int state) {
-        setPlaybackState(state, PLAYBACK_POSITION_INVALID, PLAYBACK_SPEED_1X);
+        setPlaybackStateInt(state, PLAYBACK_POSITION_ALWAYS_UNKNOWN, PLAYBACK_SPEED_1X,
+                false /* legacy API, converting to method with position and speed */);
     }
 
     /**
@@ -629,12 +636,28 @@
      *    playing (e.g. when state is {@link #PLAYSTATE_ERROR}).
      */
     public void setPlaybackState(int state, long timeInMs, float playbackSpeed) {
+        setPlaybackStateInt(state, timeInMs, playbackSpeed, true);
+    }
+
+    private void setPlaybackStateInt(int state, long timeInMs, float playbackSpeed,
+            boolean hasPosition) {
         synchronized(mCacheLock) {
             if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs)
                     || (mPlaybackSpeed != playbackSpeed)) {
                 // store locally
                 mPlaybackState = state;
-                mPlaybackPositionMs = timeInMs;
+                // distinguish between an application not knowing the current playback position
+                // at the moment and an application using the API where only the playback state
+                // is passed, not the playback position.
+                if (hasPosition) {
+                    if (timeInMs < 0) {
+                        mPlaybackPositionMs = PLAYBACK_POSITION_INVALID;
+                    } else {
+                        mPlaybackPositionMs = timeInMs;
+                    }
+                } else {
+                    mPlaybackPositionMs = PLAYBACK_POSITION_ALWAYS_UNKNOWN;
+                }
                 mPlaybackSpeed = playbackSpeed;
                 // keep track of when the state change occurred
                 mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();