From 06b25fb2367d1962da5445d1c3c033c73a4b95eb Mon Sep 17 00:00:00 2001 From: Hassan Shojania Date: Mon, 6 Feb 2017 21:09:42 -0800 Subject: [PATCH] Modular DRM for MediaPlayer Bug: 34559906 Test: Manual through the test app Change-Id: I1b1ca61e74f250d63ff5ff462905facb81fe44e0 --- api/current.txt | 8 +- api/system-current.txt | 8 +- api/test-current.txt | 8 +- media/java/android/media/MediaPlayer.java | 358 ++++++++++++------ media/jni/android_media_MediaPlayer.cpp | 419 ++-------------------- 5 files changed, 272 insertions(+), 529 deletions(-) diff --git a/api/current.txt b/api/current.txt index a03ef38035fbd..05f94d20126d1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 { diff --git a/api/system-current.txt b/api/system-current.txt index 406d51c4f428c..beb1adfcc5010 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -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 { diff --git a/api/test-current.txt b/api/test-current.txt index 1be19ed328580..29a3766c83f77 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -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 { diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 85c3c1c7068d5..5008a5f8c5c27 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -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 *

- * 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 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 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 hmapOptionalParameters = + (optionalParameters != null) ? + new HashMap(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. *

@@ -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(); diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index c941766c07d68..636727eb0ac01 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -58,86 +58,20 @@ #include "android_util_Binder.h" // Modular DRM begin -#include - #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, "", "(ILjava/lang/String;)V"); - gFields.stateException.classId = static_cast(env->NewGlobalRef(clazz)); + GET_METHOD_ID(gStateExceptionFields.init, clazz, "", "(ILjava/lang/String;)V"); + gStateExceptionFields.classId = static_cast(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(err), + jobject exception = env->NewObject(gStateExceptionFields.classId, + gStateExceptionFields.init, static_cast(err), env->NewStringUTF(msg)); env->Throw(static_cast(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 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 JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) { Vector vector; @@ -1414,74 +1300,8 @@ static Vector 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 HashMapToKeyedVector(JNIEnv *env, - jobject &hashMap, bool* pIsOK) -{ - jclass clazz = gFields.stringClassId; - KeyedVector 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(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(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 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 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 mp = getMediaPlayer(env, thiz); - if (mp == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return NULL; - } - - Vector 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 optParams; - if (joptParams != NULL) { - bool isOK; - optParams = HashMapToKeyedVector(env, joptParams, &isOK); - if (!isOK) { - return NULL; - } - } - - Vector 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 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 releaseKeySetId; - if (jreleaseKeySetId != NULL) { - releaseKeySetId = JByteArrayToVector(env, jreleaseKeySetId); - } - - Vector response(JByteArrayToVector(env, jresponse)); - Vector 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 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 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 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 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