Loading core/java/android/content/ContentProvider.java +16 −0 Original line number Diff line number Diff line Loading @@ -1207,6 +1207,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return null; } /** * Returns the device id associated with the {@link Context} of the caller. * * @see Context#getDeviceId() * @hide */ public final int getCallingDeviceId() { if (android.permission.flags.Flags.deviceAwarePermissionApisEnabled()) { final AttributionSource attributionSource = mCallingAttributionSource.get(); if (attributionSource != null) { return attributionSource.getDeviceId(); } } return Context.DEVICE_ID_DEFAULT; } /** * @removed */ Loading core/java/android/provider/Settings.java +69 −34 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import android.app.NotificationManager; import android.app.SearchManager; import android.app.WallpaperManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; Loading Loading @@ -3369,15 +3370,15 @@ public final class Settings { } private static final class GenerationTracker { @NonNull private final String mName; @NonNull private final Key mKey; @NonNull private final MemoryIntArray mArray; @NonNull private final Consumer<String> mErrorHandler; @NonNull private final Consumer<Key> mErrorHandler; private final int mIndex; private int mCurrentGeneration; GenerationTracker(@NonNull String name, @NonNull MemoryIntArray array, int index, int generation, Consumer<String> errorHandler) { mName = name; GenerationTracker(@NonNull Key key, @NonNull MemoryIntArray array, int index, int generation, @NonNull Consumer<Key> errorHandler) { mKey = key; mArray = array; mIndex = index; mErrorHandler = errorHandler; Loading Loading @@ -3405,7 +3406,7 @@ public final class Settings { return mArray.get(mIndex); } catch (IOException e) { Log.e(TAG, "Error getting current generation", e); mErrorHandler.accept(mName); mErrorHandler.accept(mKey); } return -1; } Loading @@ -3422,6 +3423,28 @@ public final class Settings { super.finalize(); } } private static final class Key { private final String mName; private final int mDeviceId; Key(String name, int deviceId) { mName = name; mDeviceId = deviceId; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Key key)) return false; return mDeviceId == key.mDeviceId && Objects.equals(mName, key.mName); } @Override public int hashCode() { return Objects.hash(mName, mDeviceId); } } } private static void maybeCloseGenerationArray(@Nullable MemoryIntArray array) { Loading Loading @@ -3479,9 +3502,9 @@ public final class Settings { private static final String NAME_EQ_PLACEHOLDER = "name=?"; // Cached values of queried settings. // Key is the setting's name, value is the setting's value. // Key is composed by the setting's name and deviceId, value is the setting's value. // Must synchronize on 'this' to access mValues and mValuesVersion. private final ArrayMap<String, String> mValues = new ArrayMap<>(); private final ArrayMap<GenerationTracker.Key, String> mValues = new ArrayMap<>(); // Cached values for queried prefixes. // Key is the prefix, value is all of the settings under the prefix, mapped from a setting's Loading @@ -3505,19 +3528,22 @@ public final class Settings { private final ArraySet<String> mAllFields; private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk; // Mapping from the name of a setting (or the prefix of a namespace) to a generation tracker // Mapping of key to generation trackers for queried settings. // Key is composed by the setting's name and deviceId, value is the generation tracker. // Must synchronize on 'this' to access mGenerationTrackers. @GuardedBy("this") private ArrayMap<String, GenerationTracker> mGenerationTrackers = new ArrayMap<>(); private final ArrayMap<GenerationTracker.Key, GenerationTracker> mGenerationTrackers = new ArrayMap<>(); private Consumer<String> mGenerationTrackerErrorHandler = (String name) -> { private final Consumer<GenerationTracker.Key> mGenerationTrackerErrorHandler = (key) -> { synchronized (NameValueCache.this) { Log.e(TAG, "Error accessing generation tracker - removing"); final GenerationTracker tracker = mGenerationTrackers.get(name); final GenerationTracker tracker = mGenerationTrackers.get(key); if (tracker != null) { tracker.destroy(); mGenerationTrackers.remove(name); mGenerationTrackers.remove(key); } mValues.remove(name); mValues.remove(key); } }; Loading Loading @@ -3621,11 +3647,18 @@ public final class Settings { @UnsupportedAppUsage public String getStringForUser(ContentResolver cr, String name, final int userHandle) { final boolean isSelf = (userHandle == UserHandle.myUserId()); final AttributionSource attributionSource = cr.getAttributionSource(); final int deviceId = android.companion.virtualdevice.flags.Flags.deviceAwareSettingsOverride() && android.permission.flags.Flags.deviceAwarePermissionApisEnabled() && attributionSource != null ? attributionSource.getDeviceId() : Context.DEVICE_ID_DEFAULT; final GenerationTracker.Key key = new GenerationTracker.Key(name, deviceId); final boolean useCache = isSelf && !isInSystemServer(); boolean needsGenerationTracker = false; if (useCache) { synchronized (NameValueCache.this) { final GenerationTracker generationTracker = mGenerationTrackers.get(name); final GenerationTracker generationTracker = mGenerationTrackers.get(key); if (generationTracker != null) { if (generationTracker.isGenerationChanged()) { if (DEBUG) { Loading @@ -3636,14 +3669,14 @@ public final class Settings { } // When a generation number changes, remove cached value, remove the old // generation tracker and request a new one mValues.remove(name); mValues.remove(key); generationTracker.destroy(); mGenerationTrackers.remove(name); } else if (mValues.containsKey(name)) { mGenerationTrackers.remove(key); } else if (mValues.containsKey(key)) { if (DEBUG) { Log.i(TAG, "Cache hit for setting:" + name); } return mValues.get(name); return mValues.get(key); } } } Loading Loading @@ -3761,23 +3794,23 @@ public final class Settings { } // Always make sure to close any pre-existing tracker before // replacing it, to prevent memory leaks var oldTracker = mGenerationTrackers.get(name); var oldTracker = mGenerationTrackers.get(key); if (oldTracker != null) { oldTracker.destroy(); } mGenerationTrackers.put(name, new GenerationTracker(name, mGenerationTrackers.put(key, new GenerationTracker(key, array, index, generation, mGenerationTrackerErrorHandler)); } else { maybeCloseGenerationArray(array); } } if (mGenerationTrackers.get(name) != null && !mGenerationTrackers.get(name).isGenerationChanged()) { GenerationTracker tracker = mGenerationTrackers.get(key); if (tracker != null && !tracker.isGenerationChanged()) { if (DEBUG) { Log.i(TAG, "Updating cache for setting:" + name); } mValues.put(name, value); mValues.put(key, value); } } } else { Loading Loading @@ -3822,12 +3855,12 @@ public final class Settings { String value = c.moveToNext() ? c.getString(0) : null; synchronized (NameValueCache.this) { if (mGenerationTrackers.get(name) != null && !mGenerationTrackers.get(name).isGenerationChanged()) { GenerationTracker tracker = mGenerationTrackers.get(key); if (tracker != null && !tracker.isGenerationChanged()) { if (DEBUG) { Log.i(TAG, "Updating cache for setting:" + name + " using query"); } mValues.put(name, value); mValues.put(key, value); } } return value; Loading Loading @@ -3859,13 +3892,15 @@ public final class Settings { private Map<String, String> getStringsForPrefixStripPrefix( ContentResolver cr, String prefix, List<String> names) { final GenerationTracker.Key trackerKey = new GenerationTracker.Key(prefix, Context.DEVICE_ID_DEFAULT); String namespace = prefix.substring(0, prefix.length() - 1); ArrayMap<String, String> keyValues = new ArrayMap<>(); int substringLength = prefix.length(); int currentGeneration = -1; boolean needsGenerationTracker = false; synchronized (NameValueCache.this) { final GenerationTracker generationTracker = mGenerationTrackers.get(prefix); final GenerationTracker generationTracker = mGenerationTrackers.get(trackerKey); if (generationTracker != null) { if (generationTracker.isGenerationChanged()) { if (DEBUG) { Loading @@ -3876,7 +3911,7 @@ public final class Settings { // When a generation number changes, remove cached values, remove the old // generation tracker and request a new one generationTracker.destroy(); mGenerationTrackers.remove(prefix); mGenerationTrackers.remove(trackerKey); mPrefixToValues.remove(prefix); needsGenerationTracker = true; } else { Loading Loading @@ -3993,20 +4028,20 @@ public final class Settings { } // Always make sure to close any pre-existing tracker before // replacing it, to prevent memory leaks var oldTracker = mGenerationTrackers.get(prefix); var oldTracker = mGenerationTrackers.get(trackerKey); if (oldTracker != null) { oldTracker.destroy(); } mGenerationTrackers.put(prefix, new GenerationTracker(prefix, array, index, generation, mGenerationTrackers.put(trackerKey, new GenerationTracker(trackerKey, array, index, generation, mGenerationTrackerErrorHandler)); currentGeneration = generation; } else { maybeCloseGenerationArray(array); } } if (mGenerationTrackers.get(prefix) != null && currentGeneration == mGenerationTrackers.get(prefix).getCurrentGeneration()) { GenerationTracker tracker = mGenerationTrackers.get(trackerKey); if (tracker != null && currentGeneration == tracker.getCurrentGeneration()) { if (DEBUG) { Log.i(TAG, "Updating cache for prefix:" + prefix); } Loading core/proto/android/providers/settings/generation.proto +1 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ message GenerationRegistryProto { } message BackingStoreProto { optional int32 key = 1; optional int64 key = 1; optional int32 backing_store_size = 2; optional int32 num_cached_entries = 3; repeated CacheEntryProto cache_entries = 4; Loading packages/SettingsProvider/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ android_library { "aconfig_new_storage_flags_lib", "aconfigd_java_utils", "aconfig_demo_flags_java_lib", "android.companion.virtualdevice.flags-aconfig-java", "configinfra_framework_flags_java_lib", "device_config_service_flags_java", "libaconfig_java_proto_lite", Loading @@ -62,6 +63,7 @@ android_test { "test/**/*.java", ], static_libs: [ "CtsVirtualDeviceCommonLib", // Note we statically link SettingsProviderLib to do some unit tests. It's not accessible otherwise // because this test is not an instrumentation test. (because the target runs in the system process.) "SettingsProviderLib", Loading @@ -70,6 +72,7 @@ android_test { "device_config_service_flags_java", "flag-junit", "junit", "junit-params", "libaconfig_java_proto_lite", "mockito-target-minus-junit4", "platform-test-annotations", Loading packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java +42 −36 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.providers.settings; import android.annotation.NonNull; import android.content.Context; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; Loading @@ -24,6 +25,7 @@ import android.providers.settings.BackingStoreProto; import android.providers.settings.CacheEntryProto; import android.providers.settings.GenerationRegistryProto; import android.util.ArrayMap; import android.util.LongSparseArray; import android.util.MemoryIntArray; import android.util.Slog; import android.util.proto.ProtoOutputStream; Loading @@ -50,11 +52,12 @@ final class GenerationRegistry { // Key -> backingStore mapping @GuardedBy("mLock") private final ArrayMap<Integer, MemoryIntArray> mKeyToBackingStoreMap = new ArrayMap(); private final LongSparseArray<MemoryIntArray> mKeyToBackingStoreMap = new LongSparseArray<>(); // Key -> (String->Index map) mapping @GuardedBy("mLock") private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>(); private final LongSparseArray<ArrayMap<String, Integer>> mKeyToIndexMapMap = new LongSparseArray<>(); @GuardedBy("mLock") private int mNumBackingStore = 0; Loading Loading @@ -90,18 +93,19 @@ final class GenerationRegistry { * Increment the generation number if the setting is already cached in the backing stores. * Otherwise, do nothing. */ public void incrementGeneration(int key, String name) { final boolean isConfig = (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); public void incrementGeneration(long key, String name) { final boolean isConfig = SettingsState.isConfigSettingsKey(key); // Only store the prefix if the mutated setting is a config final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name; incrementGenerationInternal(key, indexMapKey); } private void incrementGenerationInternal(int key, @NonNull String indexMapKey) { private void incrementGenerationInternal(long key, @NonNull String indexMapKey) { if (SettingsState.isGlobalSettingsKey(key)) { // Global settings are shared across users, so ignore the userId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); // Global settings are shared across users and devices, so ignore the userId and the // deviceId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, Context.DEVICE_ID_DEFAULT); } synchronized (mLock) { final MemoryIntArray backingStore = getBackingStoreLocked(key, Loading Loading @@ -132,9 +136,8 @@ final class GenerationRegistry { // A new, non-predefined setting has been inserted, increment the tracking number for all unset // settings public void incrementGenerationForUnsetSettings(int key) { final boolean isConfig = (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); public void incrementGenerationForUnsetSettings(long key) { final boolean isConfig = SettingsState.isConfigSettingsKey(key); if (isConfig) { // No need to track new settings for configs return; Loading @@ -147,10 +150,12 @@ final class GenerationRegistry { * of a cached setting. If it was not in the backing store, first create the entry in it before * returning the result. */ public void addGenerationData(Bundle bundle, int key, String indexMapKey) { public void addGenerationData(Bundle bundle, long key, String indexMapKey) { if (SettingsState.isGlobalSettingsKey(key)) { // Global settings are shared across users, so ignore the userId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); // Global settings are shared across users and devices, so ignore the userId // and the deviceId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, Context.DEVICE_ID_DEFAULT); } synchronized (mLock) { final MemoryIntArray backingStore = getBackingStoreLocked(key, Loading Loading @@ -181,22 +186,22 @@ final class GenerationRegistry { } } public void addGenerationDataForUnsetSettings(Bundle bundle, int key) { public void addGenerationDataForUnsetSettings(Bundle bundle, long key) { addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS); } public void onUserRemoved(int userId) { final int secureKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SECURE, userId); final int systemKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SYSTEM, userId); public void onUserAndDeviceRemoved(int userId, int deviceId) { final long secureKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SECURE, userId, deviceId); final long systemKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SYSTEM, userId, deviceId); synchronized (mLock) { if (mKeyToIndexMapMap.containsKey(secureKey)) { if (mKeyToIndexMapMap.get(secureKey) != null) { destroyBackingStoreLocked(secureKey); mKeyToIndexMapMap.remove(secureKey); mNumBackingStore = mNumBackingStore - 1; } if (mKeyToIndexMapMap.containsKey(systemKey)) { if (mKeyToIndexMapMap.get(systemKey) != null) { destroyBackingStoreLocked(systemKey); mKeyToIndexMapMap.remove(systemKey); mNumBackingStore = mNumBackingStore - 1; Loading @@ -205,7 +210,7 @@ final class GenerationRegistry { } @GuardedBy("mLock") private MemoryIntArray getBackingStoreLocked(int key, boolean createIfNotExist) { private MemoryIntArray getBackingStoreLocked(long key, boolean createIfNotExist) { MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); if (!createIfNotExist) { return backingStore; Loading @@ -222,9 +227,8 @@ final class GenerationRegistry { mKeyToBackingStoreMap.put(key, backingStore); mNumBackingStore += 1; if (DEBUG) { Slog.e(LOG_TAG, "Created backing store for " + SettingsState.keyToString(key) + " on user: " + SettingsState.getUserIdFromKey(key)); Slog.e(LOG_TAG, "Created backing store for key " + SettingsState.keyToString(key)); } } catch (IOException e) { Slog.e(LOG_TAG, "Error creating generation tracker", e); Loading @@ -234,7 +238,7 @@ final class GenerationRegistry { } @GuardedBy("mLock") private void destroyBackingStoreLocked(int key) { private void destroyBackingStoreLocked(long key) { MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); if (backingStore != null) { try { Loading @@ -249,8 +253,8 @@ final class GenerationRegistry { } } private static int getKeyIndexLocked(int key, String indexMapKey, ArrayMap<Integer, ArrayMap<String, Integer>> keyToIndexMapMap, private static int getKeyIndexLocked(long key, String indexMapKey, LongSparseArray<ArrayMap<String, Integer>> keyToIndexMapMap, MemoryIntArray backingStore, boolean createIfNotExist) throws IOException { ArrayMap<String, Integer> nameToIndexMap = keyToIndexMapMap.get(key); if (nameToIndexMap == null) { Loading @@ -271,8 +275,10 @@ final class GenerationRegistry { nameToIndexMap.put(indexMapKey, index); if (DEBUG) { Slog.i(LOG_TAG, "Allocated index:" + index + " for setting:" + indexMapKey + " of type:" + SettingsState.keyToString(key) + " on user:" + SettingsState.getUserIdFromKey(key)); + " of type:" + SettingsState.settingTypeToString(SettingsState.getTypeFromKey(key)) + " on user:" + SettingsState.getUserIdFromKey(key) + " on device:" + SettingsState.getDeviceIdFromKey(key)); } } else { if (DEBUG) { Loading Loading @@ -306,10 +312,10 @@ final class GenerationRegistry { for (int i = 0; i < numBackingStores; i++) { final long token = proto.start(GenerationRegistryProto.BACKING_STORES); final int key = mKeyToBackingStoreMap.keyAt(i); final long key = mKeyToBackingStoreMap.keyAt(i); proto.write(BackingStoreProto.KEY, key); proto.write(BackingStoreProto.BACKING_STORE_SIZE, mKeyToBackingStoreMap.valueAt(i).size()); mKeyToBackingStoreMap.get(key).size()); proto.write(BackingStoreProto.NUM_CACHED_ENTRIES, mKeyToIndexMapMap.get(key).size()); final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key); Loading Loading @@ -339,7 +345,6 @@ final class GenerationRegistry { } proto.end(token); } } } Loading @@ -350,11 +355,12 @@ final class GenerationRegistry { final int numBackingStores = mKeyToBackingStoreMap.size(); pw.println("Number of backing stores:" + numBackingStores); for (int i = 0; i < numBackingStores; i++) { final int key = mKeyToBackingStoreMap.keyAt(i); final long key = mKeyToBackingStoreMap.keyAt(i); pw.print("_Backing store for type:"); pw.print(SettingsState.settingTypeToString( SettingsState.getTypeFromKey(key))); pw.print(" user:"); pw.print(SettingsState.getUserIdFromKey(key)); pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size()); pw.print(" deviceId:"); pw.print(SettingsState.getDeviceIdFromKey(key)); pw.print(" size:" + mKeyToBackingStoreMap.get(key).size()); pw.println(" cachedEntries:" + mKeyToIndexMapMap.get(key).size()); final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key); final MemoryIntArray backingStore = getBackingStoreLocked(key, Loading Loading
core/java/android/content/ContentProvider.java +16 −0 Original line number Diff line number Diff line Loading @@ -1207,6 +1207,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return null; } /** * Returns the device id associated with the {@link Context} of the caller. * * @see Context#getDeviceId() * @hide */ public final int getCallingDeviceId() { if (android.permission.flags.Flags.deviceAwarePermissionApisEnabled()) { final AttributionSource attributionSource = mCallingAttributionSource.get(); if (attributionSource != null) { return attributionSource.getDeviceId(); } } return Context.DEVICE_ID_DEFAULT; } /** * @removed */ Loading
core/java/android/provider/Settings.java +69 −34 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import android.app.NotificationManager; import android.app.SearchManager; import android.app.WallpaperManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; Loading Loading @@ -3369,15 +3370,15 @@ public final class Settings { } private static final class GenerationTracker { @NonNull private final String mName; @NonNull private final Key mKey; @NonNull private final MemoryIntArray mArray; @NonNull private final Consumer<String> mErrorHandler; @NonNull private final Consumer<Key> mErrorHandler; private final int mIndex; private int mCurrentGeneration; GenerationTracker(@NonNull String name, @NonNull MemoryIntArray array, int index, int generation, Consumer<String> errorHandler) { mName = name; GenerationTracker(@NonNull Key key, @NonNull MemoryIntArray array, int index, int generation, @NonNull Consumer<Key> errorHandler) { mKey = key; mArray = array; mIndex = index; mErrorHandler = errorHandler; Loading Loading @@ -3405,7 +3406,7 @@ public final class Settings { return mArray.get(mIndex); } catch (IOException e) { Log.e(TAG, "Error getting current generation", e); mErrorHandler.accept(mName); mErrorHandler.accept(mKey); } return -1; } Loading @@ -3422,6 +3423,28 @@ public final class Settings { super.finalize(); } } private static final class Key { private final String mName; private final int mDeviceId; Key(String name, int deviceId) { mName = name; mDeviceId = deviceId; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Key key)) return false; return mDeviceId == key.mDeviceId && Objects.equals(mName, key.mName); } @Override public int hashCode() { return Objects.hash(mName, mDeviceId); } } } private static void maybeCloseGenerationArray(@Nullable MemoryIntArray array) { Loading Loading @@ -3479,9 +3502,9 @@ public final class Settings { private static final String NAME_EQ_PLACEHOLDER = "name=?"; // Cached values of queried settings. // Key is the setting's name, value is the setting's value. // Key is composed by the setting's name and deviceId, value is the setting's value. // Must synchronize on 'this' to access mValues and mValuesVersion. private final ArrayMap<String, String> mValues = new ArrayMap<>(); private final ArrayMap<GenerationTracker.Key, String> mValues = new ArrayMap<>(); // Cached values for queried prefixes. // Key is the prefix, value is all of the settings under the prefix, mapped from a setting's Loading @@ -3505,19 +3528,22 @@ public final class Settings { private final ArraySet<String> mAllFields; private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk; // Mapping from the name of a setting (or the prefix of a namespace) to a generation tracker // Mapping of key to generation trackers for queried settings. // Key is composed by the setting's name and deviceId, value is the generation tracker. // Must synchronize on 'this' to access mGenerationTrackers. @GuardedBy("this") private ArrayMap<String, GenerationTracker> mGenerationTrackers = new ArrayMap<>(); private final ArrayMap<GenerationTracker.Key, GenerationTracker> mGenerationTrackers = new ArrayMap<>(); private Consumer<String> mGenerationTrackerErrorHandler = (String name) -> { private final Consumer<GenerationTracker.Key> mGenerationTrackerErrorHandler = (key) -> { synchronized (NameValueCache.this) { Log.e(TAG, "Error accessing generation tracker - removing"); final GenerationTracker tracker = mGenerationTrackers.get(name); final GenerationTracker tracker = mGenerationTrackers.get(key); if (tracker != null) { tracker.destroy(); mGenerationTrackers.remove(name); mGenerationTrackers.remove(key); } mValues.remove(name); mValues.remove(key); } }; Loading Loading @@ -3621,11 +3647,18 @@ public final class Settings { @UnsupportedAppUsage public String getStringForUser(ContentResolver cr, String name, final int userHandle) { final boolean isSelf = (userHandle == UserHandle.myUserId()); final AttributionSource attributionSource = cr.getAttributionSource(); final int deviceId = android.companion.virtualdevice.flags.Flags.deviceAwareSettingsOverride() && android.permission.flags.Flags.deviceAwarePermissionApisEnabled() && attributionSource != null ? attributionSource.getDeviceId() : Context.DEVICE_ID_DEFAULT; final GenerationTracker.Key key = new GenerationTracker.Key(name, deviceId); final boolean useCache = isSelf && !isInSystemServer(); boolean needsGenerationTracker = false; if (useCache) { synchronized (NameValueCache.this) { final GenerationTracker generationTracker = mGenerationTrackers.get(name); final GenerationTracker generationTracker = mGenerationTrackers.get(key); if (generationTracker != null) { if (generationTracker.isGenerationChanged()) { if (DEBUG) { Loading @@ -3636,14 +3669,14 @@ public final class Settings { } // When a generation number changes, remove cached value, remove the old // generation tracker and request a new one mValues.remove(name); mValues.remove(key); generationTracker.destroy(); mGenerationTrackers.remove(name); } else if (mValues.containsKey(name)) { mGenerationTrackers.remove(key); } else if (mValues.containsKey(key)) { if (DEBUG) { Log.i(TAG, "Cache hit for setting:" + name); } return mValues.get(name); return mValues.get(key); } } } Loading Loading @@ -3761,23 +3794,23 @@ public final class Settings { } // Always make sure to close any pre-existing tracker before // replacing it, to prevent memory leaks var oldTracker = mGenerationTrackers.get(name); var oldTracker = mGenerationTrackers.get(key); if (oldTracker != null) { oldTracker.destroy(); } mGenerationTrackers.put(name, new GenerationTracker(name, mGenerationTrackers.put(key, new GenerationTracker(key, array, index, generation, mGenerationTrackerErrorHandler)); } else { maybeCloseGenerationArray(array); } } if (mGenerationTrackers.get(name) != null && !mGenerationTrackers.get(name).isGenerationChanged()) { GenerationTracker tracker = mGenerationTrackers.get(key); if (tracker != null && !tracker.isGenerationChanged()) { if (DEBUG) { Log.i(TAG, "Updating cache for setting:" + name); } mValues.put(name, value); mValues.put(key, value); } } } else { Loading Loading @@ -3822,12 +3855,12 @@ public final class Settings { String value = c.moveToNext() ? c.getString(0) : null; synchronized (NameValueCache.this) { if (mGenerationTrackers.get(name) != null && !mGenerationTrackers.get(name).isGenerationChanged()) { GenerationTracker tracker = mGenerationTrackers.get(key); if (tracker != null && !tracker.isGenerationChanged()) { if (DEBUG) { Log.i(TAG, "Updating cache for setting:" + name + " using query"); } mValues.put(name, value); mValues.put(key, value); } } return value; Loading Loading @@ -3859,13 +3892,15 @@ public final class Settings { private Map<String, String> getStringsForPrefixStripPrefix( ContentResolver cr, String prefix, List<String> names) { final GenerationTracker.Key trackerKey = new GenerationTracker.Key(prefix, Context.DEVICE_ID_DEFAULT); String namespace = prefix.substring(0, prefix.length() - 1); ArrayMap<String, String> keyValues = new ArrayMap<>(); int substringLength = prefix.length(); int currentGeneration = -1; boolean needsGenerationTracker = false; synchronized (NameValueCache.this) { final GenerationTracker generationTracker = mGenerationTrackers.get(prefix); final GenerationTracker generationTracker = mGenerationTrackers.get(trackerKey); if (generationTracker != null) { if (generationTracker.isGenerationChanged()) { if (DEBUG) { Loading @@ -3876,7 +3911,7 @@ public final class Settings { // When a generation number changes, remove cached values, remove the old // generation tracker and request a new one generationTracker.destroy(); mGenerationTrackers.remove(prefix); mGenerationTrackers.remove(trackerKey); mPrefixToValues.remove(prefix); needsGenerationTracker = true; } else { Loading Loading @@ -3993,20 +4028,20 @@ public final class Settings { } // Always make sure to close any pre-existing tracker before // replacing it, to prevent memory leaks var oldTracker = mGenerationTrackers.get(prefix); var oldTracker = mGenerationTrackers.get(trackerKey); if (oldTracker != null) { oldTracker.destroy(); } mGenerationTrackers.put(prefix, new GenerationTracker(prefix, array, index, generation, mGenerationTrackers.put(trackerKey, new GenerationTracker(trackerKey, array, index, generation, mGenerationTrackerErrorHandler)); currentGeneration = generation; } else { maybeCloseGenerationArray(array); } } if (mGenerationTrackers.get(prefix) != null && currentGeneration == mGenerationTrackers.get(prefix).getCurrentGeneration()) { GenerationTracker tracker = mGenerationTrackers.get(trackerKey); if (tracker != null && currentGeneration == tracker.getCurrentGeneration()) { if (DEBUG) { Log.i(TAG, "Updating cache for prefix:" + prefix); } Loading
core/proto/android/providers/settings/generation.proto +1 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ message GenerationRegistryProto { } message BackingStoreProto { optional int32 key = 1; optional int64 key = 1; optional int32 backing_store_size = 2; optional int32 num_cached_entries = 3; repeated CacheEntryProto cache_entries = 4; Loading
packages/SettingsProvider/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ android_library { "aconfig_new_storage_flags_lib", "aconfigd_java_utils", "aconfig_demo_flags_java_lib", "android.companion.virtualdevice.flags-aconfig-java", "configinfra_framework_flags_java_lib", "device_config_service_flags_java", "libaconfig_java_proto_lite", Loading @@ -62,6 +63,7 @@ android_test { "test/**/*.java", ], static_libs: [ "CtsVirtualDeviceCommonLib", // Note we statically link SettingsProviderLib to do some unit tests. It's not accessible otherwise // because this test is not an instrumentation test. (because the target runs in the system process.) "SettingsProviderLib", Loading @@ -70,6 +72,7 @@ android_test { "device_config_service_flags_java", "flag-junit", "junit", "junit-params", "libaconfig_java_proto_lite", "mockito-target-minus-junit4", "platform-test-annotations", Loading
packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java +42 −36 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.providers.settings; import android.annotation.NonNull; import android.content.Context; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; Loading @@ -24,6 +25,7 @@ import android.providers.settings.BackingStoreProto; import android.providers.settings.CacheEntryProto; import android.providers.settings.GenerationRegistryProto; import android.util.ArrayMap; import android.util.LongSparseArray; import android.util.MemoryIntArray; import android.util.Slog; import android.util.proto.ProtoOutputStream; Loading @@ -50,11 +52,12 @@ final class GenerationRegistry { // Key -> backingStore mapping @GuardedBy("mLock") private final ArrayMap<Integer, MemoryIntArray> mKeyToBackingStoreMap = new ArrayMap(); private final LongSparseArray<MemoryIntArray> mKeyToBackingStoreMap = new LongSparseArray<>(); // Key -> (String->Index map) mapping @GuardedBy("mLock") private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>(); private final LongSparseArray<ArrayMap<String, Integer>> mKeyToIndexMapMap = new LongSparseArray<>(); @GuardedBy("mLock") private int mNumBackingStore = 0; Loading Loading @@ -90,18 +93,19 @@ final class GenerationRegistry { * Increment the generation number if the setting is already cached in the backing stores. * Otherwise, do nothing. */ public void incrementGeneration(int key, String name) { final boolean isConfig = (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); public void incrementGeneration(long key, String name) { final boolean isConfig = SettingsState.isConfigSettingsKey(key); // Only store the prefix if the mutated setting is a config final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name; incrementGenerationInternal(key, indexMapKey); } private void incrementGenerationInternal(int key, @NonNull String indexMapKey) { private void incrementGenerationInternal(long key, @NonNull String indexMapKey) { if (SettingsState.isGlobalSettingsKey(key)) { // Global settings are shared across users, so ignore the userId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); // Global settings are shared across users and devices, so ignore the userId and the // deviceId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, Context.DEVICE_ID_DEFAULT); } synchronized (mLock) { final MemoryIntArray backingStore = getBackingStoreLocked(key, Loading Loading @@ -132,9 +136,8 @@ final class GenerationRegistry { // A new, non-predefined setting has been inserted, increment the tracking number for all unset // settings public void incrementGenerationForUnsetSettings(int key) { final boolean isConfig = (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); public void incrementGenerationForUnsetSettings(long key) { final boolean isConfig = SettingsState.isConfigSettingsKey(key); if (isConfig) { // No need to track new settings for configs return; Loading @@ -147,10 +150,12 @@ final class GenerationRegistry { * of a cached setting. If it was not in the backing store, first create the entry in it before * returning the result. */ public void addGenerationData(Bundle bundle, int key, String indexMapKey) { public void addGenerationData(Bundle bundle, long key, String indexMapKey) { if (SettingsState.isGlobalSettingsKey(key)) { // Global settings are shared across users, so ignore the userId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); // Global settings are shared across users and devices, so ignore the userId // and the deviceId in the key key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, Context.DEVICE_ID_DEFAULT); } synchronized (mLock) { final MemoryIntArray backingStore = getBackingStoreLocked(key, Loading Loading @@ -181,22 +186,22 @@ final class GenerationRegistry { } } public void addGenerationDataForUnsetSettings(Bundle bundle, int key) { public void addGenerationDataForUnsetSettings(Bundle bundle, long key) { addGenerationData(bundle, key, /* indexMapKey= */ DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS); } public void onUserRemoved(int userId) { final int secureKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SECURE, userId); final int systemKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SYSTEM, userId); public void onUserAndDeviceRemoved(int userId, int deviceId) { final long secureKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SECURE, userId, deviceId); final long systemKey = SettingsState.makeKey( SettingsState.SETTINGS_TYPE_SYSTEM, userId, deviceId); synchronized (mLock) { if (mKeyToIndexMapMap.containsKey(secureKey)) { if (mKeyToIndexMapMap.get(secureKey) != null) { destroyBackingStoreLocked(secureKey); mKeyToIndexMapMap.remove(secureKey); mNumBackingStore = mNumBackingStore - 1; } if (mKeyToIndexMapMap.containsKey(systemKey)) { if (mKeyToIndexMapMap.get(systemKey) != null) { destroyBackingStoreLocked(systemKey); mKeyToIndexMapMap.remove(systemKey); mNumBackingStore = mNumBackingStore - 1; Loading @@ -205,7 +210,7 @@ final class GenerationRegistry { } @GuardedBy("mLock") private MemoryIntArray getBackingStoreLocked(int key, boolean createIfNotExist) { private MemoryIntArray getBackingStoreLocked(long key, boolean createIfNotExist) { MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); if (!createIfNotExist) { return backingStore; Loading @@ -222,9 +227,8 @@ final class GenerationRegistry { mKeyToBackingStoreMap.put(key, backingStore); mNumBackingStore += 1; if (DEBUG) { Slog.e(LOG_TAG, "Created backing store for " + SettingsState.keyToString(key) + " on user: " + SettingsState.getUserIdFromKey(key)); Slog.e(LOG_TAG, "Created backing store for key " + SettingsState.keyToString(key)); } } catch (IOException e) { Slog.e(LOG_TAG, "Error creating generation tracker", e); Loading @@ -234,7 +238,7 @@ final class GenerationRegistry { } @GuardedBy("mLock") private void destroyBackingStoreLocked(int key) { private void destroyBackingStoreLocked(long key) { MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); if (backingStore != null) { try { Loading @@ -249,8 +253,8 @@ final class GenerationRegistry { } } private static int getKeyIndexLocked(int key, String indexMapKey, ArrayMap<Integer, ArrayMap<String, Integer>> keyToIndexMapMap, private static int getKeyIndexLocked(long key, String indexMapKey, LongSparseArray<ArrayMap<String, Integer>> keyToIndexMapMap, MemoryIntArray backingStore, boolean createIfNotExist) throws IOException { ArrayMap<String, Integer> nameToIndexMap = keyToIndexMapMap.get(key); if (nameToIndexMap == null) { Loading @@ -271,8 +275,10 @@ final class GenerationRegistry { nameToIndexMap.put(indexMapKey, index); if (DEBUG) { Slog.i(LOG_TAG, "Allocated index:" + index + " for setting:" + indexMapKey + " of type:" + SettingsState.keyToString(key) + " on user:" + SettingsState.getUserIdFromKey(key)); + " of type:" + SettingsState.settingTypeToString(SettingsState.getTypeFromKey(key)) + " on user:" + SettingsState.getUserIdFromKey(key) + " on device:" + SettingsState.getDeviceIdFromKey(key)); } } else { if (DEBUG) { Loading Loading @@ -306,10 +312,10 @@ final class GenerationRegistry { for (int i = 0; i < numBackingStores; i++) { final long token = proto.start(GenerationRegistryProto.BACKING_STORES); final int key = mKeyToBackingStoreMap.keyAt(i); final long key = mKeyToBackingStoreMap.keyAt(i); proto.write(BackingStoreProto.KEY, key); proto.write(BackingStoreProto.BACKING_STORE_SIZE, mKeyToBackingStoreMap.valueAt(i).size()); mKeyToBackingStoreMap.get(key).size()); proto.write(BackingStoreProto.NUM_CACHED_ENTRIES, mKeyToIndexMapMap.get(key).size()); final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key); Loading Loading @@ -339,7 +345,6 @@ final class GenerationRegistry { } proto.end(token); } } } Loading @@ -350,11 +355,12 @@ final class GenerationRegistry { final int numBackingStores = mKeyToBackingStoreMap.size(); pw.println("Number of backing stores:" + numBackingStores); for (int i = 0; i < numBackingStores; i++) { final int key = mKeyToBackingStoreMap.keyAt(i); final long key = mKeyToBackingStoreMap.keyAt(i); pw.print("_Backing store for type:"); pw.print(SettingsState.settingTypeToString( SettingsState.getTypeFromKey(key))); pw.print(" user:"); pw.print(SettingsState.getUserIdFromKey(key)); pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size()); pw.print(" deviceId:"); pw.print(SettingsState.getDeviceIdFromKey(key)); pw.print(" size:" + mKeyToBackingStoreMap.get(key).size()); pw.println(" cachedEntries:" + mKeyToIndexMapMap.get(key).size()); final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key); final MemoryIntArray backingStore = getBackingStoreLocked(key, Loading