blob: f45be127c99c6399263530ab62eec547720db8b6 [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
Svet Ganov53a441c2016-04-19 19:38:00 -070017#include "core_jni_helpers.h"
18#include <cutils/ashmem.h>
Svetoslav Ganov04df7382016-05-10 18:55:47 -070019#include <linux/ashmem.h>
Svet Ganov53a441c2016-04-19 19:38:00 -070020#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
Svet Ganov53a441c2016-04-19 19:38:00 -070047 int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
48 if (setProtResult < 0) {
49 jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
50 return -1;
51 }
52
53 return fd;
54}
55
56static jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd,
57 jboolean owner, jboolean writable)
58{
59 if (fd < 0) {
60 jniThrowException(env, "java/io/IOException", "bad file descriptor");
61 return -1;
62 }
63
64 int ashmemSize = ashmem_get_size_region(fd);
65 if (ashmemSize <= 0) {
66 jniThrowException(env, "java/io/IOException", "bad ashmem size");
67 return -1;
68 }
69
70 int protMode = (owner || writable) ? (PROT_READ | PROT_WRITE) : PROT_READ;
71 void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0);
72 if (ashmemAddr == MAP_FAILED) {
73 jniThrowException(env, "java/io/IOException", "cannot mmap ashmem");
74 return -1;
75 }
76
77 if (owner) {
78 int size = ashmemSize / sizeof(std::atomic_int);
79 new (ashmemAddr) std::atomic_int[size];
80 }
81
82 if (owner && !writable) {
83 int setProtResult = ashmem_set_prot_region(fd, PROT_READ);
84 if (setProtResult < 0) {
85 jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
86 return -1;
87 }
88 }
89
90 return reinterpret_cast<jlong>(ashmemAddr);
91}
92
93static void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint fd,
94 jlong ashmemAddr, jboolean owner)
95{
96 if (fd < 0) {
97 jniThrowException(env, "java/io/IOException", "bad file descriptor");
98 return;
99 }
100
101 int ashmemSize = ashmem_get_size_region(fd);
102 if (ashmemSize <= 0) {
103 jniThrowException(env, "java/io/IOException", "bad ashmem size");
104 return;
105 }
106
107 int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize);
108 if (unmapResult < 0) {
109 jniThrowException(env, "java/io/IOException", "munmap failed");
110 return;
111 }
112
113 // We don't deallocate the atomic ints we created with placement new in the ashmem
114 // region as the kernel we reclaim all pages when the ashmem region is destroyed.
115 if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) {
116 jniThrowException(env, "java/io/IOException", "ashmem unpinning failed");
117 return;
118 }
119
120 close(fd);
121}
122
123static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz,
124 jint fd, jlong address, jint index, jboolean owner)
125{
126 if (fd < 0) {
127 jniThrowException(env, "java/io/IOException", "bad file descriptor");
128 return -1;
129 }
130
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700131 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
132 jniThrowException(env, "java/io/IOException", "ashmem region was purged");
133 return -1;
Svet Ganov53a441c2016-04-19 19:38:00 -0700134 }
135
136 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700137 return value->load(std::memory_order_relaxed);
Svet Ganov53a441c2016-04-19 19:38:00 -0700138}
139
140static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz,
141 jint fd, jlong address, jint index, jint newValue, jboolean owner)
142{
143 if (fd < 0) {
144 jniThrowException(env, "java/io/IOException", "bad file descriptor");
145 return;
146 }
147
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700148 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
149 jniThrowException(env, "java/io/IOException", "ashmem region was purged");
150 return;
Svet Ganov53a441c2016-04-19 19:38:00 -0700151 }
152
153 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
154 value->store(newValue, std::memory_order_relaxed);
Svet Ganov53a441c2016-04-19 19:38:00 -0700155}
156
157static jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) {
158 if (fd < 0) {
159 jniThrowException(env, "java/io/IOException", "bad file descriptor");
160 return -1;
161 }
162
163 // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
164 // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
165 // should return ENOTTY for all other valid file descriptors
166 int ashmemSize = ashmem_get_size_region(fd);
167 if (ashmemSize < 0) {
168 if (errno == ENOTTY) {
169 // ENOTTY means that the ioctl does not apply to this object,
170 // i.e., it is not an ashmem region.
171 return -1;
172 }
173 // Some other error, throw exception
174 jniThrowIOException(env, errno);
175 return -1;
176 }
177 return ashmemSize / sizeof(std::atomic_int);
178}
179
180static const JNINativeMethod methods[] = {
181 {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create},
182 {"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open},
183 {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close},
184 {"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get},
185 {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set},
186 {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size},
187};
188
189int register_android_util_MemoryIntArray(JNIEnv* env)
190{
191 return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods));
192}
193
194}