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 <android_runtime/AndroidRuntime.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 // round up length to page boundary
34 length = (((length - 1) / getpagesize()) + 1) * getpagesize();
35 int result = ashmem_create_region(namestr, length);
36
37 if (name)
38 env->ReleaseStringUTFChars(name, namestr);
39
40 if (result < 0) {
41 jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
42 return NULL;
43 }
44
45 return jniCreateFileDescriptor(env, result);
46 }
47
android_os_MemoryFile_mmap(JNIEnv * env,jobject clazz,jobject fileDescriptor,jint length,jint prot)48 static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
49 jint length, jint prot)
50 {
51 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
52 jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
53 if (!result)
54 jniThrowException(env, "java/io/IOException", "mmap failed");
55 return result;
56 }
57
android_os_MemoryFile_munmap(JNIEnv * env,jobject clazz,jint addr,jint length)58 static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jint addr, jint length)
59 {
60 int result = munmap((void *)addr, length);
61 if (result < 0)
62 jniThrowException(env, "java/io/IOException", "munmap failed");
63 }
64
android_os_MemoryFile_close(JNIEnv * env,jobject clazz,jobject fileDescriptor)65 static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor)
66 {
67 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
68 if (fd >= 0) {
69 jniSetFileDescriptorOfFD(env, fileDescriptor, -1);
70 close(fd);
71 }
72 }
73
android_os_MemoryFile_read(JNIEnv * env,jobject clazz,jobject fileDescriptor,jint address,jbyteArray buffer,jint srcOffset,jint destOffset,jint count,jboolean unpinned)74 static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
75 jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
76 jint count, jboolean unpinned)
77 {
78 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
79 if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
80 ashmem_unpin_region(fd, 0, 0);
81 jniThrowException(env, "java/io/IOException", "ashmem region was purged");
82 return -1;
83 }
84
85 env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
86
87 if (unpinned) {
88 ashmem_unpin_region(fd, 0, 0);
89 }
90 return count;
91 }
92
android_os_MemoryFile_write(JNIEnv * env,jobject clazz,jobject fileDescriptor,jint address,jbyteArray buffer,jint srcOffset,jint destOffset,jint count,jboolean unpinned)93 static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,
94 jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
95 jint count, jboolean unpinned)
96 {
97 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
98 if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
99 ashmem_unpin_region(fd, 0, 0);
100 jniThrowException(env, "java/io/IOException", "ashmem region was purged");
101 return -1;
102 }
103
104 env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
105
106 if (unpinned) {
107 ashmem_unpin_region(fd, 0, 0);
108 }
109 return count;
110 }
111
android_os_MemoryFile_pin(JNIEnv * env,jobject clazz,jobject fileDescriptor,jboolean pin)112 static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin)
113 {
114 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
115 int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));
116 if (result < 0) {
117 jniThrowException(env, "java/io/IOException", NULL);
118 }
119 }
120
android_os_MemoryFile_get_mapped_size(JNIEnv * env,jobject clazz,jobject fileDescriptor)121 static jint android_os_MemoryFile_get_mapped_size(JNIEnv* env, jobject clazz,
122 jobject fileDescriptor) {
123 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
124 // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
125 // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
126 // should return ENOTTY for all other valid file descriptors
127 int result = ashmem_get_size_region(fd);
128 if (result < 0) {
129 if (errno == ENOTTY) {
130 // ENOTTY means that the ioctl does not apply to this object,
131 // i.e., it is not an ashmem region.
132 return (jint) -1;
133 }
134 // Some other error, throw exception
135 jniThrowIOException(env, errno);
136 return (jint) -1;
137 }
138 return (jint) result;
139 }
140
141 static const JNINativeMethod methods[] = {
142 {"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
143 {"native_mmap", "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap},
144 {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap},
145 {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
146 {"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
147 {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
148 {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
149 {"native_get_mapped_size", "(Ljava/io/FileDescriptor;)I",
150 (void*)android_os_MemoryFile_get_mapped_size}
151 };
152
153 static const char* const kClassPathName = "android/os/MemoryFile";
154
register_android_os_MemoryFile(JNIEnv * env)155 int register_android_os_MemoryFile(JNIEnv* env)
156 {
157 jclass clazz;
158
159 clazz = env->FindClass(kClassPathName);
160 LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils");
161
162 return AndroidRuntime::registerNativeMethods(
163 env, kClassPathName,
164 methods, NELEM(methods));
165 }
166
167 }
168