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

This commit is contained in:
Jean-Michel Trivi
2013-04-22 23:41:04 +00:00
committed by Android (Google) Code Review
5 changed files with 182 additions and 23 deletions

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);
}

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)) {