xref: /aosp_15_r20/frameworks/base/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2024 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 // See: ApplicationSharedMemory.md
18 
19 #include <cutils/ashmem.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <nativehelper/JNIHelp.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 
26 #include <atomic>
27 #include <cstddef>
28 #include <new>
29 
30 #include "core_jni_helpers.h"
31 
32 #include "android_app_PropertyInvalidatedCache.h"
33 
34 namespace {
35 
36 using namespace android::app::PropertyInvalidatedCache;
37 
38 // Atomics should be safe to use across processes if they are lock free.
39 static_assert(std::atomic<int64_t>::is_always_lock_free == true,
40               "atomic<int64_t> is not always lock free");
41 
42 // This is the data structure that is shared between processes.
43 //
44 // Tips for extending:
45 // - Atomics are safe for cross-process use as they are lock free, if they are accessed as
46 //   individual values.
47 // - Consider multi-ABI systems, e.g. devices that support launching both 64-bit and 32-bit
48 //   app processes. Use fixed-size types (e.g. `int64_t`) to ensure that the data structure is
49 //   the same size across all ABIs. Avoid implicit assumptions about struct packing/padding.
50 class alignas(8) SharedMemory { // Ensure that `sizeof(SharedMemory)` is the same across 32-bit and
51                                 // 64-bit systems.
52 private:
53     volatile std::atomic<int64_t> latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis;
54 
55     // LINT.IfChange(invalid_network_time)
56     static constexpr int64_t INVALID_NETWORK_TIME = -1;
57     // LINT.ThenChange(frameworks/base/core/java/com/android/internal/os/ApplicationSharedMemory.java:invalid_network_time)
58 
59 public:
60     // Default constructor sets initial values
SharedMemory()61     SharedMemory()
62           : latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(INVALID_NETWORK_TIME) {}
63 
getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() const64     int64_t getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() const {
65         return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis;
66     }
67 
setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset)68     void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) {
69         latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset;
70     }
71 
72     // The nonce storage for pic.  The sizing is suitable for the system server module.
73     SystemCacheNonce systemPic;
74 };
75 
76 // Update the expected value when modifying the members of SharedMemory.
77 // The goal of this assertion is to ensure that the data structure is the same size across 32-bit
78 // and 64-bit systems.
79 static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size");
80 
nativeCreate(JNIEnv * env,jclass)81 static jint nativeCreate(JNIEnv* env, jclass) {
82     // Create anonymous shared memory region
83     int fd = ashmem_create_region("ApplicationSharedMemory", sizeof(SharedMemory));
84     if (fd < 0) {
85         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to create ashmem: %s",
86                              strerror(errno));
87     }
88     return fd;
89 }
90 
nativeMap(JNIEnv * env,jclass,jint fd,jboolean isMutable)91 static jlong nativeMap(JNIEnv* env, jclass, jint fd, jboolean isMutable) {
92     void* ptr = mmap(nullptr, sizeof(SharedMemory), isMutable ? PROT_READ | PROT_WRITE : PROT_READ,
93                      MAP_SHARED, fd, 0);
94     if (ptr == MAP_FAILED) {
95         close(fd);
96         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to mmap shared memory: %s",
97                              strerror(errno));
98     }
99 
100     return reinterpret_cast<jlong>(ptr);
101 }
102 
nativeInit(JNIEnv * env,jclass,jlong ptr)103 static void nativeInit(JNIEnv* env, jclass, jlong ptr) {
104     new (reinterpret_cast<SharedMemory*>(ptr)) SharedMemory();
105 }
106 
nativeUnmap(JNIEnv * env,jclass,jlong ptr)107 static void nativeUnmap(JNIEnv* env, jclass, jlong ptr) {
108     if (munmap(reinterpret_cast<void*>(ptr), sizeof(SharedMemory)) == -1) {
109         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
110                              "Failed to munmap shared memory: %s", strerror(errno));
111     }
112 }
113 
nativeDupAsReadOnly(JNIEnv * env,jclass,jint fd)114 static jint nativeDupAsReadOnly(JNIEnv* env, jclass, jint fd) {
115     // Duplicate file descriptor
116     fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
117     if (fd < 0) {
118         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to dup fd: %s",
119                              strerror(errno));
120     }
121 
122     // Set new file descriptor to read-only
123     if (ashmem_set_prot_region(fd, PROT_READ)) {
124         close(fd);
125         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
126                              "Failed to ashmem_set_prot_region: %s", strerror(errno));
127     }
128 
129     return fd;
130 }
131 
nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr,jlong offset)132 static void nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr,
133                                                                                  jlong offset) {
134     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
135     sharedMemory->setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(offset);
136 }
137 
nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr)138 static jlong nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr) {
139     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
140     return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
141 }
142 
143 // This is a FastNative method.  It takes the usual JNIEnv* and jclass* arguments.
nativeGetSystemNonceBlock(JNIEnv *,jclass *,jlong ptr)144 static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) {
145     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
146     return reinterpret_cast<jlong>(&sharedMemory->systemPic);
147 }
148 
149 static const JNINativeMethod gMethods[] = {
150         {"nativeCreate", "()I", (void*)nativeCreate},
151         {"nativeMap", "(IZ)J", (void*)nativeMap},
152         {"nativeInit", "(J)V", (void*)nativeInit},
153         {"nativeUnmap", "(J)V", (void*)nativeUnmap},
154         {"nativeDupAsReadOnly", "(I)I", (void*)nativeDupAsReadOnly},
155         {"nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(JJ)V",
156          (void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
157         {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J",
158          (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
159         {"nativeGetSystemNonceBlock", "(J)J", (void*) nativeGetSystemNonceBlock},
160 };
161 
162 static const char kApplicationSharedMemoryClassName[] =
163         "com/android/internal/os/ApplicationSharedMemory";
164 static jclass gApplicationSharedMemoryClass;
165 
166 } // anonymous namespace
167 
168 namespace android {
169 
register_com_android_internal_os_ApplicationSharedMemory(JNIEnv * env)170 int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) {
171     gApplicationSharedMemoryClass =
172             MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName));
173     RegisterMethodsOrDie(env, "com/android/internal/os/ApplicationSharedMemory", gMethods,
174                          NELEM(gMethods));
175     return JNI_OK;
176 }
177 
178 } // namespace android
179