Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8240d923 authored by Andreas Huber's avatar Andreas Huber
Browse files

New Crypto JAVA class to facilitate decryption via MediaCodec.

Change-Id: Ic4e395faa84f003793c2804f2badabab9e7f1034
related-to-bug: 6275919
parent cf15200d
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

/**
 * Crypto class can be used in conjunction with MediaCodec to decode
 * encrypted media data.
 * @hide
*/
public final class Crypto {
    public static final native boolean isCryptoSchemeSupported(byte[] uuid);

    public Crypto(byte[] uuid, byte[] initData) {
        native_setup(uuid, initData);
    }

    public final native boolean requiresSecureDecoderComponent(String mime);

    @Override
    protected void finalize() {
        native_finalize();
    }

    public native final void release();
    private static native final void native_init();
    private native final void native_setup(byte[] uuid, byte[] initData);
    private native final void native_finalize();

    static {
        System.loadLibrary("media_jni");
        native_init();
    }

    private int mNativeContext;
}
+9 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.media;

import android.media.Crypto;
import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -25,8 +26,7 @@ import java.util.Map;
 * encoder/decoder components.
 * @hide
*/
public class MediaCodec
{
final public class MediaCodec {
    /** Per buffer metadata includes an offset and size specifying
        the range of valid data in the associated codec buffer.
    */
@@ -113,11 +113,14 @@ public class MediaCodec
     *
     *  @param surface Specify a surface on which to render the output of this
     *                 decoder.
     *  @param crypto  Specify a crypto object to facilitate secure decryption
     *                 of the media data.
     *  @param flags   Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
     *                 component as an encoder.
    */
    public void configure(
            Map<String, Object> format, Surface surface, int flags) {
            Map<String, Object> format,
            Surface surface, Crypto crypto, int flags) {
        String[] keys = null;
        Object[] values = null;

@@ -133,11 +136,12 @@ public class MediaCodec
            }
        }

        native_configure(keys, values, surface, flags);
        native_configure(keys, values, surface, crypto, flags);
    }

    private native final void native_configure(
            String[] keys, Object[] values, Surface surface, int flags);
            String[] keys, Object[] values,
            Surface surface, Crypto crypto, int flags);

    /** After successfully configuring the component, call start. On return
     *  you can query the component for its input/output buffers.
+1 −2
Original line number Diff line number Diff line
@@ -23,8 +23,7 @@ import java.util.Map;
 * MediaExtractor
 * @hide
*/
public class MediaExtractor
{
final public class MediaExtractor {
    public MediaExtractor(String path) {
        native_setup(path);
    }
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    android_media_Crypto.cpp \
    android_media_MediaCodec.cpp \
    android_media_MediaCodecList.cpp \
    android_media_MediaExtractor.cpp \
+291 −0
Original line number Diff line number Diff line
/*
 * Copyright 2012, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "Crypto-JNI"
#include <utils/Log.h>

#include "android_media_Crypto.h"

#include "android_runtime/AndroidRuntime.h"
#include "jni.h"
#include "JNIHelp.h"

#include <binder/IServiceManager.h>
#include <media/ICrypto.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>

namespace android {

struct fields_t {
    jfieldID context;
};

static fields_t gFields;

static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
    return (JCrypto *)env->GetIntField(thiz, gFields.context);
}

JCrypto::JCrypto(
        JNIEnv *env, jobject thiz,
        const uint8_t uuid[16], const void *initData, size_t initSize) {
    mObject = env->NewWeakGlobalRef(thiz);

    mCrypto = MakeCrypto(uuid, initData, initSize);
}

JCrypto::~JCrypto() {
    mCrypto.clear();

    JNIEnv *env = AndroidRuntime::getJNIEnv();

    env->DeleteWeakGlobalRef(mObject);
    mObject = NULL;
}

// static
sp<ICrypto> JCrypto::MakeCrypto() {
    sp<IServiceManager> sm = defaultServiceManager();

    sp<IBinder> binder =
        sm->getService(String16("media.player"));

    sp<IMediaPlayerService> service =
        interface_cast<IMediaPlayerService>(binder);

    if (service == NULL) {
        return NULL;
    }

    sp<ICrypto> crypto = service->makeCrypto();

    if (crypto == NULL || crypto->initCheck() != OK) {
        return NULL;
    }

    return crypto;
}

// static
sp<ICrypto> JCrypto::MakeCrypto(
        const uint8_t uuid[16], const void *initData, size_t initSize) {
    sp<ICrypto> crypto = MakeCrypto();

    if (crypto == NULL) {
        return NULL;
    }

    status_t err = crypto->createPlugin(uuid, initData, initSize);

    if (err != OK) {
        return NULL;
    }

    return crypto;
}

bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
    if (mCrypto == NULL) {
        return false;
    }

    return mCrypto->requiresSecureDecoderComponent(mime);
}

// static
bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
    sp<ICrypto> crypto = MakeCrypto();

    if (crypto == NULL) {
        return false;
    }

    return crypto->isCryptoSchemeSupported(uuid);
}

status_t JCrypto::initCheck() const {
    return mCrypto == NULL ? NO_INIT : OK;
}

// static
sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
    jclass clazz = env->FindClass("android/media/Crypto");
    CHECK(clazz != NULL);

    if (!env->IsInstanceOf(obj, clazz)) {
        return NULL;
    }

    sp<JCrypto> jcrypto = getCrypto(env, obj);

    if (jcrypto == NULL) {
        return NULL;
    }

    return jcrypto->mCrypto;
}

}  // namespace android

using namespace android;

static sp<JCrypto> setCrypto(
        JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
    sp<JCrypto> old = (JCrypto *)env->GetIntField(thiz, gFields.context);
    if (crypto != NULL) {
        crypto->incStrong(thiz);
    }
    if (old != NULL) {
        old->decStrong(thiz);
    }
    env->SetIntField(thiz, gFields.context, (int)crypto.get());

    return old;
}

static void android_media_Crypto_release(JNIEnv *env, jobject thiz) {
    setCrypto(env, thiz, NULL);
}

static void android_media_Crypto_native_init(JNIEnv *env) {
    jclass clazz = env->FindClass("android/media/Crypto");
    CHECK(clazz != NULL);

    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
    CHECK(gFields.context != NULL);
}

static void android_media_Crypto_native_setup(
        JNIEnv *env, jobject thiz,
        jbyteArray uuidObj, jbyteArray initDataObj) {
    jsize uuidLength = env->GetArrayLength(uuidObj);

    if (uuidLength != 16) {
        jniThrowException(
                env,
                "java/lang/IllegalArgumentException",
                NULL);
        return;
    }

    jboolean isCopy;
    jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);

    jsize initDataLength = env->GetArrayLength(initDataObj);
    jbyte *initData = env->GetByteArrayElements(initDataObj, &isCopy);

    sp<JCrypto> crypto = new JCrypto(
            env, thiz, (const uint8_t *)uuid, initData, initDataLength);

    status_t err = crypto->initCheck();

    env->ReleaseByteArrayElements(initDataObj, initData, 0);
    initData = NULL;

    env->ReleaseByteArrayElements(uuidObj, uuid, 0);
    uuid = NULL;

    if (err != OK) {
        jniThrowException(
                env,
                "java/io/IOException",
                "Failed to instantiate crypto object.");
        return;
    }

    setCrypto(env,thiz, crypto);
}

static void android_media_Crypto_native_finalize(
        JNIEnv *env, jobject thiz) {
    android_media_Crypto_release(env, thiz);
}

static jboolean android_media_Crypto_isCryptoSchemeSupported(
        JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
    jsize uuidLength = env->GetArrayLength(uuidObj);

    if (uuidLength != 16) {
        jniThrowException(
                env,
                "java/lang/IllegalArgumentException",
                NULL);
        return false;
    }

    jboolean isCopy;
    jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);

    bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);

    env->ReleaseByteArrayElements(uuidObj, uuid, 0);
    uuid = NULL;

    return result;
}

static jboolean android_media_Crypto_requiresSecureDecoderComponent(
        JNIEnv *env, jobject thiz, jstring mimeObj) {
    if (mimeObj == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return false;
    }

    sp<JCrypto> crypto = getCrypto(env, thiz);

    if (crypto == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return false;
    }

    const char *mime = env->GetStringUTFChars(mimeObj, NULL);

    if (mime == NULL) {
        return false;
    }

    bool result = crypto->requiresSecureDecoderComponent(mime);

    env->ReleaseStringUTFChars(mimeObj, mime);
    mime = NULL;

    return result;
}

static JNINativeMethod gMethods[] = {
    { "release", "()V", (void *)android_media_Crypto_release },
    { "native_init", "()V", (void *)android_media_Crypto_native_init },

    { "native_setup", "([B[B)V",
      (void *)android_media_Crypto_native_setup },

    { "native_finalize", "()V",
      (void *)android_media_Crypto_native_finalize },

    { "isCryptoSchemeSupported", "([B)Z",
      (void *)android_media_Crypto_isCryptoSchemeSupported },

    { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
      (void *)android_media_Crypto_requiresSecureDecoderComponent },
};

int register_android_media_Crypto(JNIEnv *env) {
    return AndroidRuntime::registerNativeMethods(env,
                "android/media/Crypto", gMethods, NELEM(gMethods));
}
Loading