From 0619b33685064da8c971456f971658eaa97d4d6c Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 15 Aug 2016 09:25:02 -0700 Subject: [PATCH] Initial commit of Java support for hardware binder (DO NOT MERGE) Change-Id: If1098ab921a11bae8eca2a70a3c3070e4daa0ea2 --- core/java/android/os/HwBinder.java | 59 ++ core/java/android/os/HwParcel.java | 120 ++++ core/java/android/os/HwRemoteBinder.java | 56 ++ core/java/android/os/IHwBinder.java | 28 + core/java/android/os/IHwInterface.java | 22 + core/jni/Android.mk | 7 +- core/jni/AndroidRuntime.cpp | 6 + core/jni/android_os_HwBinder.cpp | 288 ++++++++++ core/jni/android_os_HwBinder.h | 60 ++ core/jni/android_os_HwParcel.cpp | 913 +++++++++++++++++++++++++++++++ core/jni/android_os_HwParcel.h | 75 +++ core/jni/android_os_HwRemoteBinder.cpp | 196 +++++++ core/jni/android_os_HwRemoteBinder.h | 60 ++ core/jni/hwbinder/EphemeralStorage.cpp | 178 ++++++ core/jni/hwbinder/EphemeralStorage.h | 84 +++ 15 files changed, 2151 insertions(+), 1 deletion(-) create mode 100644 core/java/android/os/HwBinder.java create mode 100644 core/java/android/os/HwParcel.java create mode 100644 core/java/android/os/HwRemoteBinder.java create mode 100644 core/java/android/os/IHwBinder.java create mode 100644 core/java/android/os/IHwInterface.java create mode 100644 core/jni/android_os_HwBinder.cpp create mode 100644 core/jni/android_os_HwBinder.h create mode 100644 core/jni/android_os_HwParcel.cpp create mode 100644 core/jni/android_os_HwParcel.h create mode 100644 core/jni/android_os_HwRemoteBinder.cpp create mode 100644 core/jni/android_os_HwRemoteBinder.h create mode 100644 core/jni/hwbinder/EphemeralStorage.cpp create mode 100644 core/jni/hwbinder/EphemeralStorage.h diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java new file mode 100644 index 000000000000..5ff79f752c39 --- /dev/null +++ b/core/java/android/os/HwBinder.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public abstract class HwBinder implements IHwBinder { + private static final String TAG = "HwBinder"; + + private static final NativeAllocationRegistry sNativeRegistry; + + public HwBinder() { + native_setup(); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public final native void transact( + int code, HwParcel request, HwParcel reply, int flags); + + public abstract void onTransact( + int code, HwParcel request, HwParcel reply, int flags); + + public native final void registerService(String serviceName); + public static native final IHwBinder getService(String serviceName); + + // Returns address of the "freeFunction". + private static native final long native_init(); + + private native final void native_setup(); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwBinder.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java new file mode 100644 index 000000000000..fe7cdccd3e09 --- /dev/null +++ b/core/java/android/os/HwParcel.java @@ -0,0 +1,120 @@ +/* + * 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. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public class HwParcel { + private static final String TAG = "HwParcel"; + + public static final int STATUS_SUCCESS = 0; + public static final int STATUS_ERROR = -1; + + private static final NativeAllocationRegistry sNativeRegistry; + + private HwParcel(boolean allocate) { + native_setup(allocate); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public HwParcel() { + native_setup(true /* allocate */); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public native final void writeInterfaceToken(String interfaceName); + public native final void writeInt8(byte val); + public native final void writeInt16(short val); + public native final void writeInt32(int val); + public native final void writeInt64(long val); + public native final void writeFloat(float val); + public native final void writeDouble(double val); + public native final void writeString(String val); + + public native final void writeInt8Array(int size, byte[] val); + public native final void writeInt8Vector(byte[] val); + public native final void writeInt16Array(int size, short[] val); + public native final void writeInt16Vector(short[] val); + public native final void writeInt32Array(int size, int[] val); + public native final void writeInt32Vector(int[] val); + public native final void writeInt64Array(int size, long[] val); + public native final void writeInt64Vector(long[] val); + public native final void writeFloatArray(int size, float[] val); + public native final void writeFloatVector(float[] val); + public native final void writeDoubleArray(int size, double[] val); + public native final void writeDoubleVector(double[] val); + public native final void writeStringArray(int size, String[] val); + public native final void writeStringVector(String[] val); + + public native final void writeStrongBinder(IHwBinder binder); + + public native final void enforceInterface(String interfaceName); + public native final byte readInt8(); + public native final short readInt16(); + public native final int readInt32(); + public native final long readInt64(); + public native final float readFloat(); + public native final double readDouble(); + public native final String readString(); + + public native final byte[] readInt8Array(int size); + public native final byte[] readInt8Vector(); + public native final short[] readInt16Array(int size); + public native final short[] readInt16Vector(); + public native final int[] readInt32Array(int size); + public native final int[] readInt32Vector(); + public native final long[] readInt64Array(int size); + public native final long[] readInt64Vector(); + public native final float[] readFloatArray(int size); + public native final float[] readFloatVector(); + public native final double[] readDoubleArray(int size); + public native final double[] readDoubleVector(); + public native final String[] readStringArray(int size); + public native final String[] readStringVector(); + + public native final IHwBinder readStrongBinder(); + + public native final void writeStatus(int status); + public native final void verifySuccess(); + public native final void releaseTemporaryStorage(); + + public native final void send(); + + // Returns address of the "freeFunction". + private static native final long native_init(); + + private native final void native_setup(boolean allocate); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwParcel.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} + diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java new file mode 100644 index 000000000000..83866b3cceb7 --- /dev/null +++ b/core/java/android/os/HwRemoteBinder.java @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package android.os; + +import libcore.util.NativeAllocationRegistry; + +/** @hide */ +public class HwRemoteBinder implements IHwBinder { + private static final String TAG = "HwRemoteBinder"; + + private static final NativeAllocationRegistry sNativeRegistry; + + public HwRemoteBinder() { + native_setup_empty(); + + sNativeRegistry.registerNativeAllocation( + this, + mNativeContext); + } + + public IHwInterface queryLocalInterface(String descriptor) { + return null; + } + + public native final void transact( + int code, HwParcel request, HwParcel reply, int flags); + + private static native final long native_init(); + + private native final void native_setup_empty(); + + static { + long freeFunction = native_init(); + + sNativeRegistry = new NativeAllocationRegistry( + HwRemoteBinder.class.getClassLoader(), + freeFunction, + 128 /* size */); + } + + private long mNativeContext; +} diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java new file mode 100644 index 000000000000..88af4fb13710 --- /dev/null +++ b/core/java/android/os/IHwBinder.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package android.os; + +/** @hide */ +public interface IHwBinder { + // MUST match libhwbinder/IBinder.h definition !!! + public static final int FIRST_CALL_TRANSACTION = 1; + + public void transact( + int code, HwParcel request, HwParcel reply, int flags); + + public IHwInterface queryLocalInterface(String descriptor); +} diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java new file mode 100644 index 000000000000..7c5ac6f44a49 --- /dev/null +++ b/core/java/android/os/IHwInterface.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +package android.os; + +/** @hide */ +public interface IHwInterface { + public IHwBinder asBinder(); +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 60e888d17fc0..8175bf67135a 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -80,6 +80,9 @@ LOCAL_SRC_FILES:= \ android_text_AndroidBidi.cpp \ android_text_StaticLayout.cpp \ android_os_Debug.cpp \ + android_os_HwBinder.cpp \ + android_os_HwParcel.cpp \ + android_os_HwRemoteBinder.cpp \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ android_os_Parcel.cpp \ @@ -177,7 +180,8 @@ LOCAL_SRC_FILES:= \ com_android_internal_os_PathClassLoaderFactory.cpp \ com_android_internal_os_Zygote.cpp \ com_android_internal_util_VirtualRefBasePtr.cpp \ - com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp + com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \ + hwbinder/EphemeralStorage.cpp \ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ @@ -260,6 +264,7 @@ LOCAL_SHARED_LIBRARIES := \ libradio_metadata \ libnativeloader \ libmemunreachable \ + libhwbinder \ LOCAL_SHARED_LIBRARIES += \ libhwui \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6cbc06cd8847..109d3fbcd847 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -156,6 +156,9 @@ extern int register_android_database_SQLiteGlobal(JNIEnv* env); extern int register_android_database_SQLiteDebug(JNIEnv* env); extern int register_android_nio_utils(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); +extern int register_android_os_HwBinder(JNIEnv *env); +extern int register_android_os_HwParcel(JNIEnv *env); +extern int register_android_os_HwRemoteBinder(JNIEnv *env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); @@ -1287,6 +1290,9 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), REG_JNI(register_android_os_Parcel), + REG_JNI(register_android_os_HwBinder), + REG_JNI(register_android_os_HwParcel), + REG_JNI(register_android_os_HwRemoteBinder), REG_JNI(register_android_nio_utils), REG_JNI(register_android_graphics_Canvas), REG_JNI(register_android_graphics_Graphics), diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp new file mode 100644 index 000000000000..a45e1eef2e8f --- /dev/null +++ b/core/jni/android_os_HwBinder.cpp @@ -0,0 +1,288 @@ +/* + * 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_HwBinder" +#include + +#include "android_os_HwBinder.h" + +#include "android_os_HwParcel.h" +#include "android_os_HwRemoteBinder.h" + +#include +#include +#include +#include +#include +#include + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwBinder" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID onTransactID; + +} gFields; + +// static +void JHwBinder::InitClass(JNIEnv *env) { + ScopedLocalRef clazz( + env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.onTransactID = + GetMethodIDOrDie( + env, + clazz.get(), + "onTransact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V"); +} + +// static +sp JHwBinder::SetNativeContext( + JNIEnv *env, jobject thiz, const sp &context) { + sp old = + (JHwBinder *)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 JHwBinder::GetNativeContext( + JNIEnv *env, jobject thiz) { + return (JHwBinder *)env->GetLongField(thiz, gFields.contextID); +} + +JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwBinder::~JHwBinder() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +status_t JHwBinder::onTransact( + uint32_t code, + const hardware::Parcel &data, + hardware::Parcel *reply, + uint32_t flags, + TransactCallback callback) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + ScopedLocalRef requestObj(env, JHwParcel::NewObject(env)); + JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( + const_cast(&data), false /* assumeOwnership */); + + ScopedLocalRef replyObj(env, JHwParcel::NewObject(env)); + + sp replyContext = + JHwParcel::GetNativeContext(env, replyObj.get()); + + replyContext->setParcel(reply, false /* assumeOwnership */); + replyContext->setTransactCallback(callback); + + env->CallVoidMethod( + mObject, + gFields.onTransactID, + code, + requestObj.get(), + replyObj.get(), + flags); + + status_t err = OK; + + if (!replyContext->wasSent()) { + // The implementation never finished the transaction. + err = UNKNOWN_ERROR; // XXX special error code instead? + + reply->setDataPosition(0 /* pos */); + } + + // Release all temporary storage now that scatter-gather data + // has been consolidated, either by calling the TransactCallback, + // if wasSent() == true or clearing the reply parcel (setDataOffset above). + replyContext->getStorage()->release(env); + + // We cannot permanently pass ownership of "data" and "reply" over to their + // Java object wrappers (we don't own them ourselves). + + JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( + NULL /* parcel */, false /* assumeOwnership */); + + replyContext->setParcel( + NULL /* parcel */, false /* assumeOwnership */); + + return err; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp binder = (JHwBinder *)nativeContext; + + if (binder != NULL) { + binder->decStrong(NULL /* id */); + } +} + +static jlong JHwBinder_native_init(JNIEnv *env) { + JHwBinder::InitClass(env); + + return reinterpret_cast(&releaseNativeContext); +} + +static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) { + sp context = new JHwBinder(env, thiz); + + JHwBinder::SetNativeContext(env, thiz, context); +} + +static void JHwBinder_native_transact( + JNIEnv * /* env */, + jobject /* thiz */, + jint /* code */, + jobject /* requestObj */, + jobject /* replyObj */, + jint /* flags */) { + CHECK(!"Should not be here"); +} + +static void JHwBinder_native_registerService( + JNIEnv *env, jobject thiz, jstring serviceNameObj) { + if (serviceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL); + + if (serviceName == NULL) { + return; // XXX exception already pending? + } + + const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0); + + sp binder = JHwBinder::GetNativeContext(env, thiz); + + status_t err = hardware::defaultServiceManager()->addService( + String16(reinterpret_cast(serviceName)), + binder, + kVersion); + + env->ReleaseStringCritical(serviceNameObj, serviceName); + serviceName = NULL; + + if (err == OK) { + LOG(INFO) << "Starting thread pool."; + ::android::hardware::ProcessState::self()->startThreadPool(); + } + + signalExceptionForError(env, err); +} + +static jobject JHwBinder_native_getService( + JNIEnv *env, jclass /* clazzObj */, jstring serviceNameObj) { + if (serviceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return NULL; + } + + const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL); + + if (serviceName == NULL) { + return NULL; // XXX exception already pending? + } + + const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0); + + LOG(INFO) << "looking for service '" + << String8(String16( + reinterpret_cast(serviceName))).string() + << "'"; + + sp service = + hardware::defaultServiceManager()->getService( + String16(reinterpret_cast(serviceName)), + kVersion); + + env->ReleaseStringCritical(serviceNameObj, serviceName); + serviceName = NULL; + + if (service == NULL) { + signalExceptionForError(env, NAME_NOT_FOUND); + return NULL; + } + + return JHwRemoteBinder::NewObject(env, service); +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwBinder_native_init }, + { "native_setup", "()V", (void *)JHwBinder_native_setup }, + + { "transact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V", + (void *)JHwBinder_native_transact }, + + { "registerService", "(Ljava/lang/String;)V", + (void *)JHwBinder_native_registerService }, + + { "getService", "(Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;", + (void *)JHwBinder_native_getService }, +}; + +namespace android { + +int register_android_os_HwBinder(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h new file mode 100644 index 000000000000..2ebc38164da8 --- /dev/null +++ b/core/jni/android_os_HwBinder.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef _ANDROID_OS_HW_BINDER_H +#define _ANDROID_OS_HW_BINDER_H + +#include +#include +#include +#include + +namespace android { + +struct JHwBinder : public hardware::BBinder { + static void InitClass(JNIEnv *env); + + static sp SetNativeContext( + JNIEnv *env, jobject thiz, const sp &context); + + static sp GetNativeContext(JNIEnv *env, jobject thiz); + + JHwBinder(JNIEnv *env, jobject thiz); + +protected: + virtual ~JHwBinder(); + + virtual status_t onTransact( + uint32_t code, + const hardware::Parcel &data, + hardware::Parcel *reply, + uint32_t flags, + TransactCallback callback); + +private: + jclass mClass; + jobject mObject; + + DISALLOW_COPY_AND_ASSIGN(JHwBinder); +}; + +int register_android_os_HwBinder(JNIEnv *env); + +} // namespace android + +#endif // _ANDROID_OS_HW_BINDER_H + + diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp new file mode 100644 index 000000000000..0202303b10ea --- /dev/null +++ b/core/jni/android_os_HwParcel.cpp @@ -0,0 +1,913 @@ +/* + * 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_HwParcel" +#include + +#include "android_os_HwParcel.h" + +#include "android_os_HwBinder.h" +#include "android_os_HwRemoteBinder.h" + +#include +#include +#include +#include + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwParcel" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID constructID; + +} gFields; + +void signalExceptionForError(JNIEnv *env, status_t err) { + switch (err) { + case OK: + break; + + case NO_MEMORY: + { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + break; + } + + case INVALID_OPERATION: + { + jniThrowException( + env, "java/lang/UnsupportedOperationException", NULL); + break; + } + + case BAD_VALUE: + { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + } + + case BAD_INDEX: + { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL); + break; + } + + case BAD_TYPE: + { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + break; + } + + case NAME_NOT_FOUND: + { + jniThrowException(env, "java/util/NoSuchElementException", NULL); + break; + } + + case PERMISSION_DENIED: + { + jniThrowException(env, "java/lang/SecurityException", NULL); + break; + } + + case NO_INIT: + { + jniThrowException( + env, "java/lang/RuntimeException", "Not initialized"); + break; + } + + case ALREADY_EXISTS: + { + jniThrowException( + env, "java/lang/RuntimeException", "Item already exists"); + break; + } + + default: + { + jniThrowException( + env, "java/lang/RuntimeException", "Unknown error"); + + break; + } + } +} + +// static +void JHwParcel::InitClass(JNIEnv *env) { + ScopedLocalRef clazz( + env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "", "(Z)V"); +} + +// static +sp JHwParcel::SetNativeContext( + JNIEnv *env, jobject thiz, const sp &context) { + sp old = (JHwParcel *)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 JHwParcel::GetNativeContext(JNIEnv *env, jobject thiz) { + return (JHwParcel *)env->GetLongField(thiz, gFields.contextID); +} + +JHwParcel::JHwParcel(JNIEnv *env, jobject thiz) + : mParcel(NULL), + mOwnsParcel(false), + mTransactCallback(nullptr), + mWasSent(false) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwParcel::~JHwParcel() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + mStorage.release(env); + + setParcel(NULL, false /* assumeOwnership */); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +hardware::Parcel *JHwParcel::getParcel() { + return mParcel; +} + +EphemeralStorage *JHwParcel::getStorage() { + return &mStorage; +} + +void JHwParcel::setParcel(hardware::Parcel *parcel, bool assumeOwnership) { + if (mParcel && mOwnsParcel) { + delete mParcel; + } + + mParcel = parcel; + mOwnsParcel = assumeOwnership; +} + +// static +jobject JHwParcel::NewObject(JNIEnv *env) { + ScopedLocalRef clazz(env, FindClassOrDie(env, CLASS_PATH)); + + return env->NewObject( + clazz.get(), gFields.constructID, false /* allocate */); +} + +void JHwParcel::setTransactCallback( + ::android::hardware::IBinder::TransactCallback cb) { + mTransactCallback = cb; +} + +void JHwParcel::send() { + CHECK(mTransactCallback != nullptr); + CHECK(mParcel != nullptr); + + mTransactCallback(*mParcel); + mTransactCallback = nullptr; + + mWasSent = true; +} + +bool JHwParcel::wasSent() const { + return mWasSent; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp parcel = (JHwParcel *)nativeContext; + + if (parcel != NULL) { + parcel->decStrong(NULL /* id */); + } +} + +static jlong JHwParcel_native_init(JNIEnv *env) { + JHwParcel::InitClass(env); + + return reinterpret_cast(&releaseNativeContext); +} + +static void JHwParcel_native_setup( + JNIEnv *env, jobject thiz, jboolean allocate) { + sp context = new JHwParcel(env, thiz); + + if (allocate) { + context->setParcel(new hardware::Parcel, true /* assumeOwnership */); + } + + JHwParcel::SetNativeContext(env, thiz, context); +} + +static void JHwParcel_native_writeInterfaceToken( + JNIEnv *env, jobject thiz, jstring interfaceNameObj) { + if (interfaceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL); + if (interfaceName) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = parcel->writeInterfaceToken( + String16( + reinterpret_cast(interfaceName), + env->GetStringLength(interfaceNameObj))); + + env->ReleaseStringCritical(interfaceNameObj, interfaceName); + interfaceName = NULL; + + signalExceptionForError(env, err); + } +} + +static void JHwParcel_native_enforceInterface( + JNIEnv *env, jobject thiz, jstring interfaceNameObj) { + // XXX original binder Parcel enforceInterface implementation does some + // mysterious things regarding strictModePolicy(), figure out if we need + // that here as well. + if (interfaceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL); + if (interfaceName) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + bool valid = parcel->enforceInterface( + String16( + reinterpret_cast(interfaceName), + env->GetStringLength(interfaceNameObj))); + + env->ReleaseStringCritical(interfaceNameObj, interfaceName); + interfaceName = NULL; + + if (!valid) { + jniThrowException( + env, + "java/lang/SecurityException", + "HWBinder invocation to an incorrect interface"); + } + } +} + +#define DEFINE_PARCEL_WRITER(Suffix,Type) \ +static void JHwParcel_native_write ## Suffix( \ + JNIEnv *env, jobject thiz, Type val) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + status_t err = parcel->write ## Suffix(val); \ + signalExceptionForError(env, err); \ +} + +#define DEFINE_PARCEL_READER(Suffix,Type) \ +static Type JHwParcel_native_read ## Suffix( \ + JNIEnv *env, jobject thiz) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + Type val; \ + status_t err = parcel->read ## Suffix(&val); \ + signalExceptionForError(env, err); \ + \ + return val; \ +} + +DEFINE_PARCEL_WRITER(Int8,jbyte) +DEFINE_PARCEL_WRITER(Int16,jshort) +DEFINE_PARCEL_WRITER(Int32,jint) +DEFINE_PARCEL_WRITER(Int64,jlong) +DEFINE_PARCEL_WRITER(Float,jfloat) +DEFINE_PARCEL_WRITER(Double,jdouble) + +DEFINE_PARCEL_READER(Int8,jbyte) +DEFINE_PARCEL_READER(Int16,jshort) +DEFINE_PARCEL_READER(Int32,jint) +DEFINE_PARCEL_READER(Int64,jlong) +DEFINE_PARCEL_READER(Float,jfloat) +DEFINE_PARCEL_READER(Double,jdouble) + +static void JHwParcel_native_writeStatus( + JNIEnv *env, jobject thiz, jint statusCode) { + using hardware::Status; + + Status status; + switch (statusCode) { + case 0: // kStatusSuccess + status = Status::ok(); + break; + case -1: // kStatusError + status = Status::fromStatusT(UNKNOWN_ERROR); + break; + default: + CHECK(!"Should not be here"); + } + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = status.writeToParcel(parcel); + signalExceptionForError(env, err); +} + +static void JHwParcel_native_verifySuccess(JNIEnv *env, jobject thiz) { + using hardware::Status; + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + Status status; + status_t err = status.readFromParcel(*parcel); + signalExceptionForError(env, err); +} + +static void JHwParcel_native_releaseTemporaryStorage( + JNIEnv *env, jobject thiz) { + JHwParcel::GetNativeContext(env, thiz)->getStorage()->release(env); +} + +static void JHwParcel_native_send(JNIEnv *env, jobject thiz) { + JHwParcel::GetNativeContext(env, thiz)->send(); +} + +static void JHwParcel_native_writeString( + JNIEnv *env, jobject thiz, jstring valObj) { + if (valObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + sp impl = JHwParcel::GetNativeContext(env, thiz); + + const hidl_string *s = + impl->getStorage()->allocTemporaryString(env, valObj); + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer(s, sizeof(*s), &parentHandle); + + if (err == OK) { + err = s->writeEmbeddedToParcel( + parcel, parentHandle, 0 /* parentOffset */); + } + + signalExceptionForError(env, err); +} + +#define DEFINE_PARCEL_ARRAY_WRITER(Suffix,Type) \ +static void JHwParcel_native_write ## Suffix ## Array( \ + JNIEnv *env, jobject thiz, jint size, Type ## Array valObj) { \ + if (valObj == NULL) { \ + jniThrowException(env, "java/lang/NullPointerException", NULL); \ + return; \ + } \ + \ + jsize len = env->GetArrayLength(valObj); \ + \ + if (len != size) { \ + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); \ + return; \ + } \ + \ + sp impl = JHwParcel::GetNativeContext(env, thiz); \ + \ + const Type *val = \ + impl->getStorage()->allocTemporary ## Suffix ## Array(env, valObj); \ + \ + hardware::Parcel *parcel = impl->getParcel(); \ + \ + size_t parentHandle; \ + status_t err = parcel->writeBuffer( \ + val, size * sizeof(*val), &parentHandle); \ + \ + signalExceptionForError(env, err); \ +} + +#define DEFINE_PARCEL_VECTOR_WRITER(Suffix,Type) \ +static void JHwParcel_native_write ## Suffix ## Vector( \ + JNIEnv *env, jobject thiz, Type ## Array valObj) { \ + if (valObj == NULL) { \ + jniThrowException(env, "java/lang/NullPointerException", NULL); \ + return; \ + } \ + \ + sp impl = JHwParcel::GetNativeContext(env, thiz); \ + \ + const hidl_vec *vec = \ + impl->getStorage()->allocTemporary ## Suffix ## Vector(env, valObj); \ + \ + hardware::Parcel *parcel = impl->getParcel(); \ + \ + size_t parentHandle; \ + status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle); \ + \ + if (err == OK) { \ + size_t childHandle; \ + \ + err = vec->writeEmbeddedToParcel( \ + parcel, \ + parentHandle, \ + 0 /* parentOffset */, \ + &childHandle); \ + } \ + \ + signalExceptionForError(env, err); \ +} + +DEFINE_PARCEL_ARRAY_WRITER(Int8,jbyte) +DEFINE_PARCEL_ARRAY_WRITER(Int16,jshort) +DEFINE_PARCEL_ARRAY_WRITER(Int32,jint) +DEFINE_PARCEL_ARRAY_WRITER(Int64,jlong) +DEFINE_PARCEL_ARRAY_WRITER(Float,jfloat) +DEFINE_PARCEL_ARRAY_WRITER(Double,jdouble) + +DEFINE_PARCEL_VECTOR_WRITER(Int8,jbyte) +DEFINE_PARCEL_VECTOR_WRITER(Int16,jshort) +DEFINE_PARCEL_VECTOR_WRITER(Int32,jint) +DEFINE_PARCEL_VECTOR_WRITER(Int64,jlong) +DEFINE_PARCEL_VECTOR_WRITER(Float,jfloat) +DEFINE_PARCEL_VECTOR_WRITER(Double,jdouble) + +static void JHwParcel_native_writeStrongBinder( + JNIEnv *env, jobject thiz, jobject binderObj) { + sp binder; + if (binderObj != NULL) { + ScopedLocalRef hwBinderKlass( + env, FindClassOrDie(env, PACKAGE_PATH "/HwBinder")); + + ScopedLocalRef hwRemoteBinderKlass( + env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder")); + + if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) { + binder = JHwBinder::GetNativeContext(env, binderObj); + } else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) { + binder = JHwRemoteBinder::GetNativeContext( + env, binderObj)->getBinder(); + } else { + signalExceptionForError(env, INVALID_OPERATION); + return; + } + } + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + status_t err = parcel->writeStrongBinder(binder); + signalExceptionForError(env, err); +} + +static jstring MakeStringObjFromHidlString(JNIEnv *env, const hidl_string &s) { + String16 utf16String(s.c_str(), s.size()); + + return env->NewString( + reinterpret_cast(utf16String.string()), + utf16String.size()); +} + +static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + + const hidl_string *s = static_cast( + parcel->readBuffer(&parentHandle)); + + if (s == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + status_t err = const_cast(s)->readEmbeddedFromParcel( + *parcel, parentHandle, 0 /* parentOffset */); + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + return MakeStringObjFromHidlString(env, *s); +} + +#define DEFINE_PARCEL_ARRAY_READER(Suffix,Type,NewType) \ +static Type ## Array JHwParcel_native_read ## Suffix ## Array( \ + JNIEnv *env, jobject thiz, jint size) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + size_t parentHandle; \ + const Type *val = static_cast( \ + parcel->readBuffer(&parentHandle)); \ + \ + Type ## Array valObj = env->New ## NewType ## Array(size); \ + env->Set ## NewType ## ArrayRegion(valObj, 0, size, val); \ + \ + return valObj; \ +} + +#define DEFINE_PARCEL_VECTOR_READER(Suffix,Type,NewType) \ +static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ + JNIEnv *env, jobject thiz) { \ + hardware::Parcel *parcel = \ + JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ + \ + size_t parentHandle; \ + \ + const hidl_vec *vec = \ + (const hidl_vec *)parcel->readBuffer(&parentHandle); \ + \ + if (vec == NULL) { \ + signalExceptionForError(env, UNKNOWN_ERROR); \ + return NULL; \ + } \ + \ + size_t childHandle; \ + \ + status_t err = const_cast *>(vec) \ + ->readEmbeddedFromParcel( \ + *parcel, \ + parentHandle, \ + 0 /* parentOffset */, \ + &childHandle); \ + \ + if (err != OK) { \ + signalExceptionForError(env, err); \ + return NULL; \ + } \ + \ + Type ## Array valObj = env->New ## NewType ## Array(vec->size()); \ + env->Set ## NewType ## ArrayRegion(valObj, 0, vec->size(), &(*vec)[0]); \ + \ + return valObj; \ +} + +DEFINE_PARCEL_ARRAY_READER(Int8,jbyte,Byte) +DEFINE_PARCEL_ARRAY_READER(Int16,jshort,Short) +DEFINE_PARCEL_ARRAY_READER(Int32,jint,Int) +DEFINE_PARCEL_ARRAY_READER(Int64,jlong,Long) +DEFINE_PARCEL_ARRAY_READER(Float,jfloat,Float) +DEFINE_PARCEL_ARRAY_READER(Double,jdouble,Double) + +DEFINE_PARCEL_VECTOR_READER(Int8,jbyte,Byte) +DEFINE_PARCEL_VECTOR_READER(Int16,jshort,Short) +DEFINE_PARCEL_VECTOR_READER(Int32,jint,Int) +DEFINE_PARCEL_VECTOR_READER(Int64,jlong,Long) +DEFINE_PARCEL_VECTOR_READER(Float,jfloat,Float) +DEFINE_PARCEL_VECTOR_READER(Double,jdouble,Double) + +static jobjectArray MakeStringArray( + JNIEnv *env, const hidl_string *array, size_t size) { + ScopedLocalRef stringKlass( + env, + env->FindClass("java/lang/String")); + + // XXX Why can't I use ScopedLocalRef<> for the arrayObj and the stringObjs? + + jobjectArray arrayObj = env->NewObjectArray(size, stringKlass.get(), NULL); + + for (size_t i = 0; i < size; ++i) { + jstring stringObj = MakeStringObjFromHidlString(env, array[i]); + + env->SetObjectArrayElement( + arrayObj, + i, + stringObj); + } + + return arrayObj; +} + +static jobjectArray JHwParcel_native_readStringArray( + JNIEnv *env, jobject thiz, jint size) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + const hidl_string *val = static_cast( + parcel->readBuffer(&parentHandle)); + + if (val == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + status_t err = OK; + for (jint i = 0; (err == OK) && (i < size); ++i) { + err = const_cast(&val[i]) + ->readEmbeddedFromParcel( + *parcel, + parentHandle, + i * sizeof(hidl_string)); + } + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + return MakeStringArray(env, val, size); +} + +static void JHwParcel_native_writeStringArray( + JNIEnv *env, jobject thiz, jint size, jobjectArray arrayObj) { + if (arrayObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + jsize len = env->GetArrayLength(arrayObj); + + if (len != size) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + sp impl = JHwParcel::GetNativeContext(env, thiz); + + hidl_string *strings = impl->getStorage()->allocStringArray(len); + + for (jsize i = 0; i < len; ++i) { + ScopedLocalRef stringObj( + env, + (jstring)env->GetObjectArrayElement(arrayObj, i)); + + const hidl_string *s = + impl->getStorage()->allocTemporaryString(env, stringObj.get()); + + strings[i].setToExternal(s->c_str(), s->size()); + } + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer( + strings, sizeof(hidl_string) * len, &parentHandle); + + for (jsize i = 0; (err == OK) && (i < len); ++i) { + err = strings[i].writeEmbeddedToParcel( + parcel, parentHandle, i * sizeof(hidl_string)); + } + + signalExceptionForError(env, err); +} + +static jobjectArray JHwParcel_native_readStringVector( + JNIEnv *env, jobject thiz) { + typedef hidl_vec string_vec; + + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + + const string_vec *vec= + (const string_vec *)parcel->readBuffer(&parentHandle); + + if (vec == NULL) { + signalExceptionForError(env, UNKNOWN_ERROR); + return NULL; + } + + size_t childHandle; + status_t err = const_cast(vec)->readEmbeddedFromParcel( + *parcel, parentHandle, 0 /* parentOffset */, &childHandle); + + for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { + err = const_cast *>(vec) + ->readEmbeddedFromParcel( + *parcel, + childHandle, + i * sizeof(hidl_string), + nullptr /* childHandle */); + } + + if (err != OK) { + signalExceptionForError(env, err); + return NULL; + } + + return MakeStringArray(env, &(*vec)[0], vec->size()); +} + +static void JHwParcel_native_writeStringVector( + JNIEnv *env, jobject thiz, jobjectArray arrayObj) { + typedef hidl_vec string_vec; + + if (arrayObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + jsize len = env->GetArrayLength(arrayObj); + + sp impl = JHwParcel::GetNativeContext(env, thiz); + + string_vec *vec = + (string_vec *)impl->getStorage()->allocTemporaryStorage( + sizeof(string_vec)); + + hidl_string *strings = impl->getStorage()->allocStringArray(len); + vec->setToExternal(strings, len); + + for (jsize i = 0; i < len; ++i) { + ScopedLocalRef stringObj( + env, + (jstring)env->GetObjectArrayElement(arrayObj, i)); + + const hidl_string *s = + impl->getStorage()->allocTemporaryString(env, stringObj.get()); + + strings[i].setToExternal(s->c_str(), s->size()); + } + + hardware::Parcel *parcel = impl->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle); + + if (err == OK) { + size_t childHandle; + err = vec->writeEmbeddedToParcel( + parcel, + parentHandle, + 0 /* parentOffset */, + &childHandle); + + for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { + err = (*vec)[i].writeEmbeddedToParcel( + parcel, + childHandle, + i * sizeof(hidl_string)); + } + } + + signalExceptionForError(env, err); +} + +static jobject JHwParcel_native_readStrongBinder(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + sp binder = parcel->readStrongBinder(); + + if (binder == NULL) { + return NULL; + } + + return JHwRemoteBinder::NewObject(env, binder); +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwParcel_native_init }, + { "native_setup", "(Z)V", (void *)JHwParcel_native_setup }, + + { "writeInterfaceToken", "(Ljava/lang/String;)V", + (void *)JHwParcel_native_writeInterfaceToken }, + + { "writeInt8", "(B)V", (void *)JHwParcel_native_writeInt8 }, + { "writeInt16", "(S)V", (void *)JHwParcel_native_writeInt16 }, + { "writeInt32", "(I)V", (void *)JHwParcel_native_writeInt32 }, + { "writeInt64", "(J)V", (void *)JHwParcel_native_writeInt64 }, + { "writeFloat", "(F)V", (void *)JHwParcel_native_writeFloat }, + { "writeDouble", "(D)V", (void *)JHwParcel_native_writeDouble }, + + { "writeString", "(Ljava/lang/String;)V", + (void *)JHwParcel_native_writeString }, + + { "writeInt8Array", "(I[B)V", (void *)JHwParcel_native_writeInt8Array }, + { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector }, + { "writeInt16Array", "(I[S)V", (void *)JHwParcel_native_writeInt16Array }, + { "writeInt16Vector", "([S)V", (void *)JHwParcel_native_writeInt16Vector }, + { "writeInt32Array", "(I[I)V", (void *)JHwParcel_native_writeInt32Array }, + { "writeInt32Vector", "([I)V", (void *)JHwParcel_native_writeInt32Vector }, + { "writeInt64Array", "(I[J)V", (void *)JHwParcel_native_writeInt64Array }, + { "writeInt64Vector", "([J)V", (void *)JHwParcel_native_writeInt64Vector }, + { "writeFloatArray", "(I[F)V", (void *)JHwParcel_native_writeFloatArray }, + { "writeFloatVector", "([F)V", (void *)JHwParcel_native_writeFloatVector }, + { "writeDoubleArray", "(I[D)V", (void *)JHwParcel_native_writeDoubleArray }, + + { "writeDoubleVector", "([D)V", + (void *)JHwParcel_native_writeDoubleVector }, + + { "writeStringArray", "(I[Ljava/lang/String;)V", + (void *)JHwParcel_native_writeStringArray }, + + { "writeStringVector", "([Ljava/lang/String;)V", + (void *)JHwParcel_native_writeStringVector }, + + { "writeStrongBinder", "(L" PACKAGE_PATH "/IHwBinder;)V", + (void *)JHwParcel_native_writeStrongBinder }, + + { "enforceInterface", "(Ljava/lang/String;)V", + (void *)JHwParcel_native_enforceInterface }, + + { "readInt8", "()B", (void *)JHwParcel_native_readInt8 }, + { "readInt16", "()S", (void *)JHwParcel_native_readInt16 }, + { "readInt32", "()I", (void *)JHwParcel_native_readInt32 }, + { "readInt64", "()J", (void *)JHwParcel_native_readInt64 }, + { "readFloat", "()F", (void *)JHwParcel_native_readFloat }, + { "readDouble", "()D", (void *)JHwParcel_native_readDouble }, + + { "readString", "()Ljava/lang/String;", + (void *)JHwParcel_native_readString }, + + { "readInt8Array", "(I)[B", (void *)JHwParcel_native_readInt8Array }, + { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector }, + { "readInt16Array", "(I)[S", (void *)JHwParcel_native_readInt16Array }, + { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector }, + { "readInt32Array", "(I)[I", (void *)JHwParcel_native_readInt32Array }, + { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector }, + { "readInt64Array", "(I)[J", (void *)JHwParcel_native_readInt64Array }, + { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector }, + { "readFloatArray", "(I)[F", (void *)JHwParcel_native_readFloatArray }, + { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector }, + { "readDoubleArray", "(I)[D", (void *)JHwParcel_native_readDoubleArray }, + { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector }, + + { "readStringArray", "(I)[Ljava/lang/String;", + (void *)JHwParcel_native_readStringArray }, + + { "readStringVector", "()[Ljava/lang/String;", + (void *)JHwParcel_native_readStringVector }, + + { "readStrongBinder", "()L" PACKAGE_PATH "/IHwBinder;", + (void *)JHwParcel_native_readStrongBinder }, + + { "writeStatus", "(I)V", (void *)JHwParcel_native_writeStatus }, + + { "verifySuccess", "()V", (void *)JHwParcel_native_verifySuccess }, + + { "releaseTemporaryStorage", "()V", + (void *)JHwParcel_native_releaseTemporaryStorage }, + + { "send", "()V", (void *)JHwParcel_native_send }, +}; + +namespace android { + +int register_android_os_HwParcel(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h new file mode 100644 index 000000000000..708bbba1901c --- /dev/null +++ b/core/jni/android_os_HwParcel.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef ANDROID_OS_HW_PARCEL_H +#define ANDROID_OS_HW_PARCEL_H + +#include "hwbinder/EphemeralStorage.h" + +#include +#include +#include +#include +#include + +namespace android { + +struct JHwParcel : public RefBase { + static void InitClass(JNIEnv *env); + + static sp SetNativeContext( + JNIEnv *env, jobject thiz, const sp &context); + + static sp GetNativeContext(JNIEnv *env, jobject thiz); + + static jobject NewObject(JNIEnv *env); + + JHwParcel(JNIEnv *env, jobject thiz); + + void setParcel(hardware::Parcel *parcel, bool assumeOwnership); + hardware::Parcel *getParcel(); + + EphemeralStorage *getStorage(); + + void setTransactCallback(::android::hardware::IBinder::TransactCallback cb); + + void send(); + bool wasSent() const; + +protected: + virtual ~JHwParcel(); + +private: + jclass mClass; + jobject mObject; + + hardware::Parcel *mParcel; + bool mOwnsParcel; + + EphemeralStorage mStorage; + + ::android::hardware::IBinder::TransactCallback mTransactCallback; + bool mWasSent; + + DISALLOW_COPY_AND_ASSIGN(JHwParcel); +}; + +void signalExceptionForError(JNIEnv *env, status_t err); +int register_android_os_HwParcel(JNIEnv *env); + +} // namespace android + +#endif // ANDROID_OS_HW_PARCEL_H diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp new file mode 100644 index 000000000000..23d4fced2183 --- /dev/null +++ b/core/jni/android_os_HwRemoteBinder.cpp @@ -0,0 +1,196 @@ +/* + * 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 "JHwRemoteBinder" +#include + +#include "android_os_HwRemoteBinder.h" + +#include "android_os_HwParcel.h" + +#include +#include +#include +#include +#include + +#include "core_jni_helpers.h" + +using android::AndroidRuntime; + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "HwRemoteBinder" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct fields_t { + jfieldID contextID; + jmethodID constructID; + +} gFields; + +// static +void JHwRemoteBinder::InitClass(JNIEnv *env) { + ScopedLocalRef clazz(env, FindClassOrDie(env, CLASS_PATH)); + + gFields.contextID = + GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); + + gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "", "()V"); +} + +// static +sp JHwRemoteBinder::SetNativeContext( + JNIEnv *env, jobject thiz, const sp &context) { + sp old = + (JHwRemoteBinder *)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 JHwRemoteBinder::GetNativeContext( + JNIEnv *env, jobject thiz) { + return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID); +} + +// static +jobject JHwRemoteBinder::NewObject( + JNIEnv *env, const sp &binder) { + ScopedLocalRef clazz(env, FindClassOrDie(env, CLASS_PATH)); + + // XXX Have to look up the constructor here because otherwise that static + // class initializer isn't called and gFields.constructID is undefined :( + + jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "", "()V"); + + jobject obj = env->NewObject(clazz.get(), constructID); + JHwRemoteBinder::GetNativeContext(env, obj)->setBinder(binder); + + return obj; +} + +JHwRemoteBinder::JHwRemoteBinder( + JNIEnv *env, jobject thiz, const sp &binder) + : mBinder(binder) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); +} + +JHwRemoteBinder::~JHwRemoteBinder() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +sp JHwRemoteBinder::getBinder() { + return mBinder; +} + +void JHwRemoteBinder::setBinder(const sp &binder) { + mBinder = binder; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static void releaseNativeContext(void *nativeContext) { + sp binder = (JHwRemoteBinder *)nativeContext; + + if (binder != NULL) { + binder->decStrong(NULL /* id */); + } +} + +static jlong JHwRemoteBinder_native_init(JNIEnv *env) { + JHwRemoteBinder::InitClass(env); + + return reinterpret_cast(&releaseNativeContext); +} + +static void JHwRemoteBinder_native_setup_empty(JNIEnv *env, jobject thiz) { + sp context = + new JHwRemoteBinder(env, thiz, NULL /* service */); + + JHwRemoteBinder::SetNativeContext(env, thiz, context); +} + +static void JHwRemoteBinder_native_transact( + JNIEnv *env, + jobject thiz, + jint code, + jobject requestObj, + jobject replyObj, + jint flags) { + sp binder = + JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder(); + + if (requestObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); + return; + } + + const hardware::Parcel *request = + JHwParcel::GetNativeContext(env, requestObj)->getParcel(); + + hardware::Parcel *reply = + JHwParcel::GetNativeContext(env, replyObj)->getParcel(); + + status_t err = binder->transact(code, *request, reply, flags); + signalExceptionForError(env, err); +} + +static JNINativeMethod gMethods[] = { + { "native_init", "()J", (void *)JHwRemoteBinder_native_init }, + + { "native_setup_empty", "()V", + (void *)JHwRemoteBinder_native_setup_empty }, + + { "transact", + "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V", + (void *)JHwRemoteBinder_native_transact }, +}; + +namespace android { + +int register_android_os_HwRemoteBinder(JNIEnv *env) { + return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); +} + +} // namespace android + diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h new file mode 100644 index 000000000000..fd33338986a0 --- /dev/null +++ b/core/jni/android_os_HwRemoteBinder.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef ANDROID_OS_HW_REMOTE_BINDER_H +#define ANDROID_OS_HW_REMOTE_BINDER_H + +#include +#include +#include +#include + +namespace android { + +struct JHwRemoteBinder : public RefBase { + static void InitClass(JNIEnv *env); + + static sp SetNativeContext( + JNIEnv *env, jobject thiz, const sp &context); + + static sp GetNativeContext(JNIEnv *env, jobject thiz); + + static jobject NewObject(JNIEnv *env, const sp &binder); + + JHwRemoteBinder( + JNIEnv *env, jobject thiz, const sp &binder); + + sp getBinder(); + void setBinder(const sp &binder); + +protected: + virtual ~JHwRemoteBinder(); + +private: + jclass mClass; + jobject mObject; + + sp mBinder; + + DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder); +}; + +int register_android_os_HwRemoteBinder(JNIEnv *env); + +} // namespace android + +#endif // ANDROID_OS_HW_REMOTE_BINDER_H + diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp new file mode 100644 index 000000000000..e5087081ba55 --- /dev/null +++ b/core/jni/hwbinder/EphemeralStorage.cpp @@ -0,0 +1,178 @@ +/* + * 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_TAG "EphemeralStorage" +//#define LOG_NDEBUG 0 + +#include + +#include "EphemeralStorage.h" + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +namespace android { + +EphemeralStorage::EphemeralStorage() { +} + +EphemeralStorage::~EphemeralStorage() { + CHECK(mItems.empty()) + << "All item storage should have been released by now."; +} + +hidl_string *EphemeralStorage::allocStringArray(size_t size) { + Item item; + item.mType = TYPE_STRING_ARRAY; + item.mObj = NULL; + item.mPtr = new hidl_string[size]; + mItems.push_back(item); + + return static_cast(item.mPtr); +} + +void *EphemeralStorage::allocTemporaryStorage(size_t size) { + Item item; + item.mType = TYPE_STORAGE; + item.mObj = NULL; + item.mPtr = malloc(size); + mItems.push_back(item); + + return item.mPtr; +} + +const hidl_string *EphemeralStorage::allocTemporaryString( + JNIEnv *env, jstring stringObj) { + jstring obj = (jstring)env->NewGlobalRef(stringObj); + const char *val = env->GetStringUTFChars(obj, NULL); + + Item item; + item.mType = TYPE_STRING; + item.mObj = obj; + item.mPtr = (void *)val; + mItems.push_back(item); + + hidl_string *s = allocStringArray(1 /* size */); + s->setToExternal((char *)val, strlen(val)); + + return s; +} + +#define DEFINE_ALLOC_ARRAY_METHODS(Suffix,Type,NewType) \ +const Type *EphemeralStorage::allocTemporary ## Suffix ## Array( \ + JNIEnv *env, Type ## Array arrayObj) { \ + Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj); \ + const Type *val = env->Get ## NewType ## ArrayElements(obj, NULL); \ + \ + Item item; \ + item.mType = TYPE_ ## Suffix ## _ARRAY; \ + item.mObj = obj; \ + item.mPtr = (void *)val; \ + mItems.push_back(item); \ + \ + return val; \ +} + +#define DEFINE_ALLOC_VECTOR_METHODS(Suffix,Type,NewType) \ +const hidl_vec *EphemeralStorage::allocTemporary ## Suffix ## Vector( \ + JNIEnv *env, Type ## Array arrayObj) { \ + Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj); \ + jsize len = env->GetArrayLength(obj); \ + const Type *val = env->Get ## NewType ## ArrayElements(obj, NULL); \ + \ + Item item; \ + item.mType = TYPE_ ## Suffix ## _ARRAY; \ + item.mObj = obj; \ + item.mPtr = (void *)val; \ + mItems.push_back(item); \ + \ + hidl_vec *vec = \ + (hidl_vec *)allocTemporaryStorage(sizeof(hidl_vec)); \ + \ + vec->setToExternal(const_cast(val), len); \ + \ + return vec; \ +} + +DEFINE_ALLOC_ARRAY_METHODS(Int8,jbyte,Byte) +DEFINE_ALLOC_ARRAY_METHODS(Int16,jshort,Short) +DEFINE_ALLOC_ARRAY_METHODS(Int32,jint,Int) +DEFINE_ALLOC_ARRAY_METHODS(Int64,jlong,Long) +DEFINE_ALLOC_ARRAY_METHODS(Float,jfloat,Float) +DEFINE_ALLOC_ARRAY_METHODS(Double,jdouble,Double) + +DEFINE_ALLOC_VECTOR_METHODS(Int8,jbyte,Byte) +DEFINE_ALLOC_VECTOR_METHODS(Int16,jshort,Short) +DEFINE_ALLOC_VECTOR_METHODS(Int32,jint,Int) +DEFINE_ALLOC_VECTOR_METHODS(Int64,jlong,Long) +DEFINE_ALLOC_VECTOR_METHODS(Float,jfloat,Float) +DEFINE_ALLOC_VECTOR_METHODS(Double,jdouble,Double) + +#define DEFINE_RELEASE_ARRAY_CASE(Suffix,Type,NewType) \ + case TYPE_ ## Suffix ## _ARRAY: \ + { \ + env->Release ## NewType ## ArrayElements( \ + (Type ## Array)item.mObj, \ + (Type *)item.mPtr, \ + 0 /* mode */); \ + \ + env->DeleteGlobalRef(item.mObj); \ + break; \ + } + +void EphemeralStorage::release(JNIEnv *env) { + for (size_t i = mItems.size(); i--;) { + const Item &item = mItems[i]; + + switch (item.mType) { + case TYPE_STRING_ARRAY: + { + delete[] static_cast(item.mPtr); + break; + } + + case TYPE_STORAGE: + { + free(item.mPtr); + break; + } + + case TYPE_STRING: + { + env->ReleaseStringUTFChars( + (jstring)item.mObj, (const char *)item.mPtr); + + env->DeleteGlobalRef(item.mObj); + break; + } + + DEFINE_RELEASE_ARRAY_CASE(Int8,jbyte,Byte) + DEFINE_RELEASE_ARRAY_CASE(Int16,jshort,Short) + DEFINE_RELEASE_ARRAY_CASE(Int32,jint,Int) + DEFINE_RELEASE_ARRAY_CASE(Int64,jlong,Long) + DEFINE_RELEASE_ARRAY_CASE(Float,jfloat,Float) + DEFINE_RELEASE_ARRAY_CASE(Double,jdouble,Double) + + default: + CHECK(!"Should not be here"); + } + } + + mItems.clear(); +} + +} // namespace android + diff --git a/core/jni/hwbinder/EphemeralStorage.h b/core/jni/hwbinder/EphemeralStorage.h new file mode 100644 index 000000000000..1273003e6176 --- /dev/null +++ b/core/jni/hwbinder/EphemeralStorage.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef EPHEMERAL_STORAGE_H_ + +#define EPHEMERAL_STORAGE_H_ + +#include +#include +#include +#include + +namespace android { + +#define DECLARE_ALLOC_METHODS(Suffix,Type) \ + const Type *allocTemporary ## Suffix ## Array( \ + JNIEnv *env, Type ## Array arrayObj); \ + \ + const ::android::hardware::hidl_vec * \ + allocTemporary ## Suffix ## Vector( \ + JNIEnv *env, Type ## Array arrayObj); + +struct EphemeralStorage { + EphemeralStorage(); + ~EphemeralStorage(); + + void release(JNIEnv *env); + + hardware::hidl_string *allocStringArray(size_t size); + + void *allocTemporaryStorage(size_t size); + + const ::android::hardware::hidl_string *allocTemporaryString( + JNIEnv *env, jstring stringObj); + + DECLARE_ALLOC_METHODS(Int8,jbyte) + DECLARE_ALLOC_METHODS(Int16,jshort) + DECLARE_ALLOC_METHODS(Int32,jint) + DECLARE_ALLOC_METHODS(Int64,jlong) + DECLARE_ALLOC_METHODS(Float,jfloat) + DECLARE_ALLOC_METHODS(Double,jdouble) + +private: + enum Type { + TYPE_STRING_ARRAY, + TYPE_STORAGE, + TYPE_STRING, + TYPE_Int8_ARRAY, + TYPE_Int16_ARRAY, + TYPE_Int32_ARRAY, + TYPE_Int64_ARRAY, + TYPE_Float_ARRAY, + TYPE_Double_ARRAY, + }; + + struct Item { + Type mType; + jobject mObj; + void *mPtr; + }; + + Vector mItems; + + DISALLOW_COPY_AND_ASSIGN(EphemeralStorage); +}; + +#undef DECLARE_ALLOC_METHODS + +} // namespace android + +#endif // EPHEMERAL_STORAGE_H_ -- 2.11.0