[SettingsProvider] IpcDataCache for Settings

(Currently disabled, so this CL will be a no-op, only adding some
implementations.)

The old caching mechanism causes performance janks because the all the
settings of the same type share the same cache. An update of a single
setting leads to the invalidation of the whole cache.

This CL adds a new caching mechanism, using IpcDataCache. In this CL,
all settings of a same type still shares one cache. In the next CL,
clusters of caches will be created based on the frequency of cache
invalidations.

BUG: 228619157
Test: atest SettingsProviderTest
Change-Id: Id823994eebafb4d15f31502ba6a96f70892d8e6d
This commit is contained in:
Songchun Fan
2022-12-05 13:41:13 -08:00
parent 58e200f57c
commit d4e1a667eb
3 changed files with 494 additions and 41 deletions

View File

@@ -72,6 +72,7 @@ import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.IBinder;
import android.os.IpcDataCache;
import android.os.LocaleList;
import android.os.PowerManager;
import android.os.PowerManager.AutoPowerSaveModeTriggers;
@@ -124,6 +125,9 @@ public final class Settings {
/** @hide */
public static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false;
/** @hide default value of whether IpcDataCache is enabled or not */
public static final boolean IPC_DATA_CACHE_ENABLED = false;
// Intent actions for Settings
/**
@@ -2901,8 +2905,8 @@ public final class Settings {
public static final String AUTHORITY = "settings";
private static final String TAG = "Settings";
private static final boolean LOCAL_LOGV = false;
static final String TAG = "Settings";
static final boolean LOCAL_LOGV = false;
// Used in system server calling uid workaround in call()
private static boolean sInSystemServer = false;
@@ -3012,10 +3016,10 @@ public final class Settings {
}
}
private static final class ContentProviderHolder {
static final class ContentProviderHolder {
private final Object mLock = new Object();
private final Uri mUri;
final Uri mUri;
@GuardedBy("mLock")
@UnsupportedAppUsage
private IContentProvider mContentProvider;
@@ -3042,14 +3046,14 @@ public final class Settings {
}
// Thread-safe.
private static class NameValueCache {
static class NameValueCache {
private static final boolean DEBUG = false;
private static final String[] SELECT_VALUE_PROJECTION = new String[] {
static final String[] SELECT_VALUE_PROJECTION = new String[] {
Settings.NameValueTable.VALUE
};
private static final String NAME_EQ_PLACEHOLDER = "name=?";
static final String NAME_EQ_PLACEHOLDER = "name=?";
// Must synchronize on 'this' to access mValues and mValuesVersion.
private final ArrayMap<String, String> mValues = new ArrayMap<>();
@@ -3070,6 +3074,14 @@ public final class Settings {
private final ArraySet<String> mAllFields;
private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk;
private final String mSettingsType;
// Caches for settings key -> value, only for the current user
private final IpcDataCache<SettingsIpcDataCache.GetQuery, String> mValueCache;
// Cache for settings namespace -> list of settings, only for the current user
private final IpcDataCache<SettingsIpcDataCache.ListQuery, HashMap<String, String>>
mNamespaceCache;
@GuardedBy("this")
private GenerationTracker mGenerationTracker;
@@ -3095,6 +3107,11 @@ public final class Settings {
mReadableFieldsWithMaxTargetSdk = new ArrayMap<>();
getPublicSettingsForClass(callerClass, mAllFields, mReadableFields,
mReadableFieldsWithMaxTargetSdk);
mSettingsType = callerClass.getSimpleName().toLowerCase();
mValueCache = IPC_DATA_CACHE_ENABLED ? SettingsIpcDataCache.createValueCache(
mProviderHolder, mCallGetCommand, mUri, mSettingsType) : null;
mNamespaceCache = IPC_DATA_CACHE_ENABLED ? SettingsIpcDataCache.createListCache(
mProviderHolder, mCallListCommand, mSettingsType) : null;
}
public boolean putStringForUser(ContentResolver cr, String name, String value,
@@ -3193,6 +3210,30 @@ public final class Settings {
}
}
if (IPC_DATA_CACHE_ENABLED) {
if (userHandle != UserHandle.myUserId()) {
if (LOCAL_LOGV) {
Log.v(TAG, "get setting for user " + userHandle
+ " by user " + UserHandle.myUserId()
+ " so skipping cache");
}
try {
return SettingsIpcDataCache.getValueFromContentProviderCall(
mProviderHolder, mCallGetCommand, mUri, userHandle, cr, name);
} catch (RemoteException e) {
return null;
}
}
try {
return mValueCache.query(new SettingsIpcDataCache.GetQuery(cr, name));
} catch (RuntimeException e) {
// Failed to query the server
return null;
}
}
// Fall back to old cache mechanism
final boolean isSelf = (userHandle == UserHandle.myUserId());
int currentGeneration = -1;
if (isSelf) {
@@ -3389,6 +3430,38 @@ public final class Settings {
List<String> names) {
String namespace = prefix.substring(0, prefix.length() - 1);
Config.enforceReadPermission(namespace);
if (mCallListCommand == null) {
// No list command specified, return empty map
return new ArrayMap<>();
}
if (IPC_DATA_CACHE_ENABLED) {
ArrayMap<String, String> results = new ArrayMap<>();
HashMap<String, String> flagsToValues;
try {
flagsToValues = mNamespaceCache.query(
new SettingsIpcDataCache.ListQuery(cr, prefix));
} catch (RuntimeException e) {
// Failed to query the server, return an empty map
return results;
}
if (flagsToValues != null) {
if (!names.isEmpty()) {
for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
if (names.contains(flag.getKey())) {
results.put(flag.getKey(), flag.getValue());
}
}
} else {
results.putAll(flagsToValues);
}
}
return results;
}
// Fall back to old cache mechanism
ArrayMap<String, String> keyValues = new ArrayMap<>();
int currentGeneration = -1;
@@ -3428,10 +3501,7 @@ public final class Settings {
}
}
if (mCallListCommand == null) {
// No list command specified, return empty map
return keyValues;
}
IContentProvider cp = mProviderHolder.getProvider(cr);
try {
@@ -3537,7 +3607,13 @@ public final class Settings {
}
}
public void clearGenerationTrackerForTest() {
public void clearCachesForTest() {
if (IPC_DATA_CACHE_ENABLED) {
mValueCache.clear();
mNamespaceCache.clear();
return;
}
// Fall back to old cache mechanism
synchronized (NameValueCache.this) {
if (mGenerationTracker != null) {
mGenerationTracker.destroy();
@@ -3546,6 +3622,12 @@ public final class Settings {
mGenerationTracker = null;
}
}
public void invalidateCache() {
if (IPC_DATA_CACHE_ENABLED) {
SettingsIpcDataCache.invalidateCache(mSettingsType);
}
}
}
/**
@@ -3822,7 +3904,12 @@ public final class Settings {
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
sNameValueCache.clearGenerationTrackerForTest();
sNameValueCache.clearCachesForTest();
}
/** @hide */
public static void invalidateValueCache() {
sNameValueCache.invalidateCache();
}
/** @hide */
@@ -6261,7 +6348,12 @@ public final class Settings {
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
sNameValueCache.clearGenerationTrackerForTest();
sNameValueCache.clearCachesForTest();
}
/** @hide */
public static void invalidateValueCache() {
sNameValueCache.invalidateCache();
}
/** @hide */
@@ -16355,7 +16447,12 @@ public final class Settings {
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
sNameValueCache.clearGenerationTrackerForTest();
sNameValueCache.clearCachesForTest();
}
/** @hide */
public static void invalidateValueCache() {
sNameValueCache.invalidateCache();
}
/** @hide */
@@ -18549,7 +18646,17 @@ public final class Settings {
/** @hide */
public static void clearProviderForTest() {
sProviderHolder.clearProviderForTest();
sNameValueCache.clearGenerationTrackerForTest();
sNameValueCache.clearCachesForTest();
}
/** @hide */
public static void invalidateValueCache() {
sNameValueCache.invalidateCache();
}
/** @hide */
public static void invalidateNamespaceCache() {
sNameValueCache.invalidateCache();
}
private static void handleMonitorCallback(

View File

@@ -0,0 +1,315 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.provider;
import static android.provider.Settings.CALL_METHOD_USER_KEY;
import static android.provider.Settings.ContentProviderHolder;
import static android.provider.Settings.LOCAL_LOGV;
import static android.provider.Settings.NameValueCache.NAME_EQ_PLACEHOLDER;
import static android.provider.Settings.NameValueCache.SELECT_VALUE_PROJECTION;
import static android.provider.Settings.TAG;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.IContentProvider;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IpcDataCache;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Objects;
/** @hide */
final class SettingsIpcDataCache {
private static final boolean DEBUG = true;
private static final int NUM_MAX_ENTRIES = 2048;
static class GetQuery {
@NonNull final ContentResolver mContentResolver;
@NonNull final String mName;
GetQuery(@NonNull ContentResolver contentResolver, @NonNull String name) {
mContentResolver = contentResolver;
mName = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof GetQuery)) return false;
GetQuery getQuery = (GetQuery) o;
return mContentResolver.equals(
getQuery.mContentResolver) && mName.equals(getQuery.mName);
}
@Override
public int hashCode() {
return Objects.hash(mContentResolver, mName);
}
}
private static class GetQueryHandler extends IpcDataCache.QueryHandler<GetQuery, String> {
@NonNull final ContentProviderHolder mContentProviderHolder;
@NonNull final String mCallGetCommand;
@NonNull final Uri mUri;
final int mUserId;
private GetQueryHandler(
ContentProviderHolder contentProviderHolder, String callGetCommand, Uri uri) {
mContentProviderHolder = contentProviderHolder;
mCallGetCommand = callGetCommand;
mUri = uri;
mUserId = UserHandle.myUserId();
}
@Nullable
@Override
public String apply(GetQuery query) {
try {
return getValueFromContentProviderCall(mContentProviderHolder, mCallGetCommand,
mUri, mUserId, query);
} catch (RemoteException e) {
// Throw to prevent caching
e.rethrowAsRuntimeException();
}
return null;
}
}
@NonNull
static IpcDataCache<GetQuery, String> createValueCache(
@NonNull ContentProviderHolder contentProviderHolder,
@NonNull String callGetCommand, @NonNull Uri uri, @NonNull String type) {
if (DEBUG) {
Log.i(TAG, "Creating value cache for type:" + type);
}
IpcDataCache.Config config = new IpcDataCache.Config(
NUM_MAX_ENTRIES, IpcDataCache.MODULE_SYSTEM, type /* apiName */);
return new IpcDataCache<>(config.child("get"),
new GetQueryHandler(contentProviderHolder, callGetCommand, uri));
}
@Nullable
private static String getValueFromContentProviderCall(
@NonNull ContentProviderHolder providerHolder, @NonNull String callGetCommand,
@NonNull Uri uri, int userId, @NonNull GetQuery query)
throws RemoteException {
final ContentResolver cr = query.mContentResolver;
final String name = query.mName;
return getValueFromContentProviderCall(providerHolder, callGetCommand, uri, userId, cr,
name);
}
@Nullable
static String getValueFromContentProviderCall(
@NonNull ContentProviderHolder providerHolder, @NonNull String callGetCommand,
@NonNull Uri uri, int userId, ContentResolver cr, String name) throws RemoteException {
final IContentProvider cp = providerHolder.getProvider(cr);
// Try the fast path first, not using query(). If this
// fails (alternate Settings provider that doesn't support
// this interface?) then we fall back to the query/table
// interface.
if (callGetCommand != null) {
try {
Bundle args = new Bundle();
if (userId != UserHandle.myUserId()) {
args.putInt(CALL_METHOD_USER_KEY, userId);
}
Bundle b;
// If we're in system server and in a binder transaction we need to clear the
// calling uid. This works around code in system server that did not call
// clearCallingIdentity, previously this wasn't needed because reading settings
// did not do permission checking but that's no longer the case.
// Long term this should be removed and callers should properly call
// clearCallingIdentity or use a ContentResolver from the caller as needed.
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
b = cp.call(cr.getAttributionSource(),
providerHolder.mUri.getAuthority(), callGetCommand, name,
args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
b = cp.call(cr.getAttributionSource(),
providerHolder.mUri.getAuthority(), callGetCommand, name, args);
}
if (b != null) {
return b.getString(Settings.NameValueTable.VALUE);
}
// If the response Bundle is null, we fall through
// to the query interface below.
} catch (RemoteException e) {
// Not supported by the remote side? Fall through
// to query().
}
}
Cursor c = null;
try {
Bundle queryArgs = ContentResolver.createSqlQueryBundle(
NAME_EQ_PLACEHOLDER, new String[]{name}, null);
// Same workaround as above.
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
c = cp.query(cr.getAttributionSource(), uri,
SELECT_VALUE_PROJECTION, queryArgs, null);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
c = cp.query(cr.getAttributionSource(), uri,
SELECT_VALUE_PROJECTION, queryArgs, null);
}
if (c == null) {
Log.w(TAG, "Can't get key " + name + " from " + uri);
return null;
}
String value = c.moveToNext() ? c.getString(0) : null;
if (LOCAL_LOGV) {
Log.v(TAG, "cache miss [" + uri.getLastPathSegment() + "]: "
+ name + " = " + (value == null ? "(null)" : value));
}
return value;
} finally {
if (c != null) c.close();
}
}
static class ListQuery {
@NonNull ContentResolver mContentResolver;
@NonNull final String mPrefix;
ListQuery(@NonNull ContentResolver contentResolver, String prefix) {
mContentResolver = contentResolver;
mPrefix = prefix;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ListQuery)) return false;
ListQuery listQuery = (ListQuery) o;
return mContentResolver.equals(listQuery.mContentResolver) && mPrefix.equals(
listQuery.mPrefix);
}
@Override
public int hashCode() {
return Objects.hash(mContentResolver, mPrefix);
}
}
private static class ListQueryHandler extends
IpcDataCache.QueryHandler<ListQuery, HashMap<String, String>> {
@NonNull final ContentProviderHolder mContentProviderHolder;
@NonNull final String mCallListCommand;
ListQueryHandler(@NonNull ContentProviderHolder contentProviderHolder,
@NonNull String callListCommand) {
mContentProviderHolder = contentProviderHolder;
mCallListCommand = callListCommand;
}
@Nullable
@Override
public HashMap<String, String> apply(@NonNull ListQuery query) {
try {
return getListFromContentProviderCall(query);
} catch (RemoteException e) {
// Throw to prevent caching
e.rethrowAsRuntimeException();
}
return null;
}
@Nullable
private HashMap<String, String> getListFromContentProviderCall(ListQuery query)
throws RemoteException {
final ContentResolver cr = query.mContentResolver;
final IContentProvider cp = mContentProviderHolder.getProvider(cr);
final String prefix = query.mPrefix;
final String namespace = prefix.substring(0, prefix.length() - 1);
HashMap<String, String> keyValues = new HashMap<>();
Bundle args = new Bundle();
args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
Bundle b;
// b/252663068: if we're in system server and the caller did not call
// clearCallingIdentity, the read would fail due to mismatched AttributionSources.
// TODO(b/256013480): remove this bypass after fixing the callers in system server.
if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
&& Settings.isInSystemServer()
&& Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
// Fetch all flags for the namespace at once for caching purposes
b = cp.call(cr.getAttributionSource(),
mContentProviderHolder.mUri.getAuthority(), mCallListCommand, null,
args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
// Fetch all flags for the namespace at once for caching purposes
b = cp.call(cr.getAttributionSource(),
mContentProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
}
if (b == null) {
// Invalid response, return an empty map
return keyValues;
}
// Cache all flags for the namespace
HashMap<String, String> flagsToValues =
(HashMap) b.getSerializable(Settings.NameValueTable.VALUE,
java.util.HashMap.class);
return flagsToValues;
}
}
@NonNull
static IpcDataCache<ListQuery, HashMap<String, String>> createListCache(
@NonNull ContentProviderHolder providerHolder,
@NonNull String callListCommand, String type) {
if (DEBUG) {
Log.i(TAG, "Creating cache for settings type:" + type);
}
IpcDataCache.Config config = new IpcDataCache.Config(
NUM_MAX_ENTRIES, IpcDataCache.MODULE_SYSTEM, type /* apiName */);
return new IpcDataCache<>(config.child("get"),
new ListQueryHandler(providerHolder, callListCommand));
}
static void invalidateCache(String type) {
if (DEBUG) {
Log.i(TAG, "Cache invalidated for type:" + type);
}
IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, type);
}
}

View File

@@ -1298,30 +1298,40 @@ public class SettingsProvider extends ContentProvider {
boolean makeDefault, int operation, int mode) {
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
final String callingPackage = resolveCallingPackage();
boolean someSettingChanged = false;
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
someSettingChanged = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
callingPackage, false, null,
/* overrideableByRestore */ false);
break;
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
someSettingChanged = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, false, null);
break;
}
case MUTATION_OPERATION_RESET: {
mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
someSettingChanged = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix);
} return true;
break;
}
}
}
return false;
if (Settings.IPC_DATA_CACHE_ENABLED) {
if (someSettingChanged) {
Settings.Config.invalidateValueCache();
Settings.Config.invalidateNamespaceCache();
}
}
return someSettingChanged;
}
private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
@@ -1482,36 +1492,45 @@ public class SettingsProvider extends ContentProvider {
}
final String callingPackage = getCallingPackage();
boolean someSettingChanged = false;
// Perform the mutation.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
someSettingChanged = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
callingPackage, forceNotify,
CRITICAL_GLOBAL_SETTINGS, overrideableByRestore);
break;
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
someSettingChanged = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, forceNotify, CRITICAL_GLOBAL_SETTINGS);
break;
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
someSettingChanged = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS);
break;
}
case MUTATION_OPERATION_RESET: {
mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL,
someSettingChanged = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, callingPackage, mode, tag);
} return true;
break;
}
}
}
return false;
if (Settings.IPC_DATA_CACHE_ENABLED && someSettingChanged) {
Settings.Global.invalidateValueCache();
}
return someSettingChanged;
}
private PackageInfo getCallingPackageInfo(int userId) {
@@ -1954,31 +1973,39 @@ public class SettingsProvider extends ContentProvider {
cacheFile.delete();
}
boolean someSettingChanged = false;
// Mutate the value.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
someSettingChanged = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null, overrideableByRestore);
break;
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
someSettingChanged = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, false, null);
break;
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
someSettingChanged = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null);
break;
}
default:
Slog.e(LOG_TAG, "Unknown operation code: " + operation);
}
Slog.e(LOG_TAG, "Unknown operation code: " + operation);
return false;
}
if (Settings.IPC_DATA_CACHE_ENABLED && someSettingChanged) {
Settings.System.invalidateValueCache();
}
return someSettingChanged;
}
private boolean hasWriteSecureSettingsPermission() {
@@ -2969,6 +2996,9 @@ public class SettingsProvider extends ContentProvider {
final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
final SettingsState systemSettingsState = mSettingsStates.get(systemKey);
if (systemSettingsState != null) {
if (Settings.IPC_DATA_CACHE_ENABLED) {
Settings.System.invalidateValueCache();
}
if (permanently) {
mSettingsStates.remove(systemKey);
systemSettingsState.destroyLocked(null);
@@ -2986,6 +3016,9 @@ public class SettingsProvider extends ContentProvider {
final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
final SettingsState secureSettingsState = mSettingsStates.get(secureKey);
if (secureSettingsState != null) {
if (Settings.IPC_DATA_CACHE_ENABLED) {
Settings.Secure.invalidateValueCache();
}
if (permanently) {
mSettingsStates.remove(secureKey);
secureSettingsState.destroyLocked(null);
@@ -3140,25 +3173,25 @@ public class SettingsProvider extends ContentProvider {
return settingsState.getSettingLocked(name);
}
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
null);
}
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState == null) {
return;
return false;
}
banConfigurationIfNecessary(type, prefix, settingsState);
boolean someSettingChanged = false;
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (packageName.equals(setting.getPackageName())) {
if ((tag != null && !tag.equals(setting.getTag()))
@@ -3179,7 +3212,6 @@ public class SettingsProvider extends ContentProvider {
case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
@@ -3200,7 +3232,6 @@ public class SettingsProvider extends ContentProvider {
case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
for (String name : settingsState.getSettingNamesLocked()) {
boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
@@ -3228,7 +3259,6 @@ public class SettingsProvider extends ContentProvider {
case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
Setting setting = settingsState.getSettingLocked(name);
boolean someSettingChanged = false;
if (prefix != null && !setting.getName().startsWith(prefix)) {
continue;
}
@@ -3249,6 +3279,7 @@ public class SettingsProvider extends ContentProvider {
}
} break;
}
return someSettingChanged;
}
public void removeSettingsForPackageLocked(String packageName, int userId) {