Merge "Opt-in mechanism for RemoteControlClient position anti-drift check" into jb-mr2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
fb0b6a8179
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user