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

Commit 6400043a authored by Felipe Leme's avatar Felipe Leme Committed by android-build-merger
Browse files

Added option to pre-create user templates to optimize first user creation time.

am: 7ad2f6bd

Change-Id: I8594bfe6912e56b32b7cda0ee1a321f098264d5b
parents ed6c714c 7ad2f6bd
Loading
Loading
Loading
Loading
+73 −4
Original line number Diff line number Diff line
@@ -16,12 +16,17 @@

package android.content.pm;

import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.DebugUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Per-user information.
@@ -94,6 +99,25 @@ public class UserInfo implements Parcelable {
     */
    public static final int FLAG_DEMO = 0x00000200;

    /**
     * @hide
     */
    @IntDef(flag = true, prefix = "FLAG_", value = {
            FLAG_PRIMARY,
            FLAG_ADMIN,
            FLAG_GUEST,
            FLAG_RESTRICTED,
            FLAG_INITIALIZED,
            FLAG_MANAGED_PROFILE,
            FLAG_DISABLED,
            FLAG_QUIET_MODE,
            FLAG_EPHEMERAL,
            FLAG_DEMO
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface UserInfoFlag {
    }

    public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;

    @UnsupportedAppUsage
@@ -128,6 +152,18 @@ public class UserInfo implements Parcelable {
    @UnsupportedAppUsage
    public boolean guestToRemove;

    /**
     * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
     * number of users at the first boot, so the actual creation later is faster.
     *
     * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
     * user operations (other than user creation per se).
     *
     * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
     * {@code false}.
     */
    public boolean preCreated;

    @UnsupportedAppUsage
    public UserInfo(int id, String name, int flags) {
        this(id, name, null, flags);
@@ -155,6 +191,13 @@ public class UserInfo implements Parcelable {

    @UnsupportedAppUsage
    public boolean isGuest() {
        return isGuest(flags);
    }

    /**
     * Checks if the flag denotes a guest user.
     */
    public static boolean isGuest(@UserInfoFlag int flags) {
        return (flags & FLAG_GUEST) == FLAG_GUEST;
    }

@@ -165,6 +208,13 @@ public class UserInfo implements Parcelable {

    @UnsupportedAppUsage
    public boolean isManagedProfile() {
        return isManagedProfile(flags);
    }

    /**
     * Checks if the flag denotes a managed profile.
     */
    public static boolean isManagedProfile(@UserInfoFlag int flags) {
        return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
    }

@@ -252,6 +302,7 @@ public class UserInfo implements Parcelable {
        lastLoggedInTime = orig.lastLoggedInTime;
        lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
        partial = orig.partial;
        preCreated = orig.preCreated;
        profileGroupId = orig.profileGroupId;
        restrictedProfileParentId = orig.restrictedProfileParentId;
        guestToRemove = orig.guestToRemove;
@@ -268,6 +319,22 @@ public class UserInfo implements Parcelable {
        return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
    }

    /** @hide */
    public String toFullString() {
        return "UserInfo[id=" + id
                + ", name=" + name
                + ", flags=" + flagsToString(flags)
                + (preCreated ? " (pre-created)" : "")
                + (partial ? " (partial)" : "")
                + "]";
    }

    /** @hide */
    public static String flagsToString(int flags) {
        return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
    }

    @Override
    public int describeContents() {
        return 0;
    }
@@ -281,9 +348,10 @@ public class UserInfo implements Parcelable {
        dest.writeLong(creationTime);
        dest.writeLong(lastLoggedInTime);
        dest.writeString(lastLoggedInFingerprint);
        dest.writeInt(partial ? 1 : 0);
        dest.writeBoolean(partial);
        dest.writeBoolean(preCreated);
        dest.writeInt(profileGroupId);
        dest.writeInt(guestToRemove ? 1 : 0);
        dest.writeBoolean(guestToRemove);
        dest.writeInt(restrictedProfileParentId);
        dest.writeInt(profileBadge);
    }
@@ -308,10 +376,11 @@ public class UserInfo implements Parcelable {
        creationTime = source.readLong();
        lastLoggedInTime = source.readLong();
        lastLoggedInFingerprint = source.readString();
        partial = source.readInt() != 0;
        partial = source.readBoolean();
        profileGroupId = source.readInt();
        guestToRemove = source.readInt() != 0;
        guestToRemove = source.readBoolean();
        restrictedProfileParentId = source.readInt();
        profileBadge = source.readInt();
        preCreated = source.readBoolean();
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ interface IUserManager {
     */

    UserInfo createUser(in String name, int flags);
    UserInfo preCreateUser(int flags);
    UserInfo createProfileForUser(in String name, int flags, int userHandle,
            in String[] disallowedPackages);
    UserInfo createRestrictedProfile(String name, int parentUserHandle);
@@ -92,6 +93,7 @@ interface IUserManager {
    boolean someUserHasSeedAccount(in String accountName, in String accountType);
    boolean isManagedProfile(int userId);
    boolean isDemoUser(int userId);
    boolean isPreCreated(int userId);
    UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
            in String[] disallowedPackages);
    boolean isUserUnlockingOrUnlocked(int userId);
+38 −5
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -2003,18 +2004,20 @@ public class UserManager {

    /**
     * Creates a user with the specified name and options. For non-admin users, default user
     * restrictions are going to be applied.
     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
     * restrictions will be applied.
     *
     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
     *
     * @param name the user's name
     * @param flags flags that identify the type of user and other properties.
     * @param flags UserInfo flags that identify the type of user and other properties.
     * @see UserInfo
     *
     * @return the UserInfo object for the created user, or null if the user could not be created.
     * @return the UserInfo object for the created user, or {@code null} if the user could not be
     * created.
     * @hide
     */
    @UnsupportedAppUsage
    public UserInfo createUser(String name, int flags) {
    public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
        UserInfo user = null;
        try {
            user = mService.createUser(name, flags);
@@ -2030,6 +2033,36 @@ public class UserManager {
        return user;
    }

    /**
     * Pre-creates a user with the specified name and options. For non-admin users, default user
     * restrictions will be applied.
     *
     * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
     * at the first boot, so they when the "real" user is created (for example,
     * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
     * less time.
     *
     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
     *
     * @param flags UserInfo flags that identify the type of user and other properties.
     * @see UserInfo
     *
     * @return the UserInfo object for the created user, or {@code null} if the user could not be
     * created.
     *
     * @throw {@link IllegalArgumentException} if {@code flags} contains
     * {@link UserInfo#FLAG_MANAGED_PROFILE}.
     *
     * @hide
     */
    public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
        try {
            return mService.preCreateUser(flags);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Creates a guest user and configures it.
     * @param context an application context
+43 −23
Original line number Diff line number Diff line
@@ -134,12 +134,12 @@ class UserController implements Handler.Callback {
    static final int CONTINUE_USER_SWITCH_MSG = 20;
    static final int USER_SWITCH_TIMEOUT_MSG = 30;
    static final int START_PROFILES_MSG = 40;
    static final int SYSTEM_USER_START_MSG = 50;
    static final int SYSTEM_USER_CURRENT_MSG = 60;
    static final int USER_START_MSG = 50;
    static final int USER_CURRENT_MSG = 60;
    static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
    static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
    static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
    static final int SYSTEM_USER_UNLOCK_MSG = 100;
    static final int USER_UNLOCK_MSG = 100;
    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
    static final int START_USER_SWITCH_FG_MSG = 120;

@@ -369,6 +369,7 @@ class UserController implements Handler.Callback {
                }
            }

            if (!mInjector.getUserManager().isPreCreated(userId)) {
                mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
                        userId, 0));
                Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
@@ -380,6 +381,7 @@ class UserController implements Handler.Callback {
                        AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
                        Binder.getCallingUid(), Binder.getCallingPid(), userId);
            }
        }

        // We need to delay unlocking managed profiles until the parent user
        // is also unlocked.
@@ -439,8 +441,7 @@ class UserController implements Handler.Callback {

            // Dispatch unlocked to system services; when fully dispatched,
            // that calls through to the next "unlocked" phase
            mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
                    .sendToTarget();
            mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();
        });
        return true;
    }
@@ -556,6 +557,17 @@ class UserController implements Handler.Callback {
            }
        }

        if (userInfo.preCreated) {
            Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
            // Pre-created user was started right after creation so services could properly
            // intialize it; it should be stopped right away as it's not really a "real" user.
            // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
            // on SystemService instead.
            stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
                    /* keyEvictedCallback= */ null);
            return;
        }

        // Spin up app widgets prior to boot-complete, so they can be ready promptly
        mInjector.startUserWidgets(userId);

@@ -808,7 +820,8 @@ class UserController implements Handler.Callback {
            mInjector.systemServiceManagerCleanupUser(userId);
            mInjector.stackSupervisorRemoveUser(userId);
            // Remove the user if it is ephemeral.
            if (getUserInfo(userId).isEphemeral()) {
            UserInfo userInfo = getUserInfo(userId);
            if (userInfo.isEphemeral() && !userInfo.preCreated) {
                mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
            }

@@ -1065,6 +1078,11 @@ class UserController implements Handler.Callback {
                return false;
            }

            if (foreground && userInfo.preCreated) {
                Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
                return false;
            }

            if (foreground && mUserSwitchUiEnabled) {
                mInjector.getWindowManager().startFreezingScreen(
                        R.anim.screen_user_exit, R.anim.screen_user_enter);
@@ -1159,13 +1177,11 @@ class UserController implements Handler.Callback {
                // Booting up a new user, need to tell system services about it.
                // Note that this is on the same handler as scheduling of broadcasts,
                // which is important because it needs to go first.
                mHandler.sendMessage(
                        mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0));
            }

            if (foreground) {
                mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
                        oldUserId));
                mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
                mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
                mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
                mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -1174,6 +1190,10 @@ class UserController implements Handler.Callback {
                        oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
            }

            if (userInfo.preCreated) {
                needStart = false;
            }

            if (needStart) {
                // Send USER_STARTED broadcast
                Intent intent = new Intent(Intent.ACTION_USER_STARTED);
@@ -2131,13 +2151,13 @@ class UserController implements Handler.Callback {
            case START_PROFILES_MSG:
                startProfiles();
                break;
            case SYSTEM_USER_START_MSG:
            case USER_START_MSG:
                mInjector.batteryStatsServiceNoteEvent(
                        BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
                        Integer.toString(msg.arg1), msg.arg1);
                mInjector.getSystemServiceManager().startUser(msg.arg1);
                break;
            case SYSTEM_USER_UNLOCK_MSG:
            case USER_UNLOCK_MSG:
                final int userId = msg.arg1;
                mInjector.getSystemServiceManager().unlockUser(userId);
                // Loads recents on a worker thread that allows disk I/O
@@ -2146,7 +2166,7 @@ class UserController implements Handler.Callback {
                });
                finishUserUnlocked((UserState) msg.obj);
                break;
            case SYSTEM_USER_CURRENT_MSG:
            case USER_CURRENT_MSG:
                mInjector.batteryStatsServiceNoteEvent(
                        BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
                        Integer.toString(msg.arg2), msg.arg2);
+8 −2
Original line number Diff line number Diff line
@@ -2376,6 +2376,7 @@ class PackageManagerShellCommand extends ShellCommand {
        int userId = -1;
        int flags = 0;
        String opt;
        boolean preCreateOnly = false;
        while ((opt = getNextOption()) != null) {
            if ("--profileOf".equals(opt)) {
                userId = UserHandle.parseUserArg(getNextArgRequired());
@@ -2389,6 +2390,8 @@ class PackageManagerShellCommand extends ShellCommand {
                flags |= UserInfo.FLAG_GUEST;
            } else if ("--demo".equals(opt)) {
                flags |= UserInfo.FLAG_DEMO;
            } else if ("--pre-create-only".equals(opt)) {
                preCreateOnly = true;
            } else {
                getErrPrintWriter().println("Error: unknown option " + opt);
                return 1;
@@ -2412,7 +2415,7 @@ class PackageManagerShellCommand extends ShellCommand {
            accm.addSharedAccountsFromParentUser(parentUserId, userId,
                    (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
        } else if (userId < 0) {
            info = um.createUser(name, flags);
            info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
        } else {
            info = um.createProfileForUser(name, flags, userId, null);
        }
@@ -3308,8 +3311,11 @@ class PackageManagerShellCommand extends ShellCommand {
        pw.println("  trim-caches DESIRED_FREE_SPACE [internal|UUID]");
        pw.println("    Trim cache files to reach the given free space.");
        pw.println("");
        pw.println("  list users");
        pw.println("    Lists the current users.");
        pw.println("");
        pw.println("  create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
        pw.println("      [--guest] USER_NAME");
        pw.println("      [--guest] [--pre-create-only] USER_NAME");
        pw.println("    Create a new user with the given USER_NAME, printing the new user identifier");
        pw.println("    of the user.");
        pw.println("");
Loading