Add SharedMemory API

SharedMemory is parcelable and AIDL friendly
and exposes cool things like dropping write permissions
that are useful for shared memory to have.

Also exposes getFileDescriptor() on MemoryFile
since that was necessary to use MemoryFile
with IPC previously and there are a sizeable
number of reflections that access it, so just make
it public.

Test: SharedMemory CTS tests pass

Change-Id: I71a322abf2eb55267d7299a8e41f847339af8b08
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
new file mode 100644
index 0000000..24d0811
--- /dev/null
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SharedMemory"
+
+#include "core_jni_helpers.h"
+
+#include <cutils/ashmem.h>
+#include <utils/Log.h>
+#include "JNIHelp.h"
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <limits>
+#include <unistd.h>
+
+namespace {
+
+static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
+    static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass,
+            "<init>", "(Ljava/lang/String;I)V");
+
+    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
+    if (detailMessage.get() == NULL) {
+        // Not really much we can do here. We're probably dead in the water,
+        // but let's try to stumble on...
+        env->ExceptionClear();
+    }
+
+    jobject exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
+            detailMessage.get(), error);
+    env->Throw(reinterpret_cast<jthrowable>(exception));
+}
+
+static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {
+
+    // Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
+    const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr;
+
+    int fd = ashmem_create_region(name, size);
+
+    // Capture the error, if there is one, before calling ReleaseStringUTFChars
+    int err = fd < 0 ? errno : 0;
+
+    if (name) {
+        env->ReleaseStringUTFChars(jname, name);
+    }
+
+    if (fd < 0) {
+        throwErrnoException(env, "SharedMemory_create", err);
+        return nullptr;
+    }
+
+    return jniCreateFileDescriptor(env, fd);
+}
+
+static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) {
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    if (!ashmem_valid(fd)) {
+        return -1;
+    }
+    size_t size = ashmem_get_size_region(fd);
+    return static_cast<jint>(std::min(size, static_cast<size_t>(std::numeric_limits<jint>::max())));
+}
+
+static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    int err = 0;
+    if (ashmem_set_prot_region(fd, prot)) {
+        err = errno;
+    }
+    return err;
+}
+
+static const JNINativeMethod methods[] = {
+    {"nCreate", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)SharedMemory_create},
+    {"nGetSize", "(Ljava/io/FileDescriptor;)I", (void*)SharedMemory_getSize},
+    {"nSetProt", "(Ljava/io/FileDescriptor;I)I", (void*)SharedMemory_setProt},
+};
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_os_SharedMemory(JNIEnv* env)
+{
+    return RegisterMethodsOrDie(env, "android/os/SharedMemory", methods, NELEM(methods));
+}
+
+} // namespace android