Home | History | Annotate | Download | only in jni
      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