Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aeffc9de authored by Adam Bookatz's avatar Adam Bookatz
Browse files

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
parent 9d8f4ade
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+16 −22
Original line number Diff line number Diff line
@@ -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;
        // 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();
            }

        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;
        }
        return sIsHeadlessSystemUser;
    }

    /**
+9 −0
Original line number Diff line number Diff line
@@ -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);

+47 −30
Original line number Diff line number Diff line
@@ -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)");