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