summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt32
-rw-r--r--media/java/android/media/MediaPlayer2.java291
2 files changed, 254 insertions, 69 deletions
diff --git a/api/current.txt b/api/current.txt
index 00d4cb5f97dd..f55599b17292 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25408,6 +25408,7 @@ package android.media {
method public java.lang.Object setAuxEffectSendLevel(float);
method public java.lang.Object setDataSource(android.media.DataSourceDesc);
method public java.lang.Object setDisplay(android.view.SurfaceHolder);
+ method public void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
method public java.lang.Object setNextDataSource(android.media.DataSourceDesc);
method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>);
method public java.lang.Object setPlaybackParams(android.media.PlaybackParams);
@@ -25486,6 +25487,33 @@ package android.media {
field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
}
+ public static class MediaPlayer2.DrmEventCallback {
+ ctor public MediaPlayer2.DrmEventCallback();
+ method public void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm);
+ method public android.media.MediaPlayer2.DrmPreparationInfo onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
+ method public byte[] onDrmKeyRequest(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm.KeyRequest);
+ method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int, byte[]);
+ }
+
+ public static final class MediaPlayer2.DrmInfo {
+ method public java.util.Map<java.util.UUID, byte[]> getPssh();
+ method public java.util.List<java.util.UUID> getSupportedSchemes();
+ }
+
+ public static final class MediaPlayer2.DrmPreparationInfo {
+ }
+
+ public static final class MediaPlayer2.DrmPreparationInfo.Builder {
+ ctor public MediaPlayer2.DrmPreparationInfo.Builder();
+ method public android.media.MediaPlayer2.DrmPreparationInfo build();
+ method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setInitData(byte[]);
+ method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeySetId(byte[]);
+ method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeyType(int);
+ method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setMimeType(java.lang.String);
+ method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setOptionalParameters(java.util.Map<java.lang.String, java.lang.String>);
+ method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setUuid(java.util.UUID);
+ }
+
public static class MediaPlayer2.EventCallback {
ctor public MediaPlayer2.EventCallback();
method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
@@ -25513,6 +25541,10 @@ package android.media {
field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
}
+ public static final class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
+ ctor public MediaPlayer2.NoDrmSchemeException(java.lang.String);
+ }
+
public static class MediaPlayer2.TrackInfo {
method public android.media.MediaFormat getFormat();
method public java.lang.String getLanguage();
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index df96994b0c36..a836f4ade9f4 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -60,6 +60,7 @@ import java.net.URL;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -67,6 +68,7 @@ import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -399,8 +401,7 @@ public class MediaPlayer2 implements AutoCloseable
clearSourceInfos();
// Modular DRM clean up
- mOnDrmConfigHelper = null;
- synchronized (mDrmEventCbLock) {
+ synchronized (mDrmEventCallbackLock) {
mDrmEventCallbackRecords.clear();
}
@@ -2889,7 +2890,7 @@ public class MediaPlayer2 implements AutoCloseable
}
private void sendDrmEvent(final DrmEventNotifier notifier) {
- synchronized (mDrmEventCbLock) {
+ synchronized (mDrmEventCallbackLock) {
try {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
cb.first.execute(() -> notifier.notify(cb.second));
@@ -2901,12 +2902,28 @@ public class MediaPlayer2 implements AutoCloseable
}
}
+ private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier)
+ throws InterruptedException, ExecutionException {
+ synchronized (mDrmEventCallbackLock) {
+ mDrmEventCallbackRecords.get(0);
+ for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+ CompletableFuture<T> ret = new CompletableFuture<>();
+ cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second)));
+ return ret.get();
+ }
+ }
+ return null;
+ }
+
private interface EventNotifier {
void notify(EventCallback callback);
}
- private interface DrmEventNotifier {
- void notify(DrmEventCallback callback);
+ private interface DrmEventNotifier<T> {
+ default void notify(DrmEventCallback callback) { }
+ default T notifyWait(DrmEventCallback callback) {
+ return null;
+ }
}
/* Do not change these values without updating their counterparts
@@ -3351,73 +3368,209 @@ public class MediaPlayer2 implements AutoCloseable
// Modular DRM begin
/**
- * Interface definition of a callback to be invoked when the app
- * can do DRM configuration (get/set properties) before the session
- * is opened. This facilitates configuration of the properties, like
- * 'securityLevel', which has to be set after DRM scheme creation but
- * before the DRM session is opened.
+ * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM
+ * protected playback session.
*
- * The only allowed DRM calls in this listener are
- * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)}
- * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}.
- * @hide
+ * @see DrmPreparationInfo.Builder
*/
- public interface OnDrmConfigHelper {
+ public static final class DrmPreparationInfo {
+
/**
- * Called to give the app the opportunity to configure DRM before the session is created
- *
- * @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the DataSourceDesc of this data source
+ * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object.
*/
- public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd);
- }
+ public static final class Builder {
+
+ private UUID mUUID;
+ private byte[] mKeySetId;
+ private byte[] mInitData;
+ private String mMimeType;
+ private int mKeyType;
+ private Map<String, String> mOptionalParameters;
+
+ /**
+ * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from
+ * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}.
+ *
+ * @param uuid of selected crypto scheme
+ * @return this
+ */
+ public Builder setUuid(@NonNull UUID uuid) {
+ this.mUUID = uuid;
+ return this;
+ }
- /**
- * Register a callback to be invoked for configuration of the DRM object before
- * the session is created.
- * The callback will be invoked synchronously during the execution
- * of {@link #prepareDrm}.
- *
- * @param listener the callback that will be run
- * @hide
- */
- // This is a synchronous call.
- public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
- mOnDrmConfigHelper = listener;
- }
+ /**
+ * Set identifier of a persisted offline key obtained from
+ * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}.
+ *
+ * A {@code keySetId} can be used to restore persisted offline keys into a new playback
+ * session of a DRM protected data source. When {@code keySetId} is set, {@code initData},
+ * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored.
+ *
+ * @param keySetId identifier of a persisted offline key
+ * @return this
+ */
+ public Builder setKeySetId(@Nullable byte[] keySetId) {
+ this.mKeySetId = keySetId;
+ return this;
+ }
+
+ /**
+ * Set container-specific DRM initialization data. Its meaning is interpreted based on
+ * {@code mimeType}. For example, it could contain the content ID, key ID or other data
+ * obtained from the content metadata that is required to generate a
+ * {@link MediaDrm.KeyRequest}.
+ *
+ * @param initData container-specific DRM initialization data
+ * @return this
+ */
+ public Builder setInitData(@Nullable byte[] initData) {
+ this.mInitData = initData;
+ return this;
+ }
+
+ /**
+ * Set mime type of the content
+ *
+ * @param mimeType mime type to the content
+ * @return this
+ */
+ public Builder setMimeType(@Nullable String mimeType) {
+ this.mMimeType = mimeType;
+ return this;
+ }
+
+ /**
+ * Set type of the key request. The request may be to acquire keys
+ * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content,
+ * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys
+ * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed.
+ *
+ * @param keyType type of the key request
+ * @return this
+ */
+ public Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) {
+ this.mKeyType = keyType;
+ return this;
+ }
+
+ /**
+ * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to
+ * the license server.
+ *
+ * @param optionalParameters optional parameters to be included in a key request
+ * @return this
+ */
+ public Builder setOptionalParameters(
+ @Nullable Map<String, String> optionalParameters) {
+ this.mOptionalParameters = optionalParameters;
+ return this;
+ }
+
+ /**
+ * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder
+ */
+ public MediaPlayer2.DrmPreparationInfo build() {
+ return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType,
+ mOptionalParameters);
+ }
+
+ }
- private OnDrmConfigHelper mOnDrmConfigHelper;
+ private final UUID mUUID;
+ private final byte[] mKeySetId;
+ private final byte[] mInitData;
+ private final String mMimeType;
+ private final int mKeyType;
+ private final Map<String, String> mOptionalParameters;
+
+ private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType,
+ int mKeyType, Map<String, String> optionalParameters) {
+ this.mUUID = mUUID;
+ this.mKeySetId = mKeySetId;
+ this.mInitData = mInitData;
+ this.mMimeType = mMimeType;
+ this.mKeyType = mKeyType;
+ this.mOptionalParameters = optionalParameters;
+ }
+
+ }
/**
* Interface definition for callbacks to be invoked when the player has the corresponding
* DRM events.
- * @hide
*/
public static class DrmEventCallback {
/**
- * Called to indicate DRM info is available
+ * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that
+ * bundles DRM initialization parameters.
*
* @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the DataSourceDesc of this data source
- * @param drmInfo DRM info of the source including PSSH, and subset
- * of crypto schemes supported by this device
+ * @param dsd the {@link DataSourceDesc} of this data source
+ * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes
+ * supported by this device
+ * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip
+ * DRM initialization
*/
- public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { }
+ public DrmPreparationInfo onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+ return null;
+ }
/**
- * Called to notify the client that {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}
- * is finished and ready for key request/response.
+ * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
+ * {@code dsd}
*
* @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the DataSourceDesc of this data source
+ * @param dsd the {@link DataSourceDesc} of this data source
* @param status the result of DRM preparation.
+ * @param keySetId optional identifier that can be used to restore DRM playback initiated
+ * with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
+ *
+ * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
+ */
+ public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
+
+ /**
+ * Called to give the app the opportunity to configure DRM before the session is created.
+ *
+ * This facilitates configuration of the properties, like 'securityLevel', which
+ * has to be set after DRM scheme creation but before the DRM session is opened.
+ *
+ * The only allowed DRM calls in this listener are
+ * {@link MediaDrm#getPropertyString(String)},
+ * {@link MediaDrm#getPropertyByteArray(String)},
+ * {@link MediaDrm#setPropertyString(String, String)},
+ * {@link MediaDrm#setPropertyByteArray(String, byte[])},
+ * {@link MediaDrm#setOnExpirationUpdateListener},
+ * and {@link MediaDrm#setOnKeyStatusChangeListener}.
+ *
+ * @param mp the {@code MediaPlayer2} associated with this callback
+ * @param dsd the {@link DataSourceDesc} of this data source
+ * @param drm handle to get/set DRM properties and listeners for this data source
*/
- public void onDrmPrepared(
- MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
+ public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @NonNull MediaDrm drm) { }
+
+ /**
+ * Called to indicate the DRM session for {@code dsd} is ready for key request/response
+ *
+ * @param mp the {@code MediaPlayer2} associated with this callback
+ * @param dsd the {@link DataSourceDesc} of this data source
+ * @param request a {@link MediaDrm.KeyRequest} prepared using the
+ * {@link DrmPreparationInfo} returned from
+ * {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)}
+ * @return the response to {@code request} (from license server)
+ */
+ public byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+ @NonNull MediaDrm.KeyRequest request) {
+ return null;
+ }
+
}
- private final Object mDrmEventCbLock = new Object();
- private ArrayList<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
+ private final Object mDrmEventCallbackLock = new Object();
+ private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
new ArrayList<Pair<Executor, DrmEventCallback>>();
/**
@@ -3425,10 +3578,9 @@ public class MediaPlayer2 implements AutoCloseable
*
* @param eventCallback the callback that will be run
* @param executor the executor through which the callback should be invoked
- * @hide
*/
// This is a synchronous call.
- public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+ public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull DrmEventCallback eventCallback) {
if (eventCallback == null) {
throw new IllegalArgumentException("Illegal null EventCallback");
@@ -3437,8 +3589,9 @@ public class MediaPlayer2 implements AutoCloseable
throw new IllegalArgumentException(
"Illegal null Executor for the EventCallback");
}
- synchronized (mDrmEventCbLock) {
- mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
+ synchronized (mDrmEventCallbackLock) {
+ mDrmEventCallbackRecords = Collections.singletonList(
+ new Pair<Executor, DrmEventCallback>(executor, eventCallback));
}
}
@@ -3450,7 +3603,7 @@ public class MediaPlayer2 implements AutoCloseable
*/
// This is a synchronous call.
public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
- synchronized (mDrmEventCbLock) {
+ synchronized (mDrmEventCallbackLock) {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
if (cb.second == eventCallback) {
mDrmEventCallbackRecords.remove(cb);
@@ -3564,7 +3717,7 @@ public class MediaPlayer2 implements AutoCloseable
/**
* Prepares the DRM for the given data source
* <p>
- * If {@link OnDrmConfigHelper} is registered, it will be called during
+ * If {@link DrmEventCallback} is registered, it will be called during
* preparation to allow configuration of the DRM properties before opening the
* DRM session. It should be used only for a series of
* {@link #getDrmPropertyString(DataSourceDesc, String)} and
@@ -3587,8 +3740,7 @@ public class MediaPlayer2 implements AutoCloseable
* @param dsd The DRM protected data source
*
* @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
- * from the source through {@link #getDrmInfo(DataSourceDesc)} or registering a
- * {@link DrmEventCallback#onDrmInfo}.
+ * from the source listening to {@link DrmEventCallback#onDrmInfo}.
*
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
* @hide
@@ -3661,8 +3813,8 @@ public class MediaPlayer2 implements AutoCloseable
sendDrmEvent(new DrmEventNotifier() {
@Override
public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(
- MediaPlayer2.this, dsd, prepareDrmStatus);
+ callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus,
+ /* TODO: keySetId */ null);
}
});
@@ -3876,7 +4028,6 @@ public class MediaPlayer2 implements AutoCloseable
/**
* Encapsulates the DRM properties of the source.
- * @hide
*/
public static final class DrmInfo {
private Map<UUID, byte[]> mMapPssh;
@@ -4013,10 +4164,8 @@ public class MediaPlayer2 implements AutoCloseable
}; // DrmInfo
/**
- * Thrown when a DRM method is called before preparing a DRM scheme through
- * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}.
+ * Thrown when a DRM method is called when there is no active DRM session.
* Extends MediaDrm.MediaDrmException
- * @hide
*/
public static final class NoDrmSchemeException extends MediaDrmException {
public NoDrmSchemeException(String detailMessage) {
@@ -4291,9 +4440,9 @@ public class MediaPlayer2 implements AutoCloseable
}
void prepare(UUID uuid) throws UnsupportedSchemeException,
- ResourceBusyException, NotProvisionedException {
- final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
- Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+ ResourceBusyException, NotProvisionedException, InterruptedException,
+ ExecutionException {
+ Log.v(TAG, "prepareDrm: uuid: " + uuid);
synchronized (this) {
if (mActiveDrmUUID != null) {
@@ -4334,9 +4483,13 @@ public class MediaPlayer2 implements AutoCloseable
} // synchronized
// call the callback outside the lock
- if (onDrmConfigHelper != null) {
- onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
- }
+ sendDrmEventWait(new DrmEventNotifier<Void>() {
+ @Override
+ public Void notifyWait(DrmEventCallback callback) {
+ callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj);
+ return null;
+ }
+ });
synchronized (this) {
mDrmConfigAllowed = false;
@@ -4506,7 +4659,7 @@ public class MediaPlayer2 implements AutoCloseable
@Override
public void notify(DrmEventCallback callback) {
callback.onDrmPrepared(
- MediaPlayer2.this, mDSD, finalStatus);
+ MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null);
}
});