[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:
@@ -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(
|
||||
|
||||
315
core/java/android/provider/SettingsIpcDataCache.java
Normal file
315
core/java/android/provider/SettingsIpcDataCache.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user