From e080da9ee027fcd030aa92ea26fd0ed9f031674f Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Wed, 21 Dec 2016 17:10:35 -0800 Subject: [PATCH] Settings recovery support This change allows the system to perform iterative reset of changes to settings in order to recover from bad a state such as a reboot loop. To enable this we add the notion of a default value. The default can be set by any package but if the package that set it is a part of the system, i.e. trusted, then other packages that are not a part of the system, i.e. untrusted, cannot change the default. The settings setter APIs that do not take a default effectively clear the default. Putting a setting from a system component always makes it the default and if the package in not trusted then value is not made the default. The rationale is that the system is tested and its values are safe but third-party components are not trusted and their values are not safe. The reset modes from the least intrusive are: untrusted defaults - reset only settings set by untrusted components to their defaults or clear them otherwise; untrusted clear - clear settings set by untrusted components (or snap to default if provided by the system); trusted defaults - reset all settings to defaults set by the system or clear them otherwise. Also a package can reset to defaults changes it made to the global and secure settings. It is also possible to associate a setting with an optional token which can then be used to reset settings set by this package and associated with the token allowing parallel experiments over disjoint settings subsets. The default values are also useful for experiment (or more precisely iterative tuning of devices' behavior in production) as the stable configuration can be set to the "graduated" safe defaults and set the values to the experimental ones to measure impact. Test: tests pass Change-Id: I838955ea3bb28337f416ee244dff2fb1199b6943 --- api/system-current.txt | 4 + .../content/pm/PackageManagerInternal.java | 5 + core/java/android/provider/Settings.java | 396 ++++++++++++++-- packages/SettingsProvider/Android.mk | 2 +- .../providers/settings/SettingsProvider.java | 444 +++++++++++++----- .../providers/settings/SettingsService.java | 117 ++++- .../providers/settings/SettingsState.java | 353 ++++++++++++-- packages/SettingsProvider/test/Android.mk | 4 + .../SettingsProvider/test/AndroidManifest.xml | 3 +- .../settings/BaseSettingsProviderTest.java | 192 +++++++- .../SettingsProviderPerformanceTest.java | 6 + .../settings/SettingsProviderTest.java | 282 ++++++++++- .../providers/settings/SettingsStateTest.java | 20 +- .../server/pm/PackageManagerService.java | 4 + 14 files changed, 1573 insertions(+), 259 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index f38b02e7a8382..8022ef27cc5a1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -35753,6 +35753,8 @@ package android.provider { method public static boolean putInt(android.content.ContentResolver, java.lang.String, int); method public static boolean putLong(android.content.ContentResolver, java.lang.String, long); method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String); + method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean); + method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); field public static final java.lang.String ADB_ENABLED = "adb_enabled"; field public static final java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on"; field public static final java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios"; @@ -35826,6 +35828,8 @@ package android.provider { method public static boolean putInt(android.content.ContentResolver, java.lang.String, int); method public static boolean putLong(android.content.ContentResolver, java.lang.String, long); method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String); + method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean); + method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean); field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled"; diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 1ba68a64dab42..2590a6beda390 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -221,4 +221,9 @@ public abstract class PackageManagerInternal { public abstract void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj, Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage, int userId); + + /** + * @return The SetupWizard package name. + */ + public abstract String getSetupWizardPackageName(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 37222ade98866..53f0a5ca34be2 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -16,11 +16,17 @@ package android.provider; +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.annotation.UserIdInt; import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.Application; @@ -66,6 +72,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ILockSettings; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.HashMap; @@ -347,7 +355,6 @@ public final class Settings { * Input: Nothing. *

* Output: Nothing. - */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_WIFI_SETTINGS = @@ -1211,8 +1218,6 @@ public final class Settings { public static final String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS"; - - /** * Activity Action: Show Default apps settings. *

@@ -1387,6 +1392,21 @@ public final class Settings { */ public static final String CALL_METHOD_USER_KEY = "_user"; + /** + * @hide - Boolean argument extra to the fast-path call()-based requests + */ + public static final String CALL_METHOD_MAKE_DEFAULT_KEY = "_make_default"; + + /** + * @hide - User handle argument extra to the fast-path call()-based requests + */ + public static final String CALL_METHOD_RESET_MODE_KEY = "_reset_mode"; + + /** + * @hide - String argument extra to the fast-path call()-based requests + */ + public static final String CALL_METHOD_TAG_KEY = "_tag"; + /** @hide - Private call() method to write to 'system' table */ public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system"; @@ -1396,6 +1416,12 @@ public final class Settings { /** @hide - Private call() method to write to 'global' table */ public static final String CALL_METHOD_PUT_GLOBAL= "PUT_global"; + /** @hide - Private call() method to reset to defaults the 'global' table */ + public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global"; + + /** @hide - Private call() method to reset to defaults the 'secure' table */ + public static final String CALL_METHOD_RESET_SECURE = "RESET_secure"; + /** * Activity Extra: Limit available options in launched activity based on the given authority. *

@@ -1471,6 +1497,55 @@ public final class Settings { public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes"; + /** + * Reset mode: reset to defaults only settings changed by the + * calling package. If there is a default set the setting + * will be set to it, otherwise the setting will be deleted. + * This is the only type of reset available to non-system clients. + * @hide + */ + public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; + + /** + * Reset mode: reset all settings set by untrusted packages, which is + * packages that aren't a part of the system, to the current defaults. + * If there is a default set the setting will be set to it, otherwise + * the setting will be deleted. This mode is only available to the system. + * @hide + */ + public static final int RESET_MODE_UNTRUSTED_DEFAULTS = 2; + + /** + * Reset mode: delete all settings set by untrusted packages, which is + * packages that aren't a part of the system. If a setting is set by an + * untrusted package it will be deleted if its default is not provided + * by the system, otherwise the setting will be set to its default. + * This mode is only available to the system. + * @hide + */ + public static final int RESET_MODE_UNTRUSTED_CHANGES = 3; + + /** + * Reset mode: reset all settings to defaults specified by trusted + * packages, which is packages that are a part of the system, and + * delete all settings set by untrusted packages. If a setting has + * a default set by a system package it will be set to the default, + * otherwise the setting will be deleted. This mode is only available + * to the system. + * @hide + */ + public static final int RESET_MODE_TRUSTED_DEFAULTS = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + RESET_MODE_PACKAGE_DEFAULTS, + RESET_MODE_UNTRUSTED_DEFAULTS, + RESET_MODE_UNTRUSTED_CHANGES, + RESET_MODE_TRUSTED_DEFAULTS + }) + public @interface ResetMode{} + /** * Activity Extra: Number of certificates *

@@ -1574,22 +1649,44 @@ public final class Settings { } } + private static final class ContentProviderHolder { + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final Uri mUri; + @GuardedBy("mLock") + private IContentProvider mContentProvider; + + public ContentProviderHolder(Uri uri) { + mUri = uri; + } + + public IContentProvider getProvider(ContentResolver contentResolver) { + synchronized (mLock) { + if (mContentProvider == null) { + mContentProvider = contentResolver + .acquireProvider(mUri.getAuthority()); + } + return mContentProvider; + } + } + } + // Thread-safe. private static class NameValueCache { private static final boolean DEBUG = false; - private final Uri mUri; - private static final String[] SELECT_VALUE_PROJECTION = new String[] { Settings.NameValueTable.VALUE }; + private static final String NAME_EQ_PLACEHOLDER = "name=?"; // Must synchronize on 'this' to access mValues and mValuesVersion. private final HashMap mValues = new HashMap<>(); - // Initially null; set lazily and held forever. Synchronized on 'this'. - private IContentProvider mContentProvider = null; + private final Uri mUri; + private final ContentProviderHolder mProviderHolder; // The method we'll call (or null, to not use) on the provider // for the fast path of retrieving settings. @@ -1599,30 +1696,27 @@ public final class Settings { @GuardedBy("this") private GenerationTracker mGenerationTracker; - public NameValueCache(Uri uri, String getCommand, String setCommand) { + public NameValueCache(Uri uri, String getCommand, String setCommand, + ContentProviderHolder providerHolder) { mUri = uri; mCallGetCommand = getCommand; mCallSetCommand = setCommand; - } - - private IContentProvider lazyGetProvider(ContentResolver cr) { - IContentProvider cp = null; - synchronized (NameValueCache.this) { - cp = mContentProvider; - if (cp == null) { - cp = mContentProvider = cr.acquireProvider(mUri.getAuthority()); - } - } - return cp; + mProviderHolder = providerHolder; } public boolean putStringForUser(ContentResolver cr, String name, String value, - final int userHandle) { + String tag, boolean makeDefault, final int userHandle) { try { Bundle arg = new Bundle(); arg.putString(Settings.NameValueTable.VALUE, value); arg.putInt(CALL_METHOD_USER_KEY, userHandle); - IContentProvider cp = lazyGetProvider(cr); + if (tag != null) { + arg.putString(CALL_METHOD_TAG_KEY, tag); + } + if (makeDefault) { + arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true); + } + IContentProvider cp = mProviderHolder.getProvider(cr); cp.call(cr.getPackageName(), mCallSetCommand, name, arg); } catch (RemoteException e) { Log.w(TAG, "Can't set key " + name + " in " + mUri, e); @@ -1653,7 +1747,7 @@ public final class Settings { + " by user " + UserHandle.myUserId() + " so skipping cache"); } - IContentProvider cp = lazyGetProvider(cr); + IContentProvider cp = mProviderHolder.getProvider(cr); // Try the fast path first, not using query(). If this // fails (alternate Settings provider that doesn't support @@ -1802,10 +1896,14 @@ public final class Settings { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/system"); + private static final ContentProviderHolder sProviderHolder = + new ContentProviderHolder(CONTENT_URI); + private static final NameValueCache sNameValueCache = new NameValueCache( CONTENT_URI, CALL_METHOD_GET_SYSTEM, - CALL_METHOD_PUT_SYSTEM); + CALL_METHOD_PUT_SYSTEM, + sProviderHolder); private static final HashSet MOVED_TO_SECURE; static { @@ -1997,7 +2095,7 @@ public final class Settings { + " to android.provider.Settings.Global, value is unchanged."); return false; } - return sNameValueCache.putStringForUser(resolver, name, value, userHandle); + return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle); } /** @@ -4153,11 +4251,15 @@ public final class Settings { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/secure"); + private static final ContentProviderHolder sProviderHolder = + new ContentProviderHolder(CONTENT_URI); + // Populated lazily, guarded by class object: private static final NameValueCache sNameValueCache = new NameValueCache( CONTENT_URI, CALL_METHOD_GET_SECURE, - CALL_METHOD_PUT_SECURE); + CALL_METHOD_PUT_SECURE, + sProviderHolder); private static ILockSettings sLockSettings = null; @@ -4357,6 +4459,13 @@ public final class Settings { /** @hide */ public static boolean putStringForUser(ContentResolver resolver, String name, String value, int userHandle) { + return putStringForUser(resolver, name, value, null, false, userHandle); + } + + /** @hide */ + public static boolean putStringForUser(@NonNull ContentResolver resolver, + @NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault, @UserIdInt int userHandle) { if (LOCATION_MODE.equals(name)) { // Map LOCATION_MODE to underlying location provider storage API return setLocationModeForUser(resolver, Integer.parseInt(value), userHandle); @@ -4364,9 +4473,115 @@ public final class Settings { if (MOVED_TO_GLOBAL.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System" + " to android.provider.Settings.Global"); - return Global.putStringForUser(resolver, name, value, userHandle); + return Global.putStringForUser(resolver, name, value, + tag, makeDefault, userHandle); + } + return sNameValueCache.putStringForUser(resolver, name, value, tag, + makeDefault, userHandle); + } + + /** + * Store a name/value pair into the database. + *

+ * The method takes an optional tag to associate with the setting + * which can be used to clear only settings made by your package and + * associated with this tag by passing the tag to {@link + * #resetToDefaults(ContentResolver, String)}. Anyone can override + * the current tag. Also if another package changes the setting + * then the tag will be set to the one specified in the set call + * which can be null. Also any of the settings setters that do not + * take a tag as an argument effectively clears the tag. + *

+ * For example, if you set settings A and B with tags T1 and T2 and + * another app changes setting A (potentially to the same value), it + * can assign to it a tag T3 (note that now the package that changed + * the setting is not yours). Now if you reset your changes for T1 and + * T2 only setting B will be reset and A not (as it was changed by + * another package) but since A did not change you are in the desired + * initial state. Now if the other app changes the value of A (assuming + * you registered an observer in the beginning) you would detect that + * the setting was changed by another app and handle this appropriately + * (ignore, set back to some value, etc). + *

+ * Also the method takes an argument whether to make the value the + * default for this setting. If the system already specified a default + * value, then the one passed in here will not + * be set as the default. + *

+ * + * @param resolver to access the database with. + * @param name to store. + * @param value to associate with the name. + * @param tag to associate with the setting. + * @param makeDefault whether to make the value the default one. + * @return true if the value was set, false on database errors. + * + * @see #resetToDefaults(ContentResolver, String) + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public static boolean putString(@NonNull ContentResolver resolver, + @NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault) { + return putStringForUser(resolver, name, value, tag, makeDefault, + UserHandle.myUserId()); + } + + /** + * Reset the settings to their defaults. This would reset only + * settings set by the caller's package. Think of it of a way to undo your own + * changes to the global settings. Passing in the optional tag will reset only + * settings changed by your package and associated with this tag. + * + * @param resolver Handle to the content resolver. + * @param tag Optional tag which should be associated with the settings to reset. + * + * @see #putString(ContentResolver, String, String, String, boolean) + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public static void resetToDefaults(@NonNull ContentResolver resolver, + @Nullable String tag) { + resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS, + UserHandle.myUserId()); + } + + /** + * + * Reset the settings to their defaults for a given user with a specific mode. The + * optional tag argument is valid only for {@link #RESET_MODE_PACKAGE_DEFAULTS} + * allowing resetting the settings made by a package and associated with the tag. + * + * @param resolver Handle to the content resolver. + * @param tag Optional tag which should be associated with the settings to reset. + * @param mode The reset mode. + * @param userHandle The user for which to reset to defaults. + * + * @see #RESET_MODE_PACKAGE_DEFAULTS + * @see #RESET_MODE_UNTRUSTED_DEFAULTS + * @see #RESET_MODE_UNTRUSTED_CHANGES + * @see #RESET_MODE_TRUSTED_DEFAULTS + * + * @hide + */ + public static void resetToDefaultsAsUser(@NonNull ContentResolver resolver, + @Nullable String tag, @ResetMode int mode, @IntRange(from = 0) int userHandle) { + try { + Bundle arg = new Bundle(); + arg.putInt(CALL_METHOD_USER_KEY, userHandle); + if (tag != null) { + arg.putString(CALL_METHOD_TAG_KEY, tag); + } + arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); + IContentProvider cp = sProviderHolder.getProvider(resolver); + cp.call(resolver.getPackageName(), CALL_METHOD_RESET_SECURE, null, arg); + } catch (RemoteException e) { + Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); } - return sNameValueCache.putStringForUser(resolver, name, value, userHandle); } /** @@ -9133,11 +9348,15 @@ public final class Settings { LOW_POWER_MODE_TRIGGER_LEVEL }; + private static final ContentProviderHolder sProviderHolder = + new ContentProviderHolder(CONTENT_URI); + // Populated lazily, guarded by class object: - private static NameValueCache sNameValueCache = new NameValueCache( + private static final NameValueCache sNameValueCache = new NameValueCache( CONTENT_URI, CALL_METHOD_GET_GLOBAL, - CALL_METHOD_PUT_GLOBAL); + CALL_METHOD_PUT_GLOBAL, + sProviderHolder); // Certain settings have been moved from global to the per-user secure namespace private static final HashSet MOVED_TO_SECURE; @@ -9181,12 +9400,122 @@ public final class Settings { */ public static boolean putString(ContentResolver resolver, String name, String value) { - return putStringForUser(resolver, name, value, UserHandle.myUserId()); + return putStringForUser(resolver, name, value, null, false, UserHandle.myUserId()); + } + + /** + * Store a name/value pair into the database. + *

+ * The method takes an optional tag to associate with the setting + * which can be used to clear only settings made by your package and + * associated with this tag by passing the tag to {@link + * #resetToDefaults(ContentResolver, String)}. Anyone can override + * the current tag. Also if another package changes the setting + * then the tag will be set to the one specified in the set call + * which can be null. Also any of the settings setters that do not + * take a tag as an argument effectively clears the tag. + *

+ * For example, if you set settings A and B with tags T1 and T2 and + * another app changes setting A (potentially to the same value), it + * can assign to it a tag T3 (note that now the package that changed + * the setting is not yours). Now if you reset your changes for T1 and + * T2 only setting B will be reset and A not (as it was changed by + * another package) but since A did not change you are in the desired + * initial state. Now if the other app changes the value of A (assuming + * you registered an observer in the beginning) you would detect that + * the setting was changed by another app and handle this appropriately + * (ignore, set back to some value, etc). + *

+ * Also the method takes an argument whether to make the value the + * default for this setting. If the system already specified a default + * value, then the one passed in here will not + * be set as the default. + *

+ * + * @param resolver to access the database with. + * @param name to store. + * @param value to associate with the name. + * @param tag to associated with the setting. + * @param makeDefault whether to make the value the default one. + * @return true if the value was set, false on database errors. + * + * @see #resetToDefaults(ContentResolver, String) + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public static boolean putString(@NonNull ContentResolver resolver, + @NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault) { + return putStringForUser(resolver, name, value, tag, makeDefault, + UserHandle.myUserId()); + } + + /** + * Reset the settings to their defaults. This would reset only + * settings set by the caller's package. Think of it of a way to undo your own + * changes to the secure settings. Passing in the optional tag will reset only + * settings changed by your package and associated with this tag. + * + * @param resolver Handle to the content resolver. + * @param tag Optional tag which should be associated with the settings to reset. + * + * @see #putString(ContentResolver, String, String, String, boolean) + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public static void resetToDefaults(@NonNull ContentResolver resolver, + @Nullable String tag) { + resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS, + UserHandle.myUserId()); + } + + /** + * Reset the settings to their defaults for a given user with a specific mode. The + * optional tag argument is valid only for {@link #RESET_MODE_PACKAGE_DEFAULTS} + * allowing resetting the settings made by a package and associated with the tag. + * + * @param resolver Handle to the content resolver. + * @param tag Optional tag which should be associated with the settings to reset. + * @param mode The reset mode. + * @param userHandle The user for which to reset to defaults. + * + * @see #RESET_MODE_PACKAGE_DEFAULTS + * @see #RESET_MODE_UNTRUSTED_DEFAULTS + * @see #RESET_MODE_UNTRUSTED_CHANGES + * @see #RESET_MODE_TRUSTED_DEFAULTS + * + * @hide + */ + public static void resetToDefaultsAsUser(@NonNull ContentResolver resolver, + @Nullable String tag, @ResetMode int mode, @IntRange(from = 0) int userHandle) { + try { + Bundle arg = new Bundle(); + arg.putInt(CALL_METHOD_USER_KEY, userHandle); + if (tag != null) { + arg.putString(CALL_METHOD_TAG_KEY, tag); + } + arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); + IContentProvider cp = sProviderHolder.getProvider(resolver); + cp.call(resolver.getPackageName(), CALL_METHOD_RESET_GLOBAL, null, arg); + } catch (RemoteException e) { + Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); + } } /** @hide */ public static boolean putStringForUser(ContentResolver resolver, String name, String value, int userHandle) { + return putStringForUser(resolver, name, value, null, false, userHandle); + } + + /** @hide */ + public static boolean putStringForUser(@NonNull ContentResolver resolver, + @NonNull String name, @Nullable String value, @Nullable String tag, + boolean makeDefault, @UserIdInt int userHandle) { if (LOCAL_LOGV) { Log.v(TAG, "Global.putString(name=" + name + ", value=" + value + " for " + userHandle); @@ -9195,9 +9524,11 @@ public final class Settings { if (MOVED_TO_SECURE.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global" + " to android.provider.Settings.Secure, value is unchanged."); - return Secure.putStringForUser(resolver, name, value, userHandle); + return Secure.putStringForUser(resolver, name, value, tag, + makeDefault, userHandle); } - return sNameValueCache.putStringForUser(resolver, name, value, userHandle); + return sNameValueCache.putStringForUser(resolver, name, value, tag, + makeDefault, userHandle); } /** @@ -9418,7 +9749,6 @@ public final class Settings { return putString(cr, name, Float.toString(value)); } - /** * Subscription to be used for voice call on a multi sim device. The supported values * are 0 = SUB1, 1 = SUB2 and etc. diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk index 2b833b2bc7c75..710214c33ebdb 100644 --- a/packages/SettingsProvider/Android.mk +++ b/packages/SettingsProvider/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-subdir-java-files) \ +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ src/com/android/providers/settings/EventLogTags.logtags LOCAL_JAVA_LIBRARIES := telephony-common ims-common diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index c149876cedaa8..3e62158d591cd 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -84,6 +84,10 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import static android.os.Process.ROOT_UID; +import static android.os.Process.SYSTEM_UID; +import static android.os.Process.SHELL_UID; + /** *

* This class is a content provider that publishes the system settings. @@ -147,6 +151,7 @@ public class SettingsProvider extends ContentProvider { private static final int MUTATION_OPERATION_INSERT = 1; private static final int MUTATION_OPERATION_DELETE = 2; private static final int MUTATION_OPERATION_UPDATE = 3; + private static final int MUTATION_OPERATION_RESET = 4; private static final String[] ALL_COLUMNS = new String[] { Settings.NameValueTable._ID, @@ -292,13 +297,17 @@ public class SettingsProvider extends ContentProvider { case Settings.CALL_METHOD_PUT_GLOBAL: { String value = getSettingValue(args); - insertGlobalSetting(name, value, requestingUserId, false); + String tag = getSettingTag(args); + final boolean makeDefault = getSettingMakeDefault(args); + insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false); break; } case Settings.CALL_METHOD_PUT_SECURE: { String value = getSettingValue(args); - insertSecureSetting(name, value, requestingUserId, false); + String tag = getSettingTag(args); + final boolean makeDefault = getSettingMakeDefault(args); + insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false); break; } @@ -308,6 +317,20 @@ public class SettingsProvider extends ContentProvider { break; } + case Settings.CALL_METHOD_RESET_GLOBAL: { + final int mode = getResetModeEnforcingPermission(args); + String tag = getSettingTag(args); + resetGlobalSetting(requestingUserId, mode, tag); + break; + } + + case Settings.CALL_METHOD_RESET_SECURE: { + final int mode = getResetModeEnforcingPermission(args); + String tag = getSettingTag(args); + resetSecureSetting(requestingUserId, mode, tag); + break; + } + default: { Slog.w(LOG_TAG, "call() with invalid method: " + method); } break; @@ -399,13 +422,15 @@ public class SettingsProvider extends ContentProvider { switch (table) { case TABLE_GLOBAL: { - if (insertGlobalSetting(name, value, UserHandle.getCallingUserId(), false)) { + if (insertGlobalSetting(name, value, null, false, + UserHandle.getCallingUserId(), false)) { return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name); } } break; case TABLE_SECURE: { - if (insertSecureSetting(name, value, UserHandle.getCallingUserId(), false)) { + if (insertSecureSetting(name, value, null, false, + UserHandle.getCallingUserId(), false)) { return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name); } } break; @@ -503,12 +528,14 @@ public class SettingsProvider extends ContentProvider { switch (args.table) { case TABLE_GLOBAL: { final int userId = UserHandle.getCallingUserId(); - return updateGlobalSetting(args.name, value, userId, false) ? 1 : 0; + return updateGlobalSetting(args.name, value, null, false, + userId, false) ? 1 : 0; } case TABLE_SECURE: { final int userId = UserHandle.getCallingUserId(); - return updateSecureSetting(args.name, value, userId, false) ? 1 : 0; + return updateSecureSetting(args.name, value, null, false, + userId, false) ? 1 : 0; } case TABLE_SYSTEM: { @@ -586,10 +613,9 @@ public class SettingsProvider extends ContentProvider { SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); if (globalSettings != null) { dumpSettingsLocked(globalSettings, pw); + pw.println(); + globalSettings.dumpHistoricalOperations(pw); } - pw.println(); - - globalSettings.dumpHistoricalOperations(pw); } pw.println("SECURE SETTINGS (user " + userId + ")"); @@ -597,20 +623,18 @@ public class SettingsProvider extends ContentProvider { SETTINGS_TYPE_SECURE, userId); if (secureSettings != null) { dumpSettingsLocked(secureSettings, pw); + pw.println(); + secureSettings.dumpHistoricalOperations(pw); } - pw.println(); - - secureSettings.dumpHistoricalOperations(pw); pw.println("SYSTEM SETTINGS (user " + userId + ")"); SettingsState systemSettings = mSettingsRegistry.getSettingsLocked( SETTINGS_TYPE_SYSTEM, userId); if (systemSettings != null) { dumpSettingsLocked(systemSettings, pw); + pw.println(); + systemSettings.dumpHistoricalOperations(pw); } - pw.println(); - - systemSettings.dumpHistoricalOperations(pw); } private void dumpSettingsLocked(SettingsState settingsState, PrintWriter pw) { @@ -624,9 +648,16 @@ public class SettingsProvider extends ContentProvider { pw.print("_id:"); pw.print(toDumpString(setting.getId())); pw.print(" name:"); pw.print(toDumpString(name)); if (setting.getPackageName() != null) { - pw.print(" pkg:"); pw.print(toDumpString(setting.getPackageName())); + pw.print(" pkg:"); pw.print(setting.getPackageName()); } pw.print(" value:"); pw.print(toDumpString(setting.getValue())); + if (setting.getDefaultValue() != null) { + pw.print(" default:"); pw.print(setting.getDefaultValue()); + pw.print(" defaultSystemSet:"); pw.print(setting.isDefaultSystemSet()); + } + if (setting.getTag() != null) { + pw.print(" tag:"); pw.print(setting.getTag()); + } pw.println(); } } @@ -691,73 +722,79 @@ public class SettingsProvider extends ContentProvider { // value with a forced update to ensure that all cross profile dependencies // are taken into account. Also make sure the settings update to.. the same // value passes the security checks, so clear binder calling id. - if (newRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION) - != prevRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION)) { + if (newRestrictions.getBoolean(UserManager.DISALLOW_SHARE_LOCATION) + != prevRestrictions.getBoolean(UserManager.DISALLOW_SHARE_LOCATION)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Setting setting = getSecureSetting( Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId); updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - setting != null ? setting.getValue() : null, userId, true); + setting != null ? setting.getValue() : null, null, + true, userId, true); } } finally { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) - != prevRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) { + if (newRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) + != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Setting setting = getGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS); + String value = setting != null ? setting.getValue() : null; updateGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS, - setting != null ? setting.getValue() : null, userId, true); + value, null, true, userId, true); } } finally { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES) - != prevRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES)) { + if (newRestrictions.getBoolean(UserManager.DISALLOW_DEBUGGING_FEATURES) + != prevRestrictions.getBoolean(UserManager.DISALLOW_DEBUGGING_FEATURES)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Setting setting = getGlobalSetting(Settings.Global.ADB_ENABLED); + String value = setting != null ? setting.getValue() : null; updateGlobalSetting(Settings.Global.ADB_ENABLED, - setting != null ? setting.getValue() : null, userId, true); + value, null, true, userId, true); } } finally { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS) - != prevRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS)) { + if (newRestrictions.getBoolean(UserManager.ENSURE_VERIFY_APPS) + != prevRestrictions.getBoolean(UserManager.ENSURE_VERIFY_APPS)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Setting enable = getGlobalSetting( Settings.Global.PACKAGE_VERIFIER_ENABLE); + String enableValue = enable != null ? enable.getValue() : null; updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE, - enable != null ? enable.getValue() : null, userId, true); + enableValue, null, true, userId, true); Setting include = getGlobalSetting( Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB); + String includeValue = include != null ? include.getValue() : null; updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, - include != null ? include.getValue() : null, userId, true); + includeValue, null, true, userId, true); } } finally { Binder.restoreCallingIdentity(identity); } } - if (newRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) - != prevRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { + if (newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) + != prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Setting setting = getGlobalSetting( Settings.Global.PREFERRED_NETWORK_MODE); + String value = setting != null ? setting.getValue() : null; updateGlobalSetting(Settings.Global.PREFERRED_NETWORK_MODE, - setting != null ? setting.getValue() : null, userId, true); + value, null, true, userId, true); } } finally { Binder.restoreCallingIdentity(identity); @@ -806,34 +843,49 @@ public class SettingsProvider extends ContentProvider { } } - private boolean updateGlobalSetting(String name, String value, int requestingUserId, - boolean forceNotify) { + private boolean updateGlobalSetting(String name, String value, String tag, + boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { - Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")"); + Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ", " + + ", " + tag + ", " + makeDefault + ", " + requestingUserId + + ", " + forceNotify + ")"); } - return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE, - forceNotify); + return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId, + MUTATION_OPERATION_UPDATE, forceNotify, 0); } - private boolean insertGlobalSetting(String name, String value, int requestingUserId, - boolean forceNotify) { + private boolean insertGlobalSetting(String name, String value, String tag, + boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { - Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ")"); + Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ", " + + ", " + tag + ", " + makeDefault + ", " + requestingUserId + + ", " + forceNotify + ")"); } - return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT, - forceNotify); + return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId, + MUTATION_OPERATION_INSERT, forceNotify, 0); } private boolean deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify) { if (DEBUG) { - Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")"); + Slog.v(LOG_TAG, "deleteGlobalSetting(" + name + ", " + requestingUserId + + ", " + forceNotify + ")"); } - return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE, - forceNotify); + return mutateGlobalSetting(name, null, null, false, requestingUserId, + MUTATION_OPERATION_DELETE, forceNotify, 0); } - private boolean mutateGlobalSetting(String name, String value, int requestingUserId, - int operation, boolean forceNotify) { + private void resetGlobalSetting(int requestingUserId, int mode, String tag) { + if (DEBUG) { + Slog.v(LOG_TAG, "resetGlobalSetting(" + requestingUserId + ", " + + mode + ", " + tag + ")"); + } + mutateGlobalSetting(null, null, tag, false, requestingUserId, + MUTATION_OPERATION_RESET, false, mode); + } + + private boolean mutateGlobalSetting(String name, String value, String tag, + boolean makeDefault, int requestingUserId, int operation, boolean forceNotify, + int mode) { // Make sure the caller can change the settings - treated as secure. enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); @@ -842,7 +894,7 @@ public class SettingsProvider extends ContentProvider { // If this is a setting that is currently restricted for this user, do not allow // unrestricting changes. - if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value, + if (name != null && isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) { return false; } @@ -851,9 +903,9 @@ public class SettingsProvider extends ContentProvider { synchronized (mLock) { switch (operation) { case MUTATION_OPERATION_INSERT: { - return mSettingsRegistry - .insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, - name, value, getCallingPackage(), forceNotify); + return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL, + UserHandle.USER_SYSTEM, name, value, tag, makeDefault, + getCallingPackage(), forceNotify); } case MUTATION_OPERATION_DELETE: { @@ -862,10 +914,15 @@ public class SettingsProvider extends ContentProvider { } case MUTATION_OPERATION_UPDATE: { - return mSettingsRegistry - .updateSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, - name, value, getCallingPackage(), forceNotify); + return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL, + UserHandle.USER_SYSTEM, name, value, tag, makeDefault, + getCallingPackage(), forceNotify); } + + case MUTATION_OPERATION_RESET: { + mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL, + UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag); + } return true; } } @@ -934,39 +991,53 @@ public class SettingsProvider extends ContentProvider { } } - private boolean insertSecureSetting(String name, String value, int requestingUserId, - boolean forceNotify) { + private boolean insertSecureSetting(String name, String value, String tag, + boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", " - + requestingUserId + ")"); + + ", " + tag + ", " + makeDefault + ", " + requestingUserId + + ", " + forceNotify + ")"); } - return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT, - forceNotify); + return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId, + MUTATION_OPERATION_INSERT, forceNotify, 0); } private boolean deleteSecureSetting(String name, int requestingUserId, boolean forceNotify) { if (DEBUG) { - Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + ")"); + Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + + ", " + forceNotify + ")"); } - return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE, - forceNotify); + return mutateSecureSetting(name, null, null, false, requestingUserId, + MUTATION_OPERATION_DELETE, forceNotify, 0); } - private boolean updateSecureSetting(String name, String value, int requestingUserId, - boolean forceNotify) { + private boolean updateSecureSetting(String name, String value, String tag, + boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { Slog.v(LOG_TAG, "updateSecureSetting(" + name + ", " + value + ", " - + requestingUserId + ")"); + + ", " + tag + ", " + makeDefault + ", " + requestingUserId + + ", " + forceNotify +")"); } - return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE, - forceNotify); + return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId, + MUTATION_OPERATION_UPDATE, forceNotify, 0); } - private boolean mutateSecureSetting(String name, String value, int requestingUserId, - int operation, boolean forceNotify) { + private void resetSecureSetting(int requestingUserId, int mode, String tag) { + if (DEBUG) { + Slog.v(LOG_TAG, "resetSecureSetting(" + requestingUserId + ", " + + mode + ", " + tag + ")"); + } + + mutateSecureSetting(null, null, tag, false, requestingUserId, + MUTATION_OPERATION_RESET, false, mode); + } + + private boolean mutateSecureSetting(String name, String value, String tag, + boolean makeDefault, int requestingUserId, int operation, boolean forceNotify, + int mode) { // Make sure the caller can change the settings. enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); @@ -975,7 +1046,7 @@ public class SettingsProvider extends ContentProvider { // If this is a setting that is currently restricted for this user, do not allow // unrestricting changes. - if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value, + if (name != null && isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) { return false; } @@ -990,7 +1061,8 @@ public class SettingsProvider extends ContentProvider { // Special cases for location providers (sigh). if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { - return updateLocationProvidersAllowedLocked(value, owningUserId, forceNotify); + return updateLocationProvidersAllowedLocked(value, tag, owningUserId, makeDefault, + forceNotify); } // Mutate the value. @@ -998,7 +1070,8 @@ public class SettingsProvider extends ContentProvider { switch (operation) { case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, - owningUserId, name, value, getCallingPackage(), forceNotify); + owningUserId, name, value, tag, makeDefault, + getCallingPackage(), forceNotify); } case MUTATION_OPERATION_DELETE: { @@ -1008,8 +1081,14 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_UPDATE: { return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE, - owningUserId, name, value, getCallingPackage(), forceNotify); + owningUserId, name, value, tag, makeDefault, + getCallingPackage(), forceNotify); } + + case MUTATION_OPERATION_RESET: { + mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE, + UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag); + } return true; } } @@ -1138,7 +1217,7 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_INSERT: { validateSystemSettingValue(name, value); return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM, - owningUserId, name, value, getCallingPackage(), false); + owningUserId, name, value, null, false, getCallingPackage(), false); } case MUTATION_OPERATION_DELETE: { @@ -1149,7 +1228,7 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_UPDATE: { validateSystemSettingValue(name, value); return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM, - owningUserId, name, value, getCallingPackage(), false); + owningUserId, name, value, null, false, getCallingPackage(), false); } } @@ -1240,7 +1319,8 @@ public class SettingsProvider extends ContentProvider { case Settings.Secure.ALWAYS_ON_VPN_APP: case Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN: // Whitelist system uid (ConnectivityService) and root uid to change always-on vpn - if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { + final int appId = UserHandle.getAppId(callingUid); + if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) { return false; } restriction = UserManager.DISALLOW_CONFIG_VPN; @@ -1294,9 +1374,10 @@ public class SettingsProvider extends ContentProvider { String name, int userId) { // System/root/shell can mutate whatever secure settings they want. final int callingUid = Binder.getCallingUid(); - if (callingUid == android.os.Process.SYSTEM_UID - || callingUid == Process.SHELL_UID - || callingUid == Process.ROOT_UID) { + final int appId = UserHandle.getAppId(callingUid); + if (appId == android.os.Process.SYSTEM_UID + || appId == Process.SHELL_UID + || appId == Process.ROOT_UID) { return; } @@ -1392,8 +1473,8 @@ public class SettingsProvider extends ContentProvider { * * @returns whether the enabled location providers changed. */ - private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId, - boolean forceNotify) { + private boolean updateLocationProvidersAllowedLocked(String value, String tag, + int owningUserId, boolean makeDefault, boolean forceNotify) { if (TextUtils.isEmpty(value)) { return false; } @@ -1466,7 +1547,7 @@ public class SettingsProvider extends ContentProvider { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders, - getCallingPackage(), forceNotify); + tag, makeDefault, getCallingPackage(), forceNotify); } private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( @@ -1509,8 +1590,8 @@ public class SettingsProvider extends ContentProvider { } Bundle result = new Bundle(); result.putString(Settings.NameValueTable.VALUE, - setting != null && !setting.isNull() ? setting.getValue() : null); - mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getkey()); + !setting.isNull() ? setting.getValue() : null); + mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey()); return result; } @@ -1528,6 +1609,51 @@ public class SettingsProvider extends ContentProvider { return (args != null) ? args.getString(Settings.NameValueTable.VALUE) : null; } + private static String getSettingTag(Bundle args) { + return (args != null) ? args.getString(Settings.CALL_METHOD_TAG_KEY) : null; + } + + private static boolean getSettingMakeDefault(Bundle args) { + return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY); + } + + private static int getResetModeEnforcingPermission(Bundle args) { + final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0; + switch (mode) { + case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: { + if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) { + throw new SecurityException("Only system, shell/root on a " + + "debuggable build can reset to untrusted defaults"); + } + return mode; + } + case Settings.RESET_MODE_UNTRUSTED_CHANGES: { + if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) { + throw new SecurityException("Only system, shell/root on a " + + "debuggable build can reset untrusted changes"); + } + return mode; + } + case Settings.RESET_MODE_TRUSTED_DEFAULTS: { + if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) { + throw new SecurityException("Only system, shell/root on a " + + "debuggable build can reset to trusted defaults"); + } + return mode; + } + case Settings.RESET_MODE_PACKAGE_DEFAULTS: { + return mode; + } + } + throw new IllegalArgumentException("Invalid reset mode: " + mode); + } + + private static boolean isCallerSystemOrShellOrRootOnDebuggableBuild() { + final int appId = UserHandle.getAppId(Binder.getCallingUid()); + return appId == SYSTEM_UID || (Build.IS_DEBUGGABLE + && (appId == SHELL_UID || appId == ROOT_UID)); + } + private static String getValidTableOrThrow(Uri uri) { if (uri.getPathSegments().size() > 0) { String table = uri.getPathSegments().get(0); @@ -1767,8 +1893,8 @@ public class SettingsProvider extends ContentProvider { private void ensureSettingsStateLocked(int key) { if (mSettingsStates.get(key) == null) { final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key)); - SettingsState settingsState = new SettingsState(mLock, getSettingsFile(key), key, - maxBytesPerPackage, mHandlerThread.getLooper()); + SettingsState settingsState = new SettingsState(getContext(), mLock, + getSettingsFile(key), key, maxBytesPerPackage, mHandlerThread.getLooper()); mSettingsStates.put(key, settingsState); } } @@ -1815,12 +1941,15 @@ public class SettingsProvider extends ContentProvider { } public boolean insertSettingLocked(int type, int userId, String name, String value, - String packageName, boolean forceNotify) { + String tag, boolean makeDefault, String packageName, boolean forceNotify) { final int key = makeKey(type, userId); + boolean success = false; SettingsState settingsState = peekSettingsStateLocked(key); - final boolean success = settingsState != null - && settingsState.insertSettingLocked(name, value, packageName); + if (settingsState != null) { + success = settingsState.insertSettingLocked(name, value, + tag, makeDefault, packageName); + } if (forceNotify || success) { notifyForSettingsChange(key, name); @@ -1831,11 +1960,11 @@ public class SettingsProvider extends ContentProvider { public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify) { final int key = makeKey(type, userId); + boolean success = false; SettingsState settingsState = peekSettingsStateLocked(key); - if (settingsState == null) { - return false; + if (settingsState != null) { + success = settingsState.deleteSettingLocked(name); } - final boolean success = settingsState.deleteSettingLocked(name); if (forceNotify || success) { notifyForSettingsChange(key, name); @@ -1854,12 +1983,15 @@ public class SettingsProvider extends ContentProvider { } public boolean updateSettingLocked(int type, int userId, String name, String value, - String packageName, boolean forceNotify) { + String tag, boolean makeDefault, String packageName, boolean forceNotify) { final int key = makeKey(type, userId); + boolean success = false; SettingsState settingsState = peekSettingsStateLocked(key); - final boolean success = settingsState != null - && settingsState.updateSettingLocked(name, value, packageName); + if (settingsState != null) { + success = settingsState.updateSettingLocked(name, value, tag, + makeDefault, packageName); + } if (forceNotify || success) { notifyForSettingsChange(key, name); @@ -1868,6 +2000,72 @@ public class SettingsProvider extends ContentProvider { return success; } + public void resetSettingsLocked(int type, int userId, String packageName, int mode, + String tag) { + final int key = makeKey(type, userId); + SettingsState settingsState = peekSettingsStateLocked(key); + if (settingsState == null) { + return; + } + + switch (mode) { + case Settings.RESET_MODE_PACKAGE_DEFAULTS: { + for (String name : settingsState.getSettingNamesLocked()) { + Setting setting = settingsState.getSettingLocked(name); + if (packageName.equals(setting.getPackageName())) { + if (tag != null && !tag.equals(setting.getTag())) { + continue; + } + if (settingsState.resetSettingLocked(name, packageName)) { + notifyForSettingsChange(key, name); + } + } + } + } break; + + case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: { + for (String name : settingsState.getSettingNamesLocked()) { + Setting setting = settingsState.getSettingLocked(name); + if (!SettingsState.isSystemPackage(getContext(), + setting.getPackageName())) { + if (settingsState.resetSettingLocked(name, packageName)) { + notifyForSettingsChange(key, name); + } + } + } + } break; + + case Settings.RESET_MODE_UNTRUSTED_CHANGES: { + for (String name : settingsState.getSettingNamesLocked()) { + Setting setting = settingsState.getSettingLocked(name); + if (!SettingsState.isSystemPackage(getContext(), + setting.getPackageName())) { + if (setting.isDefaultSystemSet()) { + if (settingsState.resetSettingLocked(name, packageName)) { + notifyForSettingsChange(key, name); + } + } else if (settingsState.deleteSettingLocked(name)) { + notifyForSettingsChange(key, name); + } + } + } + } break; + + case Settings.RESET_MODE_TRUSTED_DEFAULTS: { + for (String name : settingsState.getSettingNamesLocked()) { + Setting setting = settingsState.getSettingLocked(name); + if (setting.isDefaultSystemSet()) { + if (settingsState.resetSettingLocked(name, packageName)) { + notifyForSettingsChange(key, name); + } + } else if (settingsState.deleteSettingLocked(name)) { + notifyForSettingsChange(key, name); + } + } + } break; + } + } + public void onPackageRemovedLocked(String packageName, int userId) { // Global and secure settings are signature protected. Apps signed // by the platform certificate are generally not uninstalled and @@ -2005,7 +2203,7 @@ public class SettingsProvider extends ContentProvider { while (!cursor.isAfterLast()) { String name = cursor.getString(nameColumnIdx); String value = cursor.getString(valueColumnIdx); - settingsState.insertSettingLocked(name, value, + settingsState.insertSettingLocked(name, value, null, true, SettingsState.SYSTEM_PACKAGE_NAME); cursor.moveToNext(); } @@ -2037,7 +2235,7 @@ public class SettingsProvider extends ContentProvider { String androidId = Long.toHexString(new SecureRandom().nextLong()); secureSettings.insertSettingLocked(Settings.Secure.ANDROID_ID, androidId, - SettingsState.SYSTEM_PACKAGE_NAME); + null, true, SettingsState.SYSTEM_PACKAGE_NAME); Slog.d(LOG_TAG, "Generated and saved new ANDROID_ID [" + androidId + "] for user " + userId); @@ -2221,7 +2419,8 @@ public class SettingsProvider extends ContentProvider { String reason = "Settings rebuilt! Current version: " + curVersion + " while expected: " + newVersion; getGlobalSettingsLocked().insertSettingLocked( - Settings.Global.DATABASE_DOWNGRADE_REASON, reason, "android"); + Settings.Global.DATABASE_DOWNGRADE_REASON, + reason, null, true, SettingsState.SYSTEM_PACKAGE_NAME); } // Set the global settings version if owner. @@ -2290,11 +2489,11 @@ public class SettingsProvider extends ContentProvider { if (userId == UserHandle.USER_SYSTEM) { final SettingsState globalSettings = getGlobalSettingsLocked(); globalSettings.updateSettingLocked(Settings.Global.ZEN_MODE, - Integer.toString(Settings.Global.ZEN_MODE_OFF), - SettingsState.SYSTEM_PACKAGE_NAME); + Integer.toString(Settings.Global.ZEN_MODE_OFF), null, + true, SettingsState.SYSTEM_PACKAGE_NAME); globalSettings.updateSettingLocked(Settings.Global.MODE_RINGER, - Integer.toString(AudioManager.RINGER_MODE_NORMAL), - SettingsState.SYSTEM_PACKAGE_NAME); + Integer.toString(AudioManager.RINGER_MODE_NORMAL), null, + true, SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 119; } @@ -2304,7 +2503,7 @@ public class SettingsProvider extends ContentProvider { SettingsState secureSettings = getSecureSettingsLocked(userId); secureSettings.insertSettingLocked(Settings.Secure.DOUBLE_TAP_TO_WAKE, getContext().getResources().getBoolean( - R.bool.def_double_tap_to_wake) ? "1" : "0", + R.bool.def_double_tap_to_wake) ? "1" : "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME); currentVersion = 120; @@ -2329,8 +2528,7 @@ public class SettingsProvider extends ContentProvider { currentSetting.isNull()) { secureSettings.insertSettingLocked( Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, - defaultComponent, - SettingsState.SYSTEM_PACKAGE_NAME); + defaultComponent, null, true, SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 122; } @@ -2347,7 +2545,7 @@ public class SettingsProvider extends ContentProvider { Settings.Global.ADD_USERS_WHEN_LOCKED, getContext().getResources().getBoolean( R.bool.def_add_users_from_lockscreen) ? "1" : "0", - SettingsState.SYSTEM_PACKAGE_NAME); + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } } currentVersion = 123; @@ -2358,7 +2556,7 @@ public class SettingsProvider extends ContentProvider { String defaultDisabledProfiles = (getContext().getResources().getString( R.string.def_bluetooth_disabled_profiles)); globalSettings.insertSettingLocked(Settings.Global.BLUETOOTH_DISABLED_PROFILES, - defaultDisabledProfiles, SettingsState.SYSTEM_PACKAGE_NAME); + defaultDisabledProfiles, null, true, SettingsState.SYSTEM_PACKAGE_NAME); currentVersion = 124; } @@ -2373,7 +2571,7 @@ public class SettingsProvider extends ContentProvider { Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, getContext().getResources().getBoolean( R.bool.def_show_ime_with_hard_keyboard) ? "1" : "0", - SettingsState.SYSTEM_PACKAGE_NAME); + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 125; } @@ -2400,7 +2598,7 @@ public class SettingsProvider extends ContentProvider { } secureSettings.insertSettingLocked( Settings.Secure.ENABLED_VR_LISTENERS, b.toString(), - SettingsState.SYSTEM_PACKAGE_NAME); + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } } @@ -2420,7 +2618,7 @@ public class SettingsProvider extends ContentProvider { final SettingsState secureSettings = getSecureSettingsLocked(userId); secureSettings.insertSettingLocked( Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, - showNotifications.getValue(), + showNotifications.getValue(), null, true, SettingsState.SYSTEM_PACKAGE_NAME); } @@ -2430,7 +2628,7 @@ public class SettingsProvider extends ContentProvider { final SettingsState secureSettings = getSecureSettingsLocked(userId); secureSettings.insertSettingLocked( Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, - allowPrivate.getValue(), + allowPrivate.getValue(), null, true, SettingsState.SYSTEM_PACKAGE_NAME); } } @@ -2456,7 +2654,7 @@ public class SettingsProvider extends ContentProvider { if (policyAccess.isNull()) { systemSecureSettings.insertSettingLocked( Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, - defaultPolicyAccess, + defaultPolicyAccess, null, true, SettingsState.SYSTEM_PACKAGE_NAME); } else { StringBuilder currentSetting = @@ -2465,7 +2663,7 @@ public class SettingsProvider extends ContentProvider { currentSetting.append(defaultPolicyAccess); systemSecureSettings.updateSettingLocked( Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, - currentSetting.toString(), + currentSetting.toString(), null, true, SettingsState.SYSTEM_PACKAGE_NAME); } } @@ -2485,7 +2683,7 @@ public class SettingsProvider extends ContentProvider { Settings.Secure.LONG_PRESS_TIMEOUT, String.valueOf(getContext().getResources().getInteger( R.integer.def_long_press_timeout_millis)), - SettingsState.SYSTEM_PACKAGE_NAME); + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 130; } @@ -2498,9 +2696,9 @@ public class SettingsProvider extends ContentProvider { if (dozeExplicitlyDisabled) { secureSettings.insertSettingLocked(Settings.Secure.DOZE_PULSE_ON_PICK_UP, - "0", SettingsState.SYSTEM_PACKAGE_NAME); + "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME); secureSettings.insertSettingLocked(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, - "0", SettingsState.SYSTEM_PACKAGE_NAME); + "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 131; } @@ -2515,7 +2713,7 @@ public class SettingsProvider extends ContentProvider { Settings.Secure.MULTI_PRESS_TIMEOUT, String.valueOf(getContext().getResources().getInteger( R.integer.def_multi_press_timeout_millis)), - SettingsState.SYSTEM_PACKAGE_NAME); + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 132; @@ -2527,9 +2725,8 @@ public class SettingsProvider extends ContentProvider { String defaultSyncParentSounds = (getContext().getResources() .getBoolean(R.bool.def_sync_parent_sounds) ? "1" : "0"); systemSecureSettings.insertSettingLocked( - Settings.Secure.SYNC_PARENT_SOUNDS, - defaultSyncParentSounds, - SettingsState.SYSTEM_PACKAGE_NAME); + Settings.Secure.SYNC_PARENT_SOUNDS, defaultSyncParentSounds, + null, true, SettingsState.SYSTEM_PACKAGE_NAME); currentVersion = 133; } @@ -2541,7 +2738,8 @@ public class SettingsProvider extends ContentProvider { String defaultEndButtonBehavior = Integer.toString(getContext() .getResources().getInteger(R.integer.def_end_button_behavior)); systemSettings.insertSettingLocked(Settings.System.END_BUTTON_BEHAVIOR, - defaultEndButtonBehavior, SettingsState.SYSTEM_PACKAGE_NAME); + defaultEndButtonBehavior, null, true, + SettingsState.SYSTEM_PACKAGE_NAME); } currentVersion = 134; } @@ -2565,13 +2763,13 @@ public class SettingsProvider extends ContentProvider { // A scorer was set so enable recommendations. globalSettings.insertSettingLocked( Global.NETWORK_RECOMMENDATIONS_ENABLED, - "1", + "1", null, true, SettingsState.SYSTEM_PACKAGE_NAME); // and clear the scorer setting since it's no longer needed. globalSettings.insertSettingLocked( Global.NETWORK_SCORER_APP, - null, + null, null, true, SettingsState.SYSTEM_PACKAGE_NAME); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java index cbeb8780be2c3..080805112c650 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java @@ -97,6 +97,7 @@ final public class SettingsService extends Binder { PUT, DELETE, LIST, + RESET, } int mUser = -1; // unspecified @@ -104,7 +105,10 @@ final public class SettingsService extends Binder { String mTable = null; String mKey = null; String mValue = null; - + String mPackageName = null; + String mToken = null; + int mResetMode = -1; + boolean mMakeDefault; MyShellCommand(SettingsProvider provider, boolean dumping) { mProvider = provider; @@ -142,6 +146,8 @@ final public class SettingsService extends Binder { mVerb = CommandVerb.DELETE; } else if ("list".equalsIgnoreCase(arg)) { mVerb = CommandVerb.LIST; + } else if ("reset".equalsIgnoreCase(arg)) { + mVerb = CommandVerb.RESET; } else { // invalid perr.println("Invalid command: " + arg); @@ -159,6 +165,35 @@ final public class SettingsService extends Binder { valid = true; break; } + } else if (mVerb == CommandVerb.RESET) { + if ("untrusted_defaults".equalsIgnoreCase(arg)) { + mResetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS; + } else if ("untrusted_clear".equalsIgnoreCase(arg)) { + mResetMode = Settings.RESET_MODE_UNTRUSTED_CHANGES; + } else if ("trusted_defaults".equalsIgnoreCase(arg)) { + mResetMode = Settings.RESET_MODE_TRUSTED_DEFAULTS; + } else { + mPackageName = arg; + mResetMode = Settings.RESET_MODE_PACKAGE_DEFAULTS; + if (peekNextArg() == null) { + valid = true; + } else { + mToken = getNextArg(); + if (peekNextArg() == null) { + valid = true; + } else { + perr.println("Too many arguments"); + return -1; + } + } + break; + } + if (peekNextArg() == null) { + valid = true; + } else { + perr.println("Too many arguments"); + return -1; + } } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) { mKey = arg; if (peekNextArg() == null) { @@ -171,8 +206,32 @@ final public class SettingsService extends Binder { } else if (mKey == null) { mKey = arg; // keep going; there's another PUT arg - } else { // PUT, final arg + } else if (mValue == null) { mValue = arg; + // keep going; there may be another PUT arg + } else if (mToken == null) { + mToken = arg; + if ("default".equalsIgnoreCase(mToken)) { + mToken = null; + mMakeDefault = true; + if (peekNextArg() == null) { + valid = true; + } else { + perr.println("Too many arguments"); + return -1; + } + break; + } + if (peekNextArg() == null) { + valid = true; + break; + } + } else { // PUT, final arg + if (!"default".equalsIgnoreCase(arg)) { + perr.println("Argument expected to be 'default'"); + return -1; + } + mMakeDefault = true; if (peekNextArg() == null) { valid = true; } else { @@ -214,7 +273,7 @@ final public class SettingsService extends Binder { pout.println(getForUser(iprovider, mUser, mTable, mKey)); break; case PUT: - putForUser(iprovider, mUser, mTable, mKey, mValue); + putForUser(iprovider, mUser, mTable, mKey, mValue, mToken, mMakeDefault); break; case DELETE: pout.println("Deleted " @@ -225,6 +284,9 @@ final public class SettingsService extends Binder { pout.println(line); } break; + case RESET: + resetForUser(iprovider, mUser, mTable, mToken); + break; default: perr.println("Unspecified command"); return -1; @@ -286,11 +348,15 @@ final public class SettingsService extends Binder { return result; } - void putForUser(IContentProvider provider, int userHandle, - final String table, final String key, final String value) { + void putForUser(IContentProvider provider, int userHandle, final String table, + final String key, final String value, String token, boolean makeDefault) { final String callPutCommand; - if ("system".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM; - else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE; + if ("system".equals(table)) { + callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM; + makeDefault = false; + getOutPrintWriter().println("Ignored makeDefault - " + + "doesn't apply to system settings"); + } else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE; else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL; else { getErrPrintWriter().println("Invalid table; no put performed"); @@ -301,6 +367,10 @@ final public class SettingsService extends Binder { Bundle arg = new Bundle(); arg.putString(Settings.NameValueTable.VALUE, value); arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + arg.putString(Settings.CALL_METHOD_TAG_KEY, token); + if (makeDefault) { + arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true); + } provider.call(resolveCallingPackage(), callPutCommand, key, arg); } catch (RemoteException e) { throw new RuntimeException("Failed in IPC", e); @@ -327,6 +397,29 @@ final public class SettingsService extends Binder { return num; } + void resetForUser(IContentProvider provider, int userHandle, + String table, String token) { + final String callResetCommand; + if ("secure".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_SECURE; + else if ("global".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_GLOBAL; + else { + getErrPrintWriter().println("Invalid table; no reset performed"); + return; + } + + try { + Bundle arg = new Bundle(); + arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, mResetMode); + arg.putString(Settings.CALL_METHOD_TAG_KEY, token); + String packageName = mPackageName != null ? mPackageName : resolveCallingPackage(); + arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + provider.call(packageName, callResetCommand, null, arg); + } catch (RemoteException e) { + throw new RuntimeException("Failed in IPC", e); + } + } + public static String resolveCallingPackage() { switch (Binder.getCallingUid()) { case Process.ROOT_UID: { @@ -360,14 +453,18 @@ final public class SettingsService extends Binder { pw.println(" Print this help text."); pw.println(" get [--user | current] NAMESPACE KEY"); pw.println(" Retrieve the current value of KEY."); - pw.println(" put [--user | current] NAMESPACE KEY VALUE"); + pw.println(" put [--user | current] NAMESPACE KEY VALUE [TOKEN] [default]"); pw.println(" Change the contents of KEY to VALUE."); + pw.println(" TOKEN to associate with the setting."); + pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace"); pw.println(" delete NAMESPACE KEY"); pw.println(" Delete the entry for KEY."); + pw.println(" reset [--user | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}"); + pw.println(" Reset the global/secure table for a package with mode."); + pw.println(" RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive"); pw.println(" list NAMESPACE"); pw.println(" Print all defined keys."); - pw.println(); - pw.println(" NAMESPACE is one of {system, secure, global}, case-insensitive"); + pw.println(" NAMESPACE is one of {system, secure, global}, case-insensitive"); } } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index d682fe9aa5f4d..8f37b98334b7c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -16,20 +16,30 @@ package com.android.providers.settings; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.Signature; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Base64; import android.util.Slog; +import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; +import com.android.server.LocalServices; import libcore.io.IoUtils; import libcore.util.Objects; import org.xmlpull.v1.XmlPullParser; @@ -46,6 +56,8 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import static android.os.Process.FIRST_APPLICATION_UID; + /** * This class contains the state for one type of settings. It is responsible * for saving the state asynchronously to an XML file after a mutation and @@ -63,6 +75,8 @@ final class SettingsState { private static final String LOG_TAG = "SettingsState"; + static final String SYSTEM_PACKAGE_NAME = "android"; + static final int SETTINGS_VERSION_NEW_ENCODING = 121; private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; @@ -71,27 +85,32 @@ final class SettingsState { public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1; public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 20000; - public static final String SYSTEM_PACKAGE_NAME = "android"; - public static final int VERSION_UNDEFINED = -1; private static final String TAG_SETTINGS = "settings"; private static final String TAG_SETTING = "setting"; private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet"; + private static final String ATTR_TAG = "tag"; + private static final String ATTR_TAG_BASE64 = "tagBase64"; private static final String ATTR_VERSION = "version"; private static final String ATTR_ID = "id"; private static final String ATTR_NAME = "name"; - /** Non-binary value will be written in this attribute. */ + /** + * Non-binary value will be written in this attributes. + */ private static final String ATTR_VALUE = "value"; + private static final String ATTR_DEFAULT_VALUE = "defaultValue"; /** - * KXmlSerializer won't like some characters. We encode such characters in base64 and - * store in this attribute. - * NOTE: A null value will have NEITHER ATTR_VALUE nor ATTR_VALUE_BASE64. + * KXmlSerializer won't like some characters. We encode such characters + * in base64 and store in this attribute. + * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64. */ private static final String ATTR_VALUE_BASE64 = "valueBase64"; + private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; // This was used in version 120 and before. private static final String NULL_VALUE_OLD_STYLE = "null"; @@ -101,11 +120,28 @@ final class SettingsState { private static final String HISTORICAL_OPERATION_DELETE = "delete"; private static final String HISTORICAL_OPERATION_PERSIST = "persist"; private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize"; + private static final String HISTORICAL_OPERATION_RESET = "reset"; + + private static final String SHELL_PACKAGE_NAME = "shell"; + private static final String ROOT_PACKAGE_NAME = "root"; + + private static final String NULL_VALUE = "null"; + + private static final Object sLock = new Object(); + + @GuardedBy("sLock") + private static final SparseIntArray sSystemUids = new SparseIntArray(); + + @GuardedBy("sLock") + private static Signature sSystemSignature; private final Object mLock; private final Handler mHandler; + @GuardedBy("mLock") + private final Context mContext; + @GuardedBy("mLock") private final ArrayMap mSettings = new ArrayMap<>(); @@ -118,7 +154,7 @@ final class SettingsState { @GuardedBy("mLock") private final File mStatePersistFile; - private final Setting mNullSetting = new Setting(null, null, null) { + private final Setting mNullSetting = new Setting(null, null, false, null, null) { @Override public boolean isNull() { return true; @@ -149,11 +185,12 @@ final class SettingsState { @GuardedBy("mLock") private int mNextHistoricalOpIdx; - public SettingsState(Object lock, File file, int key, int maxBytesPerAppPackage, - Looper looper) { + public SettingsState(Context context, Object lock, File file, int key, + int maxBytesPerAppPackage, Looper looper) { // It is important that we use the same lock as the settings provider // to ensure multiple mutations on this state are atomicaly persisted // as the async persistence should be blocked while we make changes. + mContext = context; mLock = lock; mStatePersistFile = file; mKey = key; @@ -241,37 +278,41 @@ final class SettingsState { } // The settings provider must hold its lock when calling here. - public boolean updateSettingLocked(String name, String value, String packageName) { + public boolean updateSettingLocked(String name, String value, String tag, + boolean makeValue, String packageName) { if (!hasSettingLocked(name)) { return false; } - return insertSettingLocked(name, value, packageName); + return insertSettingLocked(name, value, tag, makeValue, packageName); } // The settings provider must hold its lock when calling here. - public boolean insertSettingLocked(String name, String value, String packageName) { + public boolean insertSettingLocked(String name, String value, String tag, + boolean makeDefault, String packageName) { if (TextUtils.isEmpty(name)) { return false; } Setting oldState = mSettings.get(name); String oldValue = (oldState != null) ? oldState.value : null; + String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; Setting newState; if (oldState != null) { - if (!oldState.update(value, packageName)) { + if (!oldState.update(value, makeDefault, packageName, tag)) { return false; } newState = oldState; } else { - newState = new Setting(name, value, packageName); + newState = new Setting(name, value, makeDefault, packageName, tag); mSettings.put(name, newState); } addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); - updateMemoryUsagePerPackageLocked(packageName, oldValue, value); + updateMemoryUsagePerPackageLocked(packageName, oldValue, value, + oldDefaultValue, newState.getDefaultValue()); scheduleWriteIfNeededLocked(); @@ -292,7 +333,8 @@ final class SettingsState { Setting oldState = mSettings.remove(name); - updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, null); + updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, + null, oldState.defaultValue, null); addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); @@ -301,6 +343,35 @@ final class SettingsState { return true; } + // The settings provider must hold its lock when calling here. + public boolean resetSettingLocked(String name, String packageName) { + if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { + return false; + } + + Setting setting = mSettings.get(name); + + Setting oldSetting = new Setting(setting); + String oldValue = setting.getValue(); + String oldDefaultValue = setting.getDefaultValue(); + + if (!setting.reset(packageName)) { + return false; + } + + String newValue = setting.getValue(); + String newDefaultValue = setting.getDefaultValue(); + + updateMemoryUsagePerPackageLocked(setting.packageName, oldValue, + newValue, oldDefaultValue, newDefaultValue); + + addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); + + scheduleWriteIfNeededLocked(); + + return true; + } + // The settings provider must hold its lock when calling here. public void destroyLocked(Runnable callback) { mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); @@ -364,7 +435,7 @@ final class SettingsState { } private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, - String newValue) { + String newValue, String oldDefaultValue, String newDefaultValue) { if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { return; } @@ -375,7 +446,10 @@ final class SettingsState { final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; final int newValueSize = (newValue != null) ? newValue.length() : 0; - final int deltaSize = newValueSize - oldValueSize; + final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; + final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; + final int deltaSize = newValueSize + newDefaultValueSize + - oldValueSize - oldDefaultValueSize; Integer currentSize = mPackageToMemoryUsage.get(packageName); final int newSize = Math.max((currentSize != null) @@ -469,7 +543,8 @@ final class SettingsState { Setting setting = settings.valueAt(i); writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(), - setting.getValue(), setting.getPackageName()); + setting.getValue(), setting.getDefaultValue(), setting.getPackageName(), + setting.getTag(), setting.isDefaultSystemSet()); if (DEBUG_PERSISTENCE) { Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" + setting.getValue()); @@ -496,7 +571,8 @@ final class SettingsState { } static void writeSingleSetting(int version, XmlSerializer serializer, String id, - String name, String value, String packageName) throws IOException { + String name, String value, String defaultValue, String packageName, + String tag, boolean defaultSysSet) throws IOException { if (id == null || isBinary(id) || name == null || isBinary(name) || packageName == null || isBinary(packageName)) { // This shouldn't happen. @@ -505,38 +581,46 @@ final class SettingsState { serializer.startTag(null, TAG_SETTING); serializer.attribute(null, ATTR_ID, id); serializer.attribute(null, ATTR_NAME, name); - setValueAttribute(version, serializer, value); + setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64, + version, serializer, value); serializer.attribute(null, ATTR_PACKAGE, packageName); + if (defaultValue != null) { + setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64, + version, serializer, defaultValue); + serializer.attribute(null, ATTR_DEFAULT_SYS_SET, Boolean.toString(defaultSysSet)); + setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64, + version, serializer, tag); + } serializer.endTag(null, TAG_SETTING); } - static void setValueAttribute(int version, XmlSerializer serializer, String value) - throws IOException { + static void setValueAttribute(String attr, String attrBase64, int version, + XmlSerializer serializer, String value) throws IOException { if (version >= SETTINGS_VERSION_NEW_ENCODING) { if (value == null) { // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64. } else if (isBinary(value)) { - serializer.attribute(null, ATTR_VALUE_BASE64, base64Encode(value)); + serializer.attribute(null, attrBase64, base64Encode(value)); } else { - serializer.attribute(null, ATTR_VALUE, value); + serializer.attribute(null, attr, value); } } else { // Old encoding. if (value == null) { - serializer.attribute(null, ATTR_VALUE, NULL_VALUE_OLD_STYLE); + serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE); } else { - serializer.attribute(null, ATTR_VALUE, value); + serializer.attribute(null, attr, value); } } } - private String getValueAttribute(XmlPullParser parser) { + private String getValueAttribute(XmlPullParser parser, String attr, String base64Attr) { if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) { - final String value = parser.getAttributeValue(null, ATTR_VALUE); + final String value = parser.getAttributeValue(null, attr); if (value != null) { return value; } - final String base64 = parser.getAttributeValue(null, ATTR_VALUE_BASE64); + final String base64 = parser.getAttributeValue(null, base64Attr); if (base64 != null) { return base64Decode(base64); } @@ -544,7 +628,7 @@ final class SettingsState { return null; } else { // Old encoding. - final String stored = parser.getAttributeValue(null, ATTR_VALUE); + final String stored = parser.getAttributeValue(null, attr); if (NULL_VALUE_OLD_STYLE.equals(stored)) { return null; } else { @@ -575,7 +659,7 @@ final class SettingsState { } catch (XmlPullParserException | IOException e) { String message = "Failed parsing settings file: " + mStatePersistFile; Slog.wtf(LOG_TAG, message); - throw new IllegalStateException(message , e); + throw new IllegalStateException(message, e); } finally { IoUtils.closeQuietly(in); } @@ -615,9 +699,19 @@ final class SettingsState { if (tagName.equals(TAG_SETTING)) { String id = parser.getAttributeValue(null, ATTR_ID); String name = parser.getAttributeValue(null, ATTR_NAME); - String value = getValueAttribute(parser); + String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64); String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); - mSettings.put(name, new Setting(name, value, packageName, id)); + String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE, + ATTR_DEFAULT_VALUE_BASE64); + String tag = null; + boolean fromSystem = false; + if (defaultValue != null) { + fromSystem = Boolean.parseBoolean(parser.getAttributeValue( + null, ATTR_DEFAULT_SYS_SET)); + tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); + } + mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, + fromSystem, id)); if (DEBUG_PERSISTENCE) { Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value); @@ -664,37 +758,54 @@ final class SettingsState { class Setting { private String name; private String value; + private String defaultValue; private String packageName; private String id; + private String tag; + // Whether the default is set by the system + private boolean defaultSystemSet; public Setting(Setting other) { name = other.name; value = other.value; + defaultValue = other.defaultValue; packageName = other.packageName; id = other.id; + defaultSystemSet = other.defaultSystemSet; + tag = other.tag; } - public Setting(String name, String value, String packageName) { - init(name, value, packageName, String.valueOf(mNextId++)); + public Setting(String name, String value, boolean makeDefault, String packageName, + String tag) { + this.name = name; + update(value, makeDefault, packageName, tag); } - public Setting(String name, String value, String packageName, String id) { + public Setting(String name, String value, String defaultValue, + String packageName, String tag, boolean fromSystem, String id) { mNextId = Math.max(mNextId, Long.parseLong(id) + 1); - init(name, value, packageName, id); + if (NULL_VALUE.equals(value)) { + value = null; + } + init(name, value, tag, defaultValue, packageName, fromSystem, id); } - private void init(String name, String value, String packageName, String id) { + private void init(String name, String value, String tag, String defaultValue, + String packageName, boolean fromSystem, String id) { this.name = name; this.value = value; + this.tag = tag; + this.defaultValue = defaultValue; this.packageName = packageName; this.id = id; + this.defaultSystemSet = fromSystem; } public String getName() { return name; } - public int getkey() { + public int getKey() { return mKey; } @@ -702,10 +813,22 @@ final class SettingsState { return value; } + public String getTag() { + return tag; + } + + public String getDefaultValue() { + return defaultValue; + } + public String getPackageName() { return packageName; } + public boolean isDefaultSystemSet() { + return defaultSystemSet; + } + public String getId() { return id; } @@ -714,18 +837,62 @@ final class SettingsState { return false; } - public boolean update(String value, String packageName) { - if (Objects.equal(value, this.value)) { + /** @return whether the value changed */ + public boolean reset(String packageName) { + return update(this.defaultValue, false, packageName, null); + } + + public boolean update(String value, boolean setDefault, String packageName, String tag) { + if (NULL_VALUE.equals(value)) { + value = null; + } + + final boolean callerSystem = !isNull() && isSystemPackage(mContext, packageName); + // Settings set by the system are always defaults. + if (callerSystem) { + setDefault = true; + } + + String defaultValue = this.defaultValue; + boolean defaultSystemSet = this.defaultSystemSet; + if (setDefault) { + if (!Objects.equal(value, this.defaultValue) + && (!defaultSystemSet || callerSystem)) { + defaultValue = value; + // Default null means no default, so the tag is irrelevant + // since it is used to reset a settings subset their defaults. + // Also it is irrelevant if the system set the canonical default. + if (defaultValue == null) { + tag = null; + defaultSystemSet = false; + } + } + if (!defaultSystemSet && value != null) { + if (callerSystem) { + defaultSystemSet = true; + } + } + } + + // Is something gonna change? + if (Objects.equal(value, this.value) + && Objects.equal(defaultValue, this.defaultValue) + && Objects.equal(packageName, this.packageName) + && Objects.equal(tag, this.tag) + && defaultSystemSet == this.defaultSystemSet) { return false; } - this.value = value; - this.packageName = packageName; - this.id = String.valueOf(mNextId++); + + init(name, value, tag, defaultValue, packageName, defaultSystemSet, + String.valueOf(mNextId++)); return true; } public String toString() { - return "Setting{name=" + value + " from " + packageName + "}"; + return "Setting{name=" + name + " value=" + value + + (defaultValue != null ? " default=" + defaultValue : "") + + " packageName=" + packageName + " tag=" + tag + + " defaultSystemSet=" + defaultSystemSet + "}"; } } @@ -782,4 +949,98 @@ final class SettingsState { } return sb.toString(); } + + public static boolean isSystemPackage(Context context, String packageName) { + synchronized (sLock) { + if (SYSTEM_PACKAGE_NAME.equals(packageName)) { + return true; + } + + // Shell and Root are not considered a part of the system + if (SHELL_PACKAGE_NAME.equals(packageName) + || ROOT_PACKAGE_NAME.equals(packageName)) { + return false; + } + + // Native services running as a special UID get a pass + final int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); + if (callingAppId < FIRST_APPLICATION_UID) { + sSystemUids.put(callingAppId, callingAppId); + return true; + } + + // While some callers may have permissions to manipulate cross user + // settings or some settings are stored in the parent of a managed + // profile for the purpose of determining whether the other end is a + // system component we need to use the user id of the caller for + // pulling information about the caller from the package manager. + final int callingUserId = UserHandle.getCallingUserId(); + + final long identity = Binder.clearCallingIdentity(); + try { + final int uid; + try { + uid = context.getPackageManager().getPackageUidAsUser(packageName, 0, + callingUserId); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + // If the system or a special system UID (like telephony), done. + if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) { + sSystemUids.put(uid, uid); + return true; + } + + // If already known system component, done. + if (sSystemUids.indexOfKey(uid) >= 0) { + return true; + } + + // If SetupWizard, done. + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) { + sSystemUids.put(uid, uid); + return true; + } + + // If a persistent system app, done. + PackageInfo packageInfo; + try { + packageInfo = context.getPackageManager().getPackageInfoAsUser( + packageName, PackageManager.GET_SIGNATURES, callingUserId); + if ((packageInfo.applicationInfo.flags + & ApplicationInfo.FLAG_PERSISTENT) != 0 + && (packageInfo.applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0) { + sSystemUids.put(uid, uid); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + // Last check if system signed. + if (sSystemSignature == null) { + try { + sSystemSignature = context.getPackageManager().getPackageInfoAsUser( + SYSTEM_PACKAGE_NAME, PackageManager.GET_SIGNATURES, + UserHandle.USER_SYSTEM).signatures[0]; + } catch (PackageManager.NameNotFoundException e) { + /* impossible */ + return false; + } + } + if (sSystemSignature.equals(packageInfo.signatures[0])) { + sSystemUids.put(uid, uid); + return true; + } + } finally { + Binder.restoreCallingIdentity(identity); + } + + return false; + } + } } diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk index ef863e7df0c2e..918410ea72777 100644 --- a/packages/SettingsProvider/test/Android.mk +++ b/packages/SettingsProvider/test/Android.mk @@ -2,11 +2,15 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := tests + # Note we statically link SettingsState 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.) LOCAL_SRC_FILES := $(call all-subdir-java-files) \ ../src/com/android/providers/settings/SettingsState.java +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test + LOCAL_PACKAGE_NAME := SettingsProviderTest LOCAL_MODULE_TAGS := tests diff --git a/packages/SettingsProvider/test/AndroidManifest.xml b/packages/SettingsProvider/test/AndroidManifest.xml index 7a86b5fc321d1..71e0b153daa97 100644 --- a/packages/SettingsProvider/test/AndroidManifest.xml +++ b/packages/SettingsProvider/test/AndroidManifest.xml @@ -29,7 +29,8 @@ + diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java index 8e56f476ace7e..0454b51211398 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java @@ -25,14 +25,21 @@ import android.net.Uri; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.test.AndroidTestCase; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import libcore.io.Streams; +import org.junit.runner.RunWith; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.List; /** * Base class for the SettingContentProvider tests. */ -abstract class BaseSettingsProviderTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +abstract class BaseSettingsProviderTest { protected static final int SETTING_TYPE_GLOBAL = 1; protected static final int SETTING_TYPE_SECURE = 2; protected static final int SETTING_TYPE_SYSTEM = 3; @@ -48,23 +55,7 @@ abstract class BaseSettingsProviderTest extends AndroidTestCase { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE }; - protected int mSecondaryUserId = UserHandle.USER_SYSTEM; - - @Override - public void setContext(Context context) { - super.setContext(context); - - UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - List users = userManager.getUsers(); - final int userCount = users.size(); - for (int i = 0; i < userCount; i++) { - UserInfo user = users.get(i); - if (!user.isPrimary() && !user.isManagedProfile()) { - mSecondaryUserId = user.id; - break; - } - } - } + private int mSecondaryUserId = Integer.MIN_VALUE; protected void setStringViaFrontEndApiSetting(int type, String name, String value, int userId) { ContentResolver contentResolver = getContext().getContentResolver(); @@ -176,6 +167,163 @@ abstract class BaseSettingsProviderTest extends AndroidTestCase { return null; } + protected static void resetSettingsViaShell(int type, int resetMode) throws IOException { + final String modeString; + switch (resetMode) { + case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: { + modeString = "untrusted_defaults"; + } break; + + case Settings.RESET_MODE_UNTRUSTED_CHANGES: { + modeString = "untrusted_clear"; + } break; + + case Settings.RESET_MODE_TRUSTED_DEFAULTS: { + modeString = "trusted_defaults"; + } break; + + default: { + throw new IllegalArgumentException("Invalid reset mode: " + resetMode); + } + } + + switch (type) { + case SETTING_TYPE_GLOBAL: { + executeShellCommand("settings reset global " + modeString); + } break; + + case SETTING_TYPE_SECURE: { + executeShellCommand("settings reset secure " + modeString); + } break; + + default: { + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } + + protected static void resetToDefaultsViaShell(int type, String packageName) throws IOException { + resetToDefaultsViaShell(type, packageName, null); + } + + protected static void resetToDefaultsViaShell(int type, String packageName, String token) + throws IOException { + switch (type) { + case SETTING_TYPE_GLOBAL: { + executeShellCommand("settings reset global " + packageName + " " + token); + } break; + + case SETTING_TYPE_SECURE: { + executeShellCommand("settings reset secure " + packageName + " " + token); + } break; + + case SETTING_TYPE_SYSTEM: { + executeShellCommand("settings reset system " + packageName + " " + token); + } break; + + default: { + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } + + protected String getSetting(int type, String name) { + switch (type) { + case SETTING_TYPE_GLOBAL: { + return Settings.Global.getString(getContext().getContentResolver(), name); + } + + case SETTING_TYPE_SECURE: { + return Settings.Secure.getString(getContext().getContentResolver(), name); + } + + case SETTING_TYPE_SYSTEM: { + return Settings.System.getString(getContext().getContentResolver(), name); + } + + default: { + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } + + protected void putSetting(int type, String name, String value) { + switch (type) { + case SETTING_TYPE_GLOBAL: { + Settings.Global.putString(getContext().getContentResolver(), name, value); + } break; + + case SETTING_TYPE_SECURE: { + Settings.Secure.putString(getContext().getContentResolver(), name, value); + } break; + + case SETTING_TYPE_SYSTEM: { + Settings.System.putString(getContext().getContentResolver(), name, value); + } break; + + default: { + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } + + protected static void setSettingViaShell(int type, String name, String value, + boolean makeDefault) throws IOException { + setSettingViaShell(type, name, value, null, makeDefault); + } + + protected static void setSettingViaShell(int type, String name, String value, + String token, boolean makeDefault) throws IOException { + switch (type) { + case SETTING_TYPE_GLOBAL: { + executeShellCommand("settings put global " + name + " " + + value + (token != null ? " " + token : "") + + (makeDefault ? " default" : "")); + + } break; + + case SETTING_TYPE_SECURE: { + executeShellCommand("settings put secure " + name + " " + + value + (token != null ? " " + token : "") + + (makeDefault ? " default" : "")); + } break; + + case SETTING_TYPE_SYSTEM: { + executeShellCommand("settings put system " + name + " " + + value + (token != null ? " " + token : "") + + (makeDefault ? " default" : "")); + } break; + + default: { + throw new IllegalArgumentException("Invalid type: " + type); + } + } + } + + protected Context getContext() { + return InstrumentationRegistry.getContext(); + } + + protected int getSecondaryUserId() { + if (mSecondaryUserId == Integer.MIN_VALUE) { + UserManager userManager = (UserManager) getContext() + .getSystemService(Context.USER_SERVICE); + List users = userManager.getUsers(); + final int userCount = users.size(); + for (int i = 0; i < userCount; i++) { + UserInfo user = users.get(i); + if (!user.isPrimary() && !user.isManagedProfile()) { + mSecondaryUserId = user.id; + return mSecondaryUserId; + } + } + } + if (mSecondaryUserId == Integer.MIN_VALUE) { + mSecondaryUserId = UserHandle.USER_SYSTEM; + } + return mSecondaryUserId; + } + protected static Uri getBaseUriForType(int type) { switch (type) { case SETTING_TYPE_GLOBAL: { @@ -195,4 +343,10 @@ abstract class BaseSettingsProviderTest extends AndroidTestCase { } } } + + protected static void executeShellCommand(String command) throws IOException { + InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation() + .getUiAutomation().executeShellCommand(command).getFileDescriptor()); + Streams.readFully(is); + } } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java index a09d5fe68a6df..d34b9ed5a6ac7 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderPerformanceTest.java @@ -16,9 +16,13 @@ package com.android.providers.settings; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; +import org.junit.Test; /** * Performance tests for the SettingContentProvider. @@ -32,6 +36,7 @@ public class SettingsProviderPerformanceTest extends BaseSettingsProviderTest { private static final long MAX_AVERAGE_SET_AND_GET_SETTING_DURATION_MILLIS = 20; + @Test public void testSetAndGetPerformanceForGlobalViaFrontEndApi() throws Exception { // Start with a clean slate. insertStringViaProviderApi(SETTING_TYPE_GLOBAL, @@ -76,6 +81,7 @@ public class SettingsProviderPerformanceTest extends BaseSettingsProviderTest { < MAX_AVERAGE_SET_AND_GET_SETTING_DURATION_MILLIS); } + @Test public void testSetAndGetPerformanceForGlobalViaProviderApi() throws Exception { // Start with a clean slate. deleteStringViaProviderApi(SETTING_TYPE_GLOBAL, FAKE_SETTING_NAME); diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java index 8ca1b461b8e0d..e2a8fba2f1c7c 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java @@ -16,6 +16,11 @@ package com.android.providers.settings; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertSame; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.fail; + import android.content.ContentResolver; import android.content.ContentValues; import android.database.ContentObserver; @@ -27,6 +32,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import org.junit.Test; import java.util.concurrent.atomic.AtomicBoolean; @@ -46,57 +52,70 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { private final Object mLock = new Object(); + @Test public void testSetAndGetGlobalViaFrontEndApiForSystemUser() throws Exception { performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, UserHandle.USER_SYSTEM); } + @Test public void testSetAndGetGlobalViaFrontEndApiForNonSystemUser() throws Exception { - if (mSecondaryUserId == UserHandle.USER_SYSTEM) { + final int secondaryUserId = getSecondaryUserId(); + if (secondaryUserId == UserHandle.USER_SYSTEM) { Log.w(LOG_TAG, "No secondary user. Skipping " + "testSetAndGetGlobalViaFrontEndApiForNonSystemUser"); return; } - performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, mSecondaryUserId); + performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_GLOBAL, secondaryUserId); } + @Test public void testSetAndGetSecureViaFrontEndApiForSystemUser() throws Exception { performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, UserHandle.USER_SYSTEM); } + @Test public void testSetAndGetSecureViaFrontEndApiForNonSystemUser() throws Exception { - if (mSecondaryUserId == UserHandle.USER_SYSTEM) { + final int secondaryUserId = getSecondaryUserId(); + if (secondaryUserId == UserHandle.USER_SYSTEM) { Log.w(LOG_TAG, "No secondary user. Skipping " + "testSetAndGetSecureViaFrontEndApiForNonSystemUser"); return; } - performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, mSecondaryUserId); + performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SECURE, secondaryUserId); } + @Test public void testSetAndGetSystemViaFrontEndApiForSystemUser() throws Exception { performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, UserHandle.USER_SYSTEM); } + @Test public void testSetAndGetSystemViaFrontEndApiForNonSystemUser() throws Exception { - if (mSecondaryUserId == UserHandle.USER_SYSTEM) { + final int secondaryUserId = getSecondaryUserId(); + if (secondaryUserId == UserHandle.USER_SYSTEM) { Log.w(LOG_TAG, "No secondary user. Skipping " + "testSetAndGetSystemViaFrontEndApiForNonSystemUser"); return; } - performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, mSecondaryUserId); + performSetAndGetSettingTestViaFrontEndApi(SETTING_TYPE_SYSTEM, secondaryUserId); } + @Test public void testSetAndGetGlobalViaProviderApi() throws Exception { performSetAndGetSettingTestViaProviderApi(SETTING_TYPE_GLOBAL); } + @Test public void testSetAndGetSecureViaProviderApi() throws Exception { performSetAndGetSettingTestViaProviderApi(SETTING_TYPE_SECURE); } + @Test public void testSetAndGetSystemViaProviderApi() throws Exception { performSetAndGetSettingTestViaProviderApi(SETTING_TYPE_SYSTEM); } + @Test public void testSelectAllGlobalViaProviderApi() throws Exception { setSettingViaProviderApiAndAssertSuccessfulChange(SETTING_TYPE_GLOBAL, FAKE_SETTING_NAME, FAKE_SETTING_VALUE, false); @@ -108,6 +127,7 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } } + @Test public void testSelectAllSecureViaProviderApi() throws Exception { setSettingViaProviderApiAndAssertSuccessfulChange(SETTING_TYPE_SECURE, FAKE_SETTING_NAME, FAKE_SETTING_VALUE, false); @@ -119,6 +139,7 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } } + @Test public void testSelectAllSystemViaProviderApi() throws Exception { setSettingViaProviderApiAndAssertSuccessfulChange(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME, FAKE_SETTING_VALUE, true); @@ -130,30 +151,37 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } } + @Test public void testQueryUpdateDeleteGlobalViaProviderApi() throws Exception { doTestQueryUpdateDeleteGlobalViaProviderApiForType(SETTING_TYPE_GLOBAL); } + @Test public void testQueryUpdateDeleteSecureViaProviderApi() throws Exception { doTestQueryUpdateDeleteGlobalViaProviderApiForType(SETTING_TYPE_SECURE); } + @Test public void testQueryUpdateDeleteSystemViaProviderApi() throws Exception { doTestQueryUpdateDeleteGlobalViaProviderApiForType(SETTING_TYPE_SYSTEM); } + @Test public void testBulkInsertGlobalViaProviderApi() throws Exception { toTestBulkInsertViaProviderApiForType(SETTING_TYPE_GLOBAL); } + @Test public void testBulkInsertSystemViaProviderApi() throws Exception { toTestBulkInsertViaProviderApiForType(SETTING_TYPE_SYSTEM); } + @Test public void testBulkInsertSecureViaProviderApi() throws Exception { toTestBulkInsertViaProviderApiForType(SETTING_TYPE_SECURE); } + @Test public void testAppCannotRunsSystemOutOfMemoryWritingSystemSettings() throws Exception { int insertedCount = 0; try { @@ -164,6 +192,8 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } fail("Adding app specific settings must be bound."); } catch (Exception e) { + // expected + } finally { for (; insertedCount >= 0; insertedCount--) { Log.w(LOG_TAG, "Removing app specific setting: " + insertedCount); deleteStringViaProviderApi(SETTING_TYPE_SYSTEM, @@ -172,18 +202,22 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } } + @Test public void testQueryStringInBracketsGlobalViaProviderApiForType() throws Exception { doTestQueryStringInBracketsViaProviderApiForType(SETTING_TYPE_GLOBAL); } + @Test public void testQueryStringInBracketsSecureViaProviderApiForType() throws Exception { doTestQueryStringInBracketsViaProviderApiForType(SETTING_TYPE_SECURE); } + @Test public void testQueryStringInBracketsSystemViaProviderApiForType() throws Exception { doTestQueryStringInBracketsViaProviderApiForType(SETTING_TYPE_SYSTEM); } + @Test public void testQueryStringWithAppendedNameToUriViaProviderApi() throws Exception { // Make sure we have a clean slate. deleteStringViaProviderApi(SETTING_TYPE_SYSTEM, FAKE_SETTING_NAME); @@ -206,6 +240,228 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } } + @Test + public void testResetModePackageDefaultsGlobal() throws Exception { + testResetModePackageDefaultsCommon(SETTING_TYPE_GLOBAL); + } + + @Test + public void testResetModePackageDefaultsSecure() throws Exception { + testResetModePackageDefaultsCommon(SETTING_TYPE_SECURE); + } + + private void testResetModePackageDefaultsCommon(int type) throws Exception { + // Make sure we have a clean slate. + setSettingViaShell(type, FAKE_SETTING_NAME, null, true); + try { + // Set a value but don't make it the default + setSettingViaShell(type, FAKE_SETTING_NAME, + FAKE_SETTING_VALUE, false); + + // Reset the changes made by the "shell/root" package + resetToDefaultsViaShell(type, "shell"); + resetToDefaultsViaShell(type, "root"); + + // Make sure the old APIs don't set defaults + assertNull(getSetting(type, FAKE_SETTING_NAME)); + + // Set a value and make it the default + setSettingViaShell(type, FAKE_SETTING_NAME, + FAKE_SETTING_VALUE, true); + // Change the setting from the default + setSettingViaShell(type, FAKE_SETTING_NAME, + FAKE_SETTING_VALUE_2, false); + + // Reset the changes made by this package + resetToDefaultsViaShell(type, "shell"); + resetToDefaultsViaShell(type, "root"); + + // Make sure the old APIs don't set defaults + assertEquals(FAKE_SETTING_VALUE, getSetting(type, FAKE_SETTING_NAME)); + } finally { + // Make sure we have a clean slate. + setSettingViaShell(type, FAKE_SETTING_NAME, null, true); + } + } + + @Test + public void testResetModePackageDefaultsWithTokensGlobal() throws Exception { + testResetModePackageDefaultsWithTokensCommon(SETTING_TYPE_GLOBAL); + } + + @Test + public void testResetModePackageDefaultsWithTokensSecure() throws Exception { + testResetModePackageDefaultsWithTokensCommon(SETTING_TYPE_SECURE); + } + + private void testResetModePackageDefaultsWithTokensCommon(int type) throws Exception { + // Make sure we have a clean slate. + setSettingViaShell(type, FAKE_SETTING_NAME, null, true); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + try { + // Set a default value + setSettingViaShell(type, FAKE_SETTING_NAME, + FAKE_SETTING_VALUE, true); + // Change the default and associate a token + setSettingViaShell(type, FAKE_SETTING_NAME, + FAKE_SETTING_VALUE_2, "TOKEN1", false); + + // Set a default value + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE, "TOKEN2", true); + // Change the default and associate a token + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE_2, "TOKEN2", false); + + // Reset settings associated with TOKEN1 + resetToDefaultsViaShell(type, "shell", "TOKEN1"); + resetToDefaultsViaShell(type, "root", "TOKEN1"); + + // Make sure TOKEN1 settings are reset + assertEquals(FAKE_SETTING_VALUE, getSetting(type, + FAKE_SETTING_NAME)); + + // Make sure TOKEN2 settings are not reset + assertEquals(FAKE_SETTING_VALUE_2, getSetting(type, + FAKE_SETTING_NAME_1)); + + // Reset settings associated with TOKEN2 + resetToDefaultsViaShell(type, "shell", "TOKEN2"); + resetToDefaultsViaShell(type, "root", "TOKEN2"); + + // Make sure TOKEN2 settings are reset + assertEquals(FAKE_SETTING_VALUE, getSetting(type, + FAKE_SETTING_NAME_1)); + } finally { + // Make sure we have a clean slate. + setSettingViaShell(type, FAKE_SETTING_NAME, null, true); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + } + } + + @Test + public void testResetModeUntrustedDefaultsGlobal() throws Exception { + testResetModeUntrustedDefaultsCommon(SETTING_TYPE_GLOBAL); + } + + @Test + public void testResetModeUntrustedDefaultsSecure() throws Exception { + testResetModeUntrustedDefaultsCommon(SETTING_TYPE_SECURE); + } + + private void testResetModeUntrustedDefaultsCommon(int type) throws Exception { + // Make sure we have a clean slate. + putSetting(type, FAKE_SETTING_NAME, null); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + try { + // Set a default setting as a trusted component + putSetting(type, FAKE_SETTING_NAME, FAKE_SETTING_VALUE); + // Change the setting as a trusted component + putSetting(type, FAKE_SETTING_NAME, FAKE_SETTING_VALUE_2); + + // Set a default setting as an untrusted component + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE, true); + // Change the setting as an untrusted component + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE_2, false); + + // Reset the untrusted changes to defaults + resetSettingsViaShell(type, + Settings.RESET_MODE_UNTRUSTED_DEFAULTS); + + // Check whether only the untrusted changes set to defaults + assertEquals(FAKE_SETTING_VALUE_2, getSetting(type, FAKE_SETTING_NAME)); + assertEquals(FAKE_SETTING_VALUE, getSetting(type, FAKE_SETTING_NAME_1)); + } finally { + // Make sure we have a clean slate. + putSetting(type, FAKE_SETTING_NAME, null); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + } + } + + @Test + public void testResetModeUntrustedClearGlobal() throws Exception { + testResetModeUntrustedClearCommon(SETTING_TYPE_GLOBAL); + } + + @Test + public void testResetModeUntrustedClearSecure() throws Exception { + testResetModeUntrustedClearCommon(SETTING_TYPE_SECURE); + } + + private void testResetModeUntrustedClearCommon(int type) throws Exception { + // Make sure we have a clean slate. + putSetting(type, FAKE_SETTING_NAME, null); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + try { + // Set a default setting as a trusted component + putSetting(type, FAKE_SETTING_NAME, FAKE_SETTING_VALUE); + // Change the setting as a trusted component + putSetting(type, FAKE_SETTING_NAME, FAKE_SETTING_VALUE_2); + + // Set a default setting as an untrusted component + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE, true); + // Change the setting as an untrusted component + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE_2, false); + + // Clear the untrusted changes + resetSettingsViaShell(type, + Settings.RESET_MODE_UNTRUSTED_CHANGES); + + // Check whether only the untrusted changes set to defaults + assertEquals(FAKE_SETTING_VALUE_2, getSetting(type, FAKE_SETTING_NAME)); + assertNull(getSetting(type, FAKE_SETTING_NAME_1)); + } finally { + // Make sure we have a clean slate. + putSetting(type, FAKE_SETTING_NAME, null); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + } + } + + @Test + public void testResetModeTrustedDefaultsGlobal() throws Exception { + testResetModeTrustedDefaultsCommon(SETTING_TYPE_GLOBAL); + } + + @Test + public void testResetModeTrustedDefaultsSecure() throws Exception { + testResetModeTrustedDefaultsCommon(SETTING_TYPE_SECURE); + } + + private void testResetModeTrustedDefaultsCommon(int type) throws Exception { + // Make sure we have a clean slate. + putSetting(type, FAKE_SETTING_NAME, null); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + try { + // Set a default setting as a trusted component + putSetting(type, FAKE_SETTING_NAME, FAKE_SETTING_VALUE); + // Change the setting as a trusted component + setSettingViaShell(type, FAKE_SETTING_NAME, FAKE_SETTING_VALUE_2, false); + + // Set a default setting as an untrusted component + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE, true); + // Change the setting as an untrusted component + setSettingViaShell(type, FAKE_SETTING_NAME_1, + FAKE_SETTING_VALUE_2, false); + + // Reset to trusted defaults + resetSettingsViaShell(type, + Settings.RESET_MODE_TRUSTED_DEFAULTS); + + // Check whether snapped to trusted defaults + assertEquals(FAKE_SETTING_VALUE, getSetting(type, FAKE_SETTING_NAME)); + assertNull(getSetting(type, FAKE_SETTING_NAME_1)); + } finally { + // Make sure we have a clean slate. + putSetting(type, FAKE_SETTING_NAME, null); + setSettingViaShell(type, FAKE_SETTING_NAME_1, null, true); + } + } + private void doTestQueryStringInBracketsViaProviderApiForType(int type) { // Make sure we have a clean slate. deleteStringViaProviderApi(type, FAKE_SETTING_NAME); @@ -341,22 +597,16 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { private void setSettingViaFrontEndApiAndAssertSuccessfulChange(final int type, final String name, final String value, final int userId) throws Exception { - setSettingAndAssertSuccessfulChange(new Runnable() { - @Override - public void run() { - setStringViaFrontEndApiSetting(type, name, value, userId); - } + setSettingAndAssertSuccessfulChange(() -> { + setStringViaFrontEndApiSetting(type, name, value, userId); }, type, name, value, userId); } private void setSettingViaProviderApiAndAssertSuccessfulChange(final int type, final String name, final String value, final boolean withTableRowUri) throws Exception { - setSettingAndAssertSuccessfulChange(new Runnable() { - @Override - public void run() { - insertStringViaProviderApi(type, name, value, withTableRowUri); - } + setSettingAndAssertSuccessfulChange(() -> { + insertStringViaProviderApi(type, name, value, withTableRowUri); }, type, name, value, UserHandle.USER_SYSTEM); } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 9964467fb5eca..3f68554ffe879 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -99,10 +99,10 @@ public class SettingsStateTest extends AndroidTestCase { checkWriteSingleSetting(serializer, CRAZY_STRING, null); SettingsState.writeSingleSetting( SettingsState.SETTINGS_VERSION_NEW_ENCODING, - serializer, null, "k", "v", "package"); + serializer, null, "k", "v", null, "package", null, false); SettingsState.writeSingleSetting( SettingsState.SETTINGS_VERSION_NEW_ENCODING, - serializer, "1", "k", "v", null); + serializer, "1", "k", "v", null, null, null, false); } private void checkWriteSingleSetting(XmlSerializer serializer, String key, String value) @@ -115,7 +115,7 @@ public class SettingsStateTest extends AndroidTestCase { // Make sure the XML serializer won't crash. SettingsState.writeSingleSetting( SettingsState.SETTINGS_VERSION_NEW_ENCODING, - serializer, "1", key, value, "package"); + serializer, "1", key, value, null, "package", null, false); } /** @@ -126,19 +126,19 @@ public class SettingsStateTest extends AndroidTestCase { file.delete(); final Object lock = new Object(); - final SettingsState ssWriter = new SettingsState(lock, file, 1, + final SettingsState ssWriter = new SettingsState(getContext(), lock, file, 1, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); - ssWriter.insertSettingLocked("k1", "\u0000", "package"); - ssWriter.insertSettingLocked("k2", "abc", "p2"); - ssWriter.insertSettingLocked("k3", null, "p2"); - ssWriter.insertSettingLocked("k4", CRAZY_STRING, "p3"); + ssWriter.insertSettingLocked("k1", "\u0000", null, false, "package"); + ssWriter.insertSettingLocked("k2", "abc", null, false, "p2"); + ssWriter.insertSettingLocked("k3", null, null, false, "p2"); + ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3"); synchronized (lock) { ssWriter.persistSyncLocked(); } - final SettingsState ssReader = new SettingsState(lock, file, 1, + final SettingsState ssReader = new SettingsState(getContext(), lock, file, 1, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue()); @@ -165,7 +165,7 @@ public class SettingsStateTest extends AndroidTestCase { ""); os.close(); - final SettingsState ss = new SettingsState(lock, file, 1, + final SettingsState ss = new SettingsState(getContext(), lock, file, 1, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { SettingsState.Setting s; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 58783ad8976ed..52528c0c8157f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21616,6 +21616,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); PackageManagerService.this.requestEphemeralResolutionPhaseTwo( responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId); } + + public String getSetupWizardPackageName() { + return mSetupWizardPackage; + } } @Override