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