Loading core/java/android/provider/Settings.java +7 −0 Original line number Diff line number Diff line Loading @@ -10308,6 +10308,13 @@ public final class Settings { @Readable public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze"; /** * 1 if it is allowed to remove the primary GAIA account. 0 by default. * @hide */ public static final String ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS = "allow_primary_gaia_account_removal_for_tests"; /** * List of TV inputs that are currently hidden. This is a string * containing the IDs of all hidden TV inputs. Each ID is encoded by Loading core/res/res/values/config.xml +9 −0 Original line number Diff line number Diff line Loading @@ -4372,6 +4372,15 @@ UI is handled by ActivityManagerService --> <bool name="config_customUserSwitchUi">false</bool> <!-- Flag specifying whether the first account added can be removed or renamed. By default, this ability is enabled. When false, user will not be able to remove the first account. --> <bool name="config_canRemoveFirstAccount">true</bool> <!-- Used together with config_canRemoveOrRenameFirstAccount when set to false. By default, this is blank. Check if the first account is of this account type. If it is, then disable remove/rename. --> <string name="config_accountTypeToKeepFirstAccount"></string> <!-- A array of regex to treat a SMS as VVM SMS if the message body matches. Each item represents an entry, which consists of two parts: a comma (,) separated list of MCCMNC the regex applies to, followed by a semicolon (;), and Loading core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1723,6 +1723,8 @@ <java-symbol type="bool" name="config_startDreamImmediatelyOnDock" /> <java-symbol type="bool" name="config_carDockEnablesAccelerometer" /> <java-symbol type="bool" name="config_customUserSwitchUi" /> <java-symbol type="bool" name="config_canRemoveFirstAccount" /> <java-symbol type="string" name="config_accountTypeToKeepFirstAccount" /> <java-symbol type="bool" name="config_deskDockEnablesAccelerometer" /> <java-symbol type="bool" name="config_disableMenuKeyInLockScreen" /> <java-symbol type="bool" name="config_enableCarDockHomeLaunch" /> Loading packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -691,6 +691,7 @@ public class SettingsBackupTest { newHashSet( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O. Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS, Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS, Settings.Secure.ALWAYS_ON_VPN_APP, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, Loading services/core/java/com/android/server/accounts/AccountManagerService.java +59 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import android.util.EventLog; Loading Loading @@ -2346,6 +2347,18 @@ public class AccountManagerService } return; } if (isFirstAccountRemovalDisabled(account)) { try { response.onError( AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, "User cannot remove the first " + account.type + " account on the device."); } catch (RemoteException re) { Log.w(TAG, "RemoteException while removing account", re); } return; } final long identityToken = clearCallingIdentity(); UserAccounts accounts = getUserAccounts(userId); cancelNotification(getSigninRequiredNotificationId(accounts, account), accounts); Loading Loading @@ -2395,6 +2408,10 @@ public class AccountManagerService account.type); throw new SecurityException(msg); } if (isFirstAccountRemovalDisabled(account)) { Log.e(TAG, "Cannot remove the first " + account.type + " account on the device."); return false; } UserAccounts accounts = getUserAccountsForCaller(); final long accountId = accounts.accountsDb.findDeAccountId(account); logRecord( Loading Loading @@ -6426,6 +6443,48 @@ public class AccountManagerService } } /** * Returns true if the config_canRemoveOrRenameFirstUser is false, and the given account type * matches the one provided by config_accountTypeToKeepFirstUser. */ private boolean isFirstAccountRemovalDisabled(Account account) { // Skip if not targeting the first user. int userId = UserHandle.getCallingUserId(); if (userId != 0) { return false; } // Skip if we are allowed to remove/rename first account. if (mContext.getResources() .getBoolean(com.android.internal.R.bool.config_canRemoveFirstAccount)) { return false; } // Skip if needed for testing. if (Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS, 0 /* default */, 0 /* userHandle */) != 0) { return false; } // Skip if not targeting desired account. String typeToKeep = mContext.getResources() .getString( com.android.internal.R.string.config_accountTypeToKeepFirstAccount); if (typeToKeep.isEmpty() || !typeToKeep.equals(account.type)) { return false; } // Only restrict first account. UserAccounts accounts = getUserAccounts(0 /* userId */); Account[] accountsOfType = getAccountsFromCache(accounts, typeToKeep, Process.SYSTEM_UID, "android" /* packageName */, false); return accountsOfType.length > 0 && accountsOfType[0].equals(account); } private final class AccountManagerInternalImpl extends AccountManagerInternal { private final Object mLock = new Object(); Loading Loading
core/java/android/provider/Settings.java +7 −0 Original line number Diff line number Diff line Loading @@ -10308,6 +10308,13 @@ public final class Settings { @Readable public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze"; /** * 1 if it is allowed to remove the primary GAIA account. 0 by default. * @hide */ public static final String ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS = "allow_primary_gaia_account_removal_for_tests"; /** * List of TV inputs that are currently hidden. This is a string * containing the IDs of all hidden TV inputs. Each ID is encoded by Loading
core/res/res/values/config.xml +9 −0 Original line number Diff line number Diff line Loading @@ -4372,6 +4372,15 @@ UI is handled by ActivityManagerService --> <bool name="config_customUserSwitchUi">false</bool> <!-- Flag specifying whether the first account added can be removed or renamed. By default, this ability is enabled. When false, user will not be able to remove the first account. --> <bool name="config_canRemoveFirstAccount">true</bool> <!-- Used together with config_canRemoveOrRenameFirstAccount when set to false. By default, this is blank. Check if the first account is of this account type. If it is, then disable remove/rename. --> <string name="config_accountTypeToKeepFirstAccount"></string> <!-- A array of regex to treat a SMS as VVM SMS if the message body matches. Each item represents an entry, which consists of two parts: a comma (,) separated list of MCCMNC the regex applies to, followed by a semicolon (;), and Loading
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1723,6 +1723,8 @@ <java-symbol type="bool" name="config_startDreamImmediatelyOnDock" /> <java-symbol type="bool" name="config_carDockEnablesAccelerometer" /> <java-symbol type="bool" name="config_customUserSwitchUi" /> <java-symbol type="bool" name="config_canRemoveFirstAccount" /> <java-symbol type="string" name="config_accountTypeToKeepFirstAccount" /> <java-symbol type="bool" name="config_deskDockEnablesAccelerometer" /> <java-symbol type="bool" name="config_disableMenuKeyInLockScreen" /> <java-symbol type="bool" name="config_enableCarDockHomeLaunch" /> Loading
packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -691,6 +691,7 @@ public class SettingsBackupTest { newHashSet( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O. Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS, Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS, Settings.Secure.ALWAYS_ON_VPN_APP, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, Loading
services/core/java/com/android/server/accounts/AccountManagerService.java +59 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import android.util.EventLog; Loading Loading @@ -2346,6 +2347,18 @@ public class AccountManagerService } return; } if (isFirstAccountRemovalDisabled(account)) { try { response.onError( AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE, "User cannot remove the first " + account.type + " account on the device."); } catch (RemoteException re) { Log.w(TAG, "RemoteException while removing account", re); } return; } final long identityToken = clearCallingIdentity(); UserAccounts accounts = getUserAccounts(userId); cancelNotification(getSigninRequiredNotificationId(accounts, account), accounts); Loading Loading @@ -2395,6 +2408,10 @@ public class AccountManagerService account.type); throw new SecurityException(msg); } if (isFirstAccountRemovalDisabled(account)) { Log.e(TAG, "Cannot remove the first " + account.type + " account on the device."); return false; } UserAccounts accounts = getUserAccountsForCaller(); final long accountId = accounts.accountsDb.findDeAccountId(account); logRecord( Loading Loading @@ -6426,6 +6443,48 @@ public class AccountManagerService } } /** * Returns true if the config_canRemoveOrRenameFirstUser is false, and the given account type * matches the one provided by config_accountTypeToKeepFirstUser. */ private boolean isFirstAccountRemovalDisabled(Account account) { // Skip if not targeting the first user. int userId = UserHandle.getCallingUserId(); if (userId != 0) { return false; } // Skip if we are allowed to remove/rename first account. if (mContext.getResources() .getBoolean(com.android.internal.R.bool.config_canRemoveFirstAccount)) { return false; } // Skip if needed for testing. if (Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS, 0 /* default */, 0 /* userHandle */) != 0) { return false; } // Skip if not targeting desired account. String typeToKeep = mContext.getResources() .getString( com.android.internal.R.string.config_accountTypeToKeepFirstAccount); if (typeToKeep.isEmpty() || !typeToKeep.equals(account.type)) { return false; } // Only restrict first account. UserAccounts accounts = getUserAccounts(0 /* userId */); Account[] accountsOfType = getAccountsFromCache(accounts, typeToKeep, Process.SYSTEM_UID, "android" /* packageName */, false); return accountsOfType.length > 0 && accountsOfType[0].equals(account); } private final class AccountManagerInternalImpl extends AccountManagerInternal { private final Object mLock = new Object(); Loading