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 "core_jni_helpers.h"
22#include "JNIHelp.h"
23#include <unistd.h>
24#include <sys/mman.h>
25
26
27namespace android {
28
29static 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
46static jlong android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
47        jint length, jint prot)
48{
49    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
50    void* result = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
51    if (result == MAP_FAILED) {
52        jniThrowException(env, "java/io/IOException", "mmap failed");
53    }
54    return reinterpret_cast<jlong>(result);
55}
56
57static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jlong addr, jint length)
58{
59    int result = munmap(reinterpret_cast<void *>(addr), length);
60    if (result < 0)
61        jniThrowException(env, "java/io/IOException", "munmap failed");
62}
63
64static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor)
65{
66    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
67    if (fd >= 0) {
68        jniSetFileDescriptorOfFD(env, fileDescriptor, -1);
69        close(fd);
70    }
71}
72
73static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
74        jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,
75        jint count, jboolean unpinned)
76{
77    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
78    if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
79        ashmem_unpin_region(fd, 0, 0);
80        jniThrowException(env, "java/io/IOException", "ashmem region was purged");
81        return -1;
82    }
83
84    env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
85
86    if (unpinned) {
87        ashmem_unpin_region(fd, 0, 0);
88    }
89    return count;
90}
91
92static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,
93        jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,
94        jint count, jboolean unpinned)
95{
96    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
97    if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
98        ashmem_unpin_region(fd, 0, 0);
99        jniThrowException(env, "java/io/IOException", "ashmem region was purged");
100        return -1;
101    }
102
103    env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
104
105    if (unpinned) {
106        ashmem_unpin_region(fd, 0, 0);
107    }
108    return count;
109}
110
111static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin)
112{
113    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
114    int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));
115    if (result < 0) {
116        jniThrowException(env, "java/io/IOException", NULL);
117    }
118}
119
120static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,
121        jobject fileDescriptor) {
122    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
123    // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
124    // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
125    // should return ENOTTY for all other valid file descriptors
126    int result = ashmem_get_size_region(fd);
127    if (result < 0) {
128        if (errno == ENOTTY) {
129            // ENOTTY means that the ioctl does not apply to this object,
130            // i.e., it is not an ashmem region.
131            return (jint) -1;
132        }
133        // Some other error, throw exception
134        jniThrowIOException(env, errno);
135        return (jint) -1;
136    }
137    return (jint) result;
138}
139
140static const JNINativeMethod methods[] = {
141    {"native_open",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
142    {"native_mmap",  "(Ljava/io/FileDescriptor;II)J", (void*)android_os_MemoryFile_mmap},
143    {"native_munmap", "(JI)V", (void*)android_os_MemoryFile_munmap},
144    {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
145    {"native_read",  "(Ljava/io/FileDescriptor;J[BIIIZ)I", (void*)android_os_MemoryFile_read},
146    {"native_write", "(Ljava/io/FileDescriptor;J[BIIIZ)V", (void*)android_os_MemoryFile_write},
147    {"native_pin",   "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
148    {"native_get_size", "(Ljava/io/FileDescriptor;)I",
149            (void*)android_os_MemoryFile_get_size}
150};
151
152int register_android_os_MemoryFile(JNIEnv* env)
153{
154    return RegisterMethodsOrDie(env, "android/os/MemoryFile", methods, NELEM(methods));
155}
156
157}
158