1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "MemoryFile" 18 #include <utils/Log.h> 19 20 #include <cutils/ashmem.h> 21 #include "core_jni_helpers.h" 22 #include "JNIHelp.h" 23 #include <unistd.h> 24 #include <sys/mman.h> 25 26 27 namespace android { 28 29 static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) 30 { 31 const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL); 32 33 int result = ashmem_create_region(namestr, length); 34 35 if (name) 36 env->ReleaseStringUTFChars(name, namestr); 37 38 if (result < 0) { 39 jniThrowException(env, "java/io/IOException", "ashmem_create_region failed"); 40 return NULL; 41 } 42 43 return jniCreateFileDescriptor(env, result); 44 } 45 46 static jlong android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor, 47 jint length, jint prot) 48 { 49 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 50 void* result = mmap(NULL, length, prot, MAP_SHARED, fd, 0); 51 if (result == MAP_FAILED) { 52 jniThrowException(env, "java/io/IOException", "mmap failed"); 53 } 54 return reinterpret_cast<jlong>(result); 55 } 56 57 static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jlong addr, jint length) 58 { 59 int result = munmap(reinterpret_cast<void *>(addr), length); 60 if (result < 0) 61 jniThrowException(env, "java/io/IOException", "munmap failed"); 62 } 63 64 static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor) 65 { 66 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 67 if (fd >= 0) { 68 jniSetFileDescriptorOfFD(env, fileDescriptor, -1); 69 close(fd); 70 } 71 } 72 73 static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, 74 jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset, 75 jint count, jboolean unpinned) 76 { 77 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 78 if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 79 ashmem_unpin_region(fd, 0, 0); 80 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 81 return -1; 82 } 83 84 env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset); 85 86 if (unpinned) { 87 ashmem_unpin_region(fd, 0, 0); 88 } 89 return count; 90 } 91 92 static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, 93 jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset, 94 jint count, jboolean unpinned) 95 { 96 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 97 if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 98 ashmem_unpin_region(fd, 0, 0); 99 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 100 return -1; 101 } 102 103 env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset); 104 105 if (unpinned) { 106 ashmem_unpin_region(fd, 0, 0); 107 } 108 return count; 109 } 110 111 static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin) 112 { 113 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 114 int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0)); 115 if (result < 0) { 116 jniThrowException(env, "java/io/IOException", NULL); 117 } 118 } 119 120 static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz, 121 jobject fileDescriptor) { 122 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 123 // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. 124 // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel 125 // should return ENOTTY for all other valid file descriptors 126 int result = ashmem_get_size_region(fd); 127 if (result < 0) { 128 if (errno == ENOTTY) { 129 // ENOTTY means that the ioctl does not apply to this object, 130 // i.e., it is not an ashmem region. 131 return (jint) -1; 132 } 133 // Some other error, throw exception 134 jniThrowIOException(env, errno); 135 return (jint) -1; 136 } 137 return (jint) result; 138 } 139 140 static const JNINativeMethod methods[] = { 141 {"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open}, 142 {"native_mmap", "(Ljava/io/FileDescriptor;II)J", (void*)android_os_MemoryFile_mmap}, 143 {"native_munmap", "(JI)V", (void*)android_os_MemoryFile_munmap}, 144 {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close}, 145 {"native_read", "(Ljava/io/FileDescriptor;J[BIIIZ)I", (void*)android_os_MemoryFile_read}, 146 {"native_write", "(Ljava/io/FileDescriptor;J[BIIIZ)V", (void*)android_os_MemoryFile_write}, 147 {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin}, 148 {"native_get_size", "(Ljava/io/FileDescriptor;)I", 149 (void*)android_os_MemoryFile_get_size} 150 }; 151 152 int register_android_os_MemoryFile(JNIEnv* env) 153 { 154 return RegisterMethodsOrDie(env, "android/os/MemoryFile", methods, NELEM(methods)); 155 } 156 157 } 158