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
android_os_MemoryFile_open(JNIEnv * env,jobject clazz,jstring name,jint length)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
android_os_MemoryFile_mmap(JNIEnv * env,jobject clazz,jobject fileDescriptor,jint length,jint prot)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
android_os_MemoryFile_munmap(JNIEnv * env,jobject clazz,jlong addr,jint length)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
android_os_MemoryFile_close(JNIEnv * env,jobject clazz,jobject fileDescriptor)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
android_os_MemoryFile_read(JNIEnv * env,jobject clazz,jobject fileDescriptor,jlong address,jbyteArray buffer,jint srcOffset,jint destOffset,jint count,jboolean unpinned)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
android_os_MemoryFile_write(JNIEnv * env,jobject clazz,jobject fileDescriptor,jlong address,jbyteArray buffer,jint srcOffset,jint destOffset,jint count,jboolean unpinned)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
android_os_MemoryFile_pin(JNIEnv * env,jobject clazz,jobject fileDescriptor,jboolean pin)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
android_os_MemoryFile_get_size(JNIEnv * env,jobject clazz,jobject fileDescriptor)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
register_android_os_MemoryFile(JNIEnv * env)152 int register_android_os_MemoryFile(JNIEnv* env)
153 {
154 return RegisterMethodsOrDie(env, "android/os/MemoryFile", methods, NELEM(methods));
155 }
156
157 }
158