diff options
Diffstat (limited to 'core/jni/android_os_Parcel.cpp')
-rw-r--r-- | core/jni/android_os_Parcel.cpp | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp new file mode 100644 index 0000000..8a99049 --- /dev/null +++ b/core/jni/android_os_Parcel.cpp @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2012 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 "Parcel" +//#define LOG_NDEBUG 0 + +#include "android_os_Parcel.h" +#include "android_util_Binder.h" + +#include "JNIHelp.h" + +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <utils/Atomic.h> +#include <binder/IInterface.h> +#include <binder/IPCThreadState.h> +#include <utils/Log.h> +#include <utils/SystemClock.h> +#include <utils/List.h> +#include <utils/KeyedVector.h> +#include <cutils/logger.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/threads.h> +#include <utils/String8.h> + +#include <ScopedUtfChars.h> +#include <ScopedLocalRef.h> + +#include <android_runtime/AndroidRuntime.h> + +//#undef ALOGV +//#define ALOGV(...) fprintf(stderr, __VA_ARGS__) + +#define DEBUG_DEATH 0 +#if DEBUG_DEATH +#define LOGDEATH ALOGD +#else +#define LOGDEATH ALOGV +#endif + +namespace android { + +static struct parcel_offsets_t +{ + jfieldID mObject; + jfieldID mOwnObject; +} gParcelOffsets; + +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; +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +static jboolean android_os_Parcel_pushAllowFds(JNIEnv* env, jobject clazz, jboolean allowFds) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + jboolean ret = JNI_TRUE; + if (parcel != NULL) { + ret = (jboolean)parcel->pushAllowFds(allowFds); + } + return ret; +} + +static void android_os_Parcel_restoreAllowFds(JNIEnv* env, jobject clazz, jboolean lastValue) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + parcel->restoreAllowFds((bool)lastValue); + } +} + +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; + } + + const status_t err = parcel->writeInt32(length); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + return; + } + + void* dest = parcel->writeInplace(length); + if (dest == NULL) { + signalExceptionForError(env, clazz, NO_MEMORY); + return; + } + + jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0); + if (ar) { + memcpy(dest, ar + offset, 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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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) { + signalExceptionForError(env, clazz, err); + } + } +} + +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(jniGetFDFromFileDescriptor(env, object)); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +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; + return jniCreateFileDescriptor(env, fd); + } + return NULL; +} + +static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz, + jstring name, jint mode) +{ + if (name == NULL) { + jniThrowNullPointerException(env, 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", strerror(errno)); + return NULL; + } + jobject object = jniCreateFileDescriptor(env, fd); + if (object == NULL) { + close(fd); + } + return object; +} + +static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, jobject orig) +{ + if (orig == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + int origfd = jniGetFDFromFileDescriptor(env, orig); + if (origfd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor"); + return NULL; + } + + int fd = dup(origfd); + if (fd < 0) { + jniThrowIOException(env, errno); + return NULL; + } + jobject object = jniCreateFileDescriptor(env, fd); + if (object == NULL) { + close(fd); + } + return object; +} + +static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + if (object == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + int fd = jniGetFDFromFileDescriptor(env, object); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, object, -1); + //ALOGI("Closing ParcelFileDescriptor %d\n", fd); + close(fd); + } +} + +static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + if (object == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + int fd = jniGetFDFromFileDescriptor(env, object); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, object, -1); + } +} + +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) { + //ALOGI("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) { + //ALOGI("Initializing obj %p: creating new Parcel\n", clazz); + own = 1; + parcel = new Parcel; + } else { + //ALOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel); + } + if (parcel == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return; + } + //ALOGI("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); + //ALOGI("Destroying obj %p: deleting C++ Parcel %p\n", clazz, parcel); + delete parcel; + } else { + env->SetIntField(clazz, gParcelOffsets.mObject, 0); + //ALOGI("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; + } + + status_t err = thisParcel->appendFrom(otherParcel, offset, length); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } +} + +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) { + IPCThreadState* threadState = IPCThreadState::self(); + const int32_t oldPolicy = threadState->getStrictModePolicy(); + const bool isValid = parcel->enforceInterface( + String16(str, env->GetStringLength(name)), + threadState); + env->ReleaseStringCritical(name, str); + if (isValid) { + const int32_t newPolicy = threadState->getStrictModePolicy(); + if (oldPolicy != newPolicy) { + // Need to keep the Java-level thread-local strict + // mode policy in sync for the libcore + // enforcements, which involves an upcall back + // into Java. (We can't modify the + // Parcel.enforceInterface signature, as it's + // pseudo-public, and used via AIDL + // auto-generation...) + set_dalvik_blockguard_policy(env, newPolicy); + } + 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}, + {"pushAllowFds", "(Z)Z", (void*)android_os_Parcel_pushAllowFds}, + {"restoreAllowFds", "(Z)V", (void*)android_os_Parcel_restoreAllowFds}, + {"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}, + {"dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor}, + {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor}, + {"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor}, + {"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"; + +int register_android_os_Parcel(JNIEnv* env) +{ + jclass clazz; + + 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)); +} + +}; |