summaryrefslogtreecommitdiffstats
path: root/media/java
diff options
context:
space:
mode:
Diffstat (limited to 'media/java')
-rw-r--r--media/java/android/media/AudioManager.java4
-rw-r--r--media/java/android/media/AudioService.java107
-rw-r--r--media/java/android/media/AudioTrack.java2
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaCodec.java22
-rw-r--r--media/java/android/media/MediaDrm.java60
-rw-r--r--media/java/android/media/MediaExtractor.java48
-rw-r--r--media/java/android/media/MediaMuxer.java6
-rw-r--r--media/java/android/media/MediaPlayer.java3
-rw-r--r--media/java/android/media/MediaRouter.java17
-rw-r--r--media/java/android/media/RemoteControlClient.java38
11 files changed, 223 insertions, 86 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b80a166..917a47d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -50,6 +50,7 @@ public class AudioManager {
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
private final boolean mUseVolumeKeySounds;
+ private final Binder mToken = new Binder();
private static String TAG = "AudioManager";
/**
@@ -2075,7 +2076,8 @@ public class AudioManager {
IAudioService service = getService();
try {
// pi != null
- service.registerMediaButtonIntent(pi, eventReceiver);
+ service.registerMediaButtonIntent(pi, eventReceiver,
+ eventReceiver == null ? mToken : null);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index fd71d79..637ac85 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -357,7 +357,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final int SCO_STATE_INACTIVE = 0;
// SCO audio activation request waiting for headset service to connect
private static final int SCO_STATE_ACTIVATE_REQ = 1;
- // SCO audio state is active or starting due to a local request to start a virtual call
+ // SCO audio state is active or starting due to a request from AudioManager API
private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
// SCO audio deactivation request waiting for headset service to connect
private static final int SCO_STATE_DEACTIVATE_REQ = 5;
@@ -530,6 +530,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// Register for package removal intent broadcasts for media button receiver persistence
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
pkgFilter.addDataScheme("package");
context.registerReceiver(mReceiver, pkgFilter);
@@ -2053,8 +2056,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_INACTIVE) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice)) {
+ if (mBluetoothHeadset.connectAudio()) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
} else {
broadcastScoConnectionState(
@@ -2076,8 +2078,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice)) {
+ if (!mBluetoothHeadset.disconnectAudio()) {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -2250,12 +2251,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
switch (mScoAudioState) {
case SCO_STATE_ACTIVATE_REQ:
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
+ status = mBluetoothHeadset.connectAudio();
break;
case SCO_STATE_DEACTIVATE_REQ:
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
+ status = mBluetoothHeadset.disconnectAudio();
break;
case SCO_STATE_DEACTIVATE_EXT_REQ:
status = mBluetoothHeadset.stopVoiceRecognition(
@@ -3535,6 +3534,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
(RccPlaybackState)msg.obj /* newState */);
break;
+ case MSG_RCC_SEEK_REQUEST:
+ onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
+ ((Long)msg.obj).longValue() /* timeMs */);
case MSG_SET_RSX_CONNECTION_STATE:
onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
@@ -4037,14 +4039,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
0,
null,
SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
- } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+ } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+ || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
String packageName = intent.getData().getSchemeSpecificPart();
if (packageName != null) {
- removeMediaButtonReceiverForPackage(packageName);
+ cleanupMediaButtonReceiverForPackage(packageName, true);
}
}
+ } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
+ || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName != null) {
+ cleanupMediaButtonReceiverForPackage(packageName, false);
+ }
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
AudioSystem.setParameters("screen_state=on");
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
@@ -4851,8 +4860,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
- private static class RemoteControlStackEntry {
+ private static class RemoteControlStackEntry implements DeathRecipient {
public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ final public AudioService mService;
/**
* The target for the ACTION_MEDIA_BUTTON events.
* Always non null.
@@ -4863,6 +4873,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Always non null.
*/
final public ComponentName mReceiverComponent;
+ public IBinder mToken;
public String mCallingPackageName;
public int mCallingUid;
/**
@@ -4893,9 +4904,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
/** precondition: mediaIntent != null */
- public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
+ public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
+ ComponentName eventReceiver, IBinder token) {
+ mService = service;
mMediaIntent = mediaIntent;
mReceiverComponent = eventReceiver;
+ mToken = token;
mCallingUid = -1;
mRcClient = null;
mRccId = ++sLastRccId;
@@ -4905,6 +4919,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
RemoteControlClient.PLAYBACK_SPEED_1X);
resetPlaybackInfo();
+ if (mToken != null) {
+ try {
+ mToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ mService.mAudioHandler.post(new Runnable() {
+ @Override public void run() {
+ mService.unregisterMediaButtonIntent(mMediaIntent);
+ }
+ });
+ }
+ }
}
public void unlinkToRcClientDeath() {
@@ -4920,9 +4945,22 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ public void destroy() {
+ unlinkToRcClientDeath();
+ if (mToken != null) {
+ mToken.unlinkToDeath(this, 0);
+ mToken = null;
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mService.unregisterMediaButtonIntent(mMediaIntent);
+ }
+
@Override
protected void finalize() throws Throwable {
- unlinkToRcClientDeath();// unlink exception handled inside method
+ destroy(); // unlink exception handled inside method
super.finalize();
}
}
@@ -5021,11 +5059,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Remove any entry in the remote control stack that has the same package name as packageName
* Pre-condition: packageName != null
*/
- private void removeMediaButtonReceiverForPackage(String packageName) {
+ private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
synchronized(mRCStack) {
if (mRCStack.empty()) {
return;
} else {
+ final PackageManager pm = mContext.getPackageManager();
RemoteControlStackEntry oldTop = mRCStack.peek();
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
// iterate over the stack entries
@@ -5033,10 +5072,19 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// evaluated it, traversal order doesn't matter here)
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
- if (packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
+ if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
// a stack entry is from the package being removed, remove it from the stack
stackIterator.remove();
- rcse.unlinkToRcClientDeath();
+ rcse.destroy();
+ } else if (rcse.mReceiverComponent != null) {
+ try {
+ // Check to see if this receiver still exists.
+ pm.getReceiverInfo(rcse.mReceiverComponent, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Not found -- remove it!
+ stackIterator.remove();
+ rcse.destroy();
+ }
}
}
if (mRCStack.empty()) {
@@ -5079,7 +5127,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(mContext,
0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
- registerMediaButtonIntent(pi, eventReceiver);
+ registerMediaButtonIntent(pi, eventReceiver, null);
}
}
@@ -5089,7 +5137,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Called synchronized on mAudioFocusLock, then mRCStack
* precondition: mediaIntent != null
*/
- private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target) {
+ private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
+ IBinder token) {
// already at top of stack?
if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
return;
@@ -5111,7 +5160,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
}
if (!wasInsideStack) {
- rcse = new RemoteControlStackEntry(mediaIntent, target);
+ rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
}
mRCStack.push(rcse); // rcse is never null
@@ -5133,7 +5182,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
for (int index = mRCStack.size()-1; index >= 0; index--) {
final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
if (rcse.mMediaIntent.equals(pi)) {
- rcse.unlinkToRcClientDeath();
+ rcse.destroy();
// ok to remove element while traversing the stack since we're leaving the loop
mRCStack.removeElementAt(index);
break;
@@ -5421,12 +5470,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
* precondition: mediaIntent != null
*/
- public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
+ public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
+ IBinder token) {
Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
synchronized(mAudioFocusLock) {
synchronized(mRCStack) {
- pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver);
+ pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
// new RC client, assume every type of information shall be queried
checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
}
@@ -5820,7 +5870,16 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_QUEUE, generationId /* arg1 */,
+ // ignore position change requests if invalid generation ID
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClientGen != generationId) {
+ return;
+ }
+ }
+ }
+ // discard any unprocessed seek request in the message queue, and replace with latest
+ sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index cd50de4..399eb7b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -791,7 +791,7 @@ public class AudioTrack
* {@link #ERROR_INVALID_OPERATION}
*/
public int setPlaybackRate(int sampleRateInHz) {
- if (mState == STATE_UNINITIALIZED) {
+ if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
if (sampleRateInHz <= 0) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 25aae8f..13f6c02 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -120,7 +120,7 @@ interface IAudioService {
oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
- void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+ void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
oneway void unregisterMediaButtonIntent(in PendingIntent pi);
oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b6b49a2..45a8b99 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -22,6 +22,7 @@ import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.view.Surface;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Map;
/**
@@ -395,6 +396,27 @@ final public class MediaCodec {
* see {@link #CRYPTO_MODE_UNENCRYPTED} and {@link #CRYPTO_MODE_AES_CTR}.
*/
public int mode;
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(numSubSamples + " subsamples, key [");
+ String hexdigits = "0123456789abcdef";
+ for (int i = 0; i < key.length; i++) {
+ builder.append(hexdigits.charAt((key[i] & 0xf0) >> 4));
+ builder.append(hexdigits.charAt(key[i] & 0x0f));
+ }
+ builder.append("], iv [");
+ for (int i = 0; i < key.length; i++) {
+ builder.append(hexdigits.charAt((iv[i] & 0xf0) >> 4));
+ builder.append(hexdigits.charAt(iv[i] & 0x0f));
+ }
+ builder.append("], clear ");
+ builder.append(Arrays.toString(numBytesOfClearData));
+ builder.append(", encrypted ");
+ builder.append(Arrays.toString(numBytesOfEncryptedData));
+ return builder.toString();
+ }
};
/**
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 3cdf261..6872278 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Bundle;
+import android.os.Parcel;
import android.util.Log;
/**
@@ -136,10 +137,8 @@ public final class MediaDrm {
public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3;
public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4;
- /* Do not change these values without updating their counterparts
- * in include/media/mediadrm.h!
- */
private static final int DRM_EVENT = 200;
+
private class EventHandler extends Handler
{
private MediaDrm mMediaDrm;
@@ -161,10 +160,18 @@ public final class MediaDrm {
Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
if (mOnEventListener != null) {
- Bundle bundle = msg.getData();
- byte[] sessionId = bundle.getByteArray("sessionId");
- byte[] data = bundle.getByteArray("data");
- mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length == 0) {
+ sessionId = null;
+ }
+ byte[] data = parcel.createByteArray();
+ if (data.length == 0) {
+ data = null;
+ }
+ mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ }
}
return;
@@ -183,14 +190,14 @@ public final class MediaDrm {
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediadrm_ref,
- int what, int arg1, int arg2, Object obj)
+ int eventType, int extra, Object obj)
{
MediaDrm md = (MediaDrm)((WeakReference)mediadrm_ref).get();
if (md == null) {
return;
}
if (md.mEventHandler != null) {
- Message m = md.mEventHandler.obtainMessage(what, arg1, arg2, obj);
+ Message m = md.mEventHandler.obtainMessage(DRM_EVENT, eventType, extra, obj);
md.mEventHandler.sendMessage(m);
}
}
@@ -208,6 +215,7 @@ public final class MediaDrm {
public static final int MEDIA_DRM_KEY_TYPE_STREAMING = 1;
public static final int MEDIA_DRM_KEY_TYPE_OFFLINE = 2;
+ public static final int MEDIA_DRM_KEY_TYPE_RELEASE = 3;
public final class KeyRequest {
public KeyRequest() {}
@@ -216,28 +224,36 @@ public final class MediaDrm {
};
/**
- * A key request/response exchange occurs between the app and a license
- * server to obtain the keys to decrypt encrypted content. getKeyRequest()
- * is used to obtain an opaque key request byte array that is delivered to the
- * license server. The opaque key request byte array is returned in
- * KeyRequest.data. The recommended URL to deliver the key request to is
+ * A key request/response exchange occurs between the app and a license server
+ * to obtain or release keys used to decrypt encrypted content.
+ * getKeyRequest() is used to obtain an opaque key request byte array that is
+ * delivered to the license server. The opaque key request byte array is returned
+ * in KeyRequest.data. The recommended URL to deliver the key request to is
* returned in KeyRequest.defaultUrl.
*
* After the app has received the key request response from the server,
* it should deliver to the response to the DRM engine plugin using the method
* {@link #provideKeyResponse}.
*
- * @param sessonId the session ID for the drm session
+ * @param scope may be a sessionId or a keySetId, depending on the specified keyType.
+ * When the keyType is MEDIA_DRM_KEY_TYPE_STREAMING or MEDIA_DRM_KEY_TYPE_OFFLINE,
+ * scope should be set to the sessionId the keys will be provided to. When the keyType
+ * is MEDIA_DRM_KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys
+ * being released. Releasing keys from a device invalidates them for all sessions.
* @param init container-specific data, its meaning is interpreted based on the
* mime type provided in the mimeType parameter. It could contain, for example,
* the content ID, key ID or other data obtained from the content metadata that is
- * required in generating the key request.
+ * required in generating the key request. init may be null when keyType is
+ * MEDIA_DRM_KEY_TYPE_RELEASE.
* @param mimeType identifies the mime type of the content
- * @param keyType specifes if the request is for streaming or offline content
+ * @param keyType specifes the type of the request. The request may be to acquire
+ * keys for streaming or offline content, or to release previously acquired
+ * keys, which are identified by a keySetId.
+
* @param optionalParameters are included in the key request message to
* allow a client application to provide additional message parameters to the server.
*/
- public native KeyRequest getKeyRequest(byte[] sessionId, byte[] init,
+ public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
String mimeType, int keyType,
HashMap<String, String> optionalParameters)
throws MediaDrmException;
@@ -265,13 +281,11 @@ public final class MediaDrm {
throws MediaDrmException;
/**
- * Remove the persisted keys associated with an offline license. Keys are persisted
- * when {@link provideKeyResponse} is called with keys obtained from the method
- * {@link getKeyRequest} using keyType = MEDIA_DRM_KEY_TYPE_OFFLINE.
+ * Remove the current keys from a session.
*
- * @param keySetId identifies the saved key set to remove
+ * @param sessionId the session ID for the DRM session
*/
- public native void removeKeys(byte[] keySetId) throws MediaDrmException;
+ public native void removeKeys(byte[] sessionId) throws MediaDrmException;
/**
* Request an informative description of the key status for the session. The status is
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 749ef12..cf159f0 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -26,7 +26,10 @@ import android.net.Uri;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
import java.util.Map;
+import java.util.UUID;
/**
* MediaExtractor facilitates extraction of demuxed, typically encoded, media data
@@ -64,7 +67,7 @@ final public class MediaExtractor {
* Sets the DataSource object to be used as the data source for this extractor
* {@hide}
*/
- public native final void setDataSource(DataSource source);
+ public native final void setDataSource(DataSource source) throws IOException;
/**
* Sets the data source as a content Uri.
@@ -118,7 +121,8 @@ final public class MediaExtractor {
* @param path the path of the file, or the http URL
* @param headers the headers associated with the http request for the stream you want to play
*/
- public final void setDataSource(String path, Map<String, String> headers) {
+ public final void setDataSource(String path, Map<String, String> headers)
+ throws IOException {
String[] keys = null;
String[] values = null;
@@ -137,7 +141,7 @@ final public class MediaExtractor {
}
private native final void setDataSource(
- String path, String[] keys, String[] values);
+ String path, String[] keys, String[] values) throws IOException;
/**
* Sets the data source (file-path or http URL) to use.
@@ -151,7 +155,7 @@ final public class MediaExtractor {
* As an alternative, the application could first open the file for reading,
* and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
*/
- public final void setDataSource(String path) {
+ public final void setDataSource(String path) throws IOException {
setDataSource(path, null, null);
}
@@ -161,7 +165,7 @@ final public class MediaExtractor {
*
* @param fd the FileDescriptor for the file you want to extract from.
*/
- public final void setDataSource(FileDescriptor fd) {
+ public final void setDataSource(FileDescriptor fd) throws IOException {
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
@@ -175,7 +179,7 @@ final public class MediaExtractor {
* @param length the length in bytes of the data to be extracted
*/
public native final void setDataSource(
- FileDescriptor fd, long offset, long length);
+ FileDescriptor fd, long offset, long length) throws IOException;
@Override
protected void finalize() {
@@ -195,6 +199,38 @@ final public class MediaExtractor {
public native final int getTrackCount();
/**
+ * Get the PSSH info if present. This returns a map of uuid-to-bytes, with the uuid specifying
+ * the crypto scheme, and the bytes being the data specific to that scheme.
+ * {@hide}
+ */
+ public Map<UUID, byte[]> getPsshInfo() {
+ Map<UUID, byte[]> psshMap = null;
+ Map<String, Object> formatMap = getFileFormatNative();
+ if (formatMap != null && formatMap.containsKey("pssh")) {
+ ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh");
+ rawpssh.order(ByteOrder.nativeOrder());
+ rawpssh.rewind();
+ formatMap.remove("pssh");
+ // parse the flat pssh bytebuffer into something more manageable
+ psshMap = new HashMap<UUID, byte[]>();
+ while (rawpssh.remaining() > 0) {
+ rawpssh.order(ByteOrder.BIG_ENDIAN);
+ long msb = rawpssh.getLong();
+ long lsb = rawpssh.getLong();
+ UUID uuid = new UUID(msb, lsb);
+ rawpssh.order(ByteOrder.nativeOrder());
+ int datalen = rawpssh.getInt();
+ byte [] psshdata = new byte[datalen];
+ rawpssh.get(psshdata);
+ psshMap.put(uuid, psshdata);
+ }
+ }
+ return psshMap;
+ }
+
+ private native Map<String, Object> getFileFormatNative();
+
+ /**
* Get the track format at the specified index.
* More detail on the representation can be found at {@link android.media.MediaCodec}
*/
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 1f5ca35..c0fbd2e 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -257,8 +257,10 @@ final public class MediaMuxer {
}
/**
- * Writes an encoded sample into the muxer. The application needs to make
- * sure that the samples are written into the right tracks.
+ * Writes an encoded sample into the muxer.
+ * <p>The application needs to make sure that the samples are written into
+ * the right tracks. Also, it needs to make sure the samples for each track
+ * are written in chronological order.</p>
* @param byteBuf The encoded sample.
* @param trackIndex The track index for this sample.
* @param bufferInfo The buffer information related to this sample.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 85a32ca..f745163 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1181,7 +1181,8 @@ public class MediaPlayer
/**
* Gets the duration of the file.
*
- * @return the duration in milliseconds
+ * @return the duration in milliseconds, if no duration is available
+ * (for example, if streaming live content), -1 is returned.
*/
public native int getDuration();
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 795c3c2..61c55a5 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -767,10 +767,19 @@ public class MediaRouter {
boolean wantScan = false;
boolean blockScan = false;
WifiDisplay[] oldDisplays = oldStatus != null ?
- oldStatus.getRememberedDisplays() : new WifiDisplay[0];
- WifiDisplay[] newDisplays = newStatus.getRememberedDisplays();
- WifiDisplay[] availableDisplays = newStatus.getAvailableDisplays();
- WifiDisplay activeDisplay = newStatus.getActiveDisplay();
+ oldStatus.getRememberedDisplays() : WifiDisplay.EMPTY_ARRAY;
+ WifiDisplay[] newDisplays;
+ WifiDisplay[] availableDisplays;
+ WifiDisplay activeDisplay;
+
+ if (newStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
+ newDisplays = newStatus.getRememberedDisplays();
+ availableDisplays = newStatus.getAvailableDisplays();
+ activeDisplay = newStatus.getActiveDisplay();
+ } else {
+ newDisplays = availableDisplays = WifiDisplay.EMPTY_ARRAY;
+ activeDisplay = null;
+ }
for (int i = 0; i < newDisplays.length; i++) {
final WifiDisplay d = newDisplays[i];
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index e076ef0..4a5e82e 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -277,14 +277,12 @@ public class RemoteControlClient
*/
public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
/**
- * @hide
- * TODO un-hide and add in javadoc of setTransportControlFlags(int)
* Flag indicating a RemoteControlClient can receive changes in the media playback position
- * through the {@link #OnPlaybackPositionUpdateListener} interface. This flag must be set
+ * through the {@link OnPlaybackPositionUpdateListener} interface. This flag must be set
* in order for components that display the RemoteControlClient information, to display and
* let the user control media playback position.
* @see #setTransportControlFlags(int)
- * @see #setPlaybackPositionProvider(PlaybackPositionProvider)
+ * @see #setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener)
* @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener)
*/
public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8;
@@ -607,8 +605,6 @@ public class RemoteControlClient
}
/**
- * @hide
- * TODO un-hide
* Sets the current playback state and the matching media position for the current playback
* speed.
* @param state The current playback state, one of the following values:
@@ -660,7 +656,8 @@ public class RemoteControlClient
* {@link #FLAG_KEY_MEDIA_PAUSE},
* {@link #FLAG_KEY_MEDIA_STOP},
* {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
- * {@link #FLAG_KEY_MEDIA_NEXT}
+ * {@link #FLAG_KEY_MEDIA_NEXT},
+ * {@link #FLAG_KEY_MEDIA_POSITION_UPDATE}
*/
public void setTransportControlFlags(int transportControlFlags) {
synchronized(mCacheLock) {
@@ -673,8 +670,6 @@ public class RemoteControlClient
}
/**
- * @hide
- * TODO un-hide
* Interface definition for a callback to be invoked when the media playback position is
* requested to be updated.
* @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE
@@ -683,39 +678,38 @@ public class RemoteControlClient
/**
* Called on the implementer to notify it that the playback head should be set at the given
* position. If the position can be changed from its current value, the implementor of
- * the interface should also update the playback position using
- * {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new
+ * the interface must also update the playback position using
+ * {@link #setPlaybackState(int, long, float)} to reflect the actual new
* position being used, regardless of whether it differs from the requested position.
+ * Failure to do so would cause the system to not know the new actual playback position,
+ * and user interface components would fail to show the user where playback resumed after
+ * the position was updated.
* @param newPositionMs the new requested position in the current media, expressed in ms.
*/
void onPlaybackPositionUpdate(long newPositionMs);
}
/**
- * @hide
- * TODO un-hide
* Interface definition for a callback to be invoked when the media playback position is
* queried.
* @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE
*/
- public interface PlaybackPositionProvider {
+ public interface OnGetPlaybackPositionListener {
/**
* Called on the implementer of the interface to query the current playback position.
* @return a negative value if the current playback position (or the last valid playback
* position) is not known, or a zero or positive value expressed in ms indicating the
* current position, or the last valid known position.
*/
- long getPlaybackPosition();
+ long onGetPlaybackPosition();
}
/**
- * @hide
- * TODO un-hide
* Sets the listener to be called whenever the media playback position is requested
* to be updated.
* Notifications will be received in the same thread as the one in which RemoteControlClient
* was created.
- * @param l
+ * @param l the position update listener to be called
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
synchronized(mCacheLock) {
@@ -734,14 +728,12 @@ public class RemoteControlClient
}
/**
- * @hide
- * TODO un-hide
* Sets the listener to be called whenever the media current playback position is needed.
* Queries will be received in the same thread as the one in which RemoteControlClient
* was created.
- * @param l
+ * @param l the listener to be called to retrieve the playback position
*/
- public void setPlaybackPositionProvider(PlaybackPositionProvider l) {
+ public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
synchronized(mCacheLock) {
int oldCapa = mPlaybackPositionCapabilities;
if (l != null) {
@@ -939,7 +931,7 @@ public class RemoteControlClient
/**
* Provider registered by user of RemoteControlClient to provide the current playback position.
*/
- private PlaybackPositionProvider mPositionProvider;
+ private OnGetPlaybackPositionListener mPositionProvider;
/**
* The current remote control client generation ID across the system, as known by this object
*/