Refactor playback position tracking

Use PlaybackState directly to track the position through time.  Update
the state when necessary (track changes, etc.)

Remove complex logic and centralize sending of Play Position
Notificaiton response and scheduling.

Update dumpsys and add current package session.

Bug: 29416450
Bug: 29606822

Change-Id: Ie82b48d6d7f9e1a05b64835731433d05a3a3a41a
(cherry picked from commit eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452)
diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java
index 945963e..e864c16 100755
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -78,14 +78,13 @@
     private MediaAttributes mMediaAttributes;
     private int mTransportControlFlags;
     private PlaybackState mCurrentPlayState;
+    private long mLastStateUpdate;
     private int mPlayStatusChangedNT;
     private int mTrackChangedNT;
+    private int mPlayPosChangedNT;
     private long mTrackNumber;
-    private long mCurrentPosMs;
-    private long mPlayStartTimeMs;
     private long mSongLengthMs;
     private long mPlaybackIntervalMs;
-    private int mPlayPosChangedNT;
     private long mNextPosMs;
     private long mPrevPosMs;
     private long mSkipStartTime;
@@ -160,8 +159,7 @@
         mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
         mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
         mTrackNumber = -1L;
-        mCurrentPosMs = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
-        mPlayStartTimeMs = -1L;
+        mLastStateUpdate = -1L;
         mSongLengthMs = 0L;
         mPlaybackIntervalMs = 0L;
         mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
@@ -239,7 +237,7 @@
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
             Log.v(TAG, "MediaController playback changed: " + state.toString());
-            updatePlayPauseState(state);
+            updatePlaybackState(state);
         }
 
         @Override
@@ -269,12 +267,12 @@
         mMediaController = controller;
         if (mMediaController == null) {
             updateMetadata(null);
-            updatePlayPauseState(null);
+            updatePlaybackState(null);
             return;
         }
         mMediaController.registerCallback(mMediaControllerCb, mHandler);
         updateMetadata(mMediaController.getMetadata());
-        updatePlayPauseState(mMediaController.getPlaybackState());
+        updatePlaybackState(mMediaController.getPlaybackState());
     }
 
     /** Handles Avrcp messages. */
@@ -333,8 +331,7 @@
 
             case MESSAGE_PLAY_INTERVAL_TIMEOUT:
                 if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
-                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
-                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)getPlayPosition());
+                sendPlayPosNotificationRsp(false);
                 break;
 
             case MESSAGE_VOLUME_CHANGED:
@@ -632,56 +629,25 @@
                 builder.setState(PlaybackState.STATE_PAUSED,
                                  PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
             }
-            updatePlayPauseState(builder.build());
+            updatePlaybackState(builder.build());
         }
     }
 
-    private void updatePlayPauseState(PlaybackState state) {
+    private void updatePlaybackState(PlaybackState state) {
         if (DEBUG) Log.v(TAG,
-                "updatePlayPauseState: old=" + mCurrentPlayState + ", state=" + state);
+                "updatePlaybackState: old=" + mCurrentPlayState + ", new=" + state);
         if (state == null) {
           state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,
                          PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build();
         }
-        boolean oldPosValid = (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN);
+
         int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
         int newPlayStatus = convertPlayStateToPlayStatus(state);
 
-        if ((mCurrentPlayState.getState() == PlaybackState.STATE_PLAYING) &&
-                (mCurrentPlayState != state) && oldPosValid) {
-            mCurrentPosMs = getPlayPosition();
-        }
-
-        if (state.getState() == PlaybackState.STATE_NONE ||
-                state.getState() == PlaybackState.STATE_ERROR) {
-            mCurrentPosMs = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
-        } else {
-            mCurrentPosMs = state.getPosition();
-        }
-
-        if ((state.getState() == PlaybackState.STATE_PLAYING) &&
-                (mCurrentPlayState.getState() != PlaybackState.STATE_PLAYING)) {
-            mPlayStartTimeMs = SystemClock.elapsedRealtime();
-        }
-
         mCurrentPlayState = state;
+        mLastStateUpdate = SystemClock.elapsedRealtime();
 
-        boolean newPosValid = mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN;
-        long playPosition = getPlayPosition();
-
-        mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
-        /* need send play position changed notification when play status is changed */
-        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
-                ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) ||
-                 (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) {
-            mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
-            registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition);
-        }
-        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid &&
-                (state.getState() == PlaybackState.STATE_PLAYING)) {
-            Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
-            mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition);
-        }
+        sendPlayPosNotificationRsp(false);
 
         if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
             mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
@@ -812,22 +778,14 @@
         if (!oldAttributes.equals(mMediaAttributes)) {
             Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString());
             mTrackNumber++;
+
+            // Update the play state, which sends a notification if needed.
+            updatePlaybackState(mMediaController.getPlaybackState());
+
             if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
                 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
                 sendTrackChangedRsp();
             }
-
-            if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN &&
-                isPlayingState(mCurrentPlayState)) {
-                mPlayStartTimeMs = SystemClock.elapsedRealtime();
-            }
-            /* need send play position changed notification when track is changed */
-            if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) {
-                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
-                registerNotificationRspPlayPosNative(mPlayPosChangedNT,
-                        (int)getPlayPosition());
-                mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
-            }
         } else {
             Log.v(TAG, "Updated " + mMediaAttributes.toString() + " but no change!");
         }
@@ -874,18 +832,9 @@
                 break;
 
             case EVT_PLAY_POS_CHANGED:
-                long songPosition = getPlayPosition();
                 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
+                sendPlayPosNotificationRsp(true);
                 mPlaybackIntervalMs = (long)param * 1000L;
-                if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
-                    mNextPosMs = songPosition + mPlaybackIntervalMs;
-                    mPrevPosMs = songPosition - mPlaybackIntervalMs;
-                    if (isPlayingState(mCurrentPlayState)) {
-                        Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
-                        mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs);
-                    }
-                }
-                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition);
                 break;
 
         }
@@ -944,17 +893,17 @@
     }
 
     private long getPlayPosition() {
-        long songPosition = -1L;
-        if (mCurrentPosMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
-            if (mCurrentPlayState.getState() == PlaybackState.STATE_PLAYING) {
-                songPosition = SystemClock.elapsedRealtime() -
-                        mPlayStartTimeMs + mCurrentPosMs;
-            } else {
-                songPosition = mCurrentPosMs;
-            }
+        if (mCurrentPlayState == null)
+            return -1L;
+
+        if (mCurrentPlayState.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN)
+            return -1L;
+
+        if (isPlayingState(mCurrentPlayState)) {
+            return SystemClock.elapsedRealtime() - mLastStateUpdate + mCurrentPlayState.getPosition();
         }
-        if (DEBUG) Log.v(TAG, "position=" + songPosition);
-        return songPosition;
+
+        return -1L;
     }
 
     private int convertPlayStateToPlayStatus(PlaybackState state) {
@@ -999,6 +948,44 @@
     }
 
     /**
+     * Sends a play position notification, or schedules one to be
+     * sent later at an appropriate time. If |requested| is true,
+     * does both because this was called in reponse to a request from the
+     * TG.
+     */
+    private void sendPlayPosNotificationRsp(boolean requested) {
+        long playPositionMs = getPlayPosition();
+
+        // mNextPosMs is set to -1 when the previous position was invalid
+        // so this will be true if the new position is valid & old was invalid.
+        // mPlayPositionMs is set to -1 when the new position is invalid,
+        // and the old mPrevPosMs is >= 0 so this is true when the new is invalid
+        // and the old was valid.
+        if (requested || ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
+             ((playPositionMs >= mNextPosMs) || (playPositionMs <= mPrevPosMs)))) {
+            if (!requested) mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
+            registerNotificationRspPlayPosNative(mPlayStatusChangedNT, (int)playPositionMs);
+            if (playPositionMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
+                mNextPosMs = playPositionMs + mPlaybackIntervalMs;
+                mPrevPosMs = playPositionMs - mPlaybackIntervalMs;
+            } else {
+                mNextPosMs = -1;
+                mPrevPosMs = -1;
+            }
+        }
+
+        mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
+        if (mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) {
+            Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
+            long delay = mPlaybackIntervalMs;
+            if (mNextPosMs != -1) {
+                delay = mNextPosMs - (playPositionMs > 0 ? playPositionMs : 0);
+            }
+            mHandler.sendMessageDelayed(msg, delay);
+        }
+    }
+
+    /**
      * This is called from AudioService. It will return whether this device supports abs volume.
      * NOT USED AT THE MOMENT.
      */
@@ -1096,11 +1083,10 @@
         ProfileService.println(sb, "mMediaAttributes: " + mMediaAttributes);
         ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
         ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
+        ProfileService.println(sb, "mLastStateUpdate: " + mLastStateUpdate);
         ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
         ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
         ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
-        ProfileService.println(sb, "mCurrentPosMs: " + mCurrentPosMs);
-        ProfileService.println(sb, "mPlayStartTimeMs: " + mPlayStartTimeMs);
         ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
         ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
         ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
@@ -1118,6 +1104,8 @@
         ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
         ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
         ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString());
+        if (mMediaController != null)
+            ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName());
     }
 
     // Do not modify without updating the HAL bt_rc.h files.