blob: dbe8ed3e26d0036b54489f22b1813e3b9bb9b1ed [file] [log] [blame]
Svet Ganov53a441c2016-04-19 19:38:00 -07001/*
2 * Copyright (C) 2016 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
18#include "core_jni_helpers.h"
19#include <cutils/ashmem.h>
20#include <sys/mman.h>
21
22namespace android {
23
24static jint android_util_MemoryIntArray_create(JNIEnv* env, jobject clazz, jstring name,
25 jint size)
26{
27 if (name == NULL) {
28 jniThrowException(env, "java/io/IOException", "bad name");
29 return -1;
30 }
31
32 if (size <= 0) {
33 jniThrowException(env, "java/io/IOException", "bad size");
34 return -1;
35 }
36
37 const char* nameStr = env->GetStringUTFChars(name, NULL);
38 const int ashmemSize = sizeof(std::atomic_int) * size;
39 int fd = ashmem_create_region(nameStr, ashmemSize);
40 env->ReleaseStringUTFChars(name, nameStr);
41
42 if (fd < 0) {
43 jniThrowException(env, "java/io/IOException", "ashmem creation failed");
44 return -1;
45 }
46
47 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
48 jniThrowException(env, "java/io/IOException", "ashmem was purged");
49 return -1;
50 }
51
52 int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
53 if (setProtResult < 0) {
54 jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
55 return -1;
56 }
57
58 return fd;
59}
60
61static jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd,
62 jboolean owner, jboolean writable)
63{
64 if (fd < 0) {
65 jniThrowException(env, "java/io/IOException", "bad file descriptor");
66 return -1;
67 }
68
69 int ashmemSize = ashmem_get_size_region(fd);
70 if (ashmemSize <= 0) {
71 jniThrowException(env, "java/io/IOException", "bad ashmem size");
72 return -1;
73 }
74
75 int protMode = (owner || writable) ? (PROT_READ | PROT_WRITE) : PROT_READ;
76 void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0);
77 if (ashmemAddr == MAP_FAILED) {
78 jniThrowException(env, "java/io/IOException", "cannot mmap ashmem");
79 return -1;
80 }
81
82 if (owner) {
83 int size = ashmemSize / sizeof(std::atomic_int);
84 new (ashmemAddr) std::atomic_int[size];
85 }
86
87 if (owner && !writable) {
88 int setProtResult = ashmem_set_prot_region(fd, PROT_READ);
89 if (setProtResult < 0) {
90 jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
91 return -1;
92 }
93 }
94
95 return reinterpret_cast<jlong>(ashmemAddr);
96}
97
98static void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint fd,
99 jlong ashmemAddr, jboolean owner)
100{
101 if (fd < 0) {
102 jniThrowException(env, "java/io/IOException", "bad file descriptor");
103 return;
104 }
105
106 int ashmemSize = ashmem_get_size_region(fd);
107 if (ashmemSize <= 0) {
108 jniThrowException(env, "java/io/IOException", "bad ashmem size");
109 return;
110 }
111
112 int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize);
113 if (unmapResult < 0) {
114 jniThrowException(env, "java/io/IOException", "munmap failed");
115 return;
116 }
117
118 // We don't deallocate the atomic ints we created with placement new in the ashmem
119 // region as the kernel we reclaim all pages when the ashmem region is destroyed.
120 if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) {
121 jniThrowException(env, "java/io/IOException", "ashmem unpinning failed");
122 return;
123 }
124
125 close(fd);
126}
127
128static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz,
129 jint fd, jlong address, jint index, jboolean owner)
130{
131 if (fd < 0) {
132 jniThrowException(env, "java/io/IOException", "bad file descriptor");
133 return -1;
134 }
135
136 bool unpin = false;
137
138 if (!owner) {
139 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
140 jniThrowException(env, "java/io/IOException", "ashmem region was purged");
141 return -1;
142 }
143 unpin = true;
144 }
145
146 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
147 const int result = value->load(std::memory_order_relaxed);
148
149 if (unpin) {
150 ashmem_unpin_region(fd, 0, 0);
151 }
152
153 return result;
154}
155
156static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz,
157 jint fd, jlong address, jint index, jint newValue, jboolean owner)
158{
159 if (fd < 0) {
160 jniThrowException(env, "java/io/IOException", "bad file descriptor");
161 return;
162 }
163
164 bool unpin = false;
165
166 if (!owner) {
167 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
168 jniThrowException(env, "java/io/IOException", "ashmem region was purged");
169 return;
170 }
171 unpin = true;
172 }
173
174 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
175 value->store(newValue, std::memory_order_relaxed);
176
177 if (unpin) {
178 ashmem_unpin_region(fd, 0, 0);
179 }
180}
181
182static jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) {
183 if (fd < 0) {
184 jniThrowException(env, "java/io/IOException", "bad file descriptor");
185 return -1;
186 }
187
188 // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
189 // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
190 // should return ENOTTY for all other valid file descriptors
191 int ashmemSize = ashmem_get_size_region(fd);
192 if (ashmemSize < 0) {
193 if (errno == ENOTTY) {
194 // ENOTTY means that the ioctl does not apply to this object,
195 // i.e., it is not an ashmem region.
196 return -1;
197 }
198 // Some other error, throw exception
199 jniThrowIOException(env, errno);
200 return -1;
201 }
202 return ashmemSize / sizeof(std::atomic_int);
203}
204
205static const JNINativeMethod methods[] = {
206 {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create},
207 {"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open},
208 {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close},
209 {"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get},
210 {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set},
211 {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size},
212};
213
214int register_android_util_MemoryIntArray(JNIEnv* env)
215{
216 return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods));
217}
218
219}