Modular DRM for MediaPlayer
Bug: 34559906 Test: Manual through the test app Change-Id: I1b1ca61e74f250d63ff5ff462905facb81fe44e0
This commit is contained in:
@@ -22320,7 +22320,7 @@ package android.media {
|
||||
method public void pause() throws java.lang.IllegalStateException;
|
||||
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
|
||||
method public void prepareAsync() throws java.lang.IllegalStateException;
|
||||
method public void prepareDrm(java.util.UUID, android.media.MediaPlayer.OnDrmConfigCallback) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
|
||||
method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
|
||||
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
|
||||
method public void release();
|
||||
method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
|
||||
@@ -22348,6 +22348,7 @@ package android.media {
|
||||
method public void setNextMediaPlayer(android.media.MediaPlayer);
|
||||
method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
|
||||
method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
|
||||
method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
|
||||
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
|
||||
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
|
||||
method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
|
||||
@@ -22412,9 +22413,8 @@ package android.media {
|
||||
method public abstract void onCompletion(android.media.MediaPlayer);
|
||||
}
|
||||
|
||||
public static abstract class MediaPlayer.OnDrmConfigCallback {
|
||||
ctor public MediaPlayer.OnDrmConfigCallback();
|
||||
method public void onDrmConfig(android.media.MediaPlayer);
|
||||
public static abstract interface MediaPlayer.OnDrmConfigListener {
|
||||
method public abstract void onDrmConfig(android.media.MediaPlayer);
|
||||
}
|
||||
|
||||
public static abstract interface MediaPlayer.OnDrmInfoListener {
|
||||
|
||||
@@ -23963,7 +23963,7 @@ package android.media {
|
||||
method public void pause() throws java.lang.IllegalStateException;
|
||||
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
|
||||
method public void prepareAsync() throws java.lang.IllegalStateException;
|
||||
method public void prepareDrm(java.util.UUID, android.media.MediaPlayer.OnDrmConfigCallback) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
|
||||
method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
|
||||
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
|
||||
method public void release();
|
||||
method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
|
||||
@@ -23991,6 +23991,7 @@ package android.media {
|
||||
method public void setNextMediaPlayer(android.media.MediaPlayer);
|
||||
method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
|
||||
method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
|
||||
method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
|
||||
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
|
||||
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
|
||||
method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
|
||||
@@ -24055,9 +24056,8 @@ package android.media {
|
||||
method public abstract void onCompletion(android.media.MediaPlayer);
|
||||
}
|
||||
|
||||
public static abstract class MediaPlayer.OnDrmConfigCallback {
|
||||
ctor public MediaPlayer.OnDrmConfigCallback();
|
||||
method public void onDrmConfig(android.media.MediaPlayer);
|
||||
public static abstract interface MediaPlayer.OnDrmConfigListener {
|
||||
method public abstract void onDrmConfig(android.media.MediaPlayer);
|
||||
}
|
||||
|
||||
public static abstract interface MediaPlayer.OnDrmInfoListener {
|
||||
|
||||
@@ -22413,7 +22413,7 @@ package android.media {
|
||||
method public void pause() throws java.lang.IllegalStateException;
|
||||
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
|
||||
method public void prepareAsync() throws java.lang.IllegalStateException;
|
||||
method public void prepareDrm(java.util.UUID, android.media.MediaPlayer.OnDrmConfigCallback) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
|
||||
method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
|
||||
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
|
||||
method public void release();
|
||||
method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
|
||||
@@ -22441,6 +22441,7 @@ package android.media {
|
||||
method public void setNextMediaPlayer(android.media.MediaPlayer);
|
||||
method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
|
||||
method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
|
||||
method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
|
||||
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
|
||||
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
|
||||
method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
|
||||
@@ -22505,9 +22506,8 @@ package android.media {
|
||||
method public abstract void onCompletion(android.media.MediaPlayer);
|
||||
}
|
||||
|
||||
public static abstract class MediaPlayer.OnDrmConfigCallback {
|
||||
ctor public MediaPlayer.OnDrmConfigCallback();
|
||||
method public void onDrmConfig(android.media.MediaPlayer);
|
||||
public static abstract interface MediaPlayer.OnDrmConfigListener {
|
||||
method public abstract void onDrmConfig(android.media.MediaPlayer);
|
||||
}
|
||||
|
||||
public static abstract interface MediaPlayer.OnDrmInfoListener {
|
||||
|
||||
@@ -640,6 +640,8 @@ public class MediaPlayer extends PlayerBase
|
||||
private UUID mDrmUUID;
|
||||
private final Object mDrmLock = new Object();
|
||||
private DrmInfo mDrmInfo;
|
||||
private MediaDrm mDrmObj;
|
||||
private byte[] mDrmSessionId;
|
||||
private boolean mDrmInfoResolved;
|
||||
private boolean mActiveDrmScheme;
|
||||
private boolean mDrmConfigAllowed;
|
||||
@@ -1960,6 +1962,7 @@ public class MediaPlayer extends PlayerBase
|
||||
mOnSubtitleDataListener = null;
|
||||
|
||||
// Modular DRM clean up
|
||||
mOnDrmConfigListener = null;
|
||||
mOnDrmInfoHandlerDelegate = null;
|
||||
mOnDrmPreparedHandlerDelegate = null;
|
||||
resetDrmState();
|
||||
@@ -3171,7 +3174,7 @@ public class MediaPlayer extends PlayerBase
|
||||
onDrmInfoHandlerDelegate.notifyClient(drmInfo);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "MEDIA_DRM_INFO msg.obj NONE; UNEXPECTED" + msg.obj);
|
||||
Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -3846,16 +3849,33 @@ public class MediaPlayer extends PlayerBase
|
||||
* and setDrmPropertyString.
|
||||
*
|
||||
*/
|
||||
public static abstract class OnDrmConfigCallback
|
||||
public interface OnDrmConfigListener
|
||||
{
|
||||
/**
|
||||
* Called to give the app the opportunity to configure DRM before the session is created
|
||||
*
|
||||
* @param mp the {@code MediaPlayer} associated with this callback
|
||||
*/
|
||||
public void onDrmConfig(MediaPlayer mp) {}
|
||||
public void onDrmConfig(MediaPlayer mp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked for configuration of the DRM object before
|
||||
* the session is created.
|
||||
* The callback will be invoked synchronously half-way into the execution
|
||||
* of {@link #prepareDrm(UUID uuid)}.
|
||||
*
|
||||
* @param listener the callback that will be run
|
||||
*/
|
||||
public void setOnDrmConfigListener(OnDrmConfigListener listener)
|
||||
{
|
||||
synchronized (mDrmLock) {
|
||||
mOnDrmConfigListener = listener;
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
private OnDrmConfigListener mOnDrmConfigListener;
|
||||
|
||||
/**
|
||||
* Interface definition of a callback to be invoked when the
|
||||
* DRM info becomes available
|
||||
@@ -4053,13 +4073,11 @@ public class MediaPlayer extends PlayerBase
|
||||
return drmInfo;
|
||||
}
|
||||
|
||||
private native void _prepareDrm(@NonNull byte[] uuid, int mode)
|
||||
throws UnsupportedSchemeException, ResourceBusyException, NotProvisionedException;
|
||||
|
||||
/**
|
||||
* Prepares the DRM for the current source
|
||||
* <p>
|
||||
* If {@code OnDrmConfigCallback} is registered, it will be called half-way into
|
||||
* If {@code OnDrmConfigListener} is registered, it will be called half-way into
|
||||
* preparation to allow configuration of the DRM properties before opening the
|
||||
* DRM session. Note that the callback is called synchronously in the thread that called
|
||||
* {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
|
||||
@@ -4087,10 +4105,12 @@ public class MediaPlayer extends PlayerBase
|
||||
* @throws ResourceBusyException if required DRM resources are in use
|
||||
* @throws ProvisioningErrorException if provisioning is required but an attempt failed
|
||||
*/
|
||||
public void prepareDrm(@NonNull UUID uuid, OnDrmConfigCallback configCallback)
|
||||
public void prepareDrm(@NonNull UUID uuid)
|
||||
throws UnsupportedSchemeException,
|
||||
ResourceBusyException, ProvisioningErrorException
|
||||
{
|
||||
Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigListener: " + mOnDrmConfigListener);
|
||||
|
||||
boolean allDoneWithoutProvisioning = false;
|
||||
// get a snapshot as we'll use them outside the lock
|
||||
OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate = null;
|
||||
@@ -4099,58 +4119,46 @@ public class MediaPlayer extends PlayerBase
|
||||
|
||||
// only allowing if tied to a protected source; might releax for releasing offline keys
|
||||
if (mDrmInfo == null) {
|
||||
final String msg = String.format("prepareDrm(%s): Wrong usage: " +
|
||||
"The player must be prepared and DRM " +
|
||||
"info be retrieved before this call.", uuid);
|
||||
final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " +
|
||||
"DRM info be retrieved before this call.";
|
||||
Log.e(TAG, msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
if (mActiveDrmScheme) {
|
||||
final String msg = String.format("prepareDrm(%s): Wrong usage: There is already " +
|
||||
"an active DRM scheme with %s.", uuid, mDrmUUID);
|
||||
final String msg = "prepareDrm(): Wrong usage: There is already " +
|
||||
"an active DRM scheme with " + mDrmUUID;
|
||||
Log.e(TAG, msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
if (mPrepareDrmInProgress) {
|
||||
final String msg = String.format("prepareDrm(%s): Wrong usage: There is already " +
|
||||
"a pending prepareDrm call.", uuid);
|
||||
final String msg = "prepareDrm(): Wrong usage: There is already " +
|
||||
"a pending prepareDrm call.";
|
||||
Log.e(TAG, msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
if (mDrmProvisioningInProgress) {
|
||||
final String msg = String.format("prepareDrm(%s): Unexpectd: Provisioning is " +
|
||||
"already in progress.", uuid);
|
||||
final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress.";
|
||||
Log.e(TAG, msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
// shouldn't need this; just for safeguard
|
||||
cleanDrmObj();
|
||||
|
||||
mPrepareDrmInProgress = true;
|
||||
// local copy while the lock is held
|
||||
onDrmPreparedHandlerDelegate = mOnDrmPreparedHandlerDelegate;
|
||||
|
||||
if (configCallback != null) {
|
||||
try {
|
||||
boolean allowOpenSession = false; // just pre-openSession
|
||||
_prepareDrm(getByteArrayFromUUID(uuid), allowOpenSession ? 1 : 0);
|
||||
} catch (IllegalStateException e) {
|
||||
final String msg = String.format("prepareDrm(): Wrong usage: The player must " +
|
||||
"be in prepared state to call prepareDrm().");
|
||||
Log.e(TAG, msg);
|
||||
throw new IllegalStateException(msg);
|
||||
} catch (NotProvisionedException e) { // the pre-config step won't raise this
|
||||
final String msg = String.format("prepareDrm: Unexpected " +
|
||||
"NotProvisionedException here.");
|
||||
Log.e(TAG, msg);
|
||||
throw new ProvisioningErrorException(msg);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("prepareDrm: Exception %s", e));
|
||||
throw e;
|
||||
} finally {
|
||||
mPrepareDrmInProgress = false;
|
||||
}
|
||||
try {
|
||||
// only creating the DRM object to allow pre-openSession configuration
|
||||
prepareDrm_createDrmStep(uuid);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "prepareDrm(): Exception ", e);
|
||||
mPrepareDrmInProgress = false;
|
||||
throw e;
|
||||
}
|
||||
|
||||
mDrmConfigAllowed = true;
|
||||
@@ -4158,51 +4166,55 @@ public class MediaPlayer extends PlayerBase
|
||||
|
||||
|
||||
// call the callback outside the lock
|
||||
if (configCallback != null) {
|
||||
configCallback.onDrmConfig(this);
|
||||
if (mOnDrmConfigListener != null) {
|
||||
mOnDrmConfigListener.onDrmConfig(this);
|
||||
}
|
||||
|
||||
synchronized (mDrmLock) {
|
||||
mDrmConfigAllowed = false;
|
||||
boolean earlyExit = false;
|
||||
|
||||
try {
|
||||
boolean allowOpenSession = true; // all in
|
||||
_prepareDrm(getByteArrayFromUUID(uuid), allowOpenSession ? 1 : 0);
|
||||
prepareDrm_openSessionStep(uuid);
|
||||
|
||||
mDrmUUID = uuid;
|
||||
mActiveDrmScheme = true;
|
||||
|
||||
mPrepareDrmInProgress = false;
|
||||
|
||||
allDoneWithoutProvisioning = true;
|
||||
} catch (IllegalStateException e) {
|
||||
final String msg = String.format("prepareDrm(%s): Wrong usage: The player must be" +
|
||||
" in prepared state to call prepareDrm().", uuid);
|
||||
final String msg = "prepareDrm(): Wrong usage: The player must be " +
|
||||
"in the prepared state to call prepareDrm().";
|
||||
Log.e(TAG, msg);
|
||||
earlyExit = true;
|
||||
throw new IllegalStateException(msg);
|
||||
} catch (NotProvisionedException e) {
|
||||
Log.w(TAG, String.format("prepareDrm: NotProvisionedException"));
|
||||
Log.w(TAG, "prepareDrm: NotProvisionedException");
|
||||
|
||||
// handle provisioning internally
|
||||
// handle provisioning internally; it'll reset mPrepareDrmInProgress
|
||||
boolean result = HandleProvisioninig(uuid);
|
||||
|
||||
// if blocking mode, we're already done;
|
||||
// if non-blocking mode, we attempted to launch background provisioning
|
||||
if (result == false) {
|
||||
final String msg =
|
||||
String.format("prepareDrm: Provisioning was required but failed.");
|
||||
final String msg = "prepareDrm: Provisioning was required but failed.";
|
||||
Log.e(TAG, msg);
|
||||
earlyExit = true;
|
||||
throw new ProvisioningErrorException(msg);
|
||||
}
|
||||
|
||||
// nothing else to do;
|
||||
// if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("prepareDrm: Exception %s", e));
|
||||
Log.e(TAG, "prepareDrm: Exception " + e);
|
||||
earlyExit = true;
|
||||
throw e;
|
||||
} finally {
|
||||
mPrepareDrmInProgress = false;
|
||||
}
|
||||
if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception
|
||||
mPrepareDrmInProgress = false;
|
||||
}
|
||||
if (earlyExit) { // cleaning up object if didn't succeed
|
||||
cleanDrmObj();
|
||||
}
|
||||
} // finally
|
||||
} // synchronized
|
||||
|
||||
|
||||
@@ -4225,25 +4237,33 @@ public class MediaPlayer extends PlayerBase
|
||||
public void releaseDrm()
|
||||
throws NoDrmSchemeException
|
||||
{
|
||||
Log.v(TAG, "releaseDrm:");
|
||||
|
||||
synchronized (mDrmLock) {
|
||||
if (!mActiveDrmScheme) {
|
||||
Log.e(TAG, String.format("releaseDrm(%s): No active DRM scheme to release."));
|
||||
Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
|
||||
throw new NoDrmSchemeException("releaseDrm: No active DRM scheme to release.");
|
||||
} else {
|
||||
}
|
||||
|
||||
try {
|
||||
// we don't have the player's state in this layer. The below call raises
|
||||
// exception if we're in a non-stopped/idle state.
|
||||
|
||||
// for cleaning native/mediaserver crypto object
|
||||
_releaseDrm();
|
||||
|
||||
// for cleaning client-side MediaDrm object; only called if above has succeeded
|
||||
cleanDrmObj();
|
||||
|
||||
mActiveDrmScheme = false;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "releaseDrm: Exception ", e);
|
||||
throw e;
|
||||
}
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private native MediaDrm.KeyRequest _getKeyRequest(@NonNull byte[] scope,
|
||||
@Nullable String mimeType, @MediaDrm.KeyType int keyType,
|
||||
@Nullable Map<String, String> optionalParameters)
|
||||
throws NotProvisionedException;
|
||||
|
||||
/**
|
||||
* A key request/response exchange occurs between the app and a license server
|
||||
* to obtain or release keys used to decrypt encrypted content.
|
||||
@@ -4284,20 +4304,42 @@ public class MediaPlayer extends PlayerBase
|
||||
@MediaDrm.KeyType int keyType, @Nullable Map<String, String> optionalParameters)
|
||||
throws NoDrmSchemeException
|
||||
{
|
||||
Log.v(TAG, "getKeyRequest: " +
|
||||
" scope: " + scope + " mimeType: " + mimeType +
|
||||
" keyType: " + keyType + " optionalParameters: " + optionalParameters);
|
||||
|
||||
synchronized (mDrmLock) {
|
||||
if (!mActiveDrmScheme) {
|
||||
Log.e(TAG, String.format("getKeyRequest NoDrmSchemeException"));
|
||||
Log.e(TAG, "getKeyRequest NoDrmSchemeException");
|
||||
throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first.");
|
||||
}
|
||||
|
||||
try {
|
||||
return _getKeyRequest(scope, mimeType, keyType, optionalParameters);
|
||||
byte[] scopeOut = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
|
||||
mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
|
||||
scope; // keySetId for KEY_TYPE_RELEASE
|
||||
|
||||
byte[] initData = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
|
||||
scope : // initData for KEY_TYPE_STREAMING/OFFLINE
|
||||
null; // not used for KEY_TYPE_RELEASE
|
||||
|
||||
HashMap<String, String> hmapOptionalParameters =
|
||||
(optionalParameters != null) ?
|
||||
new HashMap<String, String>(optionalParameters) :
|
||||
null;
|
||||
|
||||
MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scopeOut, initData, mimeType,
|
||||
keyType, hmapOptionalParameters);
|
||||
Log.v(TAG, "getKeyRequest: --> request: " + request);
|
||||
|
||||
return request;
|
||||
|
||||
} catch (NotProvisionedException e) {
|
||||
Log.w(TAG, String.format("getKeyRequest NotProvisionedException: " +
|
||||
"Unexpected. Shouldn't have reached here."));
|
||||
Log.w(TAG, "getKeyRequest NotProvisionedException: " +
|
||||
"Unexpected. Shouldn't have reached here.");
|
||||
throw new IllegalStateException("getKeyRequest: Unexpected provisioning error.");
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("getKeyRequest Exception %s", e));
|
||||
Log.w(TAG, "getKeyRequest Exception " + e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -4305,10 +4347,6 @@ public class MediaPlayer extends PlayerBase
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private native byte[] _provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
|
||||
throws DeniedByServerException;
|
||||
|
||||
/**
|
||||
* A key response is received from the license server by the app, then it is
|
||||
* provided to the DRM engine plugin using provideKeyResponse. When the
|
||||
@@ -4331,25 +4369,41 @@ public class MediaPlayer extends PlayerBase
|
||||
public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
|
||||
throws NoDrmSchemeException, DeniedByServerException
|
||||
{
|
||||
Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response);
|
||||
|
||||
synchronized (mDrmLock) {
|
||||
|
||||
if (!mActiveDrmScheme) {
|
||||
Log.e(TAG, String.format("getKeyRequest NoDrmSchemeException"));
|
||||
Log.e(TAG, "getKeyRequest NoDrmSchemeException");
|
||||
throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first.");
|
||||
}
|
||||
|
||||
try {
|
||||
return _provideKeyResponse(keySetId, response);
|
||||
byte[] scope = (keySetId == null) ?
|
||||
mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
|
||||
keySetId; // keySetId for KEY_TYPE_RELEASE
|
||||
|
||||
byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
|
||||
|
||||
Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response +
|
||||
" --> " + keySetResult);
|
||||
|
||||
|
||||
return keySetResult;
|
||||
|
||||
} catch (NotProvisionedException e) {
|
||||
Log.w(TAG, "provideKeyResponse NotProvisionedException: " +
|
||||
"Unexpected. Shouldn't have reached here.");
|
||||
throw new IllegalStateException("provideKeyResponse: " +
|
||||
"Unexpected provisioning error.");
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("provideKeyResponse Exception %s", e));
|
||||
Log.w(TAG, "provideKeyResponse Exception " + e);
|
||||
throw e;
|
||||
}
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
|
||||
private native void _restoreKeys(@NonNull byte[] keySetId);
|
||||
|
||||
/**
|
||||
* Restore persisted offline keys into a new session. keySetId identifies the
|
||||
* keys to load, obtained from a prior call to {@link #provideKeyResponse}.
|
||||
@@ -4359,17 +4413,19 @@ public class MediaPlayer extends PlayerBase
|
||||
public void restoreKeys(@NonNull byte[] keySetId)
|
||||
throws NoDrmSchemeException
|
||||
{
|
||||
Log.v(TAG, "restoreKeys: keySetId: " + keySetId);
|
||||
|
||||
synchronized (mDrmLock) {
|
||||
|
||||
if (!mActiveDrmScheme) {
|
||||
Log.w(TAG, String.format("restoreKeys NoDrmSchemeException"));
|
||||
Log.w(TAG, "restoreKeys NoDrmSchemeException");
|
||||
throw new NoDrmSchemeException("restoreKeys: Has to set a DRM scheme first.");
|
||||
}
|
||||
|
||||
try {
|
||||
_restoreKeys(keySetId);
|
||||
mDrmObj.restoreKeys(mDrmSessionId, keySetId);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("restoreKeys Exception %s", e));
|
||||
Log.w(TAG, "restoreKeys Exception " + e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -4377,9 +4433,6 @@ public class MediaPlayer extends PlayerBase
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private native String _getDrmPropertyString(@NonNull String propertyName);
|
||||
|
||||
/**
|
||||
* Read a DRM engine plugin String property value, given the property name string.
|
||||
* <p>
|
||||
@@ -4393,26 +4446,29 @@ public class MediaPlayer extends PlayerBase
|
||||
public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName)
|
||||
throws NoDrmSchemeException
|
||||
{
|
||||
Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
|
||||
|
||||
String value;
|
||||
synchronized (mDrmLock) {
|
||||
|
||||
if (!mActiveDrmScheme && !mDrmConfigAllowed) {
|
||||
Log.w(TAG, String.format("getDrmPropertyString NoDrmSchemeException"));
|
||||
Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
|
||||
throw new NoDrmSchemeException("getDrmPropertyString: Has to prepareDrm() first.");
|
||||
}
|
||||
|
||||
try {
|
||||
value = _getDrmPropertyString(propertyName);
|
||||
value = mDrmObj.getPropertyString(propertyName);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("getDrmPropertyString Exception %s", e));
|
||||
Log.w(TAG, "getDrmPropertyString Exception " + e);
|
||||
throw e;
|
||||
}
|
||||
} // synchronized
|
||||
|
||||
Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private native void _setDrmPropertyString(@NonNull String propertyName, @NonNull String value);
|
||||
|
||||
/**
|
||||
* Set a DRM engine plugin String property value.
|
||||
@@ -4428,17 +4484,19 @@ public class MediaPlayer extends PlayerBase
|
||||
@NonNull String value)
|
||||
throws NoDrmSchemeException
|
||||
{
|
||||
Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
|
||||
|
||||
synchronized (mDrmLock) {
|
||||
|
||||
if ( !mActiveDrmScheme && !mDrmConfigAllowed ) {
|
||||
Log.w(TAG, String.format("setDrmPropertyString NoDrmSchemeException"));
|
||||
Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
|
||||
throw new NoDrmSchemeException("setDrmPropertyString: Has to prepareDrm() first.");
|
||||
}
|
||||
|
||||
try {
|
||||
_setDrmPropertyString(propertyName, value);
|
||||
mDrmObj.setPropertyString(propertyName, value);
|
||||
} catch ( Exception e ) {
|
||||
Log.w(TAG, String.format("setDrmPropertyString Exception %s", e));
|
||||
Log.w(TAG, "setDrmPropertyString Exception " + e);
|
||||
throw e;
|
||||
}
|
||||
} // synchronized
|
||||
@@ -4605,8 +4663,47 @@ public class MediaPlayer extends PlayerBase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
|
||||
|
||||
// Modular DRM helpers
|
||||
|
||||
private void prepareDrm_createDrmStep(@NonNull UUID uuid)
|
||||
throws UnsupportedSchemeException {
|
||||
Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
|
||||
|
||||
try {
|
||||
mDrmObj = new MediaDrm(uuid);
|
||||
Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
|
||||
} catch (Exception e) { // UnsupportedSchemeException
|
||||
Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareDrm_openSessionStep(@NonNull UUID uuid)
|
||||
throws NotProvisionedException, ResourceBusyException {
|
||||
Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
|
||||
|
||||
// TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
|
||||
// it anyway so it raises provisioning error if needed. We'd rather handle provisioning
|
||||
// at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse
|
||||
try {
|
||||
mDrmSessionId = mDrmObj.openSession();
|
||||
Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
|
||||
|
||||
// Sending it down to native/mediaserver to create the crypto object
|
||||
// This call could simply fail due to bad player state, e.g., after start().
|
||||
_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
|
||||
Log.v(TAG, "prepareDrm_openSessionStep: _prepareDrm/Crypto succeeded");
|
||||
|
||||
} catch (Exception e) { //ResourceBusyException, NotProvisionedException
|
||||
Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ProvisioningThread extends Thread
|
||||
{
|
||||
public static final int TIMEOUT_MS = 60000;
|
||||
@@ -4633,7 +4730,7 @@ public class MediaPlayer extends PlayerBase
|
||||
urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
|
||||
this.uuid = uuid;
|
||||
|
||||
Log.v(TAG, String.format("HandleProvisioninig: Thread is initialised url: %s", urlStr));
|
||||
Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -4653,30 +4750,27 @@ public class MediaPlayer extends PlayerBase
|
||||
connection.connect();
|
||||
response = Streams.readFully(connection.getInputStream());
|
||||
|
||||
Log.v(TAG, String.format("HandleProvisioninig: Thread run response %d %s",
|
||||
response.length, response));
|
||||
Log.v(TAG, "HandleProvisioninig: Thread run: response " +
|
||||
response.length + " " + response);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("HandleProvisioninig: Thread run connect %s url: %s",
|
||||
e, url));
|
||||
Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url);
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("HandleProvisioninig: Thread run openConnection %s", e));
|
||||
Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e);
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
try {
|
||||
MediaDrm drm = new MediaDrm(uuid);
|
||||
drm.provideProvisionResponse(response);
|
||||
drm.release();
|
||||
Log.v(TAG, String.format("HandleProvisioninig: Thread run " +
|
||||
"newDrm+provideProvisionResponse SUCCEEDED!"));
|
||||
mDrmObj.provideProvisionResponse(response);
|
||||
Log.v(TAG, "HandleProvisioninig: Thread run: " +
|
||||
"provideProvisionResponse SUCCEEDED!");
|
||||
|
||||
provisioningSucceeded = true;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("HandleProvisioninig: Thread run " +
|
||||
"newDrm+provideProvisionResponse %s", e));
|
||||
Log.w(TAG, "HandleProvisioninig: Thread run: " +
|
||||
"provideProvisionResponse " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4690,7 +4784,10 @@ public class MediaPlayer extends PlayerBase
|
||||
}
|
||||
mediaPlayer.mDrmProvisioningInProgress = false;
|
||||
mediaPlayer.mPrepareDrmInProgress = false;
|
||||
}
|
||||
if (!succeeded) {
|
||||
cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
|
||||
}
|
||||
} // synchronized
|
||||
|
||||
// calling the callback outside the lock
|
||||
onDrmPreparedHandlerDelegate.notifyClient(succeeded);
|
||||
@@ -4702,6 +4799,9 @@ public class MediaPlayer extends PlayerBase
|
||||
}
|
||||
mediaPlayer.mDrmProvisioningInProgress = false;
|
||||
mediaPlayer.mPrepareDrmInProgress = false;
|
||||
if (!succeeded) {
|
||||
cleanDrmObj(); // cleaning up if it hasn't gone through
|
||||
}
|
||||
}
|
||||
|
||||
finished = true;
|
||||
@@ -4714,24 +4814,18 @@ public class MediaPlayer extends PlayerBase
|
||||
// the lock is already held by the caller
|
||||
|
||||
if (mDrmProvisioningInProgress) {
|
||||
Log.e(TAG, String.format("HandleProvisioninig: Unexpected mDrmProvisioningInProgress"));
|
||||
Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
|
||||
return false;
|
||||
}
|
||||
|
||||
MediaDrm.ProvisionRequest provReq = null;
|
||||
try {
|
||||
MediaDrm drm = new MediaDrm(uuid);
|
||||
provReq = drm.getProvisionRequest();
|
||||
drm.release();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, String.format("HandleProvisioninig: getProvisionRequest failed with %s", e));
|
||||
MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
|
||||
if (provReq == null) {
|
||||
Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.v(TAG, String.format("HandleProvisioninig provReq: data %s url %s",
|
||||
(provReq != null) ? provReq.getData() : "-",
|
||||
(provReq != null) ? provReq.getDefaultUrl() : "://")
|
||||
);
|
||||
Log.v(TAG, "HandleProvisioninig provReq " +
|
||||
" data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
|
||||
|
||||
// networking in a background thread
|
||||
mDrmProvisioningInProgress = true;
|
||||
@@ -4749,7 +4843,7 @@ public class MediaPlayer extends PlayerBase
|
||||
try {
|
||||
mDrmProvisioningThread.join();
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("HandleProvisioninig: Thread.join Exception %s", e));
|
||||
Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e);
|
||||
}
|
||||
result = mDrmProvisioningThread.succeeded();
|
||||
// no longer need the thread
|
||||
@@ -4761,19 +4855,21 @@ public class MediaPlayer extends PlayerBase
|
||||
|
||||
private boolean resumePrepareDrm(UUID uuid)
|
||||
{
|
||||
Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
|
||||
|
||||
// mDrmLock is guaranteed to be held
|
||||
boolean success = false;
|
||||
try {
|
||||
boolean allowOpenSession = true; // resuming
|
||||
_prepareDrm(getByteArrayFromUUID(uuid), allowOpenSession ? 1 : 0);
|
||||
// resuming
|
||||
prepareDrm_openSessionStep(uuid);
|
||||
|
||||
mDrmUUID = uuid;
|
||||
mActiveDrmScheme = true;
|
||||
|
||||
success = true;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, String.format("HandleProvisioninig: " +
|
||||
"Thread run _prepareDrm resume failed with %s", e));
|
||||
Log.w(TAG, "HandleProvisioninig: Thread run _prepareDrm resume failed with " + e);
|
||||
// mDrmObj clean up is done by the caller
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -4782,6 +4878,12 @@ public class MediaPlayer extends PlayerBase
|
||||
private void resetDrmState()
|
||||
{
|
||||
synchronized (mDrmLock) {
|
||||
Log.v(TAG, "resetDrmState: " +
|
||||
" mDrmInfo=" + mDrmInfo +
|
||||
" mDrmProvisioningThread=" + mDrmProvisioningThread +
|
||||
" mPrepareDrmInProgress=" + mPrepareDrmInProgress +
|
||||
" mActiveDrmScheme=" + mActiveDrmScheme);
|
||||
|
||||
mDrmInfoResolved = false;
|
||||
mDrmInfo = null;
|
||||
|
||||
@@ -4791,15 +4893,33 @@ public class MediaPlayer extends PlayerBase
|
||||
mDrmProvisioningThread.join();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Log.w(TAG, String.format("resetDrmState: ProvThread.join Exception %s", e));
|
||||
Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
|
||||
}
|
||||
mDrmProvisioningThread = null;
|
||||
}
|
||||
|
||||
mPrepareDrmInProgress = false;
|
||||
mActiveDrmScheme = false;
|
||||
|
||||
cleanDrmObj();
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
private void cleanDrmObj()
|
||||
{
|
||||
// the caller holds mDrmLock
|
||||
Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
|
||||
|
||||
if (mDrmSessionId != null) {
|
||||
mDrmObj.closeSession(mDrmSessionId);
|
||||
mDrmSessionId = null;
|
||||
}
|
||||
if (mDrmObj != null) {
|
||||
mDrmObj.release();
|
||||
mDrmObj = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
|
||||
long msb = uuid.getMostSignificantBits();
|
||||
long lsb = uuid.getLeastSignificantBits();
|
||||
|
||||
@@ -58,86 +58,20 @@
|
||||
#include "android_util_Binder.h"
|
||||
|
||||
// Modular DRM begin
|
||||
#include <media/drm/DrmAPI.h>
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
var = env->FindClass(className); \
|
||||
LOG_FATAL_IF(! (var), "Unable to find class " className);
|
||||
|
||||
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
|
||||
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
|
||||
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
|
||||
|
||||
#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
|
||||
var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
|
||||
LOG_FATAL_IF(! (var), "Unable to find method " fieldName);
|
||||
|
||||
#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
|
||||
var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
|
||||
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
|
||||
|
||||
|
||||
// TODO: investigate if these can be shared with their MediaDrm counterparts
|
||||
struct RequestFields {
|
||||
jfieldID data;
|
||||
jfieldID defaultUrl;
|
||||
jfieldID requestType;
|
||||
};
|
||||
|
||||
struct HashmapFields {
|
||||
jmethodID init;
|
||||
jmethodID get;
|
||||
jmethodID put;
|
||||
jmethodID entrySet;
|
||||
};
|
||||
|
||||
struct SetFields {
|
||||
jmethodID iterator;
|
||||
};
|
||||
|
||||
struct IteratorFields {
|
||||
jmethodID next;
|
||||
jmethodID hasNext;
|
||||
};
|
||||
|
||||
struct EntryFields {
|
||||
jmethodID getKey;
|
||||
jmethodID getValue;
|
||||
};
|
||||
|
||||
struct KeyTypes {
|
||||
jint kKeyTypeStreaming;
|
||||
jint kKeyTypeOffline;
|
||||
jint kKeyTypeRelease;
|
||||
};
|
||||
|
||||
static KeyTypes gKeyTypes;
|
||||
|
||||
struct KeyRequestTypes {
|
||||
jint kKeyRequestTypeInitial;
|
||||
jint kKeyRequestTypeRenewal;
|
||||
jint kKeyRequestTypeRelease;
|
||||
};
|
||||
|
||||
static KeyRequestTypes gKeyRequestTypes;
|
||||
|
||||
struct StateExceptionFields {
|
||||
jmethodID init;
|
||||
jclass classId;
|
||||
};
|
||||
|
||||
struct drm_fields_t {
|
||||
RequestFields keyRequest;
|
||||
HashmapFields hashmap;
|
||||
SetFields set;
|
||||
IteratorFields iterator;
|
||||
EntryFields entry;
|
||||
StateExceptionFields stateException;
|
||||
jclass stringClassId;
|
||||
};
|
||||
|
||||
static drm_fields_t gFields;
|
||||
|
||||
static StateExceptionFields gStateExceptionFields;
|
||||
// Modular DRM end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -1041,50 +975,14 @@ android_media_MediaPlayer_native_init(JNIEnv *env)
|
||||
gBufferingParamsFields.init(env);
|
||||
|
||||
// Modular DRM
|
||||
FIND_CLASS(clazz, "android/media/MediaDrm");
|
||||
if (clazz) {
|
||||
jfieldID field;
|
||||
GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
|
||||
gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
|
||||
GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I");
|
||||
gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field);
|
||||
GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
|
||||
gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
} else {
|
||||
ALOGE("JNI getKeyRequest android_media_MediaPlayer_native_init couldn't "
|
||||
"get clazz android/media/MediaDrm");
|
||||
}
|
||||
|
||||
FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
|
||||
if (clazz) {
|
||||
GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
|
||||
GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
|
||||
GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I");
|
||||
|
||||
jfieldID field;
|
||||
GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I");
|
||||
gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field);
|
||||
GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I");
|
||||
gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field);
|
||||
GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I");
|
||||
gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field);
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
} else {
|
||||
ALOGE("JNI getKeyRequest android_media_MediaPlayer_native_init couldn't "
|
||||
"get clazz android/media/MediaDrm$KeyRequest");
|
||||
}
|
||||
|
||||
FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
|
||||
if (clazz) {
|
||||
GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
|
||||
gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
|
||||
GET_METHOD_ID(gStateExceptionFields.init, clazz, "<init>", "(ILjava/lang/String;)V");
|
||||
gStateExceptionFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
|
||||
|
||||
env->DeleteLocalRef(clazz);
|
||||
} else {
|
||||
ALOGE("JNI getKeyRequest android_media_MediaPlayer_native_init couldn't "
|
||||
ALOGE("JNI android_media_MediaPlayer_native_init couldn't "
|
||||
"get clazz android/media/MediaDrm$MediaDrmStateException");
|
||||
}
|
||||
|
||||
@@ -1315,8 +1213,8 @@ static void throwDrmStateException(JNIEnv *env, const char *msg, status_t err)
|
||||
{
|
||||
ALOGE("Illegal DRM state exception: %s (%d)", msg, err);
|
||||
|
||||
jobject exception = env->NewObject(gFields.stateException.classId,
|
||||
gFields.stateException.init, static_cast<int>(err),
|
||||
jobject exception = env->NewObject(gStateExceptionFields.classId,
|
||||
gStateExceptionFields.init, static_cast<int>(err),
|
||||
env->NewStringUTF(msg));
|
||||
env->Throw(static_cast<jthrowable>(exception));
|
||||
}
|
||||
@@ -1393,18 +1291,6 @@ static bool throwDrmExceptionAsNecessary(JNIEnv *env, status_t err, const char *
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: investigate if these can be shared with their MediaDrm counterparts
|
||||
static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector)
|
||||
{
|
||||
size_t length = vector.size();
|
||||
jbyteArray result = env->NewByteArray(length);
|
||||
if (result != NULL) {
|
||||
env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: investigate if these can be shared with their MediaDrm counterparts
|
||||
static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray)
|
||||
{
|
||||
Vector<uint8_t> vector;
|
||||
@@ -1414,74 +1300,8 @@ static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArr
|
||||
return vector;
|
||||
}
|
||||
|
||||
// TODO: investigate if these can be shared with their MediaDrm counterparts
|
||||
static String8 JStringToString8(JNIEnv *env, jstring const &jstr)
|
||||
{
|
||||
String8 result;
|
||||
|
||||
const char *s = env->GetStringUTFChars(jstr, NULL);
|
||||
if (s) {
|
||||
result = s;
|
||||
env->ReleaseStringUTFChars(jstr, s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: investigate if these can be shared with their MediaDrm counterparts
|
||||
static KeyedVector<String8, String8> HashMapToKeyedVector(JNIEnv *env,
|
||||
jobject &hashMap, bool* pIsOK)
|
||||
{
|
||||
jclass clazz = gFields.stringClassId;
|
||||
KeyedVector<String8, String8> keyedVector;
|
||||
*pIsOK = true;
|
||||
|
||||
jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet);
|
||||
if (entrySet) {
|
||||
jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator);
|
||||
if (iterator) {
|
||||
jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
|
||||
while (hasNext) {
|
||||
jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next);
|
||||
if (entry) {
|
||||
jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey);
|
||||
if (obj == NULL || !env->IsInstanceOf(obj, clazz)) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"HashMap key is not a String");
|
||||
env->DeleteLocalRef(entry);
|
||||
*pIsOK = false;
|
||||
break;
|
||||
}
|
||||
jstring jkey = static_cast<jstring>(obj);
|
||||
|
||||
obj = env->CallObjectMethod(entry, gFields.entry.getValue);
|
||||
if (obj == NULL || !env->IsInstanceOf(obj, clazz)) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"HashMap value is not a String");
|
||||
env->DeleteLocalRef(entry);
|
||||
*pIsOK = false;
|
||||
break;
|
||||
}
|
||||
jstring jvalue = static_cast<jstring>(obj);
|
||||
|
||||
String8 key = JStringToString8(env, jkey);
|
||||
String8 value = JStringToString8(env, jvalue);
|
||||
keyedVector.add(key, value);
|
||||
|
||||
env->DeleteLocalRef(jkey);
|
||||
env->DeleteLocalRef(jvalue);
|
||||
hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
|
||||
}
|
||||
env->DeleteLocalRef(entry);
|
||||
}
|
||||
env->DeleteLocalRef(iterator);
|
||||
}
|
||||
env->DeleteLocalRef(entrySet);
|
||||
}
|
||||
return keyedVector;
|
||||
}
|
||||
|
||||
static void android_media_MediaPlayer_prepareDrm(JNIEnv *env, jobject thiz,
|
||||
jbyteArray uuidObj, jint mode)
|
||||
jbyteArray uuidObj, jbyteArray drmSessionIdObj)
|
||||
{
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == NULL) {
|
||||
@@ -1504,13 +1324,23 @@ static void android_media_MediaPlayer_prepareDrm(JNIEnv *env, jobject thiz,
|
||||
return;
|
||||
}
|
||||
|
||||
status_t err = mp->prepareDrm(uuid.array(), mode);
|
||||
Vector<uint8_t> drmSessionId = JByteArrayToVector(env, drmSessionIdObj);
|
||||
|
||||
if (drmSessionId.size() == 0) {
|
||||
jniThrowException(
|
||||
env,
|
||||
"java/lang/IllegalArgumentException",
|
||||
"empty drmSessionId");
|
||||
return;
|
||||
}
|
||||
|
||||
status_t err = mp->prepareDrm(uuid.array(), drmSessionId);
|
||||
if (err != OK) {
|
||||
if (err == INVALID_OPERATION) {
|
||||
jniThrowException(
|
||||
env,
|
||||
"java/lang/IllegalStateException",
|
||||
"The player is not prepared yet.");
|
||||
"The player must be in prepared state.");
|
||||
} else if (err == ERROR_DRM_CANNOT_HANDLE) {
|
||||
jniThrowException(
|
||||
env,
|
||||
@@ -1536,211 +1366,10 @@ static void android_media_MediaPlayer_releaseDrm(JNIEnv *env, jobject thiz)
|
||||
jniThrowException(
|
||||
env,
|
||||
"java/lang/IllegalStateException",
|
||||
"The player is not prepared yet.");
|
||||
"Can not release DRM in an active player state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static jobject android_media_MediaPlayer_getKeyRequest(JNIEnv *env, jobject thiz, jbyteArray jscope,
|
||||
jstring jmimeType, jint jkeyType, jobject joptParams)
|
||||
{
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Vector<uint8_t> scope;
|
||||
if (jscope != NULL) {
|
||||
scope = JByteArrayToVector(env, jscope);
|
||||
}
|
||||
|
||||
String8 mimeType;
|
||||
if (jmimeType != NULL) {
|
||||
mimeType = JStringToString8(env, jmimeType);
|
||||
}
|
||||
|
||||
DrmPlugin::KeyType keyType;
|
||||
if (jkeyType == gKeyTypes.kKeyTypeStreaming) {
|
||||
keyType = DrmPlugin::kKeyType_Streaming;
|
||||
} else if (jkeyType == gKeyTypes.kKeyTypeOffline) {
|
||||
keyType = DrmPlugin::kKeyType_Offline;
|
||||
} else if (jkeyType == gKeyTypes.kKeyTypeRelease) {
|
||||
keyType = DrmPlugin::kKeyType_Release;
|
||||
} else {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", "invalid keyType");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
KeyedVector<String8, String8> optParams;
|
||||
if (joptParams != NULL) {
|
||||
bool isOK;
|
||||
optParams = HashMapToKeyedVector(env, joptParams, &isOK);
|
||||
if (!isOK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> request;
|
||||
String8 defaultUrl;
|
||||
DrmPlugin::KeyRequestType keyRequestType;
|
||||
status_t err = mp->getKeyRequest(scope, mimeType, keyType, optParams, request, defaultUrl,
|
||||
keyRequestType);
|
||||
|
||||
if (throwDrmExceptionAsNecessary(env, err, "Failed to get key request")) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALOGV("JNI getKeyRequest err %d request %d url %s keyReqType %d",
|
||||
err, (int)request.size(), defaultUrl.string(), (int)keyRequestType);
|
||||
|
||||
// Fill out return obj
|
||||
jclass clazz;
|
||||
FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
|
||||
|
||||
jobject keyObj = NULL;
|
||||
|
||||
if (clazz) {
|
||||
keyObj = env->AllocObject(clazz);
|
||||
jbyteArray jrequest = VectorToJByteArray(env, request);
|
||||
env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest);
|
||||
|
||||
jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
|
||||
env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
|
||||
|
||||
switch (keyRequestType) {
|
||||
case DrmPlugin::kKeyRequestType_Initial:
|
||||
env->SetIntField(keyObj, gFields.keyRequest.requestType,
|
||||
gKeyRequestTypes.kKeyRequestTypeInitial);
|
||||
break;
|
||||
case DrmPlugin::kKeyRequestType_Renewal:
|
||||
env->SetIntField(keyObj, gFields.keyRequest.requestType,
|
||||
gKeyRequestTypes.kKeyRequestTypeRenewal);
|
||||
break;
|
||||
case DrmPlugin::kKeyRequestType_Release:
|
||||
env->SetIntField(keyObj, gFields.keyRequest.requestType,
|
||||
gKeyRequestTypes.kKeyRequestTypeRelease);
|
||||
break;
|
||||
default:
|
||||
throwDrmStateException(env, "MediaPlayer/DRM plugin failure: unknown "
|
||||
"key request type", ERROR_DRM_UNKNOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return keyObj;
|
||||
}
|
||||
|
||||
static jbyteArray android_media_MediaPlayer_provideKeyResponse(JNIEnv *env, jobject thiz,
|
||||
jbyteArray jreleaseKeySetId, jbyteArray jresponse)
|
||||
{
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == NULL ) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (jresponse == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", "key response is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Vector<uint8_t> releaseKeySetId;
|
||||
if (jreleaseKeySetId != NULL) {
|
||||
releaseKeySetId = JByteArrayToVector(env, jreleaseKeySetId);
|
||||
}
|
||||
|
||||
Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
|
||||
Vector<uint8_t> keySetId;
|
||||
|
||||
status_t err = mp->provideKeyResponse(releaseKeySetId, response, keySetId);
|
||||
|
||||
if (throwDrmExceptionAsNecessary(env, err, "Failed to handle key response")) {
|
||||
return NULL;
|
||||
}
|
||||
return VectorToJByteArray(env, keySetId);
|
||||
}
|
||||
|
||||
static void android_media_MediaPlayer_restoreKeys(JNIEnv *env, jobject thiz, jbyteArray jkeySetId)
|
||||
{
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jkeySetId == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", "invalid keyType");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<uint8_t> keySetId;
|
||||
keySetId = JByteArrayToVector(env, jkeySetId);
|
||||
|
||||
status_t err = mp->restoreKeys(keySetId);
|
||||
|
||||
ALOGV("JNI restoreKeys err %d ", err);
|
||||
throwDrmExceptionAsNecessary(env, err, "Failed to restore keys");
|
||||
}
|
||||
|
||||
static jstring android_media_MediaPlayer_getDrmPropertyString(JNIEnv *env, jobject thiz,
|
||||
jstring jname)
|
||||
{
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (jname == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"property name String is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
String8 name = JStringToString8(env, jname);
|
||||
String8 value;
|
||||
|
||||
status_t err = mp->getDrmPropertyString(name, value);
|
||||
|
||||
ALOGV("JNI getPropertyString err %d", err);
|
||||
|
||||
if (throwDrmExceptionAsNecessary(env, err, "Failed to get property")) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return env->NewStringUTF(value.string());
|
||||
}
|
||||
|
||||
static void android_media_MediaPlayer_setDrmPropertyString(JNIEnv *env, jobject thiz,
|
||||
jstring jname, jstring jvalue)
|
||||
{
|
||||
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
|
||||
if (mp == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jname == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"property name String is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (jvalue == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"property value String is null");
|
||||
return;
|
||||
}
|
||||
|
||||
String8 name = JStringToString8(env, jname);
|
||||
String8 value = JStringToString8(env, jvalue);
|
||||
|
||||
status_t err = mp->setDrmPropertyString(name, value);
|
||||
|
||||
ALOGV("JNI setPropertyString err %d", err);
|
||||
throwDrmExceptionAsNecessary(env, err, "Failed to set property");
|
||||
}
|
||||
// Modular DRM end
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -1802,14 +1431,8 @@ static const JNINativeMethod gMethods[] = {
|
||||
"(I)Landroid/media/VolumeShaper$State;",
|
||||
(void *)android_media_MediaPlayer_getVolumeShaperState},
|
||||
// Modular DRM
|
||||
{ "_prepareDrm", "([BI)V", (void *)android_media_MediaPlayer_prepareDrm },
|
||||
{ "_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer_prepareDrm },
|
||||
{ "_releaseDrm", "()V", (void *)android_media_MediaPlayer_releaseDrm },
|
||||
{ "_getKeyRequest", "([BLjava/lang/String;ILjava/util/Map;)" "Landroid/media/MediaDrm$KeyRequest;",
|
||||
(void *)android_media_MediaPlayer_getKeyRequest },
|
||||
{ "_provideKeyResponse", "([B[B)[B", (void *)android_media_MediaPlayer_provideKeyResponse },
|
||||
{ "_getDrmPropertyString", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_MediaPlayer_getDrmPropertyString },
|
||||
{ "_setDrmPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_setDrmPropertyString },
|
||||
{ "_restoreKeys", "([B)V", (void *)android_media_MediaPlayer_restoreKeys },
|
||||
};
|
||||
|
||||
// This function only registers the native methods
|
||||
|
||||
Reference in New Issue
Block a user