Opt-in mechanism for RemoteControlClient position anti-drift check

RemoteControlClient has an interface for the framework to query
 the playback position. This mechanism is used to detect
 when the estimated position drifts from the real position by
 having the framework regularly poll (every 15s when playing at
 1x) this interface and compare against the estimation.
But this mechanism:
 - should only be used when IRemoteControlDisplay implementation
  care about position display
 - should not be used by default because the implementation of
  the position query interface might involve network traffic
  in some remote media player implementation for instance.

This CL implements an opt-in mechanism to be used by
 implementators of IRemoteControlDisplay, to request the
 anti-drift mechanism to be turned on.

bug 8120740

Change-Id: I1baa3e515546ac41e0ac9c3a41bfa3147ecf3d7f
This commit is contained in:
Jean-Michel Trivi
2013-04-19 08:56:50 -07:00
parent 9b3ebb124e
commit c3c4babf84
5 changed files with 182 additions and 23 deletions

View File

@@ -645,35 +645,42 @@ public class RemoteControlClient
sendAudioServiceNewPlaybackState_syncCacheLock();
// handle automatic playback position refreshes
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
// 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)
return;
}
if (playbackPositionShouldMove(mPlaybackState)) {
// playback position moving, schedule next position drift check
mEventHandler.sendMessageDelayed(
mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
getCheckPeriodFromSpeed(playbackSpeed));
}
initiateCheckForDrift_syncCacheLock();
}
}
}
private void initiateCheckForDrift_syncCacheLock() {
if (mEventHandler == null) {
return;
}
mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
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)
return;
}
if (playbackPositionShouldMove(mPlaybackState)) {
// playback position moving, schedule next position drift check
mEventHandler.sendMessageDelayed(
mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
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,14 +1436,30 @@ 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)) {