Loading core/java/android/app/ActivityThread.java +5 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.pm.SystemFeaturesCache; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; Loading Loading @@ -1342,6 +1343,10 @@ public final class ActivityThread extends ClientTransactionHandler ApplicationSharedMemory instance = ApplicationSharedMemory.fromFileDescriptor( applicationSharedMemoryFd, /* mutable= */ false); if (android.content.pm.Flags.cacheSdkSystemFeatures()) { SystemFeaturesCache.setInstance( new SystemFeaturesCache(instance.readSystemFeaturesCache())); } instance.closeFileDescriptor(); ApplicationSharedMemory.setInstance(instance); } Loading core/java/android/app/ApplicationPackageManager.java +19 −15 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.SystemFeaturesCache; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; Loading Loading @@ -803,16 +804,6 @@ public class ApplicationPackageManager extends PackageManager { @Override public Boolean recompute(HasSystemFeatureQuery query) { try { // As an optimization, check first to see if the feature was defined at // compile-time as either available or unavailable. // TODO(b/203143243): Consider hoisting this optimization out of the cache // after the trunk stable (build) flag has soaked and more features are // defined at compile-time. Boolean maybeHasSystemFeature = RoSystemFeatures.maybeHasFeature(query.name, query.version); if (maybeHasSystemFeature != null) { return maybeHasSystemFeature.booleanValue(); } return ActivityThread.currentActivityThread().getPackageManager(). hasSystemFeature(query.name, query.version); } catch (RemoteException e) { Loading @@ -823,12 +814,25 @@ public class ApplicationPackageManager extends PackageManager { @Override public boolean hasSystemFeature(String name, int version) { return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); // We check for system features in the following order: // * Build time-defined system features (constant, very efficient) // * SDK-defined system features (cached at process start, very efficient) // * IPC-retrieved system features (lazily cached, requires per-feature IPC) // TODO(b/375000483): Refactor all of this logic, including flag queries, into // the SystemFeaturesCache class after initial rollout and validation. Boolean maybeHasSystemFeature = RoSystemFeatures.maybeHasFeature(name, version); if (maybeHasSystemFeature != null) { return maybeHasSystemFeature; } /** @hide */ public void disableHasSystemFeatureCache() { mHasSystemFeatureCache.disableLocal(); if (com.android.internal.os.Flags.applicationSharedMemoryEnabled() && android.content.pm.Flags.cacheSdkSystemFeatures()) { maybeHasSystemFeature = SystemFeaturesCache.getInstance().maybeHasFeature(name, version); if (maybeHasSystemFeature != null) { return maybeHasSystemFeature; } } return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); } /** @hide */ Loading core/java/android/content/pm/SystemFeaturesCache.java +57 −38 Original line number Diff line number Diff line Loading @@ -16,9 +16,8 @@ package android.content.pm; import android.annotation.MainThread; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; Loading @@ -35,15 +34,52 @@ import java.util.Collection; * * @hide */ public final class SystemFeaturesCache implements Parcelable { public final class SystemFeaturesCache { // Sentinel value used for SDK-declared features that are unavailable on the current device. private static final int UNAVAILABLE_FEATURE_VERSION = Integer.MIN_VALUE; // This will be initialized just once, from the process main thread, but ready from any thread. private static volatile SystemFeaturesCache sInstance; // An array of versions for SDK-defined features, from [0, PackageManager.SDK_FEATURE_COUNT). @NonNull private final int[] mSdkFeatureVersions; /** * Installs the process-global cache instance. * * <p>Note: Usage should be gated on android.content.pm.Flags.cacheSdkSystemFeature(). In * practice, this should only be called from 1) SystemServer init, or 2) bindApplication. */ @MainThread public static void setInstance(SystemFeaturesCache instance) { if (sInstance != null) { throw new IllegalStateException("SystemFeaturesCache instance already initialized."); } sInstance = instance; } /** * Gets the process-global cache instance. * * Note: Usage should be gated on android.content.pm.Flags.cacheSdkSystemFeature(), and should * always occur after the instance has been installed early in the process lifecycle. */ public static @NonNull SystemFeaturesCache getInstance() { SystemFeaturesCache instance = sInstance; if (instance == null) { throw new IllegalStateException("SystemFeaturesCache not initialized"); } return instance; } /** Clears the process-global cache instance for testing. */ @VisibleForTesting public static void clearInstance() { sInstance = null; } /** * Populates the cache from the set of all available {@link FeatureInfo} definitions. * Loading @@ -69,20 +105,28 @@ public final class SystemFeaturesCache implements Parcelable { } } /** Only used by @{code CREATOR.createFromParcel(...)} */ private SystemFeaturesCache(@NonNull Parcel parcel) { final int[] featureVersions = parcel.createIntArray(); if (featureVersions == null) { throw new IllegalArgumentException( "Parceled SDK feature versions should never be null"); } if (featureVersions.length != PackageManager.SDK_FEATURE_COUNT) { /** * Populates the cache from an array of SDK feature versions originally obtained via {@link * #getSdkFeatureVersions()} from another instance. */ public SystemFeaturesCache(@NonNull int[] sdkFeatureVersions) { if (sdkFeatureVersions.length != PackageManager.SDK_FEATURE_COUNT) { throw new IllegalArgumentException( String.format( "Unexpected cached SDK feature count: %d (expected %d)", featureVersions.length, PackageManager.SDK_FEATURE_COUNT)); sdkFeatureVersions.length, PackageManager.SDK_FEATURE_COUNT)); } mSdkFeatureVersions = featureVersions; mSdkFeatureVersions = sdkFeatureVersions; } /** * Gets the raw cached feature versions. * * <p>Note: This should generally only be neded for (de)serialization purposes. */ // TODO(b/375000483): Consider reusing the ApplicationSharedMemory mapping for version lookup. public int[] getSdkFeatureVersions() { return mSdkFeatureVersions; } /** Loading @@ -105,29 +149,4 @@ public final class SystemFeaturesCache implements Parcelable { return mSdkFeatureVersions[sdkFeatureIndex] >= version; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeIntArray(mSdkFeatureVersions); } @NonNull public static final Parcelable.Creator<SystemFeaturesCache> CREATOR = new Parcelable.Creator<SystemFeaturesCache>() { @Override public SystemFeaturesCache createFromParcel(Parcel parcel) { return new SystemFeaturesCache(parcel); } @Override public SystemFeaturesCache[] newArray(int size) { return new SystemFeaturesCache[size]; } }; } core/java/com/android/internal/os/ApplicationSharedMemory.java +32 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.os; import android.annotation.NonNull; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; Loading Loading @@ -324,4 +325,35 @@ public class ApplicationSharedMemory implements AutoCloseable { */ @FastNative private static native long nativeGetSystemNonceBlock(long ptr); /** * Perform a one-time write of cached SDK feature versions. * * @throws IllegalStateException if the feature versions have already been written or the ashmem * is immutable. * @throws IllegalArgumentException if the provided feature version array is too large. */ public void writeSystemFeaturesCache(@NonNull int[] featureVersions) { checkMutable(); nativeWriteSystemFeaturesCache(mPtr, featureVersions); } /** * Read the cached SDK feature versions previously written to shared memory. * * Note: The result should generally be cached elsewhere for global reuse. */ // TODO(b/326623529): Consider using a MappedByteBuffer or equivalent to avoid needing a // Java copy of the cached data for potentially frequent reads. Alternatively, the JNI query // lookup for a given feature could be cheap enough to avoid the cached Java copy entirely. public @NonNull int[] readSystemFeaturesCache() { checkMapped(); return nativeReadSystemFeaturesCache(mPtr); } @FastNative private static native void nativeWriteSystemFeaturesCache(long ptr, int[] cache); @FastNative private static native int[] nativeReadSystemFeaturesCache(long ptr); } core/jni/com_android_internal_os_ApplicationSharedMemory.cpp +77 −5 Original line number Diff line number Diff line Loading @@ -23,18 +23,67 @@ #include <string.h> #include <sys/mman.h> #include <array> #include <atomic> #include <cstddef> #include <new> #include "core_jni_helpers.h" #include "android_app_PropertyInvalidatedCache.h" #include "core_jni_helpers.h" namespace { using namespace android::app::PropertyInvalidatedCache; class alignas(8) SystemFeaturesCache { public: // We only need enough space to handle the official set of SDK-defined system features (~200). // TODO(b/326623529): Reuse the exact value defined by PackageManager.SDK_FEATURE_COUNT. static constexpr int32_t kMaxSystemFeatures = 512; void writeSystemFeatures(JNIEnv* env, jintArray jfeatures) { if (featuresLength.load(std::memory_order_seq_cst) > 0) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "SystemFeaturesCache already written."); return; } int32_t jfeaturesLength = env->GetArrayLength(jfeatures); if (jfeaturesLength > kMaxSystemFeatures) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "SystemFeaturesCache only supports %d elements (vs %d requested).", kMaxSystemFeatures, jfeaturesLength); return; } env->GetIntArrayRegion(jfeatures, 0, jfeaturesLength, features.data()); featuresLength.store(jfeaturesLength, std::memory_order_seq_cst); } jintArray readSystemFeatures(JNIEnv* env) const { jint jfeaturesLength = static_cast<jint>(featuresLength.load(std::memory_order_seq_cst)); jintArray jfeatures = env->NewIntArray(jfeaturesLength); if (env->ExceptionCheck()) { return nullptr; } env->SetIntArrayRegion(jfeatures, 0, jfeaturesLength, features.data()); return jfeatures; } private: // A fixed length array of feature versions, with |featuresLength| dictating the actual size // of features that have been written. std::array<int32_t, kMaxSystemFeatures> features = {}; // The atomic acts as a barrier that precedes reads and follows writes, ensuring a // consistent view of |features| across processes. Note that r/w synchronization *within* a // process is handled at a higher level. std::atomic<int64_t> featuresLength = 0; }; static_assert(sizeof(SystemFeaturesCache) == sizeof(int32_t) * SystemFeaturesCache::kMaxSystemFeatures + sizeof(int64_t), "Unexpected SystemFeaturesCache size"); // Atomics should be safe to use across processes if they are lock free. static_assert(std::atomic<int64_t>::is_always_lock_free == true, "atomic<int64_t> is not always lock free"); Loading Loading @@ -69,14 +118,25 @@ public: latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset; } // The fixed size cache storage for SDK-defined system features. SystemFeaturesCache systemFeaturesCache; // The nonce storage for pic. The sizing is suitable for the system server module. SystemCacheNonce systemPic; }; // Update the expected value when modifying the members of SharedMemory. // Update the expected values when modifying the members of SharedMemory. // The goal of this assertion is to ensure that the data structure is the same size across 32-bit // and 64-bit systems. static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size"); // TODO(b/396674280): Add an additional fixed size check for SystemCacheNonce after resolving // ABI discrepancies. static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemFeaturesCache) + sizeof(SystemCacheNonce), "Unexpected SharedMemory size"); static_assert(offsetof(SharedMemory, systemFeaturesCache) == sizeof(int64_t), "Unexpected SystemFeaturesCache offset in SharedMemory"); static_assert(offsetof(SharedMemory, systemPic) == offsetof(SharedMemory, systemFeaturesCache) + sizeof(SystemFeaturesCache), "Unexpected SystemCachceNonce offset in SharedMemory"); static jint nativeCreate(JNIEnv* env, jclass) { // Create anonymous shared memory region Loading Loading @@ -146,6 +206,16 @@ static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) { return reinterpret_cast<jlong>(&sharedMemory->systemPic); } static void nativeWriteSystemFeaturesCache(JNIEnv* env, jclass*, jlong ptr, jintArray jfeatures) { SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr); sharedMemory->systemFeaturesCache.writeSystemFeatures(env, jfeatures); } static jintArray nativeReadSystemFeaturesCache(JNIEnv* env, jclass*, jlong ptr) { SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr); return sharedMemory->systemFeaturesCache.readSystemFeatures(env); } static const JNINativeMethod gMethods[] = { {"nativeCreate", "()I", (void*)nativeCreate}, {"nativeMap", "(IZ)J", (void*)nativeMap}, Loading @@ -157,6 +227,8 @@ static const JNINativeMethod gMethods[] = { {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J", (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis}, {"nativeGetSystemNonceBlock", "(J)J", (void*)nativeGetSystemNonceBlock}, {"nativeWriteSystemFeaturesCache", "(J[I)V", (void*)nativeWriteSystemFeaturesCache}, {"nativeReadSystemFeaturesCache", "(J)[I", (void*)nativeReadSystemFeaturesCache}, }; static const char kApplicationSharedMemoryClassName[] = Loading Loading
core/java/android/app/ActivityThread.java +5 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.pm.SystemFeaturesCache; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; Loading Loading @@ -1342,6 +1343,10 @@ public final class ActivityThread extends ClientTransactionHandler ApplicationSharedMemory instance = ApplicationSharedMemory.fromFileDescriptor( applicationSharedMemoryFd, /* mutable= */ false); if (android.content.pm.Flags.cacheSdkSystemFeatures()) { SystemFeaturesCache.setInstance( new SystemFeaturesCache(instance.readSystemFeaturesCache())); } instance.closeFileDescriptor(); ApplicationSharedMemory.setInstance(instance); } Loading
core/java/android/app/ApplicationPackageManager.java +19 −15 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.SystemFeaturesCache; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; Loading Loading @@ -803,16 +804,6 @@ public class ApplicationPackageManager extends PackageManager { @Override public Boolean recompute(HasSystemFeatureQuery query) { try { // As an optimization, check first to see if the feature was defined at // compile-time as either available or unavailable. // TODO(b/203143243): Consider hoisting this optimization out of the cache // after the trunk stable (build) flag has soaked and more features are // defined at compile-time. Boolean maybeHasSystemFeature = RoSystemFeatures.maybeHasFeature(query.name, query.version); if (maybeHasSystemFeature != null) { return maybeHasSystemFeature.booleanValue(); } return ActivityThread.currentActivityThread().getPackageManager(). hasSystemFeature(query.name, query.version); } catch (RemoteException e) { Loading @@ -823,12 +814,25 @@ public class ApplicationPackageManager extends PackageManager { @Override public boolean hasSystemFeature(String name, int version) { return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); // We check for system features in the following order: // * Build time-defined system features (constant, very efficient) // * SDK-defined system features (cached at process start, very efficient) // * IPC-retrieved system features (lazily cached, requires per-feature IPC) // TODO(b/375000483): Refactor all of this logic, including flag queries, into // the SystemFeaturesCache class after initial rollout and validation. Boolean maybeHasSystemFeature = RoSystemFeatures.maybeHasFeature(name, version); if (maybeHasSystemFeature != null) { return maybeHasSystemFeature; } /** @hide */ public void disableHasSystemFeatureCache() { mHasSystemFeatureCache.disableLocal(); if (com.android.internal.os.Flags.applicationSharedMemoryEnabled() && android.content.pm.Flags.cacheSdkSystemFeatures()) { maybeHasSystemFeature = SystemFeaturesCache.getInstance().maybeHasFeature(name, version); if (maybeHasSystemFeature != null) { return maybeHasSystemFeature; } } return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); } /** @hide */ Loading
core/java/android/content/pm/SystemFeaturesCache.java +57 −38 Original line number Diff line number Diff line Loading @@ -16,9 +16,8 @@ package android.content.pm; import android.annotation.MainThread; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; Loading @@ -35,15 +34,52 @@ import java.util.Collection; * * @hide */ public final class SystemFeaturesCache implements Parcelable { public final class SystemFeaturesCache { // Sentinel value used for SDK-declared features that are unavailable on the current device. private static final int UNAVAILABLE_FEATURE_VERSION = Integer.MIN_VALUE; // This will be initialized just once, from the process main thread, but ready from any thread. private static volatile SystemFeaturesCache sInstance; // An array of versions for SDK-defined features, from [0, PackageManager.SDK_FEATURE_COUNT). @NonNull private final int[] mSdkFeatureVersions; /** * Installs the process-global cache instance. * * <p>Note: Usage should be gated on android.content.pm.Flags.cacheSdkSystemFeature(). In * practice, this should only be called from 1) SystemServer init, or 2) bindApplication. */ @MainThread public static void setInstance(SystemFeaturesCache instance) { if (sInstance != null) { throw new IllegalStateException("SystemFeaturesCache instance already initialized."); } sInstance = instance; } /** * Gets the process-global cache instance. * * Note: Usage should be gated on android.content.pm.Flags.cacheSdkSystemFeature(), and should * always occur after the instance has been installed early in the process lifecycle. */ public static @NonNull SystemFeaturesCache getInstance() { SystemFeaturesCache instance = sInstance; if (instance == null) { throw new IllegalStateException("SystemFeaturesCache not initialized"); } return instance; } /** Clears the process-global cache instance for testing. */ @VisibleForTesting public static void clearInstance() { sInstance = null; } /** * Populates the cache from the set of all available {@link FeatureInfo} definitions. * Loading @@ -69,20 +105,28 @@ public final class SystemFeaturesCache implements Parcelable { } } /** Only used by @{code CREATOR.createFromParcel(...)} */ private SystemFeaturesCache(@NonNull Parcel parcel) { final int[] featureVersions = parcel.createIntArray(); if (featureVersions == null) { throw new IllegalArgumentException( "Parceled SDK feature versions should never be null"); } if (featureVersions.length != PackageManager.SDK_FEATURE_COUNT) { /** * Populates the cache from an array of SDK feature versions originally obtained via {@link * #getSdkFeatureVersions()} from another instance. */ public SystemFeaturesCache(@NonNull int[] sdkFeatureVersions) { if (sdkFeatureVersions.length != PackageManager.SDK_FEATURE_COUNT) { throw new IllegalArgumentException( String.format( "Unexpected cached SDK feature count: %d (expected %d)", featureVersions.length, PackageManager.SDK_FEATURE_COUNT)); sdkFeatureVersions.length, PackageManager.SDK_FEATURE_COUNT)); } mSdkFeatureVersions = featureVersions; mSdkFeatureVersions = sdkFeatureVersions; } /** * Gets the raw cached feature versions. * * <p>Note: This should generally only be neded for (de)serialization purposes. */ // TODO(b/375000483): Consider reusing the ApplicationSharedMemory mapping for version lookup. public int[] getSdkFeatureVersions() { return mSdkFeatureVersions; } /** Loading @@ -105,29 +149,4 @@ public final class SystemFeaturesCache implements Parcelable { return mSdkFeatureVersions[sdkFeatureIndex] >= version; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeIntArray(mSdkFeatureVersions); } @NonNull public static final Parcelable.Creator<SystemFeaturesCache> CREATOR = new Parcelable.Creator<SystemFeaturesCache>() { @Override public SystemFeaturesCache createFromParcel(Parcel parcel) { return new SystemFeaturesCache(parcel); } @Override public SystemFeaturesCache[] newArray(int size) { return new SystemFeaturesCache[size]; } }; }
core/java/com/android/internal/os/ApplicationSharedMemory.java +32 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.os; import android.annotation.NonNull; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; Loading Loading @@ -324,4 +325,35 @@ public class ApplicationSharedMemory implements AutoCloseable { */ @FastNative private static native long nativeGetSystemNonceBlock(long ptr); /** * Perform a one-time write of cached SDK feature versions. * * @throws IllegalStateException if the feature versions have already been written or the ashmem * is immutable. * @throws IllegalArgumentException if the provided feature version array is too large. */ public void writeSystemFeaturesCache(@NonNull int[] featureVersions) { checkMutable(); nativeWriteSystemFeaturesCache(mPtr, featureVersions); } /** * Read the cached SDK feature versions previously written to shared memory. * * Note: The result should generally be cached elsewhere for global reuse. */ // TODO(b/326623529): Consider using a MappedByteBuffer or equivalent to avoid needing a // Java copy of the cached data for potentially frequent reads. Alternatively, the JNI query // lookup for a given feature could be cheap enough to avoid the cached Java copy entirely. public @NonNull int[] readSystemFeaturesCache() { checkMapped(); return nativeReadSystemFeaturesCache(mPtr); } @FastNative private static native void nativeWriteSystemFeaturesCache(long ptr, int[] cache); @FastNative private static native int[] nativeReadSystemFeaturesCache(long ptr); }
core/jni/com_android_internal_os_ApplicationSharedMemory.cpp +77 −5 Original line number Diff line number Diff line Loading @@ -23,18 +23,67 @@ #include <string.h> #include <sys/mman.h> #include <array> #include <atomic> #include <cstddef> #include <new> #include "core_jni_helpers.h" #include "android_app_PropertyInvalidatedCache.h" #include "core_jni_helpers.h" namespace { using namespace android::app::PropertyInvalidatedCache; class alignas(8) SystemFeaturesCache { public: // We only need enough space to handle the official set of SDK-defined system features (~200). // TODO(b/326623529): Reuse the exact value defined by PackageManager.SDK_FEATURE_COUNT. static constexpr int32_t kMaxSystemFeatures = 512; void writeSystemFeatures(JNIEnv* env, jintArray jfeatures) { if (featuresLength.load(std::memory_order_seq_cst) > 0) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "SystemFeaturesCache already written."); return; } int32_t jfeaturesLength = env->GetArrayLength(jfeatures); if (jfeaturesLength > kMaxSystemFeatures) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "SystemFeaturesCache only supports %d elements (vs %d requested).", kMaxSystemFeatures, jfeaturesLength); return; } env->GetIntArrayRegion(jfeatures, 0, jfeaturesLength, features.data()); featuresLength.store(jfeaturesLength, std::memory_order_seq_cst); } jintArray readSystemFeatures(JNIEnv* env) const { jint jfeaturesLength = static_cast<jint>(featuresLength.load(std::memory_order_seq_cst)); jintArray jfeatures = env->NewIntArray(jfeaturesLength); if (env->ExceptionCheck()) { return nullptr; } env->SetIntArrayRegion(jfeatures, 0, jfeaturesLength, features.data()); return jfeatures; } private: // A fixed length array of feature versions, with |featuresLength| dictating the actual size // of features that have been written. std::array<int32_t, kMaxSystemFeatures> features = {}; // The atomic acts as a barrier that precedes reads and follows writes, ensuring a // consistent view of |features| across processes. Note that r/w synchronization *within* a // process is handled at a higher level. std::atomic<int64_t> featuresLength = 0; }; static_assert(sizeof(SystemFeaturesCache) == sizeof(int32_t) * SystemFeaturesCache::kMaxSystemFeatures + sizeof(int64_t), "Unexpected SystemFeaturesCache size"); // Atomics should be safe to use across processes if they are lock free. static_assert(std::atomic<int64_t>::is_always_lock_free == true, "atomic<int64_t> is not always lock free"); Loading Loading @@ -69,14 +118,25 @@ public: latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset; } // The fixed size cache storage for SDK-defined system features. SystemFeaturesCache systemFeaturesCache; // The nonce storage for pic. The sizing is suitable for the system server module. SystemCacheNonce systemPic; }; // Update the expected value when modifying the members of SharedMemory. // Update the expected values when modifying the members of SharedMemory. // The goal of this assertion is to ensure that the data structure is the same size across 32-bit // and 64-bit systems. static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size"); // TODO(b/396674280): Add an additional fixed size check for SystemCacheNonce after resolving // ABI discrepancies. static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemFeaturesCache) + sizeof(SystemCacheNonce), "Unexpected SharedMemory size"); static_assert(offsetof(SharedMemory, systemFeaturesCache) == sizeof(int64_t), "Unexpected SystemFeaturesCache offset in SharedMemory"); static_assert(offsetof(SharedMemory, systemPic) == offsetof(SharedMemory, systemFeaturesCache) + sizeof(SystemFeaturesCache), "Unexpected SystemCachceNonce offset in SharedMemory"); static jint nativeCreate(JNIEnv* env, jclass) { // Create anonymous shared memory region Loading Loading @@ -146,6 +206,16 @@ static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) { return reinterpret_cast<jlong>(&sharedMemory->systemPic); } static void nativeWriteSystemFeaturesCache(JNIEnv* env, jclass*, jlong ptr, jintArray jfeatures) { SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr); sharedMemory->systemFeaturesCache.writeSystemFeatures(env, jfeatures); } static jintArray nativeReadSystemFeaturesCache(JNIEnv* env, jclass*, jlong ptr) { SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr); return sharedMemory->systemFeaturesCache.readSystemFeatures(env); } static const JNINativeMethod gMethods[] = { {"nativeCreate", "()I", (void*)nativeCreate}, {"nativeMap", "(IZ)J", (void*)nativeMap}, Loading @@ -157,6 +227,8 @@ static const JNINativeMethod gMethods[] = { {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J", (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis}, {"nativeGetSystemNonceBlock", "(J)J", (void*)nativeGetSystemNonceBlock}, {"nativeWriteSystemFeaturesCache", "(J[I)V", (void*)nativeWriteSystemFeaturesCache}, {"nativeReadSystemFeaturesCache", "(J)[I", (void*)nativeReadSystemFeaturesCache}, }; static const char kApplicationSharedMemoryClassName[] = Loading