Modular DRM for MediaPlayer

Bug:  34559906
Test: Manual through the test app
Change-Id: I1b1ca61e74f250d63ff5ff462905facb81fe44e0
This commit is contained in:
Hassan Shojania
2017-02-06 21:09:42 -08:00
parent 9f1b5b73e8
commit 06b25fb236
5 changed files with 272 additions and 529 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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