1 /* 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 #include "core_jni_helpers.h" 18 #include <cutils/ashmem.h> 19 #include <linux/ashmem.h> 20 #include <sys/mman.h> 21 22 namespace android { 23 24 static 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 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 56 static jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd, 57 jboolean owner) 58 { 59 if (fd < 0) { 60 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 61 return -1; 62 } 63 64 if (!ashmem_valid(fd)) { 65 jniThrowIOException(env, errno); 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 // IMPORTANT: Ashmem allows the caller to change its size until 76 // it is memory mapped for the first time which lazily creates 77 // the underlying VFS file. So the size we get above may not 78 // reflect the size of the underlying shared memory region. Therefore, 79 // we first memory map to set the size in stone an verify if 80 // the underlying ashmem region has the same size as the one we 81 // memory mapped. This is critical as we use the underlying 82 // ashmem size for boundary checks and memory unmapping. 83 int protMode = owner ? (PROT_READ | PROT_WRITE) : PROT_READ; 84 void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0); 85 if (ashmemAddr == MAP_FAILED) { 86 jniThrowException(env, "java/io/IOException", "cannot mmap ashmem"); 87 return -1; 88 } 89 90 // Check if the mapped size is the same as the ashmem region. 91 int mmapedSize = ashmem_get_size_region(fd); 92 if (mmapedSize != ashmemSize) { 93 munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize); 94 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 95 return -1; 96 } 97 98 if (owner) { 99 int size = ashmemSize / sizeof(std::atomic_int); 100 new (ashmemAddr) std::atomic_int[size]; 101 } 102 103 if (owner) { 104 int setProtResult = ashmem_set_prot_region(fd, PROT_READ); 105 if (setProtResult < 0) { 106 jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode"); 107 return -1; 108 } 109 } 110 111 return reinterpret_cast<jlong>(ashmemAddr); 112 } 113 114 static void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint fd, 115 jlong ashmemAddr, jboolean owner) 116 { 117 if (fd < 0) { 118 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 119 return; 120 } 121 122 if (!ashmem_valid(fd)) { 123 jniThrowIOException(env, errno); 124 return; 125 } 126 127 int ashmemSize = ashmem_get_size_region(fd); 128 if (ashmemSize <= 0) { 129 jniThrowException(env, "java/io/IOException", "bad ashmem size"); 130 return; 131 } 132 133 int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize); 134 if (unmapResult < 0) { 135 jniThrowException(env, "java/io/IOException", "munmap failed"); 136 return; 137 } 138 139 // We don't deallocate the atomic ints we created with placement new in the ashmem 140 // region as the kernel we reclaim all pages when the ashmem region is destroyed. 141 if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) { 142 jniThrowException(env, "java/io/IOException", "ashmem unpinning failed"); 143 return; 144 } 145 146 close(fd); 147 } 148 149 static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz, 150 jint fd, jlong address, jint index) 151 { 152 if (fd < 0) { 153 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 154 return -1; 155 } 156 157 if (!ashmem_valid(fd)) { 158 jniThrowIOException(env, errno); 159 return -1; 160 } 161 162 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 163 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 164 return -1; 165 } 166 167 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 168 return value->load(std::memory_order_relaxed); 169 } 170 171 static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz, 172 jint fd, jlong address, jint index, jint newValue) 173 { 174 if (fd < 0) { 175 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 176 return; 177 } 178 179 if (!ashmem_valid(fd)) { 180 jniThrowIOException(env, errno); 181 return; 182 } 183 184 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 185 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 186 return; 187 } 188 189 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 190 value->store(newValue, std::memory_order_relaxed); 191 } 192 193 static jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) { 194 if (fd < 0) { 195 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 196 return -1; 197 } 198 199 if (!ashmem_valid(fd)) { 200 jniThrowIOException(env, errno); 201 return -1; 202 } 203 204 int ashmemSize = ashmem_get_size_region(fd); 205 if (ashmemSize < 0) { 206 jniThrowIOException(env, errno); 207 return -1; 208 } 209 return ashmemSize / sizeof(std::atomic_int); 210 } 211 212 static const JNINativeMethod methods[] = { 213 {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create}, 214 {"nativeOpen", "(IZ)J", (void*)android_util_MemoryIntArray_open}, 215 {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close}, 216 {"nativeGet", "(IJI)I", (void*)android_util_MemoryIntArray_get}, 217 {"nativeSet", "(IJII)V", (void*) android_util_MemoryIntArray_set}, 218 {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size}, 219 }; 220 221 int register_android_util_MemoryIntArray(JNIEnv* env) 222 { 223 return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods)); 224 } 225 226 } 227