diff options
Diffstat (limited to 'media/java')
-rw-r--r-- | media/java/android/media/AudioManager.java | 4 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 107 | ||||
-rw-r--r-- | media/java/android/media/AudioTrack.java | 2 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 2 | ||||
-rw-r--r-- | media/java/android/media/MediaCodec.java | 22 | ||||
-rw-r--r-- | media/java/android/media/MediaDrm.java | 60 | ||||
-rw-r--r-- | media/java/android/media/MediaExtractor.java | 48 | ||||
-rw-r--r-- | media/java/android/media/MediaMuxer.java | 6 | ||||
-rw-r--r-- | media/java/android/media/MediaPlayer.java | 3 | ||||
-rw-r--r-- | media/java/android/media/MediaRouter.java | 17 | ||||
-rw-r--r-- | media/java/android/media/RemoteControlClient.java | 38 |
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 */ |