• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <array>
27 #include <atomic>
28 #include <cstddef>
29 #include <new>
30 
31 #include "android_app_PropertyInvalidatedCache.h"
32 #include "core_jni_helpers.h"
33 
34 namespace {
35 
36 using namespace android::app::PropertyInvalidatedCache;
37 
38 class alignas(8) SystemFeaturesCache {
39 public:
40     // We only need enough space to handle the official set of SDK-defined system features (~200).
41     // TODO(b/326623529): Reuse the exact value defined by PackageManager.SDK_FEATURE_COUNT.
42     static constexpr int32_t kMaxSystemFeatures = 512;
43 
writeSystemFeatures(JNIEnv * env,jintArray jfeatures)44     void writeSystemFeatures(JNIEnv* env, jintArray jfeatures) {
45         if (featuresLength.load(std::memory_order_seq_cst) > 0) {
46             jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
47                                  "SystemFeaturesCache already written.");
48             return;
49         }
50 
51         int32_t jfeaturesLength = env->GetArrayLength(jfeatures);
52         if (jfeaturesLength > kMaxSystemFeatures) {
53             jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
54                                  "SystemFeaturesCache only supports %d elements (vs %d requested).",
55                                  kMaxSystemFeatures, jfeaturesLength);
56             return;
57         }
58         env->GetIntArrayRegion(jfeatures, 0, jfeaturesLength, features.data());
59         featuresLength.store(jfeaturesLength, std::memory_order_seq_cst);
60     }
61 
readSystemFeatures(JNIEnv * env) const62     jintArray readSystemFeatures(JNIEnv* env) const {
63         jint jfeaturesLength = static_cast<jint>(featuresLength.load(std::memory_order_seq_cst));
64         jintArray jfeatures = env->NewIntArray(jfeaturesLength);
65         if (env->ExceptionCheck()) {
66             return nullptr;
67         }
68 
69         env->SetIntArrayRegion(jfeatures, 0, jfeaturesLength, features.data());
70         return jfeatures;
71     }
72 
73 private:
74     // A fixed length array of feature versions, with |featuresLength| dictating the actual size
75     // of features that have been written.
76     std::array<int32_t, kMaxSystemFeatures> features = {};
77     // The atomic acts as a barrier that precedes reads and follows writes, ensuring a
78     // consistent view of |features| across processes. Note that r/w synchronization *within* a
79     // process is handled at a higher level.
80     std::atomic<int64_t> featuresLength = 0;
81 };
82 
83 static_assert(sizeof(SystemFeaturesCache) ==
84                       sizeof(int32_t) * SystemFeaturesCache::kMaxSystemFeatures + sizeof(int64_t),
85               "Unexpected SystemFeaturesCache size");
86 
87 // Atomics should be safe to use across processes if they are lock free.
88 static_assert(std::atomic<int64_t>::is_always_lock_free == true,
89               "atomic<int64_t> is not always lock free");
90 
91 // This is the data structure that is shared between processes.
92 //
93 // Tips for extending:
94 // - Atomics are safe for cross-process use as they are lock free, if they are accessed as
95 //   individual values.
96 // - Consider multi-ABI systems, e.g. devices that support launching both 64-bit and 32-bit
97 //   app processes. Use fixed-size types (e.g. `int64_t`) to ensure that the data structure is
98 //   the same size across all ABIs. Avoid implicit assumptions about struct packing/padding.
99 class alignas(8) SharedMemory { // Ensure that `sizeof(SharedMemory)` is the same across 32-bit and
100                                 // 64-bit systems.
101 private:
102     volatile std::atomic<int64_t> latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis;
103 
104     // LINT.IfChange(invalid_network_time)
105     static constexpr int64_t INVALID_NETWORK_TIME = -1;
106     // LINT.ThenChange(frameworks/base/core/java/com/android/internal/os/ApplicationSharedMemory.java:invalid_network_time)
107 
108 public:
109     // Default constructor sets initial values
SharedMemory()110     SharedMemory()
111           : latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(INVALID_NETWORK_TIME) {}
112 
getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() const113     int64_t getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() const {
114         return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis;
115     }
116 
setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset)117     void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) {
118         latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset;
119     }
120 
121     // The fixed size cache storage for SDK-defined system features.
122     SystemFeaturesCache systemFeaturesCache;
123 
124     // The nonce storage for pic.  The sizing is suitable for the system server module.
125     SystemCacheNonce systemPic;
126 };
127 
128 // Update the expected values when modifying the members of SharedMemory.
129 // The goal of this assertion is to ensure that the data structure is the same size across 32-bit
130 // and 64-bit systems.
131 // TODO(b/396674280): Add an additional fixed size check for SystemCacheNonce after resolving
132 // ABI discrepancies.
133 static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemFeaturesCache) + sizeof(SystemCacheNonce),
134               "Unexpected SharedMemory size");
135 static_assert(offsetof(SharedMemory, systemFeaturesCache) == sizeof(int64_t),
136               "Unexpected SystemFeaturesCache offset in SharedMemory");
137 static_assert(offsetof(SharedMemory, systemPic) ==
138                       offsetof(SharedMemory, systemFeaturesCache) + sizeof(SystemFeaturesCache),
139               "Unexpected SystemCachceNonce offset in SharedMemory");
140 
nativeCreate(JNIEnv * env,jclass)141 static jint nativeCreate(JNIEnv* env, jclass) {
142     // Create anonymous shared memory region
143     int fd = ashmem_create_region("ApplicationSharedMemory", sizeof(SharedMemory));
144     if (fd < 0) {
145         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to create ashmem: %s",
146                              strerror(errno));
147     }
148     return fd;
149 }
150 
nativeMap(JNIEnv * env,jclass,jint fd,jboolean isMutable)151 static jlong nativeMap(JNIEnv* env, jclass, jint fd, jboolean isMutable) {
152     void* ptr = mmap(nullptr, sizeof(SharedMemory), isMutable ? PROT_READ | PROT_WRITE : PROT_READ,
153                      MAP_SHARED, fd, 0);
154     if (ptr == MAP_FAILED) {
155         close(fd);
156         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to mmap shared memory: %s",
157                              strerror(errno));
158     }
159 
160     return reinterpret_cast<jlong>(ptr);
161 }
162 
nativeInit(JNIEnv * env,jclass,jlong ptr)163 static void nativeInit(JNIEnv* env, jclass, jlong ptr) {
164     new (reinterpret_cast<SharedMemory*>(ptr)) SharedMemory();
165 }
166 
nativeUnmap(JNIEnv * env,jclass,jlong ptr)167 static void nativeUnmap(JNIEnv* env, jclass, jlong ptr) {
168     if (munmap(reinterpret_cast<void*>(ptr), sizeof(SharedMemory)) == -1) {
169         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
170                              "Failed to munmap shared memory: %s", strerror(errno));
171     }
172 }
173 
nativeDupAsReadOnly(JNIEnv * env,jclass,jint fd)174 static jint nativeDupAsReadOnly(JNIEnv* env, jclass, jint fd) {
175     // Duplicate file descriptor
176     fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
177     if (fd < 0) {
178         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to dup fd: %s",
179                              strerror(errno));
180     }
181 
182     // Set new file descriptor to read-only
183     if (ashmem_set_prot_region(fd, PROT_READ)) {
184         close(fd);
185         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
186                              "Failed to ashmem_set_prot_region: %s", strerror(errno));
187     }
188 
189     return fd;
190 }
191 
nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr,jlong offset)192 static void nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr,
193                                                                                  jlong offset) {
194     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
195     sharedMemory->setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(offset);
196 }
197 
nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr)198 static jlong nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr) {
199     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
200     return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
201 }
202 
203 // This is a FastNative method.  It takes the usual JNIEnv* and jclass* arguments.
nativeGetSystemNonceBlock(JNIEnv *,jclass *,jlong ptr)204 static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) {
205     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
206     return reinterpret_cast<jlong>(&sharedMemory->systemPic);
207 }
208 
nativeWriteSystemFeaturesCache(JNIEnv * env,jclass *,jlong ptr,jintArray jfeatures)209 static void nativeWriteSystemFeaturesCache(JNIEnv* env, jclass*, jlong ptr, jintArray jfeatures) {
210     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
211     sharedMemory->systemFeaturesCache.writeSystemFeatures(env, jfeatures);
212 }
213 
nativeReadSystemFeaturesCache(JNIEnv * env,jclass *,jlong ptr)214 static jintArray nativeReadSystemFeaturesCache(JNIEnv* env, jclass*, jlong ptr) {
215     SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
216     return sharedMemory->systemFeaturesCache.readSystemFeatures(env);
217 }
218 
219 static const JNINativeMethod gMethods[] = {
nativeCreate()220         {"nativeCreate", "()I", (void*)nativeCreate},
nativeMap(IZ)221         {"nativeMap", "(IZ)J", (void*)nativeMap},
nativeInit(J)222         {"nativeInit", "(J)V", (void*)nativeInit},
nativeUnmap(J)223         {"nativeUnmap", "(J)V", (void*)nativeUnmap},
nativeDupAsReadOnly(I)224         {"nativeDupAsReadOnly", "(I)I", (void*)nativeDupAsReadOnly},
nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(JJ)225         {"nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(JJ)V",
226          (void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(J)227         {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J",
228          (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
nativeGetSystemNonceBlock(J)229         {"nativeGetSystemNonceBlock", "(J)J", (void*)nativeGetSystemNonceBlock},
nativeWriteSystemFeaturesCache(J[I)230         {"nativeWriteSystemFeaturesCache", "(J[I)V", (void*)nativeWriteSystemFeaturesCache},
nativeReadSystemFeaturesCache(J)231         {"nativeReadSystemFeaturesCache", "(J)[I", (void*)nativeReadSystemFeaturesCache},
232 };
233 
234 static const char kApplicationSharedMemoryClassName[] =
235         "com/android/internal/os/ApplicationSharedMemory";
236 static jclass gApplicationSharedMemoryClass;
237 
238 } // anonymous namespace
239 
240 namespace android {
241 
register_com_android_internal_os_ApplicationSharedMemory(JNIEnv * env)242 int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) {
243     gApplicationSharedMemoryClass =
244             MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName));
245     RegisterMethodsOrDie(env, "com/android/internal/os/ApplicationSharedMemory", gMethods,
246                          NELEM(gMethods));
247     return JNI_OK;
248 }
249 
250 } // namespace android
251