1 /*
2  * Copyright 2012, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MediaCrypto-JNI"
19 #include <utils/Log.h>
20 
21 #include "android_media_MediaCrypto.h"
22 
23 #include "android_runtime/AndroidRuntime.h"
24 #include "jni.h"
25 #include "JNIHelp.h"
26 
27 #include <binder/IServiceManager.h>
28 #include <cutils/properties.h>
29 #include <media/ICrypto.h>
30 #include <media/IMediaDrmService.h>
31 #include <media/stagefright/foundation/ADebug.h>
32 
33 namespace android {
34 
35 struct fields_t {
36     jfieldID context;
37 };
38 
39 static fields_t gFields;
40 
getCrypto(JNIEnv * env,jobject thiz)41 static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
42     return (JCrypto *)env->GetLongField(thiz, gFields.context);
43 }
44 
JCrypto(JNIEnv * env,jobject thiz,const uint8_t uuid[16],const void * initData,size_t initSize)45 JCrypto::JCrypto(
46         JNIEnv *env, jobject thiz,
47         const uint8_t uuid[16], const void *initData, size_t initSize) {
48     mObject = env->NewWeakGlobalRef(thiz);
49 
50     mCrypto = MakeCrypto(uuid, initData, initSize);
51 }
52 
~JCrypto()53 JCrypto::~JCrypto() {
54     mCrypto.clear();
55 
56     JNIEnv *env = AndroidRuntime::getJNIEnv();
57 
58     env->DeleteWeakGlobalRef(mObject);
59     mObject = NULL;
60 }
61 
62 // static
MakeCrypto()63 sp<ICrypto> JCrypto::MakeCrypto() {
64     sp<IServiceManager> sm = defaultServiceManager();
65 
66     sp<IBinder> binder = sm->getService(String16("media.drm"));
67     sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
68     if (service == NULL) {
69         return NULL;
70     }
71 
72     sp<ICrypto> crypto = service->makeCrypto();
73     if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
74         return NULL;
75     }
76 
77     return crypto;
78 }
79 
80 // static
MakeCrypto(const uint8_t uuid[16],const void * initData,size_t initSize)81 sp<ICrypto> JCrypto::MakeCrypto(
82         const uint8_t uuid[16], const void *initData, size_t initSize) {
83     sp<ICrypto> crypto = MakeCrypto();
84 
85     if (crypto == NULL) {
86         return NULL;
87     }
88 
89     status_t err = crypto->createPlugin(uuid, initData, initSize);
90 
91     if (err != OK) {
92         return NULL;
93     }
94 
95     return crypto;
96 }
97 
requiresSecureDecoderComponent(const char * mime) const98 bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
99     if (mCrypto == NULL) {
100         return false;
101     }
102 
103     return mCrypto->requiresSecureDecoderComponent(mime);
104 }
105 
106 // static
IsCryptoSchemeSupported(const uint8_t uuid[16])107 bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
108     sp<ICrypto> crypto = MakeCrypto();
109 
110     if (crypto == NULL) {
111         return false;
112     }
113 
114     return crypto->isCryptoSchemeSupported(uuid);
115 }
116 
initCheck() const117 status_t JCrypto::initCheck() const {
118     return mCrypto == NULL ? NO_INIT : OK;
119 }
120 
121 // static
GetCrypto(JNIEnv * env,jobject obj)122 sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
123     jclass clazz = env->FindClass("android/media/MediaCrypto");
124     CHECK(clazz != NULL);
125 
126     if (!env->IsInstanceOf(obj, clazz)) {
127         return NULL;
128     }
129 
130     sp<JCrypto> jcrypto = getCrypto(env, obj);
131 
132     if (jcrypto == NULL) {
133         return NULL;
134     }
135 
136     return jcrypto->mCrypto;
137 }
138 
139 // JNI conversion utilities
JByteArrayToVector(JNIEnv * env,jbyteArray const & byteArray)140 static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
141     Vector<uint8_t> vector;
142     size_t length = env->GetArrayLength(byteArray);
143     vector.insertAt((size_t)0, length);
144     env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
145     return vector;
146 }
147 
148 }  // namespace android
149 
150 using namespace android;
151 
setCrypto(JNIEnv * env,jobject thiz,const sp<JCrypto> & crypto)152 static sp<JCrypto> setCrypto(
153         JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
154     sp<JCrypto> old = (JCrypto *)env->GetLongField(thiz, gFields.context);
155     if (crypto != NULL) {
156         crypto->incStrong(thiz);
157     }
158     if (old != NULL) {
159         old->decStrong(thiz);
160     }
161     env->SetLongField(thiz, gFields.context, (jlong)crypto.get());
162 
163     return old;
164 }
165 
android_media_MediaCrypto_release(JNIEnv * env,jobject thiz)166 static void android_media_MediaCrypto_release(JNIEnv *env, jobject thiz) {
167     setCrypto(env, thiz, NULL);
168 }
169 
android_media_MediaCrypto_native_init(JNIEnv * env)170 static void android_media_MediaCrypto_native_init(JNIEnv *env) {
171     jclass clazz = env->FindClass("android/media/MediaCrypto");
172     CHECK(clazz != NULL);
173 
174     gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
175     CHECK(gFields.context != NULL);
176 }
177 
android_media_MediaCrypto_native_setup(JNIEnv * env,jobject thiz,jbyteArray uuidObj,jbyteArray initDataObj)178 static void android_media_MediaCrypto_native_setup(
179         JNIEnv *env, jobject thiz,
180         jbyteArray uuidObj, jbyteArray initDataObj) {
181     jsize uuidLength = env->GetArrayLength(uuidObj);
182 
183     if (uuidLength != 16) {
184         jniThrowException(
185                 env,
186                 "java/lang/IllegalArgumentException",
187                 NULL);
188         return;
189     }
190 
191     jboolean isCopy;
192     jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
193 
194     jsize initDataLength = 0;
195     jbyte *initData = NULL;
196 
197     if (initDataObj != NULL) {
198         initDataLength = env->GetArrayLength(initDataObj);
199         initData = env->GetByteArrayElements(initDataObj, &isCopy);
200     }
201 
202     sp<JCrypto> crypto = new JCrypto(
203             env, thiz, (const uint8_t *)uuid, initData, initDataLength);
204 
205     status_t err = crypto->initCheck();
206 
207     if (initDataObj != NULL) {
208         env->ReleaseByteArrayElements(initDataObj, initData, 0);
209         initData = NULL;
210     }
211 
212     env->ReleaseByteArrayElements(uuidObj, uuid, 0);
213     uuid = NULL;
214 
215     if (err != OK) {
216         jniThrowException(
217                 env,
218                 "android/media/MediaCryptoException",
219                 "Failed to instantiate crypto object.");
220         return;
221     }
222 
223     setCrypto(env,thiz, crypto);
224 }
225 
android_media_MediaCrypto_native_finalize(JNIEnv * env,jobject thiz)226 static void android_media_MediaCrypto_native_finalize(
227         JNIEnv *env, jobject thiz) {
228     android_media_MediaCrypto_release(env, thiz);
229 }
230 
android_media_MediaCrypto_isCryptoSchemeSupportedNative(JNIEnv * env,jobject,jbyteArray uuidObj)231 static jboolean android_media_MediaCrypto_isCryptoSchemeSupportedNative(
232         JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj) {
233     jsize uuidLength = env->GetArrayLength(uuidObj);
234 
235     if (uuidLength != 16) {
236         jniThrowException(
237                 env,
238                 "java/lang/IllegalArgumentException",
239                 NULL);
240         return JNI_FALSE;
241     }
242 
243     jboolean isCopy;
244     jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
245 
246     bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);
247 
248     env->ReleaseByteArrayElements(uuidObj, uuid, 0);
249     uuid = NULL;
250 
251     return result ? JNI_TRUE : JNI_FALSE;
252 }
253 
android_media_MediaCrypto_requiresSecureDecoderComponent(JNIEnv * env,jobject thiz,jstring mimeObj)254 static jboolean android_media_MediaCrypto_requiresSecureDecoderComponent(
255         JNIEnv *env, jobject thiz, jstring mimeObj) {
256     if (mimeObj == NULL) {
257         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
258         return JNI_FALSE;
259     }
260 
261     sp<JCrypto> crypto = getCrypto(env, thiz);
262 
263     if (crypto == NULL) {
264         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
265         return JNI_FALSE;
266     }
267 
268     const char *mime = env->GetStringUTFChars(mimeObj, NULL);
269 
270     if (mime == NULL) {
271         return JNI_FALSE;
272     }
273 
274     bool result = crypto->requiresSecureDecoderComponent(mime);
275 
276     env->ReleaseStringUTFChars(mimeObj, mime);
277     mime = NULL;
278 
279     return result ? JNI_TRUE : JNI_FALSE;
280 }
281 
android_media_MediaCrypto_setMediaDrmSession(JNIEnv * env,jobject thiz,jbyteArray jsessionId)282 static void android_media_MediaCrypto_setMediaDrmSession(
283         JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
284     if (jsessionId == NULL) {
285         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
286         return;
287     }
288 
289     sp<ICrypto> crypto = JCrypto::GetCrypto(env, thiz);
290 
291     if (crypto == NULL) {
292         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
293         return;
294     }
295 
296     Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
297 
298     status_t err = crypto->setMediaDrmSession(sessionId);
299 
300     if (err != OK) {
301         String8 msg("setMediaDrmSession failed");
302         if (err == ERROR_DRM_SESSION_NOT_OPENED) {
303             msg += ": session not opened";
304         } else if (err == ERROR_UNSUPPORTED) {
305             msg += ": not supported by this crypto scheme";
306         } else if (err == NO_INIT) {
307             msg += ": crypto plugin not initialized";
308         } else {
309             msg.appendFormat(": general failure (%d)", err);
310         }
311         jniThrowException(env, "android/media/MediaCryptoException", msg.string());
312     }
313 }
314 
315 static const JNINativeMethod gMethods[] = {
316     { "release", "()V", (void *)android_media_MediaCrypto_release },
317     { "native_init", "()V", (void *)android_media_MediaCrypto_native_init },
318 
319     { "native_setup", "([B[B)V",
320       (void *)android_media_MediaCrypto_native_setup },
321 
322     { "native_finalize", "()V",
323       (void *)android_media_MediaCrypto_native_finalize },
324 
325     { "isCryptoSchemeSupportedNative", "([B)Z",
326       (void *)android_media_MediaCrypto_isCryptoSchemeSupportedNative },
327 
328     { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
329       (void *)android_media_MediaCrypto_requiresSecureDecoderComponent },
330 
331     { "setMediaDrmSession", "([B)V",
332       (void *)android_media_MediaCrypto_setMediaDrmSession },
333 };
334 
register_android_media_Crypto(JNIEnv * env)335 int register_android_media_Crypto(JNIEnv *env) {
336     return AndroidRuntime::registerNativeMethods(env,
337                 "android/media/MediaCrypto", gMethods, NELEM(gMethods));
338 }
339 
340