--- /dev/null
+/*
+ * Copyright (C) 2016 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 "android_os_HwBlob"
+#include <android-base/logging.h>
+
+#include "android_os_HwBlob.h"
+
+#include "android_os_HwParcel.h"
+
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <hwbinder/Status.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include "core_jni_helpers.h"
+
+using android::AndroidRuntime;
+using android::hardware::hidl_string;
+
+#define PACKAGE_PATH "android/os"
+#define CLASS_NAME "HwBlob"
+#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME
+
+namespace android {
+
+static struct fields_t {
+ jfieldID contextID;
+ jmethodID constructID;
+
+} gFields;
+
+// static
+void JHwBlob::InitClass(JNIEnv *env) {
+ ScopedLocalRef<jclass> clazz(
+ env, FindClassOrDie(env, CLASS_PATH));
+
+ gFields.contextID =
+ GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+
+ gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V");
+}
+
+// static
+sp<JHwBlob> JHwBlob::SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) {
+ sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
+
+ if (context != NULL) {
+ context->incStrong(NULL /* id */);
+ }
+
+ if (old != NULL) {
+ old->decStrong(NULL /* id */);
+ }
+
+ env->SetLongField(thiz, gFields.contextID, (long)context.get());
+
+ return old;
+}
+
+// static
+sp<JHwBlob> JHwBlob::GetNativeContext(JNIEnv *env, jobject thiz) {
+ return (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
+}
+
+JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size)
+ : mBuffer(nullptr),
+ mSize(size),
+ mOwnsBuffer(true),
+ mHandle(0) {
+ jclass clazz = env->GetObjectClass(thiz);
+ CHECK(clazz != NULL);
+
+ mClass = (jclass)env->NewGlobalRef(clazz);
+ mObject = env->NewWeakGlobalRef(thiz);
+
+ if (size > 0) {
+ mBuffer = malloc(size);
+ }
+}
+
+JHwBlob::~JHwBlob() {
+ if (mOwnsBuffer) {
+ free(mBuffer);
+ mBuffer = nullptr;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteWeakGlobalRef(mObject);
+ mObject = NULL;
+
+ env->DeleteGlobalRef(mClass);
+ mClass = NULL;
+}
+
+void JHwBlob::setTo(const void *ptr, size_t handle) {
+ CHECK_EQ(mSize, 0u);
+ CHECK(mBuffer == nullptr);
+
+ mBuffer = const_cast<void *>(ptr);
+ mSize = SIZE_MAX; // XXX
+ mOwnsBuffer = false;
+ mHandle = handle;
+}
+
+status_t JHwBlob::getHandle(size_t *handle) const {
+ if (mOwnsBuffer) {
+ return INVALID_OPERATION;
+ }
+
+ *handle = mHandle;
+
+ return OK;
+}
+
+status_t JHwBlob::read(size_t offset, void *data, size_t size) const {
+ if (offset + size > mSize) {
+ return -ERANGE;
+ }
+
+ memcpy(data, (const uint8_t *)mBuffer + offset, size);
+
+ return OK;
+}
+
+status_t JHwBlob::write(size_t offset, const void *data, size_t size) {
+ if (offset + size > mSize) {
+ return -ERANGE;
+ }
+
+ memcpy((uint8_t *)mBuffer + offset, data, size);
+
+ return OK;
+}
+
+status_t JHwBlob::getString(size_t offset, const hidl_string **s) const {
+ if ((offset + sizeof(hidl_string)) > mSize) {
+ return -ERANGE;
+ }
+
+ *s = reinterpret_cast<const hidl_string *>(
+ (const uint8_t *)mBuffer + offset);
+
+ return OK;
+}
+
+const void *JHwBlob::data() const {
+ return mBuffer;
+}
+
+size_t JHwBlob::size() const {
+ return mSize;
+}
+
+status_t JHwBlob::putBlob(size_t offset, const sp<JHwBlob> &blob) {
+ size_t index = mSubBlobs.add();
+ BlobInfo *info = &mSubBlobs.editItemAt(index);
+
+ info->mOffset = offset;
+ info->mBlob = blob;
+
+ const void *data = blob->data();
+
+ return write(offset, &data, sizeof(data));
+}
+
+status_t JHwBlob::writeToParcel(hardware::Parcel *parcel) const {
+ size_t handle;
+ status_t err = parcel->writeBuffer(data(), size(), &handle);
+
+ if (err != OK) {
+ return err;
+ }
+
+ for (size_t i = 0; i < mSubBlobs.size(); ++i) {
+ const BlobInfo &info = mSubBlobs[i];
+
+ err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t JHwBlob::writeEmbeddedToParcel(
+ hardware::Parcel *parcel,
+ size_t parentHandle,
+ size_t parentOffset) const {
+ size_t handle;
+ status_t err = parcel->writeEmbeddedBuffer(
+ data(), size(), &handle, parentHandle, parentOffset);
+
+ if (err != OK) {
+ return err;
+ }
+
+ for (size_t i = 0; i < mSubBlobs.size(); ++i) {
+ const BlobInfo &info = mSubBlobs[i];
+
+ err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+// static
+jobject JHwBlob::NewObject(JNIEnv *env, const void *ptr, size_t handle) {
+ jobject obj = JHwBlob::NewObject(env, 0 /* size */);
+ JHwBlob::GetNativeContext(env, obj)->setTo(ptr, handle);
+
+ return obj;
+}
+
+// static
+jobject JHwBlob::NewObject(JNIEnv *env, size_t size) {
+ ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+ jmethodID constructID =
+ GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V");
+
+ // XXX Again cannot refer to gFields.constructID because InitClass may
+ // not have been called yet.
+
+ return env->NewObject(clazz.get(), constructID, size);
+}
+
+} // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static void releaseNativeContext(void *nativeContext) {
+ sp<JHwBlob> parcel = (JHwBlob *)nativeContext;
+
+ if (parcel != NULL) {
+ parcel->decStrong(NULL /* id */);
+ }
+}
+
+static jlong JHwBlob_native_init(JNIEnv *env) {
+ JHwBlob::InitClass(env);
+
+ return reinterpret_cast<jlong>(&releaseNativeContext);
+}
+
+static void JHwBlob_native_setup(
+ JNIEnv *env, jobject thiz, jint size) {
+ sp<JHwBlob> context = new JHwBlob(env, thiz, size);
+
+ JHwBlob::SetNativeContext(env, thiz, context);
+}
+
+#define DEFINE_BLOB_GETTER(Suffix,Type) \
+static Type JHwBlob_native_get ## Suffix( \
+ JNIEnv *env, jobject thiz, jlong offset) { \
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
+ \
+ Type x; \
+ status_t err = blob->read(offset, &x, sizeof(x)); \
+ \
+ if (err != OK) { \
+ signalExceptionForError(env, err); \
+ return 0; \
+ } \
+ \
+ return x; \
+}
+
+DEFINE_BLOB_GETTER(Int8,jbyte)
+DEFINE_BLOB_GETTER(Int16,jshort)
+DEFINE_BLOB_GETTER(Int32,jint)
+DEFINE_BLOB_GETTER(Int64,jlong)
+DEFINE_BLOB_GETTER(Float,jfloat)
+DEFINE_BLOB_GETTER(Double,jdouble)
+
+static jboolean JHwBlob_native_getBool(
+ JNIEnv *env, jobject thiz, jlong offset) {
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ bool x;
+ status_t err = blob->read(offset, &x, sizeof(x));
+
+ if (err != OK) {
+ signalExceptionForError(env, err);
+ return 0;
+ }
+
+ return (jboolean)x;
+}
+
+static jstring JHwBlob_native_getString(
+ JNIEnv *env, jobject thiz, jlong offset) {
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ const hidl_string *s;
+ status_t err = blob->getString(offset, &s);
+
+ if (err != OK) {
+ signalExceptionForError(env, err);
+ return nullptr;
+ }
+
+ return env->NewStringUTF(s->c_str());
+}
+
+#define DEFINE_BLOB_PUTTER(Suffix,Type) \
+static void JHwBlob_native_put ## Suffix( \
+ JNIEnv *env, jobject thiz, jlong offset, Type x) { \
+ \
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
+ \
+ status_t err = blob->write(offset, &x, sizeof(x)); \
+ \
+ if (err != OK) { \
+ signalExceptionForError(env, err); \
+ } \
+}
+
+DEFINE_BLOB_PUTTER(Int8,jbyte)
+DEFINE_BLOB_PUTTER(Int16,jshort)
+DEFINE_BLOB_PUTTER(Int32,jint)
+DEFINE_BLOB_PUTTER(Int64,jlong)
+DEFINE_BLOB_PUTTER(Float,jfloat)
+DEFINE_BLOB_PUTTER(Double,jdouble)
+
+static void JHwBlob_native_putBool(
+ JNIEnv *env, jobject thiz, jlong offset, jboolean x) {
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ bool b = (bool)x;
+ status_t err = blob->write(offset, &b, sizeof(b));
+
+ if (err != OK) {
+ signalExceptionForError(env, err);
+ }
+}
+
+static void JHwBlob_native_putString(
+ JNIEnv *env, jobject thiz, jlong offset, jstring stringObj) {
+ if (stringObj == nullptr) {
+ jniThrowException(env, "java/lang/NullPointerException", nullptr);
+ return;
+ }
+
+ const char *s = env->GetStringUTFChars(stringObj, nullptr);
+
+ if (s == nullptr) {
+ return;
+ }
+
+ size_t size = strlen(s) + 1;
+ ScopedLocalRef<jobject> subBlobObj(env, JHwBlob::NewObject(env, size));
+ sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, subBlobObj.get());
+ subBlob->write(0 /* offset */, s, size);
+
+ env->ReleaseStringUTFChars(stringObj, s);
+ s = nullptr;
+
+ hidl_string tmp;
+ tmp.setToExternal(static_cast<const char *>(subBlob->data()), size);
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+ blob->write(offset, &tmp, sizeof(tmp));
+ blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob);
+}
+
+static void JHwBlob_native_putBlob(
+ JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) {
+ if (blobObj == nullptr) {
+ jniThrowException(env, "java/lang/NullPointerException", nullptr);
+ return;
+ }
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+ sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, blobObj);
+
+ blob->putBlob(offset, subBlob);
+}
+
+static jlong JHwBlob_native_handle(JNIEnv *env, jobject thiz) {
+ size_t handle;
+ status_t err = JHwBlob::GetNativeContext(env, thiz)->getHandle(&handle);
+
+ if (err != OK) {
+ signalExceptionForError(env, err);
+ return 0;
+ }
+
+ return handle;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "native_init", "()J", (void *)JHwBlob_native_init },
+ { "native_setup", "(I)V", (void *)JHwBlob_native_setup },
+
+ { "getBool", "(J)Z", (void *)JHwBlob_native_getBool },
+ { "getInt8", "(J)B", (void *)JHwBlob_native_getInt8 },
+ { "getInt16", "(J)S", (void *)JHwBlob_native_getInt16 },
+ { "getInt32", "(J)I", (void *)JHwBlob_native_getInt32 },
+ { "getInt64", "(J)J", (void *)JHwBlob_native_getInt64 },
+ { "getFloat", "(J)F", (void *)JHwBlob_native_getFloat },
+ { "getDouble", "(J)D", (void *)JHwBlob_native_getDouble },
+ { "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString },
+
+ { "putBool", "(JZ)V", (void *)JHwBlob_native_putBool },
+ { "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 },
+ { "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 },
+ { "putInt32", "(JI)V", (void *)JHwBlob_native_putInt32 },
+ { "putInt64", "(JJ)V", (void *)JHwBlob_native_putInt64 },
+ { "putFloat", "(JF)V", (void *)JHwBlob_native_putFloat },
+ { "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble },
+ { "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString },
+
+ { "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V",
+ (void *)JHwBlob_native_putBlob },
+
+ { "handle", "()J", (void *)JHwBlob_native_handle },
+};
+
+namespace android {
+
+int register_android_os_HwBlob(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
+}
+
+} // namespace android
+
#include "android_os_HwParcel.h"
#include "android_os_HwBinder.h"
+#include "android_os_HwBlob.h"
#include "android_os_HwRemoteBinder.h"
#include <JNIHelp.h>
break;
}
+ case -ERANGE:
case BAD_INDEX:
{
jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
jobject JHwParcel::NewObject(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
- return env->NewObject(
- clazz.get(), gFields.constructID, false /* allocate */);
+ jmethodID constructID =
+ GetMethodIDOrDie(env, clazz.get(), "<init>", "(Z)V");
+
+ return env->NewObject(clazz.get(), constructID, false /* allocate */);
}
void JHwParcel::setTransactCallback(
sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
- hidl_vec<bool> *vec =
- (hidl_vec<bool> *)impl->getStorage()->allocTemporaryStorage(
- sizeof(hidl_vec<bool>));
+ void *vecPtr =
+ impl->getStorage()->allocTemporaryStorage(sizeof(hidl_vec<bool>));
+
+ hidl_vec<bool> *vec = new (vecPtr) hidl_vec<bool>;
jsize len = env->GetArrayLength(valObj);
sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
- string_vec *vec =
- (string_vec *)impl->getStorage()->allocTemporaryStorage(
- sizeof(string_vec));
+ void *vecPtr =
+ impl->getStorage()->allocTemporaryStorage(sizeof(string_vec));
+
+ string_vec *vec = new (vecPtr) string_vec;
hidl_string *strings = impl->getStorage()->allocStringArray(len);
vec->setToExternal(strings, len);
return JHwRemoteBinder::NewObject(env, binder);
}
+static jobject JHwParcel_native_readBuffer(JNIEnv *env, jobject thiz) {
+ hardware::Parcel *parcel =
+ JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+ size_t handle;
+ const void *ptr = parcel->readBuffer(&handle);
+
+ if (ptr == nullptr) {
+ jniThrowException(env, "java/util/NoSuchElementException", NULL);
+ return nullptr;
+ }
+
+ return JHwBlob::NewObject(env, ptr, handle);
+}
+
+static jobject JHwParcel_native_readEmbeddedBuffer(
+ JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset) {
+ hardware::Parcel *parcel =
+ JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+ size_t childHandle;
+
+ const void *ptr =
+ parcel->readEmbeddedBuffer(&childHandle, parentHandle, offset);
+
+ if (ptr == nullptr) {
+ jniThrowException(env, "java/util/NoSuchElementException", NULL);
+ return 0;
+ }
+
+ return JHwBlob::NewObject(env, ptr, childHandle);
+}
+
+static void JHwParcel_native_writeBuffer(
+ JNIEnv *env, jobject thiz, jobject blobObj) {
+ if (blobObj == nullptr) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return;
+ }
+
+ hardware::Parcel *parcel =
+ JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, blobObj);
+ status_t err = blob->writeToParcel(parcel);
+
+ if (err != OK) {
+ signalExceptionForError(env, err);
+ }
+}
+
static JNINativeMethod gMethods[] = {
{ "native_init", "()J", (void *)JHwParcel_native_init },
{ "native_setup", "(Z)V", (void *)JHwParcel_native_setup },
(void *)JHwParcel_native_releaseTemporaryStorage },
{ "send", "()V", (void *)JHwParcel_native_send },
+
+ { "readBuffer", "()L" PACKAGE_PATH "/HwBlob;",
+ (void *)JHwParcel_native_readBuffer },
+
+ { "readEmbeddedBuffer", "(JJ)L" PACKAGE_PATH "/HwBlob;",
+ (void *)JHwParcel_native_readEmbeddedBuffer },
+
+ { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V",
+ (void *)JHwParcel_native_writeBuffer },
};
namespace android {