summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java298
1 files changed, 298 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java
new file mode 100644
index 0000000000..7be8be6236
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java
@@ -0,0 +1,298 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.media;
+
+import android.annotation.SuppressLint;
+import android.media.MediaCrypto;
+import android.media.MediaDrm;
+import android.os.Build;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.UUID;
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+
+public final class MediaDrmProxy {
+ private static final String LOGTAG = "GeckoMediaDrmProxy";
+ private static final boolean DEBUG = false;
+ private static final UUID WIDEVINE_SCHEME_UUID =
+ new UUID(0xedef8ba979d64aceL, 0xa3c827dcd51d21edL);
+
+ private static final String WIDEVINE_KEY_SYSTEM = "com.widevine.alpha";
+ @WrapForJNI private static final String AAC = "audio/mp4a-latm";
+ @WrapForJNI private static final String AVC = "video/avc";
+ @WrapForJNI private static final String VORBIS = "audio/vorbis";
+ @WrapForJNI private static final String VP8 = "video/x-vnd.on2.vp8";
+ @WrapForJNI private static final String VP9 = "video/x-vnd.on2.vp9";
+ @WrapForJNI private static final String OPUS = "audio/opus";
+ @WrapForJNI private static final String FLAC = "audio/flac";
+
+ public static final ArrayList<MediaDrmProxy> sProxyList = new ArrayList<MediaDrmProxy>();
+
+ // A flag to avoid using the native object that has been destroyed.
+ private boolean mDestroyed;
+ private GeckoMediaDrm mImpl;
+ private String mDrmStubId;
+
+ private static boolean isSystemSupported() {
+ // Support versions >= Marshmallow
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ if (DEBUG)
+ Log.d(LOGTAG, "System Not supported !!, current SDK version is " + Build.VERSION.SDK_INT);
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressLint("NewApi")
+ @WrapForJNI
+ public static boolean isSchemeSupported(final String keySystem) {
+ if (!isSystemSupported()) {
+ return false;
+ }
+ if (keySystem.equals(WIDEVINE_KEY_SYSTEM)) {
+ return MediaDrm.isCryptoSchemeSupported(WIDEVINE_SCHEME_UUID)
+ && MediaCrypto.isCryptoSchemeSupported(WIDEVINE_SCHEME_UUID);
+ }
+ if (DEBUG) Log.d(LOGTAG, "isSchemeSupported key sytem = " + keySystem);
+ return false;
+ }
+
+ @SuppressLint("NewApi")
+ @WrapForJNI
+ public static boolean IsCryptoSchemeSupported(final String keySystem, final String container) {
+ if (!isSystemSupported()) {
+ return false;
+ }
+ if (keySystem.equals(WIDEVINE_KEY_SYSTEM)) {
+ return MediaDrm.isCryptoSchemeSupported(WIDEVINE_SCHEME_UUID, container);
+ }
+ if (DEBUG)
+ Log.d(LOGTAG, "cannot decrypt key sytem = " + keySystem + ", container = " + container);
+ return false;
+ }
+
+ // Interface for callback to native.
+ public interface Callbacks {
+ void onSessionCreated(int createSessionToken, int promiseId, byte[] sessionId, byte[] request);
+
+ void onSessionUpdated(int promiseId, byte[] sessionId);
+
+ void onSessionClosed(int promiseId, byte[] sessionId);
+
+ void onSessionMessage(byte[] sessionId, int sessionMessageType, byte[] request);
+
+ void onSessionError(byte[] sessionId, String message);
+
+ // MediaDrm.KeyStatus is available in API level 23(M)
+ // https://developer.android.com/reference/android/media/MediaDrm.KeyStatus.html
+ // For compatibility between L and M above, we'll unwrap the KeyStatus structure
+ // and store the keyid and status into SessionKeyInfo and pass to native(MediaDrmCDMProxy).
+ void onSessionBatchedKeyChanged(byte[] sessionId, SessionKeyInfo[] keyInfos);
+
+ void onRejectPromise(int promiseId, String message);
+ } // Callbacks
+
+ public static class NativeMediaDrmProxyCallbacks extends JNIObject implements Callbacks {
+ @WrapForJNI(calledFrom = "gecko")
+ NativeMediaDrmProxyCallbacks() {}
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onSessionCreated(
+ int createSessionToken, int promiseId, byte[] sessionId, byte[] request);
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onSessionUpdated(int promiseId, byte[] sessionId);
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onSessionClosed(int promiseId, byte[] sessionId);
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onSessionMessage(byte[] sessionId, int sessionMessageType, byte[] request);
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onSessionError(byte[] sessionId, String message);
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onSessionBatchedKeyChanged(byte[] sessionId, SessionKeyInfo[] keyInfos);
+
+ @Override
+ @WrapForJNI(dispatchTo = "gecko")
+ public native void onRejectPromise(int promiseId, String message);
+
+ @Override // JNIObject
+ protected void disposeNative() {
+ throw new UnsupportedOperationException();
+ }
+ } // NativeMediaDrmProxyCallbacks
+
+ // A proxy to callback from LocalMediaDrmBridge to native instance.
+ public static class MediaDrmProxyCallbacks implements GeckoMediaDrm.Callbacks {
+ private final Callbacks mNativeCallbacks;
+ private final MediaDrmProxy mProxy;
+
+ public MediaDrmProxyCallbacks(final MediaDrmProxy proxy, final Callbacks callbacks) {
+ mNativeCallbacks = callbacks;
+ mProxy = proxy;
+ }
+
+ @Override
+ public void onSessionCreated(
+ final int createSessionToken,
+ final int promiseId,
+ final byte[] sessionId,
+ final byte[] request) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onSessionCreated(createSessionToken, promiseId, sessionId, request);
+ }
+ }
+
+ @Override
+ public void onSessionUpdated(final int promiseId, final byte[] sessionId) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onSessionUpdated(promiseId, sessionId);
+ }
+ }
+
+ @Override
+ public void onSessionClosed(final int promiseId, final byte[] sessionId) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onSessionClosed(promiseId, sessionId);
+ }
+ }
+
+ @Override
+ public void onSessionMessage(
+ final byte[] sessionId, final int sessionMessageType, final byte[] request) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onSessionMessage(sessionId, sessionMessageType, request);
+ }
+ }
+
+ @Override
+ public void onSessionError(final byte[] sessionId, final String message) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onSessionError(sessionId, message);
+ }
+ }
+
+ @Override
+ public void onSessionBatchedKeyChanged(
+ final byte[] sessionId, final SessionKeyInfo[] keyInfos) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onSessionBatchedKeyChanged(sessionId, keyInfos);
+ }
+ }
+
+ @Override
+ public void onRejectPromise(final int promiseId, final String message) {
+ if (!mProxy.isDestroyed()) {
+ mNativeCallbacks.onRejectPromise(promiseId, message);
+ }
+ }
+ } // MediaDrmProxyCallbacks
+
+ public boolean isDestroyed() {
+ return mDestroyed;
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ public static MediaDrmProxy create(final String keySystem, final Callbacks nativeCallbacks) {
+ final MediaDrmProxy proxy = new MediaDrmProxy(keySystem, nativeCallbacks);
+ return proxy;
+ }
+
+ MediaDrmProxy(final String keySystem, final Callbacks nativeCallbacks) {
+ if (DEBUG) Log.d(LOGTAG, "Constructing MediaDrmProxy");
+ try {
+ mDrmStubId = UUID.randomUUID().toString();
+ final IMediaDrmBridge remoteBridge =
+ RemoteManager.getInstance().createRemoteMediaDrmBridge(keySystem, mDrmStubId);
+ mImpl = new RemoteMediaDrmBridge(remoteBridge);
+ mImpl.setCallbacks(new MediaDrmProxyCallbacks(this, nativeCallbacks));
+ sProxyList.add(this);
+ } catch (final Exception e) {
+ Log.e(LOGTAG, "Constructing MediaDrmProxy ... error", e);
+ }
+ }
+
+ @WrapForJNI
+ private void createSession(
+ final int createSessionToken,
+ final int promiseId,
+ final String initDataType,
+ final byte[] initData) {
+ if (DEBUG) Log.d(LOGTAG, "createSession, promiseId = " + promiseId);
+ mImpl.createSession(createSessionToken, promiseId, initDataType, initData);
+ }
+
+ @WrapForJNI
+ private void updateSession(final int promiseId, final String sessionId, final byte[] response) {
+ if (DEBUG)
+ Log.d(LOGTAG, "updateSession, primiseId(" + promiseId + "sessionId(" + sessionId + ")");
+ mImpl.updateSession(promiseId, sessionId, response);
+ }
+
+ @WrapForJNI
+ private void closeSession(final int promiseId, final String sessionId) {
+ if (DEBUG)
+ Log.d(LOGTAG, "closeSession, primiseId(" + promiseId + "sessionId(" + sessionId + ")");
+ mImpl.closeSession(promiseId, sessionId);
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ private String getStubId() {
+ return mDrmStubId;
+ }
+
+ @WrapForJNI
+ public boolean setServerCertificate(final byte[] cert) {
+ try {
+ mImpl.setServerCertificate(cert);
+ return true;
+ } catch (final RuntimeException e) {
+ return false;
+ }
+ }
+
+ // Get corresponding MediaCrypto object by a generated UUID for MediaCodec.
+ // Will be called on MediaFormatReader's TaskQueue.
+ @WrapForJNI
+ public static MediaCrypto getMediaCrypto(final String stubId) {
+ for (final MediaDrmProxy proxy : sProxyList) {
+ if (proxy.getStubId().equals(stubId)) {
+ return proxy.getMediaCryptoFromBridge();
+ }
+ }
+ if (DEBUG) Log.d(LOGTAG, " NULL crytpo ");
+ return null;
+ }
+
+ @WrapForJNI // Called when natvie object is destroyed.
+ private void destroy() {
+ if (DEBUG) Log.d(LOGTAG, "destroy!! Native object is destroyed.");
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+ release();
+ }
+
+ private void release() {
+ if (DEBUG) Log.d(LOGTAG, "release");
+ sProxyList.remove(this);
+ mImpl.release();
+ }
+
+ private MediaCrypto getMediaCryptoFromBridge() {
+ return mImpl != null ? mImpl.getMediaCrypto() : null;
+ }
+}