diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 20e591a80e756..5ff95452f2ef5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -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 mValues = new ArrayMap<>(); @@ -3070,6 +3074,14 @@ public final class Settings { private final ArraySet mAllFields; private final ArrayMap mReadableFieldsWithMaxTargetSdk; + private final String mSettingsType; + + // Caches for settings key -> value, only for the current user + private final IpcDataCache mValueCache; + // Cache for settings namespace -> list of settings, only for the current user + private final IpcDataCache> + 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 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 results = new ArrayMap<>(); + HashMap 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 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 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( diff --git a/core/java/android/provider/SettingsIpcDataCache.java b/core/java/android/provider/SettingsIpcDataCache.java new file mode 100644 index 0000000000000..3caf2931e039a --- /dev/null +++ b/core/java/android/provider/SettingsIpcDataCache.java @@ -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 { + @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 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> { + @NonNull final ContentProviderHolder mContentProviderHolder; + @NonNull final String mCallListCommand; + + ListQueryHandler(@NonNull ContentProviderHolder contentProviderHolder, + @NonNull String callListCommand) { + mContentProviderHolder = contentProviderHolder; + mCallListCommand = callListCommand; + } + + @Nullable + @Override + public HashMap apply(@NonNull ListQuery query) { + try { + return getListFromContentProviderCall(query); + } catch (RemoteException e) { + // Throw to prevent caching + e.rethrowAsRuntimeException(); + } + return null; + } + + @Nullable + private HashMap 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 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 flagsToValues = + (HashMap) b.getSerializable(Settings.NameValueTable.VALUE, + java.util.HashMap.class); + return flagsToValues; + } + } + + @NonNull + static IpcDataCache> 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); + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 1356e1d4c3e6e..d7f66eff782f9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -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 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) {