diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /core/jni/android_util_Binder.cpp | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'core/jni/android_util_Binder.cpp')
-rw-r--r-- | core/jni/android_util_Binder.cpp | 1510 |
1 files changed, 1510 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..24404a8 --- /dev/null +++ b/core/jni/android_util_Binder.cpp @@ -0,0 +1,1510 @@ +/* + * 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; + + 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); +} + +} |