1 /*
2  * Copyright (C) 2016 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 "android_os_HwBlob"
19 #include <android-base/logging.h>
20 
21 #include "android_os_HwBlob.h"
22 
23 #include "android_os_HwParcel.h"
24 
25 #include <nativehelper/JNIHelp.h>
26 #include <android_runtime/AndroidRuntime.h>
27 #include <hidl/Status.h>
28 #include <nativehelper/ScopedLocalRef.h>
29 #include <nativehelper/ScopedPrimitiveArray.h>
30 
31 #include "core_jni_helpers.h"
32 
33 using android::AndroidRuntime;
34 using android::hardware::hidl_string;
35 
36 #define PACKAGE_PATH    "android/os"
37 #define CLASS_NAME      "HwBlob"
38 #define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
39 
40 namespace android {
41 
42 static struct fields_t {
43     jfieldID contextID;
44     jmethodID constructID;
45 
46 } gFields;
47 
48 // static
InitClass(JNIEnv * env)49 void JHwBlob::InitClass(JNIEnv *env) {
50     ScopedLocalRef<jclass> clazz(
51             env, FindClassOrDie(env, CLASS_PATH));
52 
53     gFields.contextID =
54         GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
55 
56     gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V");
57 }
58 
59 // static
SetNativeContext(JNIEnv * env,jobject thiz,const sp<JHwBlob> & context)60 sp<JHwBlob> JHwBlob::SetNativeContext(
61         JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) {
62     sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
63 
64     if (context != nullptr) {
65         context->incStrong(nullptr /* id */);
66     }
67 
68     if (old != nullptr) {
69         old->decStrong(nullptr /* id */);
70     }
71 
72     env->SetLongField(thiz, gFields.contextID, (long)context.get());
73 
74     return old;
75 }
76 
77 // static
GetNativeContext(JNIEnv * env,jobject thiz)78 sp<JHwBlob> JHwBlob::GetNativeContext(JNIEnv *env, jobject thiz) {
79     return (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
80 }
81 
JHwBlob(JNIEnv * env,jobject thiz,size_t size)82 JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size)
83     : mBuffer(nullptr),
84       mSize(size),
85       mOwnsBuffer(true),
86       mHandle(0) {
87     if (size > 0) {
88         mBuffer = malloc(size);
89     }
90 }
91 
~JHwBlob()92 JHwBlob::~JHwBlob() {
93     if (mOwnsBuffer) {
94         free(mBuffer);
95         mBuffer = nullptr;
96     }
97 }
98 
setTo(const void * ptr,size_t handle)99 void JHwBlob::setTo(const void *ptr, size_t handle) {
100     CHECK_EQ(mSize, 0u);
101     CHECK(mBuffer == nullptr);
102 
103     mBuffer = const_cast<void *>(ptr);
104     mSize = SIZE_MAX;  // XXX
105     mOwnsBuffer = false;
106     mHandle = handle;
107 }
108 
getHandle(size_t * handle) const109 status_t JHwBlob::getHandle(size_t *handle) const {
110     if (mOwnsBuffer) {
111         return INVALID_OPERATION;
112     }
113 
114     *handle = mHandle;
115 
116     return OK;
117 }
118 
read(size_t offset,void * data,size_t size) const119 status_t JHwBlob::read(size_t offset, void *data, size_t size) const {
120     if (offset + size > mSize) {
121         return -ERANGE;
122     }
123 
124     memcpy(data, (const uint8_t *)mBuffer + offset, size);
125 
126     return OK;
127 }
128 
write(size_t offset,const void * data,size_t size)129 status_t JHwBlob::write(size_t offset, const void *data, size_t size) {
130     if (offset + size > mSize) {
131         return -ERANGE;
132     }
133 
134     memcpy((uint8_t *)mBuffer + offset, data, size);
135 
136     return OK;
137 }
138 
getString(size_t offset,const hidl_string ** s) const139 status_t JHwBlob::getString(size_t offset, const hidl_string **s) const {
140     if ((offset + sizeof(hidl_string)) > mSize) {
141         return -ERANGE;
142     }
143 
144     *s = reinterpret_cast<const hidl_string *>(
145             (const uint8_t *)mBuffer + offset);
146 
147     return OK;
148 }
149 
data() const150 const void *JHwBlob::data() const {
151     return mBuffer;
152 }
153 
data()154 void *JHwBlob::data() {
155     return mBuffer;
156 }
157 
size() const158 size_t JHwBlob::size() const {
159     return mSize;
160 }
161 
putBlob(size_t offset,const sp<JHwBlob> & blob)162 status_t JHwBlob::putBlob(size_t offset, const sp<JHwBlob> &blob) {
163     size_t index = mSubBlobs.add();
164     BlobInfo *info = &mSubBlobs.editItemAt(index);
165 
166     info->mOffset = offset;
167     info->mBlob = blob;
168 
169     const void *data = blob->data();
170 
171     return write(offset, &data, sizeof(data));
172 }
173 
writeToParcel(hardware::Parcel * parcel) const174 status_t JHwBlob::writeToParcel(hardware::Parcel *parcel) const {
175     size_t handle;
176     status_t err = parcel->writeBuffer(data(), size(), &handle);
177 
178     if (err != OK) {
179         return err;
180     }
181 
182     for (size_t i = 0; i < mSubBlobs.size(); ++i) {
183         const BlobInfo &info = mSubBlobs[i];
184 
185         err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset);
186 
187         if (err != OK) {
188             return err;
189         }
190     }
191 
192     return OK;
193 }
194 
writeEmbeddedToParcel(hardware::Parcel * parcel,size_t parentHandle,size_t parentOffset) const195 status_t JHwBlob::writeEmbeddedToParcel(
196         hardware::Parcel *parcel,
197         size_t parentHandle,
198         size_t parentOffset) const {
199     size_t handle;
200     status_t err = parcel->writeEmbeddedBuffer(
201             data(), size(), &handle, parentHandle, parentOffset);
202 
203     if (err != OK) {
204         return err;
205     }
206 
207     for (size_t i = 0; i < mSubBlobs.size(); ++i) {
208         const BlobInfo &info = mSubBlobs[i];
209 
210         err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset);
211 
212         if (err != OK) {
213             return err;
214         }
215     }
216 
217     return OK;
218 }
219 
220 // static
NewObject(JNIEnv * env,const void * ptr,size_t handle)221 jobject JHwBlob::NewObject(JNIEnv *env, const void *ptr, size_t handle) {
222     jobject obj = JHwBlob::NewObject(env, 0 /* size */);
223     JHwBlob::GetNativeContext(env, obj)->setTo(ptr, handle);
224 
225     return obj;
226 }
227 
228 // static
NewObject(JNIEnv * env,size_t size)229 jobject JHwBlob::NewObject(JNIEnv *env, size_t size) {
230     ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
231 
232     jmethodID constructID =
233         GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V");
234 
235     // XXX Again cannot refer to gFields.constructID because InitClass may
236     // not have been called yet.
237 
238     return env->NewObject(clazz.get(), constructID, size);
239 }
240 
241 }  // namespace android
242 
243 ////////////////////////////////////////////////////////////////////////////////
244 
245 using namespace android;
246 
releaseNativeContext(void * nativeContext)247 static void releaseNativeContext(void *nativeContext) {
248     sp<JHwBlob> parcel = (JHwBlob *)nativeContext;
249 
250     if (parcel != nullptr) {
251         parcel->decStrong(nullptr /* id */);
252     }
253 }
254 
JHwBlob_native_init(JNIEnv * env)255 static jlong JHwBlob_native_init(JNIEnv *env) {
256     JHwBlob::InitClass(env);
257 
258     return reinterpret_cast<jlong>(&releaseNativeContext);
259 }
260 
JHwBlob_native_setup(JNIEnv * env,jobject thiz,jint size)261 static void JHwBlob_native_setup(
262         JNIEnv *env, jobject thiz, jint size) {
263     sp<JHwBlob> context = new JHwBlob(env, thiz, size);
264 
265     JHwBlob::SetNativeContext(env, thiz, context);
266 }
267 
268 #define DEFINE_BLOB_GETTER(Suffix,Type)                                        \
269 static Type JHwBlob_native_get ## Suffix(                                      \
270         JNIEnv *env, jobject thiz, jlong offset) {                             \
271     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);                   \
272                                                                                \
273     Type x;                                                                    \
274     status_t err = blob->read(offset, &x, sizeof(x));                          \
275                                                                                \
276     if (err != OK) {                                                           \
277         signalExceptionForError(env, err);                                     \
278         return 0;                                                              \
279     }                                                                          \
280                                                                                \
281     return x;                                                                  \
282 }
283 
DEFINE_BLOB_GETTER(Int8,jbyte)284 DEFINE_BLOB_GETTER(Int8,jbyte)
285 DEFINE_BLOB_GETTER(Int16,jshort)
286 DEFINE_BLOB_GETTER(Int32,jint)
287 DEFINE_BLOB_GETTER(Int64,jlong)
288 DEFINE_BLOB_GETTER(Float,jfloat)
289 DEFINE_BLOB_GETTER(Double,jdouble)
290 
291 static jboolean JHwBlob_native_getBool(
292         JNIEnv *env, jobject thiz, jlong offset) {
293     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
294 
295     bool x;
296     status_t err = blob->read(offset, &x, sizeof(x));
297 
298     if (err != OK) {
299         signalExceptionForError(env, err);
300         return 0;
301     }
302 
303     return (jboolean)x;
304 }
305 
JHwBlob_native_getString(JNIEnv * env,jobject thiz,jlong offset)306 static jstring JHwBlob_native_getString(
307         JNIEnv *env, jobject thiz, jlong offset) {
308     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
309 
310     const hidl_string *s;
311     status_t err = blob->getString(offset, &s);
312 
313     if (err != OK) {
314         signalExceptionForError(env, err);
315         return nullptr;
316     }
317 
318     return env->NewStringUTF(s->c_str());
319 }
320 
321 #define DEFINE_BLOB_ARRAY_COPIER(Suffix,Type,NewType)                          \
322 static void JHwBlob_native_copyTo ## Suffix ## Array(                          \
323         JNIEnv *env,                                                           \
324         jobject thiz,                                                          \
325         jlong offset,                                                          \
326         Type ## Array array,                                                   \
327         jint size) {                                                           \
328     if (array == nullptr) {                                                    \
329         jniThrowException(env, "java/lang/NullPointerException", nullptr);     \
330         return;                                                                \
331     }                                                                          \
332                                                                                \
333     if (env->GetArrayLength(array) < size) {                                   \
334         signalExceptionForError(env, BAD_VALUE);                               \
335         return;                                                                \
336     }                                                                          \
337                                                                                \
338     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);                   \
339                                                                                \
340     if ((offset + size * sizeof(Type)) > blob->size()) {                       \
341         signalExceptionForError(env, -ERANGE);                                 \
342         return;                                                                \
343     }                                                                          \
344                                                                                \
345     env->Set ## NewType ## ArrayRegion(                                        \
346             array,                                                             \
347             0 /* start */,                                                     \
348             size,                                                              \
349             reinterpret_cast<const Type *>(                                    \
350                 static_cast<const uint8_t *>(blob->data()) + offset));         \
351 }
352 
DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)353 DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)
354 DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short)
355 DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int)
356 DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long)
357 DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float)
358 DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double)
359 
360 static void JHwBlob_native_copyToBoolArray(
361         JNIEnv *env,
362         jobject thiz,
363         jlong offset,
364         jbooleanArray array,
365         jint size) {
366     if (array == nullptr) {
367         jniThrowException(env, "java/lang/NullPointerException", nullptr);
368         return;
369     }
370 
371     if (env->GetArrayLength(array) < size) {
372         signalExceptionForError(env, BAD_VALUE);
373         return;
374     }
375 
376     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
377 
378     if ((offset + size * sizeof(bool)) > blob->size()) {
379         signalExceptionForError(env, -ERANGE);
380         return;
381     }
382 
383     const bool *src =
384         reinterpret_cast<const bool *>(
385                 static_cast<const uint8_t *>(blob->data()) + offset);
386 
387     jboolean *dst = env->GetBooleanArrayElements(array, nullptr /* isCopy */);
388 
389     for (jint i = 0; i < size; ++i) {
390         dst[i] = src[i];
391     }
392 
393     env->ReleaseBooleanArrayElements(array, dst, 0 /* mode */);
394     dst = nullptr;
395 }
396 
397 #define DEFINE_BLOB_PUTTER(Suffix,Type)                                        \
398 static void JHwBlob_native_put ## Suffix(                                      \
399         JNIEnv *env, jobject thiz, jlong offset, Type x) {                     \
400                                                                                \
401     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);                   \
402                                                                                \
403     status_t err = blob->write(offset, &x, sizeof(x));                         \
404                                                                                \
405     if (err != OK) {                                                           \
406         signalExceptionForError(env, err);                                     \
407     }                                                                          \
408 }
409 
DEFINE_BLOB_PUTTER(Int8,jbyte)410 DEFINE_BLOB_PUTTER(Int8,jbyte)
411 DEFINE_BLOB_PUTTER(Int16,jshort)
412 DEFINE_BLOB_PUTTER(Int32,jint)
413 DEFINE_BLOB_PUTTER(Int64,jlong)
414 DEFINE_BLOB_PUTTER(Float,jfloat)
415 DEFINE_BLOB_PUTTER(Double,jdouble)
416 
417 static void JHwBlob_native_putBool(
418         JNIEnv *env, jobject thiz, jlong offset, jboolean x) {
419 
420     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
421 
422     bool b = (bool)x;
423     status_t err = blob->write(offset, &b, sizeof(b));
424 
425     if (err != OK) {
426         signalExceptionForError(env, err);
427     }
428 }
429 
JHwBlob_native_putString(JNIEnv * env,jobject thiz,jlong offset,jstring stringObj)430 static void JHwBlob_native_putString(
431         JNIEnv *env, jobject thiz, jlong offset, jstring stringObj) {
432     if (stringObj == nullptr) {
433         jniThrowException(env, "java/lang/NullPointerException", nullptr);
434         return;
435     }
436 
437     const char *s = env->GetStringUTFChars(stringObj, nullptr);
438 
439     if (s == nullptr) {
440         return;
441     }
442 
443     size_t size = strlen(s) + 1;
444     ScopedLocalRef<jobject> subBlobObj(env, JHwBlob::NewObject(env, size));
445     sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, subBlobObj.get());
446     subBlob->write(0 /* offset */, s, size);
447 
448     env->ReleaseStringUTFChars(stringObj, s);
449     s = nullptr;
450 
451     hidl_string tmp;
452     tmp.setToExternal(static_cast<const char *>(subBlob->data()), size - 1);
453 
454     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
455     blob->write(offset, &tmp, sizeof(tmp));
456     blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob);
457 }
458 
459 #define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType)                          \
460 static void JHwBlob_native_put ## Suffix ## Array(                             \
461         JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) {        \
462     Scoped ## NewType ## ArrayRO autoArray(env, array);                        \
463                                                                                \
464     if (array == nullptr) {                                                    \
465         /* NullpointerException already pending */                             \
466         return;                                                                \
467     }                                                                          \
468                                                                                \
469     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);                   \
470                                                                                \
471     status_t err = blob->write(                                                \
472             offset, autoArray.get(), autoArray.size() * sizeof(Type));         \
473                                                                                \
474     if (err != OK) {                                                           \
475         signalExceptionForError(env, err);                                     \
476     }                                                                          \
477 }
478 
DEFINE_BLOB_ARRAY_PUTTER(Int8,jbyte,Byte)479 DEFINE_BLOB_ARRAY_PUTTER(Int8,jbyte,Byte)
480 DEFINE_BLOB_ARRAY_PUTTER(Int16,jshort,Short)
481 DEFINE_BLOB_ARRAY_PUTTER(Int32,jint,Int)
482 DEFINE_BLOB_ARRAY_PUTTER(Int64,jlong,Long)
483 DEFINE_BLOB_ARRAY_PUTTER(Float,jfloat,Float)
484 DEFINE_BLOB_ARRAY_PUTTER(Double,jdouble,Double)
485 
486 static void JHwBlob_native_putBoolArray(
487         JNIEnv *env, jobject thiz, jlong offset, jbooleanArray array) {
488     ScopedBooleanArrayRO autoArray(env, array);
489 
490     if (array == nullptr) {
491         /* NullpointerException already pending */
492         return;
493     }
494 
495     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
496 
497     if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) {
498         signalExceptionForError(env, -ERANGE);
499         return;
500     }
501 
502     const jboolean *src = autoArray.get();
503 
504     bool *dst = reinterpret_cast<bool *>(
505             static_cast<uint8_t *>(blob->data()) + offset);
506 
507     for (size_t i = 0; i < autoArray.size(); ++i) {
508         dst[i] = src[i];
509     }
510 }
511 
JHwBlob_native_putBlob(JNIEnv * env,jobject thiz,jlong offset,jobject blobObj)512 static void JHwBlob_native_putBlob(
513         JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) {
514     if (blobObj == nullptr) {
515         jniThrowException(env, "java/lang/NullPointerException", nullptr);
516         return;
517     }
518 
519     sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
520     sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, blobObj);
521 
522     blob->putBlob(offset, subBlob);
523 }
524 
JHwBlob_native_handle(JNIEnv * env,jobject thiz)525 static jlong JHwBlob_native_handle(JNIEnv *env, jobject thiz) {
526     size_t handle;
527     status_t err = JHwBlob::GetNativeContext(env, thiz)->getHandle(&handle);
528 
529     if (err != OK) {
530         signalExceptionForError(env, err);
531         return 0;
532     }
533 
534     return handle;
535 }
536 
537 static JNINativeMethod gMethods[] = {
538     { "native_init", "()J", (void *)JHwBlob_native_init },
539     { "native_setup", "(I)V", (void *)JHwBlob_native_setup },
540 
541     { "getBool", "(J)Z", (void *)JHwBlob_native_getBool },
542     { "getInt8", "(J)B", (void *)JHwBlob_native_getInt8 },
543     { "getInt16", "(J)S", (void *)JHwBlob_native_getInt16 },
544     { "getInt32", "(J)I", (void *)JHwBlob_native_getInt32 },
545     { "getInt64", "(J)J", (void *)JHwBlob_native_getInt64 },
546     { "getFloat", "(J)F", (void *)JHwBlob_native_getFloat },
547     { "getDouble", "(J)D", (void *)JHwBlob_native_getDouble },
548     { "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString },
549 
550     { "copyToBoolArray", "(J[ZI)V", (void *)JHwBlob_native_copyToBoolArray },
551     { "copyToInt8Array", "(J[BI)V", (void *)JHwBlob_native_copyToInt8Array },
552     { "copyToInt16Array", "(J[SI)V", (void *)JHwBlob_native_copyToInt16Array },
553     { "copyToInt32Array", "(J[II)V", (void *)JHwBlob_native_copyToInt32Array },
554     { "copyToInt64Array", "(J[JI)V", (void *)JHwBlob_native_copyToInt64Array },
555     { "copyToFloatArray", "(J[FI)V", (void *)JHwBlob_native_copyToFloatArray },
556     { "copyToDoubleArray", "(J[DI)V", (void *)JHwBlob_native_copyToDoubleArray },
557 
558     { "putBool", "(JZ)V", (void *)JHwBlob_native_putBool },
559     { "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 },
560     { "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 },
561     { "putInt32", "(JI)V", (void *)JHwBlob_native_putInt32 },
562     { "putInt64", "(JJ)V", (void *)JHwBlob_native_putInt64 },
563     { "putFloat", "(JF)V", (void *)JHwBlob_native_putFloat },
564     { "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble },
565     { "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString },
566 
567     { "putBoolArray", "(J[Z)V", (void *)JHwBlob_native_putBoolArray },
568     { "putInt8Array", "(J[B)V", (void *)JHwBlob_native_putInt8Array },
569     { "putInt16Array", "(J[S)V", (void *)JHwBlob_native_putInt16Array },
570     { "putInt32Array", "(J[I)V", (void *)JHwBlob_native_putInt32Array },
571     { "putInt64Array", "(J[J)V", (void *)JHwBlob_native_putInt64Array },
572     { "putFloatArray", "(J[F)V", (void *)JHwBlob_native_putFloatArray },
573     { "putDoubleArray", "(J[D)V", (void *)JHwBlob_native_putDoubleArray },
574 
575     { "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V",
576         (void *)JHwBlob_native_putBlob },
577 
578     { "handle", "()J", (void *)JHwBlob_native_handle },
579 };
580 
581 namespace android {
582 
register_android_os_HwBlob(JNIEnv * env)583 int register_android_os_HwBlob(JNIEnv *env) {
584     return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
585 }
586 
587 }  // namespace android
588 
589