Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2008 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 #define LOG_TAG "MemoryFile"
     18 #include <utils/Log.h>
     19 
     20 #include <cutils/ashmem.h>
     21 #include <android_runtime/AndroidRuntime.h>
     22 #include "JNIHelp.h"
     23 #include <unistd.h>
     24 #include <sys/mman.h>
     25 
     26 
     27 namespace android {
     28 
     29 static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
     30 {
     31     const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);
     32 
     33     int result = ashmem_create_region(namestr, length);
     34 
     35     if (name)
     36         env->ReleaseStringUTFChars(name, namestr);
     37 
     38     if (result < 0) {
     39         jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
     40         return NULL;
     41     }
     42 
     43     return jniCreateFileDescriptor(env, result);
     44 }
     45 
     46 static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
     47         jint length, jint prot)
     48 {
     49     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     50     jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
     51     if (!result)
     52         jniThrowException(env, "java/io/IOException", "mmap failed");
     53     return result;
     54 }
     55 
     56 static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jint addr, jint length)
     57 {
     58     int result = munmap((void *)addr, length);
     59     if (result < 0)
     60         jniThrowException(env, "java/io/IOException", "munmap failed");
     61 }
     62 
     63 static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor)
     64 {
     65     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     66     if (fd >= 0) {
     67         jniSetFileDescriptorOfFD(env, fileDescriptor, -1);
     68         close(fd);
     69     }
     70 }
     71 
     72 static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
     73         jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
     74         jint count, jboolean unpinned)
     75 {
     76     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     77     if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
     78         ashmem_unpin_region(fd, 0, 0);
     79         jniThrowException(env, "java/io/IOException", "ashmem region was purged");
     80         return -1;
     81     }
     82 
     83     env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
     84 
     85     if (unpinned) {
     86         ashmem_unpin_region(fd, 0, 0);
     87     }
     88     return count;
     89 }
     90 
     91 static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,
     92         jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
     93         jint count, jboolean unpinned)
     94 {
     95     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     96     if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
     97         ashmem_unpin_region(fd, 0, 0);
     98         jniThrowException(env, "java/io/IOException", "ashmem region was purged");
     99         return -1;
    100     }
    101 
    102     env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
    103 
    104     if (unpinned) {
    105         ashmem_unpin_region(fd, 0, 0);
    106     }
    107     return count;
    108 }
    109 
    110 static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin)
    111 {
    112     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    113     int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));
    114     if (result < 0) {
    115         jniThrowException(env, "java/io/IOException", NULL);
    116     }
    117 }
    118 
    119 static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,
    120         jobject fileDescriptor) {
    121     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    122     // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
    123     // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
    124     // should return ENOTTY for all other valid file descriptors
    125     int result = ashmem_get_size_region(fd);
    126     if (result < 0) {
    127         if (errno == ENOTTY) {
    128             // ENOTTY means that the ioctl does not apply to this object,
    129             // i.e., it is not an ashmem region.
    130             return (jint) -1;
    131         }
    132         // Some other error, throw exception
    133         jniThrowIOException(env, errno);
    134         return (jint) -1;
    135     }
    136     return (jint) result;
    137 }
    138 
    139 static const JNINativeMethod methods[] = {
    140     {"native_open",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
    141     {"native_mmap",  "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap},
    142     {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap},
    143     {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
    144     {"native_read",  "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
    145     {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
    146     {"native_pin",   "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
    147     {"native_get_size", "(Ljava/io/FileDescriptor;)I",
    148             (void*)android_os_MemoryFile_get_size}
    149 };
    150 
    151 static const char* const kClassPathName = "android/os/MemoryFile";
    152 
    153 int register_android_os_MemoryFile(JNIEnv* env)
    154 {
    155     jclass clazz;
    156 
    157     clazz = env->FindClass(kClassPathName);
    158     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils");
    159 
    160     return AndroidRuntime::registerNativeMethods(
    161         env, kClassPathName,
    162         methods, NELEM(methods));
    163 }
    164 
    165 }
    166