summaryrefslogtreecommitdiffstats
path: root/core/jni/android_util_Binder.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
commit9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch)
treed88beb88001f2482911e3d28e43833b50e4b4e97 /core/jni/android_util_Binder.cpp
parentd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff)
downloadframeworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/jni/android_util_Binder.cpp')
-rw-r--r--core/jni/android_util_Binder.cpp1511
1 files changed, 1511 insertions, 0 deletions
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
new file mode 100644
index 0000000..7325432
--- /dev/null
+++ b/core/jni/android_util_Binder.cpp
@@ -0,0 +1,1511 @@
+/*
+ * Copyright (C) 2006 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 "JavaBinder"
+//#define LOG_NDEBUG 0
+
+#include "android_util_Binder.h"
+#include "JNIHelp.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include <utils/Atomic.h>
+#include <utils/IInterface.h>
+#include <utils/IPCThreadState.h>
+#include <utils/Log.h>
+#include <utils/Parcel.h>
+#include <utils/ProcessState.h>
+#include <utils/IServiceManager.h>
+
+#include <android_runtime/AndroidRuntime.h>
+
+//#undef LOGV
+//#define LOGV(...) fprintf(stderr, __VA_ARGS__)
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static struct bindernative_offsets_t
+{
+ // Class state.
+ jclass mClass;
+ jmethodID mExecTransact;
+
+ // Object state.
+ jfieldID mObject;
+
+} gBinderOffsets;
+
+// ----------------------------------------------------------------------------
+
+static struct binderinternal_offsets_t
+{
+ // Class state.
+ jclass mClass;
+ jmethodID mForceGc;
+
+} gBinderInternalOffsets;
+
+// ----------------------------------------------------------------------------
+
+static struct debug_offsets_t
+{
+ // Class state.
+ jclass mClass;
+
+} gDebugOffsets;
+
+// ----------------------------------------------------------------------------
+
+static struct weakreference_offsets_t
+{
+ // Class state.
+ jclass mClass;
+ jmethodID mGet;
+
+} gWeakReferenceOffsets;
+
+static struct error_offsets_t
+{
+ jclass mClass;
+} gErrorOffsets;
+
+// ----------------------------------------------------------------------------
+
+static struct binderproxy_offsets_t
+{
+ // Class state.
+ jclass mClass;
+ jmethodID mConstructor;
+ jmethodID mSendDeathNotice;
+
+ // Object state.
+ jfieldID mObject;
+ jfieldID mSelf;
+
+} gBinderProxyOffsets;
+
+// ----------------------------------------------------------------------------
+
+static struct parcel_offsets_t
+{
+ jfieldID mObject;
+ jfieldID mOwnObject;
+} gParcelOffsets;
+
+static struct log_offsets_t
+{
+ // Class state.
+ jclass mClass;
+ jmethodID mLogE;
+} gLogOffsets;
+
+static struct file_descriptor_offsets_t
+{
+ jclass mClass;
+ jmethodID mConstructor;
+ jfieldID mDescriptor;
+} gFileDescriptorOffsets;
+
+static struct parcel_file_descriptor_offsets_t
+{
+ jclass mClass;
+ jmethodID mConstructor;
+} gParcelFileDescriptorOffsets;
+
+// ****************************************************************************
+// ****************************************************************************
+// ****************************************************************************
+
+static volatile int32_t gNumRefsCreated = 0;
+static volatile int32_t gNumProxyRefs = 0;
+static volatile int32_t gNumLocalRefs = 0;
+static volatile int32_t gNumDeathRefs = 0;
+
+static void incRefsCreated(JNIEnv* env)
+{
+ int old = android_atomic_inc(&gNumRefsCreated);
+ if (old == 200) {
+ android_atomic_and(0, &gNumRefsCreated);
+ env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
+ gBinderInternalOffsets.mForceGc);
+ } else {
+ LOGV("Now have %d binder ops", old);
+ }
+}
+
+static JavaVM* jnienv_to_javavm(JNIEnv* env)
+{
+ JavaVM* vm;
+ return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
+}
+
+static JNIEnv* javavm_to_jnienv(JavaVM* vm)
+{
+ JNIEnv* env;
+ return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
+}
+
+static void report_exception(JNIEnv* env, jthrowable excep, const char* msg)
+{
+ env->ExceptionClear();
+
+ jstring tagstr = env->NewStringUTF(LOG_TAG);
+ jstring msgstr = env->NewStringUTF(msg);
+
+ if ((tagstr == NULL) || (msgstr == NULL)) {
+ env->ExceptionClear(); /* assume exception (OOM?) was thrown */
+ LOGE("Unable to call Log.e()\n");
+ LOGE("%s", msg);
+ goto bail;
+ }
+
+ env->CallStaticIntMethod(
+ gLogOffsets.mClass, gLogOffsets.mLogE, tagstr, msgstr, excep);
+ if (env->ExceptionCheck()) {
+ /* attempting to log the failure has failed */
+ LOGW("Failed trying to log exception, msg='%s'\n", msg);
+ env->ExceptionClear();
+ }
+
+ if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) {
+ /*
+ * It's an Error: Reraise the exception, detach this thread, and
+ * wait for the fireworks. Die even more blatantly after a minute
+ * if the gentler attempt doesn't do the trick.
+ *
+ * The GetJavaVM function isn't on the "approved" list of JNI calls
+ * that can be made while an exception is pending, so we want to
+ * get the VM ptr, throw the exception, and then detach the thread.
+ */
+ JavaVM* vm = jnienv_to_javavm(env);
+ env->Throw(excep);
+ vm->DetachCurrentThread();
+ sleep(60);
+ LOGE("Forcefully exiting");
+ exit(1);
+ *((int *) 1) = 1;
+ }
+
+bail:
+ /* discard local refs created for us by VM */
+ env->DeleteLocalRef(tagstr);
+ env->DeleteLocalRef(msgstr);
+}
+
+class JavaBBinderHolder;
+
+class JavaBBinder : public BBinder
+{
+public:
+ JavaBBinder(JNIEnv* env, jobject object)
+ : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
+ {
+ LOGV("Creating JavaBBinder %p\n", this);
+ android_atomic_inc(&gNumLocalRefs);
+ incRefsCreated(env);
+ }
+
+ bool checkSubclass(const void* subclassID) const
+ {
+ return subclassID == &gBinderOffsets;
+ }
+
+ jobject object() const
+ {
+ return mObject;
+ }
+
+protected:
+ virtual ~JavaBBinder()
+ {
+ LOGV("Destroying JavaBBinder %p\n", this);
+ android_atomic_dec(&gNumLocalRefs);
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ env->DeleteGlobalRef(mObject);
+ }
+
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
+ {
+ JNIEnv* env = javavm_to_jnienv(mVM);
+
+ LOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
+
+ //printf("Transact from %p to Java code sending: ", this);
+ //data.print();
+ //printf("\n");
+ jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
+ code, (int32_t)&data, (int32_t)reply, flags);
+ jthrowable excep = env->ExceptionOccurred();
+ if (excep) {
+ report_exception(env, excep,
+ "*** Uncaught remote exception! "
+ "(Exceptions are not yet supported across processes.)");
+ res = JNI_FALSE;
+
+ /* clean up JNI local ref -- we don't return to Java code */
+ env->DeleteLocalRef(excep);
+ }
+
+ //aout << "onTransact to Java code; result=" << res << endl
+ // << "Transact from " << this << " to Java code returning "
+ // << reply << ": " << *reply << endl;
+ return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
+ }
+
+ virtual status_t dump(int fd, const Vector<String16>& args)
+ {
+ return 0;
+ }
+
+private:
+ JavaVM* const mVM;
+ jobject const mObject;
+};
+
+// ----------------------------------------------------------------------------
+
+class JavaBBinderHolder : public RefBase
+{
+public:
+ JavaBBinderHolder(JNIEnv* env, jobject object)
+ : mObject(object)
+ {
+ LOGV("Creating JavaBBinderHolder for Object %p\n", object);
+ }
+ ~JavaBBinderHolder()
+ {
+ LOGV("Destroying JavaBBinderHolder for Object %p\n", mObject);
+ }
+
+ sp<JavaBBinder> get(JNIEnv* env)
+ {
+ AutoMutex _l(mLock);
+ sp<JavaBBinder> b = mBinder.promote();
+ if (b == NULL) {
+ b = new JavaBBinder(env, mObject);
+ mBinder = b;
+ LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n",
+ b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount());
+ }
+
+ return b;
+ }
+
+ sp<JavaBBinder> getExisting()
+ {
+ AutoMutex _l(mLock);
+ return mBinder.promote();
+ }
+
+private:
+ Mutex mLock;
+ jobject mObject;
+ wp<JavaBBinder> mBinder;
+};
+
+// ----------------------------------------------------------------------------
+
+class JavaDeathRecipient : public IBinder::DeathRecipient
+{
+public:
+ JavaDeathRecipient(JNIEnv* env, jobject object)
+ : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
+ mHoldsRef(true)
+ {
+ incStrong(this);
+ android_atomic_inc(&gNumDeathRefs);
+ incRefsCreated(env);
+ }
+
+ void binderDied(const wp<IBinder>& who)
+ {
+ JNIEnv* env = javavm_to_jnienv(mVM);
+
+ LOGV("Receiving binderDied() on JavaDeathRecipient %p\n", this);
+
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
+ gBinderProxyOffsets.mSendDeathNotice, mObject);
+ jthrowable excep = env->ExceptionOccurred();
+ if (excep) {
+ report_exception(env, excep,
+ "*** Uncaught exception returned from death notification!");
+ }
+
+ clearReference();
+ }
+
+ void clearReference()
+ {
+ bool release = false;
+ mLock.lock();
+ if (mHoldsRef) {
+ mHoldsRef = false;
+ release = true;
+ }
+ mLock.unlock();
+ if (release) {
+ decStrong(this);
+ }
+ }
+
+protected:
+ virtual ~JavaDeathRecipient()
+ {
+ //LOGI("Removing death ref: recipient=%p\n", mObject);
+ android_atomic_dec(&gNumDeathRefs);
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ env->DeleteGlobalRef(mObject);
+ }
+
+private:
+ JavaVM* const mVM;
+ jobject const mObject;
+ Mutex mLock;
+ bool mHoldsRef;
+};
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+static void proxy_cleanup(const void* id, void* obj, void* cleanupCookie)
+{
+ android_atomic_dec(&gNumProxyRefs);
+ JNIEnv* env = javavm_to_jnienv((JavaVM*)cleanupCookie);
+ env->DeleteGlobalRef((jobject)obj);
+}
+
+static Mutex mProxyLock;
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
+{
+ if (val == NULL) return NULL;
+
+ if (val->checkSubclass(&gBinderOffsets)) {
+ // One of our own!
+ jobject object = static_cast<JavaBBinder*>(val.get())->object();
+ //printf("objectForBinder %p: it's our own %p!\n", val.get(), object);
+ return object;
+ }
+
+ // For the rest of the function we will hold this lock, to serialize
+ // looking/creation of Java proxies for native Binder proxies.
+ AutoMutex _l(mProxyLock);
+
+ // Someone else's... do we know about it?
+ jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
+ if (object != NULL) {
+ jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet);
+ if (res != NULL) {
+ LOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
+ return res;
+ }
+ LOGV("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
+ android_atomic_dec(&gNumProxyRefs);
+ val->detachObject(&gBinderProxyOffsets);
+ env->DeleteGlobalRef(object);
+ }
+
+ object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
+ if (object != NULL) {
+ LOGV("objectForBinder %p: created new %p!\n", val.get(), object);
+ // The proxy holds a reference to the native object.
+ env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
+ val->incStrong(object);
+
+ // The native object needs to hold a weak reference back to the
+ // proxy, so we can retrieve the same proxy if it is still active.
+ jobject refObject = env->NewGlobalRef(
+ env->GetObjectField(object, gBinderProxyOffsets.mSelf));
+ val->attachObject(&gBinderProxyOffsets, refObject,
+ jnienv_to_javavm(env), proxy_cleanup);
+
+ // Note that a new object reference has been created.
+ android_atomic_inc(&gNumProxyRefs);
+ incRefsCreated(env);
+ }
+
+ return object;
+}
+
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
+{
+ if (obj == NULL) return NULL;
+
+ if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
+ JavaBBinderHolder* jbh = (JavaBBinderHolder*)
+ env->GetIntField(obj, gBinderOffsets.mObject);
+ return jbh != NULL ? jbh->get(env) : NULL;
+ }
+
+ if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
+ return (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ }
+
+ LOGW("ibinderForJavaObject: %p is not a Binder object", obj);
+ return NULL;
+}
+
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj)
+{
+ if (obj) {
+ Parcel* p = (Parcel*)env->GetIntField(obj, gParcelOffsets.mObject);
+ if (p != NULL) {
+ return p;
+ }
+ jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!");
+ }
+ return NULL;
+}
+
+jobject newFileDescriptor(JNIEnv* env, int fd)
+{
+ jobject object = env->NewObject(
+ gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
+ if (object != NULL) {
+ //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
+ env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
+ }
+ return object;
+}
+
+jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
+{
+ return env->NewObject(
+ gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc);
+}
+
+void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
+{
+ switch (err) {
+ case UNKNOWN_ERROR:
+ jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+ 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 NOT_ENOUGH_DATA:
+ jniThrowException(env, "android/os/ParcelFormatException", "Not enough data");
+ break;
+ case NO_INIT:
+ jniThrowException(env, "java/lang/RuntimeException", "Not initialized");
+ break;
+ case ALREADY_EXISTS:
+ jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
+ break;
+ case DEAD_OBJECT:
+ jniThrowException(env, "android/os/DeadObjectException", NULL);
+ break;
+ case UNKNOWN_TRANSACTION:
+ jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
+ break;
+ case FAILED_TRANSACTION:
+ LOGE("!!! FAILED BINDER TRANSACTION !!!");
+ //jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large");
+ break;
+ default:
+ LOGE("Unknown binder error code. 0x%x", err);
+ }
+}
+
+}
+
+// ----------------------------------------------------------------------------
+
+static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
+{
+ return IPCThreadState::self()->getCallingPid();
+}
+
+static jint android_os_Binder_getCallingUid(JNIEnv* env, jobject clazz)
+{
+ return IPCThreadState::self()->getCallingUid();
+}
+
+static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
+{
+ return IPCThreadState::self()->clearCallingIdentity();
+}
+
+static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
+{
+ IPCThreadState::self()->restoreCallingIdentity(token);
+}
+
+static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
+{
+ IPCThreadState::self()->flushCommands();
+}
+
+static void android_os_Binder_init(JNIEnv* env, jobject clazz)
+{
+ JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz);
+ if (jbh == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return;
+ }
+ LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh);
+ jbh->incStrong(clazz);
+ env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh);
+}
+
+static void android_os_Binder_destroy(JNIEnv* env, jobject clazz)
+{
+ JavaBBinderHolder* jbh = (JavaBBinderHolder*)
+ env->GetIntField(clazz, gBinderOffsets.mObject);
+ env->SetIntField(clazz, gBinderOffsets.mObject, 0);
+ LOGV("Java Binder %p: removing ref on holder %p", clazz, jbh);
+ jbh->decStrong(clazz);
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gBinderMethods[] = {
+ /* name, signature, funcPtr */
+ { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
+ { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
+ { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
+ { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
+ { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
+ { "init", "()V", (void*)android_os_Binder_init },
+ { "destroy", "()V", (void*)android_os_Binder_destroy }
+};
+
+const char* const kBinderPathName = "android/os/Binder";
+
+static int int_register_android_os_Binder(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass(kBinderPathName);
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Binder");
+
+ gBinderOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gBinderOffsets.mExecTransact
+ = env->GetMethodID(clazz, "execTransact", "(IIII)Z");
+ assert(gBinderOffsets.mExecTransact);
+
+ gBinderOffsets.mObject
+ = env->GetFieldID(clazz, "mObject", "I");
+ assert(gBinderOffsets.mObject);
+
+ return AndroidRuntime::registerNativeMethods(
+ env, kBinderPathName,
+ gBinderMethods, NELEM(gBinderMethods));
+}
+
+// ****************************************************************************
+// ****************************************************************************
+// ****************************************************************************
+
+namespace android {
+
+jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz)
+{
+ return gNumLocalRefs;
+}
+
+jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
+{
+ return gNumProxyRefs;
+}
+
+jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
+{
+ return gNumDeathRefs;
+}
+
+}
+
+// ****************************************************************************
+// ****************************************************************************
+// ****************************************************************************
+
+static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
+{
+ sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
+ return javaObjectForIBinder(env, b);
+}
+
+static void android_os_BinderInternal_joinThreadPool(JNIEnv* env, jobject clazz)
+{
+ sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
+ android::IPCThreadState::self()->joinThreadPool();
+}
+
+static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz)
+{
+ LOGV("Gc has executed, clearing binder ops");
+ android_atomic_and(0, &gNumRefsCreated);
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gBinderInternalMethods[] = {
+ /* name, signature, funcPtr */
+ { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
+ { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },
+ { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }
+};
+
+const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";
+
+static int int_register_android_os_BinderInternal(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass(kBinderInternalPathName);
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class com.android.internal.os.BinderInternal");
+
+ gBinderInternalOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gBinderInternalOffsets.mForceGc
+ = env->GetStaticMethodID(clazz, "forceBinderGc", "()V");
+ assert(gBinderInternalOffsets.mForceGc);
+
+ return AndroidRuntime::registerNativeMethods(
+ env, kBinderInternalPathName,
+ gBinderInternalMethods, NELEM(gBinderInternalMethods));
+}
+
+// ****************************************************************************
+// ****************************************************************************
+// ****************************************************************************
+
+static jboolean android_os_BinderProxy_pingBinder(JNIEnv* env, jobject obj)
+{
+ IBinder* target = (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ if (target == NULL) {
+ return JNI_FALSE;
+ }
+ status_t err = target->pingBinder();
+ return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
+}
+
+static jstring android_os_BinderProxy_getInterfaceDescriptor(JNIEnv* env, jobject obj)
+{
+ IBinder* target = (IBinder*) env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ if (target != NULL) {
+ String16 desc = target->getInterfaceDescriptor();
+ return env->NewString(desc.string(), desc.size());
+ }
+ jniThrowException(env, "java/lang/RuntimeException",
+ "No binder found for object");
+ return NULL;
+}
+
+static jboolean android_os_BinderProxy_isBinderAlive(JNIEnv* env, jobject obj)
+{
+ IBinder* target = (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ if (target == NULL) {
+ return JNI_FALSE;
+ }
+ bool alive = target->isBinderAlive();
+ return alive ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
+ jint code, jobject dataObj,
+ jobject replyObj, jint flags)
+{
+ if (dataObj == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return JNI_FALSE;
+ }
+
+ Parcel* data = parcelForJavaObject(env, dataObj);
+ if (data == NULL) {
+ return JNI_FALSE;
+ }
+ Parcel* reply = parcelForJavaObject(env, replyObj);
+ if (reply == NULL && replyObj != NULL) {
+ return JNI_FALSE;
+ }
+
+ IBinder* target = (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ if (target == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
+ return JNI_FALSE;
+ }
+
+ LOGV("Java code calling transact on %p in Java object %p with code %d\n",
+ target, obj, code);
+ //printf("Transact from Java code to %p sending: ", target); data->print();
+ status_t err = target->transact(code, *data, reply, flags);
+ //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
+ if (err == NO_ERROR) {
+ return JNI_TRUE;
+ } else if (err == UNKNOWN_TRANSACTION) {
+ return JNI_FALSE;
+ }
+
+ signalExceptionForError(env, obj, err);
+ return JNI_FALSE;
+}
+
+static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
+ jobject recipient, jint flags)
+{
+ if (recipient == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return;
+ }
+
+ IBinder* target = (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ if (target == NULL) {
+ LOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
+ assert(false);
+ }
+
+ LOGV("linkToDeath: binder=%p recipient=%p\n", target, recipient);
+
+ if (!target->localBinder()) {
+ sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient);
+ status_t err = target->linkToDeath(jdr, recipient, flags);
+ if (err != NO_ERROR) {
+ // Failure adding the death recipient, so clear its reference
+ // now.
+ jdr->clearReference();
+ signalExceptionForError(env, obj, err);
+ }
+ }
+}
+
+static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
+ jobject recipient, jint flags)
+{
+ jboolean res = JNI_FALSE;
+ if (recipient == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return res;
+ }
+
+ IBinder* target = (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ if (target == NULL) {
+ LOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
+ return JNI_FALSE;
+ }
+
+ LOGV("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
+
+ if (!target->localBinder()) {
+ wp<IBinder::DeathRecipient> dr;
+ status_t err = target->unlinkToDeath(NULL, recipient, flags, &dr);
+ if (err == NO_ERROR && dr != NULL) {
+ sp<IBinder::DeathRecipient> sdr = dr.promote();
+ JavaDeathRecipient* jdr = static_cast<JavaDeathRecipient*>(sdr.get());
+ if (jdr != NULL) {
+ jdr->clearReference();
+ }
+ }
+ if (err == NO_ERROR || err == DEAD_OBJECT) {
+ res = JNI_TRUE;
+ } else {
+ jniThrowException(env, "java/util/NoSuchElementException",
+ "Death link does not exist");
+ }
+ }
+
+ return res;
+}
+
+static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
+{
+ IBinder* b = (IBinder*)
+ env->GetIntField(obj, gBinderProxyOffsets.mObject);
+ LOGV("Destroying BinderProxy %p: binder=%p\n", obj, b);
+ env->SetIntField(obj, gBinderProxyOffsets.mObject, 0);
+ b->decStrong(obj);
+ IPCThreadState::self()->flushCommands();
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gBinderProxyMethods[] = {
+ /* name, signature, funcPtr */
+ {"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
+ {"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
+ {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
+ {"transact", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
+ {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
+ {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"destroy", "()V", (void*)android_os_BinderProxy_destroy},
+};
+
+const char* const kBinderProxyPathName = "android/os/BinderProxy";
+
+static int int_register_android_os_BinderProxy(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("java/lang/ref/WeakReference");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.ref.WeakReference");
+ gWeakReferenceOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gWeakReferenceOffsets.mGet
+ = env->GetMethodID(clazz, "get", "()Ljava/lang/Object;");
+ assert(gWeakReferenceOffsets.mGet);
+
+ clazz = env->FindClass("java/lang/Error");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.Error");
+ gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+
+ clazz = env->FindClass(kBinderProxyPathName);
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.BinderProxy");
+
+ gBinderProxyOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gBinderProxyOffsets.mConstructor
+ = env->GetMethodID(clazz, "<init>", "()V");
+ assert(gBinderProxyOffsets.mConstructor);
+ gBinderProxyOffsets.mSendDeathNotice
+ = env->GetStaticMethodID(clazz, "sendDeathNotice", "(Landroid/os/IBinder$DeathRecipient;)V");
+ assert(gBinderProxyOffsets.mSendDeathNotice);
+
+ gBinderProxyOffsets.mObject
+ = env->GetFieldID(clazz, "mObject", "I");
+ assert(gBinderProxyOffsets.mObject);
+ gBinderProxyOffsets.mSelf
+ = env->GetFieldID(clazz, "mSelf", "Ljava/lang/ref/WeakReference;");
+ assert(gBinderProxyOffsets.mSelf);
+
+ return AndroidRuntime::registerNativeMethods(
+ env, kBinderProxyPathName,
+ gBinderProxyMethods, NELEM(gBinderProxyMethods));
+}
+
+// ****************************************************************************
+// ****************************************************************************
+// ****************************************************************************
+
+static jint android_os_Parcel_dataSize(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ return parcel ? parcel->dataSize() : 0;
+}
+
+static jint android_os_Parcel_dataAvail(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ return parcel ? parcel->dataAvail() : 0;
+}
+
+static jint android_os_Parcel_dataPosition(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ return parcel ? parcel->dataPosition() : 0;
+}
+
+static jint android_os_Parcel_dataCapacity(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ return parcel ? parcel->dataCapacity() : 0;
+}
+
+static void android_os_Parcel_setDataSize(JNIEnv* env, jobject clazz, jint size)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->setDataSize(size);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_setDataPosition(JNIEnv* env, jobject clazz, jint pos)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ parcel->setDataPosition(pos);
+ }
+}
+
+static void android_os_Parcel_setDataCapacity(JNIEnv* env, jobject clazz, jint size)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->setDataCapacity(size);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeNative(JNIEnv* env, jobject clazz,
+ jobject data, jint offset,
+ jint length)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel == NULL) {
+ return;
+ }
+ void *dest;
+
+ const status_t err = parcel->writeInt32(length);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+
+ dest = parcel->writeInplace(length);
+
+ if (dest == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return;
+ }
+
+ jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0);
+ if (ar) {
+ memcpy(dest, ar, length);
+ env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0);
+ }
+}
+
+
+static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->writeInt32(val);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->writeInt64(val);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeFloat(JNIEnv* env, jobject clazz, jfloat val)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->writeFloat(val);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeDouble(JNIEnv* env, jobject clazz, jdouble val)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->writeDouble(val);
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeString(JNIEnv* env, jobject clazz, jstring val)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ status_t err = NO_MEMORY;
+ if (val) {
+ const jchar* str = env->GetStringCritical(val, 0);
+ if (str) {
+ err = parcel->writeString16(str, env->GetStringLength(val));
+ env->ReleaseStringCritical(val, str);
+ }
+ } else {
+ err = parcel->writeString16(NULL, 0);
+ }
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const status_t err = parcel->writeDupFileDescriptor(
+ env->GetIntField(object, gFileDescriptorOffsets.mDescriptor));
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ }
+ }
+}
+
+static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jobject clazz)
+{
+ jbyteArray ret = NULL;
+
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ int32_t len = parcel->readInt32();
+
+ // sanity check the stored length against the true data size
+ if (len >= 0 && len <= (int32_t)parcel->dataAvail()) {
+ ret = env->NewByteArray(len);
+
+ if (ret != NULL) {
+ jbyte* a2 = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
+ if (a2) {
+ const void* data = parcel->readInplace(len);
+ memcpy(a2, data, len);
+ env->ReleasePrimitiveArrayCritical(ret, a2, 0);
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ return parcel->readInt32();
+ }
+ return 0;
+}
+
+static jlong android_os_Parcel_readLong(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ return parcel->readInt64();
+ }
+ return 0;
+}
+
+static jfloat android_os_Parcel_readFloat(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ return parcel->readFloat();
+ }
+ return 0;
+}
+
+static jdouble android_os_Parcel_readDouble(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ return parcel->readDouble();
+ }
+ return 0;
+}
+
+static jstring android_os_Parcel_readString(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ size_t len;
+ const char16_t* str = parcel->readString16Inplace(&len);
+ if (str) {
+ return env->NewString(str, len);
+ }
+ return NULL;
+ }
+ return NULL;
+}
+
+static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ return javaObjectForIBinder(env, parcel->readStrongBinder());
+ }
+ return NULL;
+}
+
+static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ int fd = parcel->readFileDescriptor();
+ if (fd < 0) return NULL;
+ fd = dup(fd);
+ if (fd < 0) return NULL;
+ jobject object = env->NewObject(
+ gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
+ if (object != NULL) {
+ //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
+ env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
+ }
+ return object;
+ }
+ return NULL;
+}
+
+static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
+ jstring name, jint mode)
+{
+ if (name == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return NULL;
+ }
+ const jchar* str = env->GetStringCritical(name, 0);
+ if (str == NULL) {
+ // Whatever, whatever.
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+ String8 name8(str, env->GetStringLength(name));
+ env->ReleaseStringCritical(name, str);
+ int flags=0;
+ switch (mode&0x30000000) {
+ case 0:
+ case 0x10000000:
+ flags = O_RDONLY;
+ break;
+ case 0x20000000:
+ flags = O_WRONLY;
+ break;
+ case 0x30000000:
+ flags = O_RDWR;
+ break;
+ }
+
+ if (mode&0x08000000) flags |= O_CREAT;
+ if (mode&0x04000000) flags |= O_TRUNC;
+ if (mode&0x02000000) flags |= O_APPEND;
+
+ int realMode = S_IRWXU|S_IRWXG;
+ if (mode&0x00000001) realMode |= S_IROTH;
+ if (mode&0x00000002) realMode |= S_IWOTH;
+
+ int fd = open(name8.string(), flags, realMode);
+ if (fd < 0) {
+ jniThrowException(env, "java/io/FileNotFoundException", NULL);
+ return NULL;
+ }
+ jobject object = newFileDescriptor(env, fd);
+ if (object == NULL) {
+ close(fd);
+ }
+ return object;
+}
+
+static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
+{
+ int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
+ if (fd >= 0) {
+ env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
+ //LOGI("Closing ParcelFileDescriptor %d\n", fd);
+ close(fd);
+ }
+}
+
+static void android_os_Parcel_freeBuffer(JNIEnv* env, jobject clazz)
+{
+ int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject);
+ if (own) {
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ //LOGI("Parcel.freeBuffer() called for C++ Parcel %p\n", parcel);
+ parcel->freeData();
+ }
+ }
+}
+
+static void android_os_Parcel_init(JNIEnv* env, jobject clazz, jint parcelInt)
+{
+ Parcel* parcel = (Parcel*)parcelInt;
+ int own = 0;
+ if (!parcel) {
+ //LOGI("Initializing obj %p: creating new Parcel\n", clazz);
+ own = 1;
+ parcel = new Parcel;
+ } else {
+ //LOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel);
+ }
+ if (parcel == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return;
+ }
+ //LOGI("Initializing obj %p from C++ Parcel %p, own=%d\n", clazz, parcel, own);
+ env->SetIntField(clazz, gParcelOffsets.mOwnObject, own);
+ env->SetIntField(clazz, gParcelOffsets.mObject, (int)parcel);
+}
+
+static void android_os_Parcel_destroy(JNIEnv* env, jobject clazz)
+{
+ int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject);
+ if (own) {
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ env->SetIntField(clazz, gParcelOffsets.mObject, 0);
+ //LOGI("Destroying obj %p: deleting C++ Parcel %p\n", clazz, parcel);
+ delete parcel;
+ } else {
+ env->SetIntField(clazz, gParcelOffsets.mObject, 0);
+ //LOGI("Destroying obj %p: leaving C++ Parcel %p\n", clazz);
+ }
+}
+
+static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jobject clazz)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel == NULL) {
+ return NULL;
+ }
+
+ // do not marshall if there are binder objects in the parcel
+ if (parcel->objectsCount())
+ {
+ jniThrowException(env, "java/lang/RuntimeException", "Tried to marshall a Parcel that contained Binder objects.");
+ return NULL;
+ }
+
+ jbyteArray ret = env->NewByteArray(parcel->dataSize());
+
+ if (ret != NULL)
+ {
+ jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
+ if (array != NULL)
+ {
+ memcpy(array, parcel->data(), parcel->dataSize());
+ env->ReleasePrimitiveArrayCritical(ret, array, 0);
+ }
+ }
+
+ return ret;
+}
+
+static void android_os_Parcel_unmarshall(JNIEnv* env, jobject clazz, jbyteArray data, jint offset, jint length)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel == NULL || length < 0) {
+ return;
+ }
+
+ jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
+ if (array)
+ {
+ parcel->setDataSize(length);
+ parcel->setDataPosition(0);
+
+ void* raw = parcel->writeInplace(length);
+ memcpy(raw, (array + offset), length);
+
+ env->ReleasePrimitiveArrayCritical(data, array, 0);
+ }
+}
+
+static void android_os_Parcel_appendFrom(JNIEnv* env, jobject clazz, jobject parcel, jint offset, jint length)
+{
+ Parcel* thisParcel = parcelForJavaObject(env, clazz);
+ if (thisParcel == NULL) {
+ return;
+ }
+ Parcel* otherParcel = parcelForJavaObject(env, parcel);
+ if (otherParcel == NULL) {
+ return;
+ }
+
+ (void) thisParcel->appendFrom(otherParcel, offset, length);
+}
+
+static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jobject clazz)
+{
+ jboolean ret = JNI_FALSE;
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ if (parcel->hasFileDescriptors()) {
+ ret = JNI_TRUE;
+ }
+ }
+ return ret;
+}
+
+static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jobject clazz, jstring name)
+{
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ // In the current implementation, the token is just the serialized interface name that
+ // the caller expects to be invoking
+ const jchar* str = env->GetStringCritical(name, 0);
+ if (str != NULL) {
+ parcel->writeInterfaceToken(String16(str, env->GetStringLength(name)));
+ env->ReleaseStringCritical(name, str);
+ }
+ }
+}
+
+static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstring name)
+{
+ jboolean ret = JNI_FALSE;
+
+ Parcel* parcel = parcelForJavaObject(env, clazz);
+ if (parcel != NULL) {
+ const jchar* str = env->GetStringCritical(name, 0);
+ if (str) {
+ bool isValid = parcel->enforceInterface(String16(str, env->GetStringLength(name)));
+ env->ReleaseStringCritical(name, str);
+ if (isValid) {
+ return; // everything was correct -> return silently
+ }
+ }
+ }
+
+ // all error conditions wind up here
+ jniThrowException(env, "java/lang/SecurityException",
+ "Binder invocation to an incorrect interface");
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gParcelMethods[] = {
+ {"dataSize", "()I", (void*)android_os_Parcel_dataSize},
+ {"dataAvail", "()I", (void*)android_os_Parcel_dataAvail},
+ {"dataPosition", "()I", (void*)android_os_Parcel_dataPosition},
+ {"dataCapacity", "()I", (void*)android_os_Parcel_dataCapacity},
+ {"setDataSize", "(I)V", (void*)android_os_Parcel_setDataSize},
+ {"setDataPosition", "(I)V", (void*)android_os_Parcel_setDataPosition},
+ {"setDataCapacity", "(I)V", (void*)android_os_Parcel_setDataCapacity},
+ {"writeNative", "([BII)V", (void*)android_os_Parcel_writeNative},
+ {"writeInt", "(I)V", (void*)android_os_Parcel_writeInt},
+ {"writeLong", "(J)V", (void*)android_os_Parcel_writeLong},
+ {"writeFloat", "(F)V", (void*)android_os_Parcel_writeFloat},
+ {"writeDouble", "(D)V", (void*)android_os_Parcel_writeDouble},
+ {"writeString", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeString},
+ {"writeStrongBinder", "(Landroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
+ {"writeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
+ {"createByteArray", "()[B", (void*)android_os_Parcel_createByteArray},
+ {"readInt", "()I", (void*)android_os_Parcel_readInt},
+ {"readLong", "()J", (void*)android_os_Parcel_readLong},
+ {"readFloat", "()F", (void*)android_os_Parcel_readFloat},
+ {"readDouble", "()D", (void*)android_os_Parcel_readDouble},
+ {"readString", "()Ljava/lang/String;", (void*)android_os_Parcel_readString},
+ {"readStrongBinder", "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
+ {"internalReadFileDescriptor", "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
+ {"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor},
+ {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor},
+ {"freeBuffer", "()V", (void*)android_os_Parcel_freeBuffer},
+ {"init", "(I)V", (void*)android_os_Parcel_init},
+ {"destroy", "()V", (void*)android_os_Parcel_destroy},
+ {"marshall", "()[B", (void*)android_os_Parcel_marshall},
+ {"unmarshall", "([BII)V", (void*)android_os_Parcel_unmarshall},
+ {"appendFrom", "(Landroid/os/Parcel;II)V", (void*)android_os_Parcel_appendFrom},
+ {"hasFileDescriptors", "()Z", (void*)android_os_Parcel_hasFileDescriptors},
+ {"writeInterfaceToken", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
+ {"enforceInterface", "(Ljava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
+};
+
+const char* const kParcelPathName = "android/os/Parcel";
+
+static int int_register_android_os_Parcel(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("android/util/Log");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.util.Log");
+ gLogOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gLogOffsets.mLogE = env->GetStaticMethodID(
+ clazz, "e", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");
+ assert(gLogOffsets.mLogE);
+
+ clazz = env->FindClass("java/io/FileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+ gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gFileDescriptorOffsets.mConstructor
+ = env->GetMethodID(clazz, "<init>", "()V");
+ gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
+ LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
+ "Unable to find descriptor field in java.io.FileDescriptor");
+
+ clazz = env->FindClass("android/os/ParcelFileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
+ gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+ gParcelFileDescriptorOffsets.mConstructor
+ = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
+
+ clazz = env->FindClass(kParcelPathName);
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel");
+
+ gParcelOffsets.mObject
+ = env->GetFieldID(clazz, "mObject", "I");
+ gParcelOffsets.mOwnObject
+ = env->GetFieldID(clazz, "mOwnObject", "I");
+
+ return AndroidRuntime::registerNativeMethods(
+ env, kParcelPathName,
+ gParcelMethods, NELEM(gParcelMethods));
+}
+
+int register_android_os_Binder(JNIEnv* env)
+{
+ if (int_register_android_os_Binder(env) < 0)
+ return -1;
+ if (int_register_android_os_BinderInternal(env) < 0)
+ return -1;
+ if (int_register_android_os_BinderProxy(env) < 0)
+ return -1;
+ if (int_register_android_os_Parcel(env) < 0)
+ return -1;
+ return 0;
+}
+
+namespace android {
+
+// Returns the Unix file descriptor for a ParcelFileDescriptor object
+int getParcelFileDescriptorFD(JNIEnv* env, jobject object)
+{
+ return env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
+}
+
+}