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