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 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 131 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 132 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 133 return -1; 134 } 135 136 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 137 return value->load(std::memory_order_relaxed); 138} 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 148 if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 149 jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 150 return; 151 } 152 153 std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 154 value->store(newValue, std::memory_order_relaxed); 155} 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 int ashmemSize = ashmem_get_size_region(fd); 164 if (ashmemSize < 0) { 165 // Some other error, throw exception 166 jniThrowIOException(env, errno); 167 return -1; 168 } 169 return ashmemSize / sizeof(std::atomic_int); 170} 171 172static const JNINativeMethod methods[] = { 173 {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create}, 174 {"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open}, 175 {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close}, 176 {"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get}, 177 {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set}, 178 {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size}, 179}; 180 181int register_android_util_MemoryIntArray(JNIEnv* env) 182{ 183 return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods)); 184} 185 186} 187