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