Merge "Opt-in mechanism for RemoteControlClient position anti-drift check" into jb-mr2-dev
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 56e98e4..8295c5f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2283,6 +2283,32 @@
/**
* @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.
* @param generationId the RemoteControlClient generation counter for which this request is
* issued. Requests for an older generation than current one will be ignored.
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 0df4f82..0cff77d9 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -5085,7 +5085,8 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0d285fc..fda8c1b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -153,6 +153,20 @@
*/
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
* issued. Requests for an older generation than current one will be ignored.
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
index e4cee06..2236129 100644
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -47,5 +47,6 @@
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
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index f77ddc4..c6ae9aa 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -645,35 +645,42 @@
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)
@@ -1012,6 +1019,12 @@
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 @@
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 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 @@
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 @@
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 @@
/** 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 @@
}
}
+ /** 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)) {