summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adam Bookatz <bookatz@google.com> 2022-10-21 12:55:11 -0700
committer Adam Bookatz <bookatz@google.com> 2022-10-25 17:25:41 -0700
commitaeffc9de3c542006fdbd19596cff12b5972778ca (patch)
treedc6593ca74dd74f59a04d80adfa8da01cee6c3b7
parent9d8f4ade3dbc2f61983720cac2087d8bf4c05a78 (diff)
Changing hsum property in OTA won't alter mode
If a device is set to a particular hsum property in its build files, but during an OTA that property changes to another value, the device continues to operate in the previous mode. This is essential otherwise user 0 will become invalid. Actual mode is now determined by the system user's base flags, rather than by the build property, so even if the property changes, the previous flags will be respected and the mode won't change. WWARNING: This will not work properly if the pre-OTA build is Q or earlier (specifically, userVersion < 8). So we are demanding that no Q- device upgrades to U+ directly while also changing the hsum mode. Attempting to do so will corrupt the system user. Bug: 251802540 Test: Manual (flash headed, then flash to default headless and confirm that it's still headed; and vice versa) Change-Id: I1b5f79c896de10eab2de1eb133d4f7a61a0c06f5
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/UserManager.java38
-rw-r--r--core/java/com/android/internal/os/RoSystemProperties.java9
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java77
4 files changed, 73 insertions, 52 deletions
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 933769a352e9..8eaa5ad7fe94 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -69,6 +69,7 @@ interface IUserManager {
boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
UserInfo getProfileParent(int userId);
boolean isSameProfileGroup(int userId, int otherUserHandle);
+ boolean isHeadlessSystemUserMode();
boolean isUserOfType(int userId, in String userType);
@UnsupportedAppUsage
UserInfo getUserInfo(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 51dc643a73f3..bf3cc59b5771 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -91,7 +91,6 @@ import java.util.Set;
public class UserManager {
private static final String TAG = "UserManager";
- private static final boolean VERBOSE = false;
@UnsupportedAppUsage
private final IUserManager mService;
@@ -104,6 +103,9 @@ public class UserManager {
/** The userType of UserHandle.myUserId(); empty string if not a profile; null until cached. */
private String mProfileTypeOfProcessUser = null;
+ /** Whether the device is in headless system user mode; null until cached. */
+ private static Boolean sIsHeadlessSystemUser = null;
+
/**
* User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
* This type of user cannot be created; it can only pre-exist on first boot.
@@ -2051,28 +2053,20 @@ public class UserManager {
* @return whether the device is running in a headless system user mode.
*/
public static boolean isHeadlessSystemUserMode() {
- final boolean realMode = RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
- if (!Build.isDebuggable()) {
- return realMode;
- }
-
- final String emulatedMode = SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY);
- switch (emulatedMode) {
- case SYSTEM_USER_MODE_EMULATION_FULL:
- if (VERBOSE) Log.v(TAG, "isHeadlessSystemUserMode(): emulating as false");
- return false;
- case SYSTEM_USER_MODE_EMULATION_HEADLESS:
- if (VERBOSE) Log.v(TAG, "isHeadlessSystemUserMode(): emulating as true");
- return true;
- case SYSTEM_USER_MODE_EMULATION_DEFAULT:
- case "": // property not set
- return realMode;
- default:
- Log.wtf(TAG, "isHeadlessSystemUserMode(): invalid value of property "
- + SYSTEM_USER_MODE_EMULATION_PROPERTY + " (" + emulatedMode + "); using"
- + " default value (headless=" + realMode + ")");
- return realMode;
+ // No need for synchronization. Once it becomes non-null, it'll be non-null forever.
+ // (Its value is determined when UMS is constructed and cannot change.)
+ // Worst case we might end up calling the AIDL method multiple times but that's fine.
+ if (sIsHeadlessSystemUser == null) {
+ // Unfortunately this API is static, but the property no longer is. So go fetch the UMS.
+ try {
+ final IUserManager service = IUserManager.Stub.asInterface(
+ ServiceManager.getService(Context.USER_SERVICE));
+ sIsHeadlessSystemUser = service.isHeadlessSystemUserMode();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
+ return sIsHeadlessSystemUser;
}
/**
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index 98d81c9598b8..6870d09c8a7f 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -51,6 +51,15 @@ public class RoSystemProperties {
// ------ ro.fw.* ------------ //
public static final boolean FW_SYSTEM_USER_SPLIT =
SystemProperties.getBoolean("ro.fw.system_user_split", false);
+ /**
+ * Indicates whether the device should run in headless system user mode,
+ * in which user 0 only runs the system, not a real user.
+ * <p>WARNING about changing this value during an non-wiping update (OTA):
+ * <li>If this value is modified via an update, the change will have no effect, since an
+ * already-existing system user cannot change its mode.
+ * <li>Changing this value during an OTA from a pre-R device is not permitted; attempting to
+ * do so will corrupt the system user.
+ */
public static final boolean MULTIUSER_HEADLESS_SYSTEM_USER =
SystemProperties.getBoolean("ro.fw.mu.headless_system_user", false);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0a89d131eda2..7be1f105da8a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.UserManager.DISALLOW_USER_SWITCH;
+import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
import android.Manifest;
import android.accounts.Account;
@@ -314,7 +315,7 @@ public class UserManagerService extends IUserManager.Stub {
@VisibleForTesting
static class UserData {
// Basic user information and properties
- UserInfo info;
+ @NonNull UserInfo info;
// Account name used when there is a strong association between a user and an account
String account;
// Account information for seeding into a newly created user. This could also be
@@ -3387,11 +3388,39 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ /** Checks whether the device is currently in headless system user mode (for any reason). */
+ @Override
+ public boolean isHeadlessSystemUserMode() {
+ synchronized (mUsersLock) {
+ final UserData systemUserData = mUsers.get(UserHandle.USER_SYSTEM);
+ return !systemUserData.info.isFull();
+ }
+ }
+
/**
- * Checks whether the device is really headless system user mode, ignoring system user mode
- * emulation.
+ * Checks whether the default state of the device is headless system user mode, i.e. what the
+ * mode would be if we did a fresh factory reset.
+ * If the mode is being emulated (via SYSTEM_USER_MODE_EMULATION_PROPERTY) then that will be
+ * returned instead.
+ * Note that, even in the absence of emulation, a device might deviate from the current default
+ * due to an OTA changing the default (which won't change the already-decided mode).
*/
- private boolean isReallyHeadlessSystemUserMode() {
+ private boolean isDefaultHeadlessSystemUserMode() {
+ if (!Build.isDebuggable()) {
+ return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
+ }
+
+ final String emulatedValue = SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY);
+ if (!TextUtils.isEmpty(emulatedValue)) {
+ if (UserManager.SYSTEM_USER_MODE_EMULATION_HEADLESS.equals(emulatedValue)) return true;
+ if (UserManager.SYSTEM_USER_MODE_EMULATION_FULL.equals(emulatedValue)) return false;
+ if (!UserManager.SYSTEM_USER_MODE_EMULATION_DEFAULT.equals(emulatedValue)) {
+ Slogf.e(LOG_TAG, "isDefaultHeadlessSystemUserMode(): ignoring invalid valued of "
+ + "property %s: %s",
+ SYSTEM_USER_MODE_EMULATION_PROPERTY, emulatedValue);
+ }
+ }
+
return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
}
@@ -3403,30 +3432,11 @@ public class UserManagerService extends IUserManager.Stub {
if (!Build.isDebuggable()) {
return;
}
-
- final String emulatedValue = SystemProperties
- .get(UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY);
- if (TextUtils.isEmpty(emulatedValue)) {
+ if (TextUtils.isEmpty(SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY))) {
return;
}
- final boolean newHeadlessSystemUserMode;
- switch (emulatedValue) {
- case UserManager.SYSTEM_USER_MODE_EMULATION_FULL:
- newHeadlessSystemUserMode = false;
- break;
- case UserManager.SYSTEM_USER_MODE_EMULATION_HEADLESS:
- newHeadlessSystemUserMode = true;
- break;
- case UserManager.SYSTEM_USER_MODE_EMULATION_DEFAULT:
- newHeadlessSystemUserMode = isReallyHeadlessSystemUserMode();
- break;
- default:
- Slogf.wtf(LOG_TAG, "emulateSystemUserModeIfNeeded(): ignoring invalid valued of "
- + "property %s: %s", UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY,
- emulatedValue);
- return;
- }
+ final boolean newHeadlessSystemUserMode = isDefaultHeadlessSystemUserMode();
// Update system user type
synchronized (mPackagesLock) {
@@ -3462,7 +3472,7 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- // Update emulated mode, which will used to triger an update on user packages
+ // Update emulated mode, which will used to trigger an update on user packages
mUpdatingSystemUserMode = true;
}
@@ -3652,7 +3662,11 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUsersLock) {
UserData userData = mUsers.get(UserHandle.USER_SYSTEM);
userData.info.flags |= UserInfo.FLAG_SYSTEM;
- if (!UserManager.isHeadlessSystemUserMode()) {
+ // We assume that isDefaultHeadlessSystemUserMode() does not change during the OTA
+ // from userVersion < 8 since it is documented that pre-R devices do not support its
+ // modification. Therefore, its current value should be the same as the pre-update
+ // version.
+ if (!isDefaultHeadlessSystemUserMode()) {
userData.info.flags |= UserInfo.FLAG_FULL;
}
userIdsToWrite.add(userData.info.id);
@@ -3861,7 +3875,7 @@ public class UserManagerService extends IUserManager.Stub {
int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
| UserInfo.FLAG_PRIMARY;
// Create the system user
- String systemUserType = UserManager.isHeadlessSystemUserMode()
+ String systemUserType = isDefaultHeadlessSystemUserMode()
? UserManager.USER_TYPE_SYSTEM_HEADLESS
: UserManager.USER_TYPE_FULL_SYSTEM;
flags |= mUserTypes.get(systemUserType).getDefaultUserInfoFlags();
@@ -6298,9 +6312,12 @@ public class UserManagerService extends IUserManager.Stub {
com.android.internal.R.bool.config_guestUserEphemeral));
pw.println(" Force ephemeral users: " + mForceEphemeralUsers);
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
- final boolean isHeadlessSystemUserMode = UserManager.isHeadlessSystemUserMode();
+ final boolean isHeadlessSystemUserMode = isHeadlessSystemUserMode();
pw.println(" Is headless-system mode: " + isHeadlessSystemUserMode);
- if (isHeadlessSystemUserMode != isReallyHeadlessSystemUserMode()) {
+ if (isHeadlessSystemUserMode != RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER) {
+ pw.println(" (differs from the current default build value)");
+ }
+ if (!TextUtils.isEmpty(SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY))) {
pw.println(" (emulated by 'cmd user set-system-user-mode-emulation')");
if (mUpdatingSystemUserMode) {
pw.println(" (and being updated after boot)");