summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/IRemoteControlClient.aidl7
-rw-r--r--media/java/android/media/MediaFocusControl.java52
-rw-r--r--media/java/android/media/MediaPlayer.java3
-rw-r--r--media/java/android/media/RemoteControlClient.java118
-rw-r--r--media/java/android/media/SoundPool.java2
-rw-r--r--media/java/android/media/SubtitleController.java95
6 files changed, 244 insertions, 33 deletions
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
index 48079f2..999b8ba 100644
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -41,6 +41,13 @@ oneway interface IRemoteControlClient
void onInformationRequested(int generationId, int infoFlags);
/**
+ * Notifies a remote control client that information for the given generation ID is
+ * requested for the given IRemoteControlDisplay alone.
+ * @param rcd the display to which current info should be sent
+ */
+ void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h);
+
+ /**
* Sets the generation counter of the current client that is displayed on the remote control.
*/
void setCurrentClientGenerationId(int clientGeneration);
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index cf5be1b..2a7a731 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -139,6 +139,7 @@ public class MediaFocusControl implements OnFinished {
private static final int MSG_RCC_NEW_PLAYBACK_STATE = 7;
private static final int MSG_RCC_SEEK_REQUEST = 8;
private static final int MSG_RCC_UPDATE_METADATA = 9;
+ private static final int MSG_RCDISPLAY_INIT_INFO = 10;
// sendMsg() flags
/** If the msg is already queued, replace it with this one. */
@@ -214,6 +215,12 @@ public class MediaFocusControl implements OnFinished {
case MSG_PROMOTE_RCC:
onPromoteRcc(msg.arg1);
break;
+
+ case MSG_RCDISPLAY_INIT_INFO:
+ // msg.obj is guaranteed to be non null
+ onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
+ msg.arg1/*w*/, msg.arg2/*h*/);
+ break;
}
}
}
@@ -852,6 +859,11 @@ public class MediaFocusControl implements OnFinished {
* Access protected by mCurrentRcLock.
*/
private IRemoteControlClient mCurrentRcClient = null;
+ /**
+ * The PendingIntent associated with mCurrentRcClient. Its value is irrelevant
+ * if mCurrentRcClient is null
+ */
+ private PendingIntent mCurrentRcClientIntent = null;
private final static int RC_INFO_NONE = 0;
private final static int RC_INFO_ALL =
@@ -1446,6 +1458,7 @@ public class MediaFocusControl implements OnFinished {
// tell the current client that it needs to send info
try {
+ //TODO change name to informationRequestForAllDisplays()
mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
} catch (RemoteException e) {
Log.e(TAG, "Current valid remote client is dead: "+e);
@@ -1460,6 +1473,36 @@ public class MediaFocusControl implements OnFinished {
}
}
+ /**
+ * Called when processing MSG_RCDISPLAY_INIT_INFO event
+ * Causes the current RemoteControlClient to send its info (metadata, playstate...) to
+ * a single RemoteControlDisplay, NOT all of them, as with MSG_RCDISPLAY_UPDATE.
+ */
+ private void onRcDisplayInitInfo(IRemoteControlDisplay newRcd, int w, int h) {
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClient != null) {
+ if (DEBUG_RC) { Log.i(TAG, "Init RCD with current info"); }
+ try {
+ // synchronously update the new RCD with the current client generation
+ // and matching PendingIntent
+ newRcd.setCurrentClientId(mCurrentRcClientGen, mCurrentRcClientIntent,
+ false);
+
+ // tell the current RCC that it needs to send info, but only to the new RCD
+ try {
+ mCurrentRcClient.informationRequestForDisplay(newRcd, w, h);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Current valid remote client is dead: ", e);
+ mCurrentRcClient = null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead display in onRcDisplayInitInfo()", e);
+ }
+ }
+ }
+ }
+ }
/**
* Helper function:
@@ -1497,6 +1540,7 @@ public class MediaFocusControl implements OnFinished {
infoFlagsAboutToBeUsed = RC_INFO_ALL;
}
mCurrentRcClient = rcse.mRcClient;
+ mCurrentRcClientIntent = rcse.mMediaIntent;
}
// will cause onRcDisplayUpdate() to be called in AudioService's handler thread
mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
@@ -1923,8 +1967,12 @@ public class MediaFocusControl implements OnFinished {
}
}
- // we have a new display, of which all the clients are now aware: have it be updated
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ // we have a new display, of which all the clients are now aware: have it be
+ // initialized wih the current gen ID and the current client info, do not
+ // reset the information for the other (existing) displays
+ sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
+ w /*arg1*/, h /*arg2*/,
+ rcd /*obj*/, 0/*delay*/);
}
}
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index deba2cc..def9aa7 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1772,6 +1772,9 @@ public class MediaPlayer implements SubtitleController.Listener
mSelectedSubtitleTrackIndex = -1;
}
setOnSubtitleDataListener(null);
+ if (track == null) {
+ return;
+ }
for (int i = 0; i < mInbandSubtitleTracks.length; i++) {
if (mInbandSubtitleTracks[i] == track) {
Log.v(TAG, "Selecting subtitle track " + i);
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 7613c89..ab6bd70 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -515,13 +515,13 @@ public class RemoteControlClient
mEditorArtwork = null;
if (mMetadataChanged & mArtworkChanged) {
// send to remote control display if conditions are met
- sendMetadataWithArtwork_syncCacheLock();
+ sendMetadataWithArtwork_syncCacheLock(null, 0, 0);
} else if (mMetadataChanged) {
// send to remote control display if conditions are met
- sendMetadata_syncCacheLock();
+ sendMetadata_syncCacheLock(null);
} else if (mArtworkChanged) {
// send to remote control display if conditions are met
- sendArtwork_syncCacheLock();
+ sendArtwork_syncCacheLock(null, 0, 0);
}
mApplied = true;
}
@@ -620,7 +620,7 @@ public class RemoteControlClient
mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();
// send to remote control display if conditions are met
- sendPlaybackState_syncCacheLock();
+ sendPlaybackState_syncCacheLock(null);
// update AudioService
sendAudioServiceNewPlaybackState_syncCacheLock();
@@ -705,7 +705,7 @@ public class RemoteControlClient
mTransportControlFlags = transportControlFlags;
// send to remote control display if conditions are met
- sendTransportControlInfo_syncCacheLock();
+ sendTransportControlInfo_syncCacheLock(null);
}
}
@@ -791,7 +791,7 @@ public class RemoteControlClient
mPositionUpdateListener = l;
if (oldCapa != mPlaybackPositionCapabilities) {
// tell RCDs that this RCC's playback position capabilities have changed
- sendTransportControlInfo_syncCacheLock();
+ sendTransportControlInfo_syncCacheLock(null);
}
}
}
@@ -813,7 +813,7 @@ public class RemoteControlClient
mPositionProvider = l;
if (oldCapa != mPlaybackPositionCapabilities) {
// tell RCDs that this RCC's playback position capabilities have changed
- sendTransportControlInfo_syncCacheLock();
+ sendTransportControlInfo_syncCacheLock(null);
}
if ((mPositionProvider != null) && (mEventHandler != null)
&& playbackPositionShouldMove(mPlaybackState)) {
@@ -1083,6 +1083,7 @@ public class RemoteControlClient
*/
private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
+ //TODO change name to informationRequestForAllDisplays()
public void onInformationRequested(int generationId, int infoFlags) {
// only post messages, we can't block here
if (mEventHandler != null) {
@@ -1096,12 +1097,30 @@ public class RemoteControlClient
mEventHandler.removeMessages(MSG_REQUEST_METADATA);
mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
+ mEventHandler.removeMessages(MSG_REQUEST_METADATA_ARTWORK);
mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE));
+ mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, null));
mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL));
- mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
- mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
+ mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, null));
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK,
+ 0, 0, null));
+ }
+ }
+
+ public void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h) {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, rcd));
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, rcd));
+ if ((w > 0) && (h > 0)) {
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, w, h, rcd));
+ } else {
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_REQUEST_METADATA, rcd));
+ }
}
}
@@ -1207,6 +1226,7 @@ public class RemoteControlClient
private final static int MSG_POSITION_DRIFT_CHECK = 11;
private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;
private final static int MSG_UPDATE_METADATA = 13;
+ private final static int MSG_REQUEST_METADATA_ARTWORK = 14;
private class EventHandler extends Handler {
public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1218,22 +1238,29 @@ public class RemoteControlClient
switch(msg.what) {
case MSG_REQUEST_PLAYBACK_STATE:
synchronized (mCacheLock) {
- sendPlaybackState_syncCacheLock();
+ sendPlaybackState_syncCacheLock((IRemoteControlDisplay)msg.obj);
}
break;
case MSG_REQUEST_METADATA:
synchronized (mCacheLock) {
- sendMetadata_syncCacheLock();
+ sendMetadata_syncCacheLock((IRemoteControlDisplay)msg.obj);
}
break;
case MSG_REQUEST_TRANSPORTCONTROL:
synchronized (mCacheLock) {
- sendTransportControlInfo_syncCacheLock();
+ sendTransportControlInfo_syncCacheLock((IRemoteControlDisplay)msg.obj);
}
break;
case MSG_REQUEST_ARTWORK:
synchronized (mCacheLock) {
- sendArtwork_syncCacheLock();
+ sendArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj,
+ msg.arg1, msg.arg2);
+ }
+ break;
+ case MSG_REQUEST_METADATA_ARTWORK:
+ synchronized (mCacheLock) {
+ sendMetadataWithArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj,
+ msg.arg1, msg.arg2);
}
break;
case MSG_NEW_INTERNAL_CLIENT_GEN:
@@ -1272,8 +1299,19 @@ public class RemoteControlClient
//===========================================================
// Communication with the IRemoteControlDisplay (the displays known to the system)
- private void sendPlaybackState_syncCacheLock() {
+ private void sendPlaybackState_syncCacheLock(IRemoteControlDisplay target) {
if (mCurrentClientGenId == mInternalClientGenId) {
+ if (target != null) {
+ try {
+ target.setPlaybackState(mInternalClientGenId,
+ mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
+ mPlaybackSpeed);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setPlaybackState() for dead display " + target, e);
+ }
+ return;
+ }
+ // target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
@@ -1289,8 +1327,17 @@ public class RemoteControlClient
}
}
- private void sendMetadata_syncCacheLock() {
+ private void sendMetadata_syncCacheLock(IRemoteControlDisplay target) {
if (mCurrentClientGenId == mInternalClientGenId) {
+ if (target != null) {
+ try {
+ target.setMetadata(mInternalClientGenId, mMetadata);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setMetadata() for dead display " + target, e);
+ }
+ return;
+ }
+ // target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
@@ -1304,8 +1351,19 @@ public class RemoteControlClient
}
}
- private void sendTransportControlInfo_syncCacheLock() {
+ private void sendTransportControlInfo_syncCacheLock(IRemoteControlDisplay target) {
if (mCurrentClientGenId == mInternalClientGenId) {
+ if (target != null) {
+ try {
+ target.setTransportControlInfo(mInternalClientGenId,
+ mTransportControlFlags, mPlaybackPositionCapabilities);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setTransportControlFlags() for dead display " + target,
+ e);
+ }
+ return;
+ }
+ // target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
@@ -1321,9 +1379,15 @@ public class RemoteControlClient
}
}
- private void sendArtwork_syncCacheLock() {
+ private void sendArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) {
// FIXME modify to cache all requested sizes?
if (mCurrentClientGenId == mInternalClientGenId) {
+ if (target != null) {
+ final DisplayInfoForClient di = new DisplayInfoForClient(target, w, h);
+ sendArtworkToDisplay(di);
+ return;
+ }
+ // target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
if (!sendArtworkToDisplay((DisplayInfoForClient) displayIterator.next())) {
@@ -1353,9 +1417,23 @@ public class RemoteControlClient
return true;
}
- private void sendMetadataWithArtwork_syncCacheLock() {
+ private void sendMetadataWithArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) {
// FIXME modify to cache all requested sizes?
if (mCurrentClientGenId == mInternalClientGenId) {
+ if (target != null) {
+ try {
+ if ((w > 0) && (h > 0)) {
+ Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, w, h);
+ target.setAllMetadata(mInternalClientGenId, mMetadata, artwork);
+ } else {
+ target.setMetadata(mInternalClientGenId, mMetadata);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in set(All)Metadata() for dead display " + target, e);
+ }
+ return;
+ }
+ // target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 5127479..06af5de 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -374,7 +374,7 @@ public class SoundPool {
* Called when a sound has completed loading.
*
* @param soundPool SoundPool object from the load() method
- * @param soundPool the sample ID of the sound loaded.
+ * @param sampleId the sample ID of the sound loaded.
* @param status the status of the load operation (0 = success)
*/
public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java
index 8090561..13205bc 100644
--- a/media/java/android/media/SubtitleController.java
+++ b/media/java/android/media/SubtitleController.java
@@ -21,6 +21,9 @@ import java.util.Vector;
import android.content.Context;
import android.media.SubtitleTrack.RenderingWidget;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.view.accessibility.CaptioningManager;
/**
@@ -37,6 +40,34 @@ public class SubtitleController {
private SubtitleTrack mSelectedTrack;
private boolean mShowing;
private CaptioningManager mCaptioningManager;
+ private Handler mHandler;
+
+ private static final int WHAT_SHOW = 1;
+ private static final int WHAT_HIDE = 2;
+ private static final int WHAT_SELECT_TRACK = 3;
+ private static final int WHAT_SELECT_DEFAULT_TRACK = 4;
+
+ private final Handler.Callback mCallback = new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case WHAT_SHOW:
+ doShow();
+ return true;
+ case WHAT_HIDE:
+ doHide();
+ return true;
+ case WHAT_SELECT_TRACK:
+ doSelectTrack((SubtitleTrack)msg.obj);
+ return true;
+ case WHAT_SELECT_DEFAULT_TRACK:
+ doSelectDefaultTrack();
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
private CaptioningManager.CaptioningChangeListener mCaptioningChangeListener =
new CaptioningManager.CaptioningChangeListener() {
@@ -112,7 +143,7 @@ public class SubtitleController {
* in-band data from the {@link MediaPlayer}. However, this does
* not change the subtitle visibility.
*
- * Must be called from the UI thread.
+ * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
*
* @param track The subtitle track to select. This must be one of the
* tracks in {@link #getTracks}.
@@ -122,9 +153,15 @@ public class SubtitleController {
if (track != null && !mTracks.contains(track)) {
return false;
}
+
+ processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track));
+ return true;
+ }
+
+ private void doSelectTrack(SubtitleTrack track) {
mTrackIsExplicit = true;
if (mSelectedTrack == track) {
- return true;
+ return;
}
if (mSelectedTrack != null) {
@@ -145,7 +182,6 @@ public class SubtitleController {
if (mListener != null) {
mListener.onSubtitleTrackSelected(track);
}
- return true;
}
/**
@@ -170,8 +206,6 @@ public class SubtitleController {
*
* The default values for these flags are DEFAULT=no, AUTOSELECT=yes
* and FORCED=no.
- *
- * Must be called from the UI thread.
*/
public SubtitleTrack getDefaultTrack() {
SubtitleTrack bestTrack = null;
@@ -226,8 +260,12 @@ public class SubtitleController {
private boolean mTrackIsExplicit = false;
private boolean mVisibilityIsExplicit = false;
- /** @hide - called from UI thread */
+ /** @hide - should be called from anchor thread */
public void selectDefaultTrack() {
+ processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK));
+ }
+
+ private void doSelectDefaultTrack() {
if (mTrackIsExplicit) {
// If track selection is explicit, but visibility
// is not, it falls back to the captioning setting
@@ -259,8 +297,9 @@ public class SubtitleController {
}
}
- /** @hide - called from UI thread */
+ /** @hide - must be called from anchor thread */
public void reset() {
+ checkAnchorLooper();
hide();
selectTrack(null);
mTracks.clear();
@@ -301,9 +340,13 @@ public class SubtitleController {
/**
* Show the selected (or default) subtitle track.
*
- * Must be called from the UI thread.
+ * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
*/
public void show() {
+ processOnAnchor(mHandler.obtainMessage(WHAT_SHOW));
+ }
+
+ private void doShow() {
mShowing = true;
mVisibilityIsExplicit = true;
if (mSelectedTrack != null) {
@@ -314,9 +357,13 @@ public class SubtitleController {
/**
* Hide the selected (or default) subtitle track.
*
- * Must be called from the UI thread.
+ * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
*/
public void hide() {
+ processOnAnchor(mHandler.obtainMessage(WHAT_HIDE));
+ }
+
+ private void doHide() {
mVisibilityIsExplicit = true;
if (mSelectedTrack != null) {
mSelectedTrack.hide();
@@ -384,25 +431,53 @@ public class SubtitleController {
* @hide
*/
public void setSubtitleWidget(RenderingWidget subtitleWidget);
+
+ /**
+ * Anchors provide the looper on which all track visibility changes
+ * (track.show/hide, setSubtitleWidget) will take place.
+ * @hide
+ */
+ public Looper getSubtitleLooper();
}
private Anchor mAnchor;
- /** @hide - called from UI thread */
+ /**
+ * @hide - called from anchor's looper (if any, both when unsetting and
+ * setting)
+ */
public void setAnchor(Anchor anchor) {
if (mAnchor == anchor) {
return;
}
if (mAnchor != null) {
+ checkAnchorLooper();
mAnchor.setSubtitleWidget(null);
}
mAnchor = anchor;
+ mHandler = null;
if (mAnchor != null) {
+ mHandler = new Handler(mAnchor.getSubtitleLooper(), mCallback);
+ checkAnchorLooper();
mAnchor.setSubtitleWidget(getRenderingWidget());
}
}
+ private void checkAnchorLooper() {
+ assert mHandler != null : "Should have a looper already";
+ assert Looper.myLooper() == mHandler.getLooper() : "Must be called from the anchor's looper";
+ }
+
+ private void processOnAnchor(Message m) {
+ assert mHandler != null : "Should have a looper already";
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ mHandler.dispatchMessage(m);
+ } else {
+ mHandler.sendMessage(m);
+ }
+ }
+
public interface Listener {
/**
* Called when a subtitle track has been selected.