android_util_MemoryIntArray.cpp revision 24c27754c5f1d23dfa679ade368977936519a58d
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 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 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 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 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 if (!ashmem_valid(fd)) { 107 jniThrowIOException(env, errno); 108 return; 109 } 110 111 int ashmemSize = ashmem_get_size_region(fd); 112 if (ashmemSize <= 0) { 113 jniThrowException(env, "java/io/IOException", "bad ashmem size"); 114 return; 115 } 116 117 int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize); 118 if (unmapResult < 0) { 119 jniThrowException(env, "java/io/IOException", "munmap failed"); 120 return; 121 } 122 123 // We don't deallocate the atomic ints we created with placement new in the ashmem 124 // region as the kernel we reclaim all pages when the ashmem region is destroyed. 125 if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) { 126 jniThrowException(env, "java/io/IOException", "ashmem unpinning failed"); 127 return; 128 } 129 130 close(fd); 131} 132 133static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz, 134 jint fd, jlong address, jint index, jboolean owner) 135{ 136 if (fd < 0) { 137 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 138 return -1; 139 } 140 141 if (!ashmem_valid(fd)) { 142 jniThrowIOException(env, errno); 143 return -1; 144 } 145 146 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 147 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 148 return -1; 149 } 150 151 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 152 return value->load(std::memory_order_relaxed); 153} 154 155static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz, 156 jint fd, jlong address, jint index, jint newValue, jboolean owner) 157{ 158 if (fd < 0) { 159 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 160 return; 161 } 162 163 if (!ashmem_valid(fd)) { 164 jniThrowIOException(env, errno); 165 return; 166 } 167 168 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 169 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 170 return; 171 } 172 173 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 174 value->store(newValue, std::memory_order_relaxed); 175} 176 177static jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) { 178 if (fd < 0) { 179 jniThrowException(env, "java/io/IOException", "bad file descriptor"); 180 return -1; 181 } 182 183 if (!ashmem_valid(fd)) { 184 jniThrowIOException(env, errno); 185 return -1; 186 } 187 188 int ashmemSize = ashmem_get_size_region(fd); 189 if (ashmemSize < 0) { 190 jniThrowIOException(env, errno); 191 return -1; 192 } 193 return ashmemSize / sizeof(std::atomic_int); 194} 195 196static const JNINativeMethod methods[] = { 197 {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create}, 198 {"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open}, 199 {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close}, 200 {"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get}, 201 {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set}, 202 {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size}, 203}; 204 205int register_android_util_MemoryIntArray(JNIEnv* env) 206{ 207 return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods)); 208} 209 210} 211