blob: a9db91be1d5b04c352018abf83209199243ec576 [file] [log] [blame]
/*
* 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 "android_os_NativeHandle.h"
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include "core_jni_helpers.h"
using android::AndroidRuntime;
using android::hardware::hidl_handle;
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 != nullptr) {
context->incStrong(nullptr /* id */);
}
if (old != nullptr) {
old->decStrong(nullptr /* 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),
mType(BlobType::GENERIC),
mOwnsBuffer(true),
mHandle(0) {
if (size > 0) {
mBuffer = calloc(size, 1);
}
}
JHwBlob::~JHwBlob() {
if (mOwnsBuffer) {
free(mBuffer);
mBuffer = nullptr;
}
}
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;
}
void *JHwBlob::data() {
return mBuffer;
}
size_t JHwBlob::size() const {
return mSize;
}
void JHwBlob::specializeBlobTo(BlobType type) {
CHECK_EQ(static_cast<int>(mType), static_cast<int>(BlobType::GENERIC));
mType = type;
}
JHwBlob::BlobType JHwBlob::type() const {
return mType;
}
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 {
CHECK_EQ(static_cast<int>(mType), static_cast<int>(BlobType::GENERIC));
size_t handle = 0;
status_t err = parcel->writeBuffer(data(), size(), &handle);
if (err != OK) {
return err;
}
return writeSubBlobsToParcel(parcel, handle);
}
status_t JHwBlob::writeEmbeddedToParcel(
hardware::Parcel *parcel,
size_t parentHandle,
size_t parentOffset) const {
size_t handle = 0;
status_t err = OK;
switch (mType) {
case BlobType::GENERIC: {
err = parcel->writeEmbeddedBuffer(data(), size(), &handle, parentHandle, parentOffset);
break;
}
case BlobType::NATIVE_HANDLE: {
err = parcel->writeEmbeddedNativeHandle(
static_cast<const native_handle *>(data()), parentHandle, parentOffset);
CHECK(mSubBlobs.empty());
break;
}
default: { err = INVALID_OPERATION; }
}
if (err != OK) {
return err;
}
return writeSubBlobsToParcel(parcel, handle);
}
status_t JHwBlob::writeSubBlobsToParcel(hardware::Parcel *parcel,
size_t parentHandle) const {
for (size_t i = 0; i < mSubBlobs.size(); ++i) {
const BlobInfo &info = mSubBlobs[i];
status_t err = info.mBlob->writeEmbeddedToParcel(parcel, parentHandle, 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.
// Cases:
// - this originates from another process (something so large should not fit
// in the binder buffer, and it should be rejected by the binder driver)
// - if this is used in process, this code makes too many heap copies (in
// order to retrofit HIDL's scatter-gather format to java types) to
// justify passing such a large amount of data over this path. So the
// alternative (updating the constructor and other code to accept other
// types, should also probably not be taken in this case).
CHECK_LE(size, std::numeric_limits<jint>::max());
return env->NewObject(clazz.get(), constructID, static_cast<jint>(size));
}
} // namespace android
////////////////////////////////////////////////////////////////////////////////
using namespace android;
static void releaseNativeContext(void *nativeContext) {
sp<JHwBlob> parcel = (JHwBlob *)nativeContext;
if (parcel != nullptr) {
parcel->decStrong(nullptr /* id */);
}
}
static jlong JHwBlob_native_init(JNIEnv *env, jclass /*clazz*/) {
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());
}
static jlong JHwBlob_native_getFieldHandle(JNIEnv* env,
jobject thiz,
jlong offset) {
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
return reinterpret_cast<jlong>(blob->data()) + offset;
}
#define DEFINE_BLOB_ARRAY_COPIER(Suffix,Type,NewType) \
static void JHwBlob_native_copyTo ## Suffix ## Array( \
JNIEnv *env, \
jobject thiz, \
jlong offset, \
Type ## Array array, \
jint size) { \
if (array == nullptr) { \
jniThrowException(env, "java/lang/NullPointerException", nullptr); \
return; \
} \
\
if (env->GetArrayLength(array) < size) { \
signalExceptionForError(env, BAD_VALUE); \
return; \
} \
\
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
\
if ((offset + size * sizeof(Type)) > blob->size()) { \
signalExceptionForError(env, -ERANGE); \
return; \
} \
\
env->Set ## NewType ## ArrayRegion( \
array, \
0 /* start */, \
size, \
reinterpret_cast<const Type *>( \
static_cast<const uint8_t *>(blob->data()) + offset)); \
}
DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)
DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short)
DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int)
DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long)
DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float)
DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double)
static void JHwBlob_native_copyToBoolArray(
JNIEnv *env,
jobject thiz,
jlong offset,
jbooleanArray array,
jint size) {
if (array == nullptr) {
jniThrowException(env, "java/lang/NullPointerException", nullptr);
return;
}
if (env->GetArrayLength(array) < size) {
signalExceptionForError(env, BAD_VALUE);
return;
}
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
if ((offset + size * sizeof(bool)) > blob->size()) {
signalExceptionForError(env, -ERANGE);
return;
}
const bool *src =
reinterpret_cast<const bool *>(
static_cast<const uint8_t *>(blob->data()) + offset);
jboolean *dst = env->GetBooleanArrayElements(array, nullptr /* isCopy */);
for (jint i = 0; i < size; ++i) {
dst[i] = src[i];
}
env->ReleaseBooleanArrayElements(array, dst, 0 /* mode */);
dst = nullptr;
}
#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 - 1);
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
blob->write(offset, &tmp, sizeof(tmp));
blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob);
}
static void JHwBlob_native_putNativeHandle(JNIEnv *env, jobject thiz,
jlong offset, jobject jHandle) {
std::unique_ptr<native_handle_t, int(*)(native_handle_t*)> nativeHandle(
JNativeHandle::MakeCppNativeHandle(env, jHandle, nullptr /* storage */),
native_handle_delete);
size_t size = 0;
if (nativeHandle != nullptr) {
size = sizeof(native_handle_t) + nativeHandle->numFds * sizeof(int)
+ nativeHandle->numInts * sizeof(int);
}
ScopedLocalRef<jobject> subBlobObj(env, JHwBlob::NewObject(env, size));
sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, subBlobObj.get());
subBlob->specializeBlobTo(JHwBlob::BlobType::NATIVE_HANDLE);
subBlob->write(0 /* offset */, nativeHandle.get(), size);
hidl_handle cppHandle;
cppHandle.setTo(static_cast<native_handle_t *>(subBlob->data()), false /* shouldOwn */);
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
blob->write(offset, &cppHandle, sizeof(cppHandle));
blob->putBlob(offset + hidl_handle::kOffsetOfNativeHandle, subBlob);
}
#define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType) \
static void JHwBlob_native_put ## Suffix ## Array( \
JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) { \
Scoped ## NewType ## ArrayRO autoArray(env, array); \
\
if (array == nullptr) { \
/* NullpointerException already pending */ \
return; \
} \
\
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
\
status_t err = blob->write( \
offset, autoArray.get(), autoArray.size() * sizeof(Type)); \
\
if (err != OK) { \
signalExceptionForError(env, err); \
} \
}
DEFINE_BLOB_ARRAY_PUTTER(Int8,jbyte,Byte)
DEFINE_BLOB_ARRAY_PUTTER(Int16,jshort,Short)
DEFINE_BLOB_ARRAY_PUTTER(Int32,jint,Int)
DEFINE_BLOB_ARRAY_PUTTER(Int64,jlong,Long)
DEFINE_BLOB_ARRAY_PUTTER(Float,jfloat,Float)
DEFINE_BLOB_ARRAY_PUTTER(Double,jdouble,Double)
static void JHwBlob_native_putBoolArray(
JNIEnv *env, jobject thiz, jlong offset, jbooleanArray array) {
ScopedBooleanArrayRO autoArray(env, array);
if (array == nullptr) {
/* NullpointerException already pending */
return;
}
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) {
signalExceptionForError(env, -ERANGE);
return;
}
const jboolean *src = autoArray.get();
bool *dst = reinterpret_cast<bool *>(
static_cast<uint8_t *>(blob->data()) + offset);
for (size_t i = 0; i < autoArray.size(); ++i) {
dst[i] = src[i];
}
}
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 },
{ "getFieldHandle", "(J)J", (void*) JHwBlob_native_getFieldHandle},
{ "copyToBoolArray", "(J[ZI)V", (void *)JHwBlob_native_copyToBoolArray },
{ "copyToInt8Array", "(J[BI)V", (void *)JHwBlob_native_copyToInt8Array },
{ "copyToInt16Array", "(J[SI)V", (void *)JHwBlob_native_copyToInt16Array },
{ "copyToInt32Array", "(J[II)V", (void *)JHwBlob_native_copyToInt32Array },
{ "copyToInt64Array", "(J[JI)V", (void *)JHwBlob_native_copyToInt64Array },
{ "copyToFloatArray", "(J[FI)V", (void *)JHwBlob_native_copyToFloatArray },
{ "copyToDoubleArray", "(J[DI)V", (void *)JHwBlob_native_copyToDoubleArray },
{ "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 },
{ "putNativeHandle", "(JL" PACKAGE_PATH "/NativeHandle;)V",
(void*)JHwBlob_native_putNativeHandle },
{ "putBoolArray", "(J[Z)V", (void *)JHwBlob_native_putBoolArray },
{ "putInt8Array", "(J[B)V", (void *)JHwBlob_native_putInt8Array },
{ "putInt16Array", "(J[S)V", (void *)JHwBlob_native_putInt16Array },
{ "putInt32Array", "(J[I)V", (void *)JHwBlob_native_putInt32Array },
{ "putInt64Array", "(J[J)V", (void *)JHwBlob_native_putInt64Array },
{ "putFloatArray", "(J[F)V", (void *)JHwBlob_native_putFloatArray },
{ "putDoubleArray", "(J[D)V", (void *)JHwBlob_native_putDoubleArray },
{ "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