153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov/* 253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * Copyright (C) 2016 The Android Open Source Project 353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * 453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * Licensed under the Apache License, Version 2.0 (the "License"); 553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * you may not use this file except in compliance with the License. 653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * You may obtain a copy of the License at 753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * 853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * http://www.apache.org/licenses/LICENSE-2.0 953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * 1053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * Unless required by applicable law or agreed to in writing, software 1153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * distributed under the License is distributed on an "AS IS" BASIS, 1253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * See the License for the specific language governing permissions and 1453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * limitations under the License. 1553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov */ 1653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 1753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov#include "core_jni_helpers.h" 1853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov#include <cutils/ashmem.h> 1904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov#include <linux/ashmem.h> 2053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov#include <sys/mman.h> 2153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 2253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovnamespace android { 2353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 2453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic jint android_util_MemoryIntArray_create(JNIEnv* env, jobject clazz, jstring name, 2553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jint size) 2653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov{ 2753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (name == NULL) { 2853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad name"); 2953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 3053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 3153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 3253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (size <= 0) { 3353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad size"); 3453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 3553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 3653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 3753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov const char* nameStr = env->GetStringUTFChars(name, NULL); 3853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov const int ashmemSize = sizeof(std::atomic_int) * size; 3953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int fd = ashmem_create_region(nameStr, ashmemSize); 4053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov env->ReleaseStringUTFChars(name, nameStr); 4153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 4253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (fd < 0) { 4353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "ashmem creation failed"); 4453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 4553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 4653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 4753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); 4853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (setProtResult < 0) { 4953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode"); 5053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 5153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 5253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 5353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return fd; 5453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 5553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 5653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd, 5753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jboolean owner, jboolean writable) 5853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov{ 5953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (fd < 0) { 6053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad file descriptor"); 6153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 6253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 6453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int ashmemSize = ashmem_get_size_region(fd); 6553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (ashmemSize <= 0) { 6653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad ashmem size"); 6753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 6853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 7053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int protMode = (owner || writable) ? (PROT_READ | PROT_WRITE) : PROT_READ; 7153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0); 7253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (ashmemAddr == MAP_FAILED) { 7353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "cannot mmap ashmem"); 7453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 7553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 7653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 7753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (owner) { 7853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int size = ashmemSize / sizeof(std::atomic_int); 7953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov new (ashmemAddr) std::atomic_int[size]; 8053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 8153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 8253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (owner && !writable) { 8353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int setProtResult = ashmem_set_prot_region(fd, PROT_READ); 8453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (setProtResult < 0) { 8553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode"); 8653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 8753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 8853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 8953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 9053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return reinterpret_cast<jlong>(ashmemAddr); 9153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 9253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 9353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint fd, 9453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jlong ashmemAddr, jboolean owner) 9553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov{ 9653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (fd < 0) { 9753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad file descriptor"); 9853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return; 9953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 10053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 10153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int ashmemSize = ashmem_get_size_region(fd); 10253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (ashmemSize <= 0) { 10353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad ashmem size"); 10453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return; 10553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 10653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 10753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize); 10853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (unmapResult < 0) { 10953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "munmap failed"); 11053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return; 11153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 11253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 11353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // We don't deallocate the atomic ints we created with placement new in the ashmem 11453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // region as the kernel we reclaim all pages when the ashmem region is destroyed. 11553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) { 11653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "ashmem unpinning failed"); 11753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return; 11853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 11953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 12053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov close(fd); 12153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 12253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 12353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz, 12453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jint fd, jlong address, jint index, jboolean owner) 12553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov{ 12653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (fd < 0) { 12753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad file descriptor"); 12853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 12953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 13053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 13104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 13204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 13304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov return -1; 13453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 13553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 13653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 13704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov return value->load(std::memory_order_relaxed); 13853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 13953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 14053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz, 14153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jint fd, jlong address, jint index, jint newValue, jboolean owner) 14253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov{ 14353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (fd < 0) { 14453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad file descriptor"); 14553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return; 14653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 14753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 14804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { 14904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov jniThrowException(env, "java/io/IOException", "ashmem region was purged"); 15004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov return; 15153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 15253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 15353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index; 15453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov value->store(newValue, std::memory_order_relaxed); 15553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 15653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 15753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) { 15853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (fd < 0) { 15953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowException(env, "java/io/IOException", "bad file descriptor"); 16053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 16153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 16253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 16353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. 16453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel 16553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // should return ENOTTY for all other valid file descriptors 16653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov int ashmemSize = ashmem_get_size_region(fd); 16753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (ashmemSize < 0) { 16853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (errno == ENOTTY) { 16953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // ENOTTY means that the ioctl does not apply to this object, 17053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // i.e., it is not an ashmem region. 17153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 17253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 17353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov // Some other error, throw exception 17453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov jniThrowIOException(env, errno); 17553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 17653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 17753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return ashmemSize / sizeof(std::atomic_int); 17853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 17953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 18053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovstatic const JNINativeMethod methods[] = { 18153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create}, 18253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov {"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open}, 18353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close}, 18453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov {"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get}, 18553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set}, 18653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size}, 18753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov}; 18853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 18953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovint register_android_util_MemoryIntArray(JNIEnv* env) 19053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov{ 19153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods)); 19253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 19353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 19453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov} 195