Android5.1--多用户模式

Android 系统多用户模式简介 支持多用户|Android 开源项目|Android Open Source Project (google.cn)。多用户模式最大的用处是一台设备可以给多个不同的用户使用,各个用户都可以安装自己的应用,每个用户的应用数据都是单独保存的。另一个场景是一个用户在前台进行着下载服务,就可以切换到另一个用户去继续使用这台设备而不需要等待下载服务的完成。多用户创建后,会获得一个类似恢复出厂设置后的新设备,仅仅只保留系统预装应用,同时部分权限会受到限制。 阅读详情


Android中的多用户与Windows的多用户类似,可以支持多个用户使用系统。通常,第一个在系统中注册的用户将默认成为系统管理员。

不同用户的设置各不相同,并且不同用户安装的应用及应用数据也不相同。但是系统中和硬件相关的设置则是共用的,例如网络设置等。

Android与Windows不同,用户切换后前面用户运行的后台进程还可以继续运行。这样,进行用户切换时无须中断一些后台进行的耗时操作(如下载)。

一、管理用户的系统服务--UserManagerService

UserManagerService的主要功能是创建和删除用户,以及查询用户信息。

1、初始化

UserManagerService是在PackageManagerService的构造方法中创建的,如下:

sUserManager = new UserManagerService(context, this, mInstallLock, mPackages);
UserManagerService的构造方法,如下:

    UserManagerService(Context context, PackageManagerService pm,
            Object installLock, Object packagesLock) {
        this(context, pm, installLock, packagesLock,
                Environment.getDataDirectory(),
                new File(Environment.getDataDirectory(), "user"));
    }
调用另一个构造方法,并多传递了两个参数:/data目录和/data/user文件。如下:

    private UserManagerService(Context context, PackageManagerService pm,
            Object installLock, Object packagesLock,
            File dataDir, File baseUserPath) {
        mContext = context;
        mPm = pm;
        mInstallLock = installLock;
        mPackagesLock = packagesLock;
        mHandler = new Handler();
        synchronized (mInstallLock) {
            synchronized (mPackagesLock) {
                mUsersDir = new File(dataDir, USER_INFO_DIR);// /data/system/users
                mUsersDir.mkdirs();
                // Make zeroth user directory, for services to migrate their files to that location
                File userZeroDir = new File(mUsersDir, "0");//第一个用户的目录/data/system/users/0
                userZeroDir.mkdirs();
                mBaseUserPath = baseUserPath;// /data/user
                FileUtils.setPermissions(mUsersDir.toString(),
                        FileUtils.S_IRWXU|FileUtils.S_IRWXG
                        |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                        -1, -1);
                mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);// /data/system/users/userlist.xml
                initDefaultGuestRestrictions();
                readUserListLocked();//分析用户的信息文件
                // Prune out any partially created/partially removed users.
                ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
                for (int i = 0; i < mUsers.size(); i++) {
                    UserInfo ui = mUsers.valueAt(i);
                    if ((ui.partial || ui.guestToRemove) && i != 0) {
                        partials.add(ui);//寻找userlist.xml文件中属性partial为true的用户
                    }
                }
                for (int i = 0; i < partials.size(); i++) {
                    UserInfo ui = partials.get(i);
                    Slog.w(LOG_TAG, "Removing partially created user #" + i
                            + " (name=" + ui.name + ")");
                    removeUserStateLocked(ui.id);//属性为partial表示创建没完成的用户,从系统中移除
                }
                sInstance = this;
            }
        }
    }
在构造方法中创建了几个目录:/data/system/users、data/system/users/0、/data/system/users/userlist.xml等。之后设置基本的限制条件,如下:

    private void initDefaultGuestRestrictions() {
        if (mGuestRestrictions.isEmpty()) {
            mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);//"no_outgoing_calls"
            mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);//"no_sms"
        }
    }
然后调用readUserListLocked()方法解析/data/system/users/userlist.xml文件,这个文件保存了系统中所有用户的id信息。文件内容如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<users nextSerialNumber="11" version="5">
    <guestRestrictions>
        <restrictions no_outgoing_calls="true" no_sms="true" />
    </guestRestrictions>
    <user id="0" />
    <user id="10" />
</users>
得到id信息后还有读取保存了用户注册信息的xml文件,以用户id的数字表示,如0.xml。内容如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<user id="0" serialNumber="0" flags="19" created="0" lastLoggedIn="1436392413074">
    <name>Owner</name>
    <restrictions />
</user>
读取用户的xml文件内容后,将根据文件内容来创建和初始化一个UserInfo对象来保存信息,并把该对象加入到mUsers列表中。如果xml文件中某个用户的属性中有partial="true',则表示这个用户没有完全创建成功,需要从系统中移除掉。

这样UserManagerService的初始化工作就完成了,主要的工作是分析userlist.xml文件,并创建了mUsers列表中的UserInfo对象。

2、UserInfo的定义

在UserManagerService中,UserInfo对象代表用户,定义如下:

public class UserInfo implements Parcelable {

    /** 8 bits for user type 用户类型 */
    public static final int FLAG_MASK_USER_TYPE = 0x000000FF;

    /**
     * Primary user. Only one user can have this flag set. Meaning of this
     * flag TBD.主用户的标志,通常是第一个Id为0的用户
     */
    public static final int FLAG_PRIMARY = 0x00000001;

    /**
     * User with administrative privileges. Such a user can create and
     * delete users.admin用户标志,有此标志才能创建和删除用户
     */
    public static final int FLAG_ADMIN   = 0x00000002;

    /**
     * Indicates a guest user that may be transient.guest用户标志
     */
    public static final int FLAG_GUEST   = 0x00000004;

    /**
     * Indicates the user has restrictions in privileges, 标识权限受限的用户,具体受限功能未定。
     * Exact meaning TBD. For instance, maybe they can't install apps or administer WiFi access pts.
     */
    public static final int FLAG_RESTRICTED = 0x00000008;

    /**
     * Indicates that this user has gone through its first-time initialization.标识该用户是否已经初始化
     */
    public static final int FLAG_INITIALIZED = 0x00000010;

    /**
     * Indicates that this user is a profile of another user, for example holding a users
     * corporate data.标识该UserInfo是用户的信息,还是一份profile
     */
    public static final int FLAG_MANAGED_PROFILE = 0x00000020;

    /**
     * Indicates that this user is disabled.标识该用户是否已经被“禁止”
     */
    public static final int FLAG_DISABLED = 0x00000040;

    public static final int NO_PROFILE_GROUP_ID = -1;//profile group id 的无效值定义

    public int id;//用户id
    public int serialNumber;//用户的序列号,不会重复
    public String name;//用户的名称
    public String iconPath;//用户的头像路径
    public int flags;//用户的标志
    public long creationTime;//创建用户的时间
    public long lastLoggedInTime;//上次登录的时间
    public int profileGroupId;//用户的profile group id

    /** User is only partially created. */
    public boolean partial;//此标志为true,表示用户没有创建完成
    public boolean guestToRemove;
注意:用户id用来标识用户,用户删除后它的id会分配给下一个新建的用户,保持id号的连续;而serialNumber则是一个在设备中不会重复的数字,用来唯一标识一个用户,如果应用中希望记录和某个用户相关的信息,最好使用serialNumber来表示用户,而不是用户id。

3、用户限制(Restriction)

Android5.1的UserManager中定义了很多和用户相关的限制,这样管理员在创建用户时就能通过这些限制来给予不同用户不同的权限,介绍如下:

    public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";//限制修改用户账号
    public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";//限制配置WiFi
    public static final String DISALLOW_INSTALL_APPS = "no_install_apps";//限制安装app
    public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";//限制卸载app
    public static final String DISALLOW_SHARE_LOCATION = "no_share_location";//限制共享地理位置
    public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";//限制安装本地应用
    public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";//限制配置蓝牙
    public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";//限制通过usb传输文件
    public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";//限制配置设备的安全设置
    public static final String DISALLOW_REMOVE_USER = "no_remove_user";//限制删除用户
    public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";//限制使用调试功能
    public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";//限制配置VPN
    public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering";//限制配置Tether
    public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";//限制进行出厂设置
    public static final String DISALLOW_ADD_USER = "no_add_user";//限制创建用户
    public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";//对该用户安装的app要进行校验
    public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";//限制用户配置小区广播
    public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";//限制配置移动网络
    public static final String DISALLOW_APPS_CONTROL = "no_control_apps";//限制进行清除应用数据和缓存的操作
    public static final String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";//限制挂载
    public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";//限制对microphone进行静音
    public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";//限制调节音量
    public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";//限制拨打电话
    public static final String DISALLOW_SMS = "no_sms";//限制发送短信
    public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";//限制用户的应用进行弹窗功能
    public static final String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";//限制对profile进行复制、粘贴
    public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";//限制

4、添加用户

UserManagerService服务中添加用户的接口是createUser(),如下:

    public UserInfo createUser(String name, int flags) {
        checkManageUsersPermission("Only the system can create users");

        return createUserInternal(name, flags, UserHandle.USER_NULL);
    }
createUser()方法首先检查调用者的权限,只有system身份的进程才能调用这个方法。然后调用createUserInternal()方法继续执行,如下:

    private UserInfo createUserInternal(String name, int flags, int parentId) {
        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
                UserManager.DISALLOW_ADD_USER, false)) {//获取是否有添加用户的权限
            Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
            return null;
        }
        final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
        final long ident = Binder.clearCallingIdentity();
        UserInfo userInfo = null;
        try {
            synchronized (mInstallLock) {
                synchronized (mPackagesLock) {
                    UserInfo parent = null;
                    if (parentId != UserHandle.USER_NULL) {
                        parent = getUserInfoLocked(parentId);
                        if (parent == null) return null;
                    }
                    // 如果添加的不是Guest用户,而且达到上限数,返回
                    if (!isGuest && isUserLimitReachedLocked()) {
                        return null;
                    }
                    // 如果添加的是Guest用户,但是已经有Guest用户存在,返回
                    if (isGuest && findCurrentGuestUserLocked() != null) {
                        return null;
                    }
                    //
                    if ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0
                            && numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
                                >= MAX_MANAGED_PROFILES) {//目前MAX_MANAGED_PROFILES最大是1
                        return null;//如果创建的是一个用户的profile,检查该用户是否已经有profile
                    }
                    int userId = getNextAvailableIdLocked();//得到新用户ID
                    userInfo = new UserInfo(userId, name, null, flags);
                    File userPath = new File(mBaseUserPath, Integer.toString(userId));
                    userInfo.serialNumber = mNextSerialNumber++;
                    long now = System.currentTimeMillis();
                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
                    userInfo.partial = true;//将partial属性设为true
                    Environment.getUserSystemDirectory(userInfo.id).mkdirs();
                    mUsers.put(userId, userInfo);//把新用户信息添加到mUsers
                    writeUserListLocked();//把用户信息写到文件userlist.xml中
                    if (parent != null) {
                        if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
                            parent.profileGroupId = parent.id;
                            writeUserLocked(parent);
                        }
                        userInfo.profileGroupId = parent.profileGroupId;
                    }
                    writeUserLocked(userInfo);//创建用户的信息文件10.xml
                    mPm.createNewUserLILPw(userId, userPath);
                    userInfo.partial = false;//创建成功,将partial设为false
                    writeUserLocked(userInfo);//更新用户的信息文件
                    updateUserIdsLocked();
                    Bundle restrictions = new Bundle();
                    mUserRestrictions.append(userId, restrictions);
                }
            }
            if (userInfo != null) {//广播用户创建成功的消息
                Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
                addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
                mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
                        android.Manifest.permission.MANAGE_USERS);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return userInfo;
    
(1)检查调用进程所属用户是否被限制添加用户。

(2)调用isUserLimitReachedLocked()方法来确定是否还能创建更多的用户。isUserLimitReachedLocked()方法将调用getMaxSupportedUsers()方法得到系统允许的最大用户数和当前用户数进行比较。

(3)调用getNextAvailableIdLocked()方法得到新的用户id。getNextAvailableIdLocked()方法会从10开始查找,如果某个用户Id还没有使用,则将它返回。

(4)根据用户id创建用户的数据目录。同时为用户创建UserInfo对象,并加入到mUsers列表中。

(5)向userlist.xml文件中加入用户的id信息。在/data/system/users/目录下创建以用户id数字表示的xml文件,保存UserInfo中的用户信息,文件中partial属性会被设为true。

(6)调用PackageManagerService的createNewuserLILPw()方法,在新建用户的目录下为所有应用创建数据目录。

(7)更新用户的信息文件,将xml文件中的partial属性移除,这样新用户就创建完成了。使用partial属性的目的是为了防止调用createNewUserLILPw()方法出错,不能成功创建用户。利用这个标志,系统重启时可以清除掉创建失败的用户。

(8)广播加入新用户的消息Intent.ACTION_USER_ADDED。

5、Guest用户

在Android5.0中,创建Guest用户需要通过UserManager的createGuest()方法来完成。如下:

    public UserInfo createGuest(Context context, String name) {
        UserInfo guest = createUser(name, UserInfo.FLAG_GUEST);//创建Guest用户
        if (guest != null) {
            Settings.Secure.putStringForUser(context.getContentResolver(),
                    Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
            try {//对Guest用户进行权限限制
                Bundle guestRestrictions = mService.getDefaultGuestRestrictions();
                guestRestrictions.putBoolean(DISALLOW_SMS, true);
                guestRestrictions.putBoolean(DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
                mService.setUserRestrictions(guestRestrictions, guest.id);
            } catch (RemoteException re) {
                Log.w(TAG, "Could not update guest restrictions");
            }
        }
        return guest;
    }
createGuest()先调用createUser()方法创建一个Guest用户,然后再调用setUserRestrictions()方法进一步对Guest用户进行限制。因此,Guest用户和普通用户的区别就在于权限不同。

6、删除用户

UserManagerService服务中删除用户的接口是removeUser()方法,如下:

    public boolean removeUser(int userHandle) {
        checkManageUsersPermission("Only the system can remove users");
        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
                UserManager.DISALLOW_REMOVE_USER, false)) {
            Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled.");
            return false;
        }//检查调用进程的权限以及进程所属的用户是否被“限制”

        long ident = Binder.clearCallingIdentity();
        try {
            final UserInfo user;
            synchronized (mPackagesLock) {
                user = mUsers.get(userHandle);
                if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
                    return false;
                }

                // 将userHandle放入mRemovingUserIds列表,防止重复删除一个用户
                mRemovingUserIds.put(userHandle, true);

                try {
                    mAppOpsService.removeUser(userHandle);
                } catch (RemoteException e) {
                    Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
                }
                // Set this to a partially created user, so that the user will be purged
                // on next startup, in case the runtime stops now before stopping and
                // removing the user completely.
                user.partial = true;
                // Mark it as disabled, so that it isn't returned any more when
                // profiles are queried.
                user.flags |= UserInfo.FLAG_DISABLED;
                writeUserLocked(user);//更新用户信息文件
            }

            if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                    && user.isManagedProfile()) {
                // Send broadcast to notify system that the user removed was a
                // managed user.
                sendProfileRemovedBroadcast(user.profileGroupId, user.id);
            }

            if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
            int res;
            try {//停止正在运行的用户
                res = ActivityManagerNative.getDefault().stopUser(userHandle,
                        new IStopUserCallback.Stub() {//回调函数
                            @Override
                            public void userStopped(int userId) {
                                finishRemoveUser(userId);
                            }
                            @Override
                            public void userStopAborted(int userId) {
                            }
                        });
            } catch (RemoteException e) {
                return false;
            }
            return res == ActivityManager.USER_OP_SUCCESS;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

removeUser()方法并没有立即删除用户相关的文件,而是首先将用户信息中的partial属性改为true。这样的好处是,如果后面的过程被意外终止了,系统重启后还是会删除该用户所有目录和文件。

考虑到被删除的用户可能正在运行,因此,removeUser()方法要调用ActivityManagerService的stopUser()方法来改变该用户的运行状态,结束后,AMS将调用函数参数中的回调方法,最终调用到finishRemoveUser()方法。如下:

    void finishRemoveUser(final int userHandle) {
        if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
        // Let other services shutdown any activity and clean up their state before completely
        // wiping the user's system directory and removing from the user list
        long ident = Binder.clearCallingIdentity();
        try {
            Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
            mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
                    android.Manifest.permission.MANAGE_USERS,

                    new BroadcastReceiver() {
                        @Override
                        public void onReceive(Context context, Intent intent) {
                            if (DBG) {
                                Slog.i(LOG_TAG,
                                        "USER_REMOVED broadcast sent, cleaning up user data "
                                        + userHandle);
                            }
                            new Thread() {
                                public void run() {
                                    synchronized (mInstallLock) {
                                        synchronized (mPackagesLock) {
                                            removeUserStateLocked(userHandle);
                                        }
                                    }
                                }
                            }.start();
                        }
                    },

                    null, Activity.RESULT_OK, null, null);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

在finishRemoveUser()方法中发出了广播Intent.ACTION_USER_REMOVED,同时也创建了一个BroadcastReceiver对象来接收这个广播。注意,这时创建的广播接收器将最后才能收到广播通知。这是为了让系统中其他关心这条广播的地方先处理,最后才回到这里继续完成删除用户的工作。收到广播后,考虑到后面的工作还很耗时,因此,在onReceiver()方法中启动了一个线程来运行removeUserStateLocked()方法。代码如下:

    private void removeUserStateLocked(final int userHandle) {
        // Cleanup package manager settings调用PackageManagerService的cleanUpUserLILPw方法删除所有用户目录下app的数据
        mPm.cleanUpUserLILPw(this, userHandle);

        // Remove this user from the list在mUsers列表中删除用户的UserInfo对象
        mUsers.remove(userHandle);

        mRestrictionsPinStates.remove(userHandle);//从mRestrictionsPinStates列表中移除用户
        // Remove user file删除用户的信息文件
        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
        userFile.delete();
        // Update the user list
        writeUserListLocked();//更新文件userlists.xml,移除用户信息
        updateUserIdsLocked();
        removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));//删除用户目录下的所有文件
    }

removeUserStateLocked()方法将删除所有用户相关的文件。首先是app的数据文件,然后是用户信息文件和userlist.xml中的用户Id信息,最后将用户目录下的所有文件都删除。

二、PackageManagerService和多用户

在多用户的环境中,所有用户安装的应用还是位于/data/app目录,但是这些应用的数据在每个用户的目录/data/user/<用户id>/下都有单独的一份。目录/data/data也还保存应用的数据,但这些数据只对id为0的用户有效。

1、创建用户的应用数据

上面讲到创建用户时会调用PackageManagerService的createNewUserLILPw()方法,如下:

    /** Called by UserManagerService */
    void createNewUserLILPw(int userHandle, File path) {
        if (mInstaller != null) {
            mInstaller.createUserConfig(userHandle);
            mSettings.createNewUserLILPw(this, mInstaller, userHandle, path);
        }
    }
createNewUserLILPw()方法只是调用了mInstaller的createUserConfig()和mSettings的createNewUserLILPw()方法。

我们先看下mInstaller的createUserConfig()方法是如何处理的,如下:

    public int createUserConfig(int userId) {
        StringBuilder builder = new StringBuilder("mkuserconfig");
        builder.append(' ');
        builder.append(userId);
        return mInstaller.execute(builder.toString());
    }
mInstaller是InstallerConnection的对象,这里调用InstallConnection对象的execute()方法,如下:

    public int execute(String cmd) {
        String res = transact(cmd);
        try {
            return Integer.parseInt(res);
        } catch (NumberFormatException ex) {
            return -1;
        }
    }
这里实现了与installd服务通信的过程,具体操作是由installd服务完成的。再看下mSettings的createNewUserLILPw()方法,我们知道mSettings中保存的是从文件中读取的所有应用的信息。如下:

    void createNewUserLILPw(PackageManagerService service, Installer installer,
            int userHandle, File path) {
        path.mkdir();//创建用户保存应用数据的目录/data/user
        FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
                | FileUtils.S_IXOTH, -1, -1);
        for (PackageSetting ps : mPackages.values()) {
            if (ps.pkg == null || ps.pkg.applicationInfo == null) {
                continue;
            }//对每一个应用在用户的目录下创建数据目录
            // Only system apps are initially installed.
            ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
            // Need to create a data directory for all apps under this user.
            installer.createUserData(ps.name,
                    UserHandle.getUid(userHandle, ps.appId), userHandle,
                    ps.pkg.applicationInfo.seinfo);
        }
        readDefaultPreferredAppsLPw(service, userHandle);
        writePackageRestrictionsLPr(userHandle);
    }
createNewUserLILPw()方法会遍历mPackages列表,这个列表保存的是系统中所有已安装的应用信息,然后针对每一个应用调用installer的createUserData()方法,调用Installer的方法最终会调用installd守护进程的命令,这里最终调用installd的“mkuserdata”命令来执行应用数据目录的创建工作。
    public int createUserData(String name, int uid, int userId, String seinfo) {
        StringBuilder builder = new StringBuilder("mkuserdata");
        builder.append(' ');
        builder.append(name);
        builder.append(' ');
        builder.append(uid);
        builder.append(' ');
        builder.append(userId);
        builder.append(' ');
        builder.append(seinfo != null ? seinfo : "!");
       
        return mInstaller.execute(builder.toString());
    }
同时每个应用的设置对象PackageSetting中都有一个数组userState,表示应用已经被安装到哪些用户中,这里调用ps的setInstalled方法就是向数组userState中插入用户的信息。

调用readDefaultPreferredAppsLPw()方法是为了分析目录/etc/preferred-apps所有的xml文件,这个目录下的xml文件保存的是设备使用者指定的响应某个intent的最合适的组件。因为每个用户都可以指定它喜欢的最合适的组件,所以每个用户都需要在mPreferredActivities列表中加入它自己的PreferredIntentResolver对象,这个对象中保存的就是intent和组件的关联数据。

2、删除用户的应用数据

PackageManagerService中删除用户的方法是cleanUpUserLILPw()方法,如下:

    /** Called by UserManagerService */
    void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
        mDirtyUsers.remove(userHandle);
        mSettings.removeUserLPw(userHandle);
        mPendingBroadcasts.remove(userHandle);
        if (mInstaller != null) {
            // Technically, we shouldn't be doing this with the package lock
            // held.  However, this is very rare, and there is already so much
            // other disk I/O going on, that we'll let it slide for now.
            mInstaller.removeUserDataDirs(userHandle);//删除用户目录下所有应用的数据
        }
        mUserNeedsBadging.delete(userHandle);
        removeUnusedPackagesLILPw(userManager, userHandle);
    }
cleanUpUserLILPw方法主要的工作就是调用mInstaller的removeUserDataDirs()方法来删除用户目录下所有应用的数据。同时调用mSettings的removeUserLPw()方法来删除PackageManagerService中和用户相关的数据,如下:

    void removeUserLPw(int userId) {
        Set<Entry<String, PackageSetting>> entries = mPackages.entrySet();
        for (Entry<String, PackageSetting> entry : entries) {
            entry.getValue().removeUser(userId);
        }<span style="font-family: Arial, Helvetica, sans-serif;">//删除每个应用信息中该用户的信息</span>
        mPreferredActivities.remove(userId);//删除mPreferredActivities列表中该用户的数据
        File file = getUserPackagesStateFile(userId);
        file.delete();//删除该用户目录下的package-restrictions.xml文件
        file = getUserPackagesStateBackupFile(userId);
        file.delete();
        removeCrossProfileIntentFiltersLPw(userId);
    }

三、ActivityManagerService和多用户

UserManagerService主要管理用户的账号信息,运行中的用户管理由ActivityManagerService实行。

1、用户的状态

用户有四种运行状态,定义如下:

public final class UserStartedState {
    // User is first coming up.
    public final static int STATE_BOOTING = 0;//用户正在启动
    // User is in the normal running state.
    public final static int STATE_RUNNING = 1;//用户正在运行
    // User is in the initial process of being stopped.
    public final static int STATE_STOPPING = 2;//用户正在停止
    // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN.
    public final static int STATE_SHUTDOWN = 3;//用户已经关闭
在AMS中,变量mCurrentUserId记录了当前的用户Id,mStartedUsers列表则保存了正在运行的用户,只有在mStartedUsers列表中的用户才会有上面这4中状态,不在列表中的是已经注册了但是没有运行的用户。

2、切换当前用户

ActivityManagerService中提供了切换当前用户的接口switchUser(),如下:

    public boolean switchUser(final int userId) {
        enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
        String userName;
        synchronized (this) {
            UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);//获取userId对应的UserInfo
            if (userInfo == null) {
                Slog.w(TAG, "No user info for user #" + userId);
                return false;
            }
            if (userInfo.isManagedProfile()) {//如果这个UserInfo只是User的另一份profile,退出
                Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
                return false;
            }
            userName = userInfo.name;
            mTargetUserId = userId;
        }
        mHandler.removeMessages(START_USER_SWITCH_MSG);
        mHandler.sendMessage(mHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));//发送切换用户的消息
        return true;
    }
switchUser()先调用getUserInfo()方法得到userId对应的用户信息,然后通过isManagedProfile()方法来判断这份UserInfo是否只是一个profile(Android中允许一个用户还拥有另一份profile)。如果确实是一个用户,则发出消息START_USER_SWITCH_MSG。

对消息START_USER_SWITCH_MSG的处理是调用方法showUserSwitchDialog,这个方法将弹出一个对话框来确定是否要进行用户切换,一旦用户点击确定按钮后,调用startUserInForeground()方法

    case START_USER_SWITCH_MSG: {
        showUserSwitchDialog(msg.arg1, (String) msg.obj);
        break;
    }
    private void showUserSwitchDialog(int userId, String userName) {
        // The dialog will show and then initiate the user switch by calling startUserInForeground
        Dialog d = new UserSwitchingDialog(this, mContext, userId, userName,
                true /* above system */);
        d.show();
    }

startUserInForeground()方法如下:

    /**
     * Start user, if its not already running, and bring it to foreground.
     */
    boolean startUserInForeground(final int userId, Dialog dlg) {
        boolean result = startUser(userId, /* foreground */ true);
        dlg.dismiss();
        return result;
    }
在startUserInForeground()方法中只是调用了startUser()方法,如下:

    private boolean startUser(final int userId, final boolean foreground) {
        if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                != PackageManager.PERMISSION_GRANTED) {
            。。。。。。//检查调用者权限
        }
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (this) {
                final int oldUserId = mCurrentUserId;
                if (oldUserId == userId) {
                    return true;//当前用户已经是参数指定的用户,退出
                }

                mStackSupervisor.setLockTaskModeLocked(null, false, "startUser");

                final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
                。。。。。。//一些错误处理,返回false
                if (foreground) {
                    mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
                            R.anim.screen_user_enter);
                }

                boolean needStart = false;

                // 如果用户还没有启动,需要先启动用户,切换用户状态为正在启动
                if (mStartedUsers.get(userId) == null) {
                    mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
                    updateStartedUserArrayLocked();
                    needStart = true;
                }

                final Integer userIdInt = Integer.valueOf(userId);
                mUserLru.remove(userIdInt);
                mUserLru.add(userIdInt);//调整用户在mUserLru列表中的位置,当前用户位于最末的位置

                if (foreground) {//前台切换
                    mCurrentUserId = userId;//切换当前用户
                    mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
                    updateCurrentProfileIdsLocked();
                    mWindowManager.setCurrentUser(userId, mCurrentProfileIds);//在WMS这设置当前用户
                    mWindowManager.lockNow(null);//锁上屏幕
                } else {
                    final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
                    updateCurrentProfileIdsLocked();
                    mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
                    mUserLru.remove(currentUserIdInt);
                    mUserLru.add(currentUserIdInt);
                }

                final UserStartedState uss = mStartedUsers.get(userId);

                // Make sure user is in the started state.  If it is currently
                // stopping, we need to knock that off.
                if (uss.mState == UserStartedState.STATE_STOPPING) {
                    // 如果用户的状态为正在停止,切换为正在运行
                    uss.mState = UserStartedState.STATE_RUNNING;
                    updateStartedUserArrayLocked();
                    needStart = true;
                } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) {
                    // 如果用户的状态为正在关闭,切换为正在启动
                    uss.mState = UserStartedState.STATE_BOOTING;
                    updateStartedUserArrayLocked();
                    needStart = true;
                }

                if (uss.mState == UserStartedState.STATE_BOOTING) {
                    //如果用户的状态为正在启动,发送消息SYSTEM_USER_START_MSG
                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
                }

                if (foreground) {
                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_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,
                            oldUserId, userId, uss));
                    mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
                            oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
                }

                if (needStart) {
                    // 如果切换前用户不在STATE_RUNNING状态,向该用户发送广播ACTION_USER_STARTED
                    Intent intent = new Intent(Intent.ACTION_USER_STARTED);
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                            | Intent.FLAG_RECEIVER_FOREGROUND);
                    intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                    broadcastIntentLocked(null, null, intent,
                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                            false, false, MY_PID, Process.SYSTEM_UID, userId);
                }

                if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
                    if (userId != UserHandle.USER_OWNER) {//如果用户还没初始化,向该用户发送广播ACTION_USER_INITIALIZE
                        Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
                        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                        broadcastIntentLocked(null, null, intent, null,
                                new IIntentReceiver.Stub() {
                                    public void performReceive(Intent intent, int resultCode,
                                            String data, Bundle extras, boolean ordered,
                                            boolean sticky, int sendingUser) {
                                        onUserInitialized(uss, foreground, oldUserId, userId);
                                    }
                                }, 0, null, null, null, AppOpsManager.OP_NONE,
                                true, false, MY_PID, Process.SYSTEM_UID,
                                userId);
                        uss.initializing = true;
                    } else {//否则直接调用UserManagerService的makeInitialized方法
                        getUserManagerLocked().makeInitialized(userInfo.id);
                    }
                }

                if (foreground) {
                    if (!uss.initializing) {//如果用户已经初始化了,将它设为前台用户
                        moveUserToForeground(uss, oldUserId, userId);
                    }
                } else {//否则先将用户加入到mStartingBackgroundUser中
                    mStackSupervisor.startBackgroundUserLocked(userId, uss);
                }

                if (needStart) {//如果用户不在STATE_RUNNING状态,向所有用户发送广播ACTION_USER_STARTING
                    Intent intent = new Intent(Intent.ACTION_USER_STARTING);
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                    broadcastIntentLocked(null, null, intent,
                            null, new IIntentReceiver.Stub() {
                                @Override
                                public void performReceive(Intent intent, int resultCode, String data,
                                        Bundle extras, boolean ordered, boolean sticky, int sendingUser)
                                        throws RemoteException {
                                }
                            }, 0, null, null,
                            INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
                            true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }

        return true;
    }

startUser()方法代码较长。方法中先确定用户的状态,如果用户还没有运行,那么mStartedUsers列表中将不会有用户的信息,因此,需要先创建表示用户状态的对象UserStartedState,然后加入到mStartedUsers列表中。同时还要调整用户在mUserLru列表中的位置到最后一项。系统中同时运行的用户数量最多为3个(目前的定义),mUserLru列表就是记录用户的登录顺序,在需要时会停止多余的用户运行。

对于已经在mStartedUsers列表中的用户,首先判定它的状态是否为STATE_RUNNING,如果不是,则需要完成状态转换,如果处于STATE_STOPPING状态,直接转换为STATE_RUNNING状态,如果处于STATE_SHUTDOWN状态,先转换为STATE_BOOTING状态。因此,switchUser()方法结束后用户有可能处于STATE_BOOTING状态,那么用户什么时候会再切换到STATE_RUNNING状态呢?稍候分析。

切换用户的工作还包括设置记录当前用户id的变量mCurrentUserId,调用WMS的setCurrentUser()方法来改变当前用户、锁定当前的屏幕等。

除了这些工作,startUser()方法最重要的工作是广播和用户相关的Intent。因为用户切换还牵扯到很多模块,例如壁纸管理、输入法、账号管理模块,他们都需要知道当前用户已经发生了变化,然后做出相应的调整。这里要注意广播的对象,有些是面向新用户的,有些是面向旧用户,有些面向所有用户。

(1)如果切换前用户不在STATE_RUNNING状态,向该用户发送广播ACTION_USER_STARTED。

(2)id不为0的用户如果还没有初始化,向该用户发送广播ACTION_USER_INITIALIZE。

(3)调用sendUserSwitchBroadcastsLocked()方法向旧用户发送广播ACTION_USER_BACKGROUND,向新用户发送广播ACTION_USER_FOREGROUND,向所有用户发送广播ACTION_USER_SWUTCHED。

(4)如果切换前用户不在STATE_RUNNING状态,向所有用户发送广播ACTION_USER_STARTING。

此外,startUser()方法中还会发两条消息:REPORT_USER_SWITCH_MSG和USER_SWITCH_TIMEOUT_MSG。其中REPORT_USER_SWITCH_MSG消息处理方法是dispatchUserSwitch,如下:

            case REPORT_USER_SWITCH_MSG: {
                dispatchUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
                break;
            }
    void dispatchUserSwitch(final UserStartedState uss, final int oldUserId,
            final int newUserId) {
        final int N = mUserSwitchObservers.beginBroadcast();
        if (N > 0) {
            final IRemoteCallback callback = new IRemoteCallback.Stub() {
                int mCount = 0;
                @Override
                public void sendResult(Bundle data) throws RemoteException {
                    synchronized (ActivityManagerService.this) {
                        if (mCurUserSwitchCallback == this) {
                            mCount++;//如果收到一条返回结果的调用,mCount++
                            if (mCount == N) {//如果所有结果都返回了,发送继续处理的消息
                                sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
                            }
                        }
                    }
                }
            };
            synchronized (this) {
                uss.switching = true;
                mCurUserSwitchCallback = callback;
            }
            for (int i=0; i<N; i++) {
                try {//调用观测对象的onUserSwitching方法
                    mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
                            newUserId, callback);
                } catch (RemoteException e) {
                }
            }
        } else {//如果没有观测对象,直接调用sendContinueUserSwitchLocked
            synchronized (this) {
                sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
            }
        }
        mUserSwitchObservers.finishBroadcast();
    }

dispatchUserSwitch()方法的主要作用是调用mUserSwitchObservers列表中IUserSwitchObserver对象的onUserSwitching()接口。如果系统中有模块对用户切换感兴趣,可以调用AMS的registerUserSwitchObserver()接口来注册一个观测对象,这个对象将会保存到mUserSwitchObservers列表中。注册观测对象的模块在它的回调接口onUserSwitching()的处理中需要在调用传递的参数对象callback的sendResult方法来通知AMS。我们看下上面代码中的sendResult()方法,只有所有注册者都调用了sendResult()方法,mCount最后才会等于N,这时才会调用sendContinueUserSwitchLocked方法来发送CONTINUE_USER_SWITCH_MSG消息来继续切换用户。

我们看下USER_SWITCH_TIMEOUT_MSG消息的处理,发送USER_SWITCH_TIMEOUT_MSG消息是为了防止对CONTINUE_USER_SWITCH_MSG消息的处理时间过长,毕竟只有所有注册者处理完成才能继续。Android5.1不同于以往版本,这里会继续发送CONTINUE_USER_SWITCH_MSG消息,继续进行用户切换。如下:

            case USER_SWITCH_TIMEOUT_MSG: {
                timeoutUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
                break;
            }
    void timeoutUserSwitch(UserStartedState uss, int oldUserId, int newUserId) {
        synchronized (this) {
            Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
            sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
        }
    }
    void sendContinueUserSwitchLocked(UserStartedState uss, int oldUserId, int newUserId) {
        mCurUserSwitchCallback = null;
        mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
        mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
                oldUserId, newUserId, uss));
    }
            case CONTINUE_USER_SWITCH_MSG: {
                continueUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
                break;
            }
    void continueUserSwitch(UserStartedState uss, int oldUserId, int newUserId) {
        completeSwitchAndInitalize(uss, newUserId, false, true);
    }
    void completeSwitchAndInitalize(UserStartedState uss, int newUserId,
            boolean clearInitializing, boolean clearSwitching) {
        boolean unfrozen = false;
        synchronized (this) {
            if (clearInitializing) {
                uss.initializing = false;
                getUserManagerLocked().makeInitialized(uss.mHandle.getIdentifier());
            }
            if (clearSwitching) {
                uss.switching = false;
            }
            if (!uss.switching && !uss.initializing) {
                mWindowManager.stopFreezingScreen();
                unfrozen = true;
            }
        }
        if (unfrozen) {
            final int N = mUserSwitchObservers.beginBroadcast();
            for (int i=0; i<N; i++) {
                try {
                    mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId);
                } catch (RemoteException e) {
                }
            }
            mUserSwitchObservers.finishBroadcast();
        }
        stopGuestUserIfBackground();
    }
CONTINUE_USER_SWITCH_MSG消息的处理方法是continueUserSwitch(),这个方法会调用completeSwitchAndInitalize()方法继续处理,处理的结果是对mUserSwitchObservers列表中所有观测对象调用他们的onUserSwitchComplete()方法。

至此,startUser()方法的工作就完成了。但是还有一个问题,状态为STATE_BOOTING的用户什么时候切换到STATE_RUNNING状态?在activityIdleInternalLocked()方法中有一段代码如下:

   for (int i = 0; i < startingUsers.size(); i++) {
     mService.finishUserSwitch(startingUsers.get(i));
  }
activityIdleInternalLocked()方法会在Activity进入Idle状态时调用,也就相当于用户已经切换完成了,会对每个startingUsers列表中的用户调用finishUserBoot()方法,如下:

    void finishUserSwitch(UserStartedState uss) {
        synchronized (this) {
            finishUserBoot(uss);

            startProfilesLocked();

            int num = mUserLru.size();
            int i = 0;
            while (num > MAX_RUNNING_USERS && i < mUserLru.size()) {
                Integer oldUserId = mUserLru.get(i);
                UserStartedState oldUss = mStartedUsers.get(oldUserId);
                if (oldUss == null) {//正常情况下不会出现为null
                    // Shouldn't happen, but be sane if it does.
                    mUserLru.remove(i);
                    num--;
                    continue;
                }
                if (oldUss.mState == UserStartedState.STATE_STOPPING
                        || oldUss.mState == UserStartedState.STATE_SHUTDOWN) {
                    // This user is already stopping, doesn't count.如果用户已经停止了,继续
                    num--;
                    i++;
                    continue;
                }
                if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId) {
                    // Owner and current can't be stopped, but count as running.
                    i++;//如果是主用户或当前用户,继续
                    continue;
                }
                // This is a user to be stopped.停止用户
                stopUserLocked(oldUserId, null);
                num--;
                i++;
            }
        }
    }
finishUserSwitch()方法完成了两件工作。一是调用方法finishUserBoot()把状态还是STATE_BOOTING的用户切换到STATE_RUNNING状态,同时发送广播ACTION_BOOT_COMPLETED给该用户,表示用户启动结束。另一件工作是停止多余的用户。从mUserLru列表的第0项开始,对于处于状态STATE_RUNNING的用户,只要不是主用户或当前用户,一律停止,只保留处于STATE_RUNNING状态的用户。

3、停止用户运行

ActivityManagerService中停止用户运行的接口是stopUser(),这个方法在检查了调用进程的权限后,调用内部方法stopUserLocked()继续停止用户的工作,stopUserLocked()代码如下:

    private int stopUserLocked(final int userId, final IStopUserCallback callback) {
        if (DEBUG_MU) Slog.i(TAG_MU, "stopUserLocked userId=" + userId);
        if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) {
            return ActivityManager.USER_OP_IS_CURRENT;
        }//如果要求停止的用户是当前用户,不能停止,返回

        final UserStartedState uss = mStartedUsers.get(userId);
        if (uss == null) {
            // User is not started, nothing to do...  but we do need to
            // callback if requested.
            if (callback != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            callback.userStopped(userId);//回调方法userStopped
                        } catch (RemoteException e) {
                        }
                    }
                });
            }
            return ActivityManager.USER_OP_SUCCESS;//0
        }

        if (callback != null) {
            uss.mStopCallbacks.add(callback);
        }

        if (uss.mState != UserStartedState.STATE_STOPPING
                && uss.mState != UserStartedState.STATE_SHUTDOWN) {
            uss.mState = UserStartedState.STATE_STOPPING;//将用户的状态切换为正在停止
            updateStartedUserArrayLocked();//更新mStartedUserArray数组,存放UserStartedState对象

            long ident = Binder.clearCallingIdentity();
            try {
                // We are going to broadcast ACTION_USER_STOPPING and then
                // once that is done send a final ACTION_SHUTDOWN and then
                // stop the user.
                final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
                stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                stoppingIntent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true);
                final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
                // This is the result receiver for the final shutdown broadcast.
                final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
                    @Override
                    public void performReceive(Intent intent, int resultCode, String data,
                            Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                        finishUserStop(uss);//收到ACTION_SHUTDOWN广播,继续执行
                    }
                };
                // This is the result receiver for the initial stopping broadcast.
                final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() {
                    @Override
                    public void performReceive(Intent intent, int resultCode, String data,
                            Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                        // On to the next.
                        synchronized (ActivityManagerService.this) {
                            if (uss.mState != UserStartedState.STATE_STOPPING) {
                                // Whoops, we are being started back up.  Abort, abort!
                                return;
                            }
                            uss.mState = UserStartedState.STATE_SHUTDOWN;//收到ACTION_USER_STOPPING广播后,改变用户状态
                        }
                        mBatteryStatsService.noteEvent(
                                BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
                                Integer.toString(userId), userId);
                        mSystemServiceManager.stopUser(userId);
                        broadcastIntentLocked(null, null, shutdownIntent,
                                null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
                                true, false, MY_PID, Process.SYSTEM_UID, userId);//再发送广播shutdownIntent(Intent.ACTION_SHUTDOWN)
                    }
                };
                // Kick things off.
                broadcastIntentLocked(null, null, stoppingIntent,
                        null, stoppingReceiver, 0, null, null,
                        INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
                        true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);//发送广播stoppingIntent(Intent.ACTION_USER_STOPPING)
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        return ActivityManager.USER_OP_SUCCESS;
    }
stopUserLocked()方法首先判断请求停止的用户是否是当前用户,是,返回。由此可见,当前正在运行的用户是不能停止的,必须先切换到其他的用户才能再停止该用户。

接下来判断用户是否处于运行状态,没有运行就post一个消息,在消息的处理方法中调用参数中的回调方法结束处理。

如果用户已经运行,先切换用户的状态为STATE_STOPPING,然后广播一个用户正在停止的消息ACTION_USER_STOPPING,同时方法中也会接收这个广播,收到后切换用户的状态为STATE_SHUTDOWN,再发出一个ACTION_SHUTDOWN的广播,方法中同样也会接受这个广播,收到后再调用finishUserStop()方法继续处理,如下:

    void finishUserStop(UserStartedState uss) {
        final int userId = uss.mHandle.getIdentifier();
        boolean stopped;
        ArrayList<IStopUserCallback> callbacks;
        synchronized (this) {
            callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
            if (mStartedUsers.get(userId) != uss) {
                stopped = false;
            } else if (uss.mState != UserStartedState.STATE_SHUTDOWN) {
                stopped = false;
            } else {
                stopped = true;
                // User can no longer run.从AMS的用户管理的数据结构中删除用户
                mStartedUsers.remove(userId);
                mUserLru.remove(Integer.valueOf(userId));
                updateStartedUserArrayLocked();

                // Clean up all state and processes associated with the user.
                // Kill all the processes for the user.
                forceStopUserLocked(userId, "finish user");//发送广播Intent.ACTION_USER_STOPPED
            }

            // Explicitly remove the old information in mRecentTasks.
            removeRecentTasksForUserLocked(userId);//清除用户相关的Recent Task列表
        }

        for (int i=0; i<callbacks.size(); i++) {
            try {//调用UserManagerService的回调方法
                if (stopped) callbacks.get(i).userStopped(userId);
                else callbacks.get(i).userStopAborted(userId);
            } catch (RemoteException e) {
            }
        }

        if (stopped) {
            mSystemServiceManager.cleanupUser(userId);//从mStackSupervisor删除用户
            synchronized (this) {
                mStackSupervisor.removeUserLocked(userId);
            }
        }
    }
finishUserStop()方法的主要工作是从mStartedUsers和mUserLru中删除用户,然后发送广播Intent.ACTION_USER_STOPPED来通知某个用户停止了,接下来调用UserManagerService的回调方法通知UserManagerService来处理,最后调用removeUserLocked()方法从mStackSupervisor列表中删除用户的相关信息。

深入理解Android系统多用户 一、引言 这篇文章以Android v28的源码为参考,介绍Android多用户的特性、使用方式和系统原理。 二、初识Android多用户 2.1 Android多用户简介 从Android 4.0开始,Google就开始在Android上布局多用户,UserManager因此而诞生,然而此时还没有对应的Binder服务。真正支持多用户是从Android 4.2 开始,即便如此,系统中也依然存... 阅读详情

相关推荐

android 多用户管理UserManager

一.概述 Android从4.2开始支持多用户模式,不同的用户运行在不同的用户空间,相关的系统设置是各不相同而且不同用户安装的应用和应用数据也是不一样的,但是系统中和硬件相关的设计则是共用的。 Android的多用户实现主要通过UserManagerService(UMS)对多用户进行创建、删除等操作。 *Binder服务端:UMS继承于IUserManager.Stub,作为Binder服务端;...

weixin_34007291的博客 4127

深入理解Android系统多用户机制

直到Android 6.0,Android多用户才比较完善,国内外的厂家也纷纷开始针对多用户这个噱头来做各种 “花里胡哨” 的操作,“手机分身”、“分身应用”、“应用双开” 应运而生,不得不说,国内的厂家在多用户这方面定制化到如今已经非常稳定和完善了。

Jason_Lee155的博客 1万+

Android 5.0 SDK功能详解:深入探索android-21.zip

Material Design是Google推出的一套设计语言,它以其现代化、响应式的视觉表现和丰富的动效脱颖而出。在这一设计哲学的引导下,应用界面不仅美观而且富有互动性。Material Design强调纸张与墨水的模拟效果,如阴影、深度、动画等元素,以营造出一种物理世界中的存在感。

weixin_35578748的博客 1167

Android 多用户 —— 从入门到应用分身

https://mp.weixin.qq.com/s/CeoijS42tkNLejkHw0waOw 从Android L(5.0)开始引入多用户API。先不论权限问题,我们可以创建多个用户。Android为什么引入这样的多用户概念? 有至少以下2个意图: 实现"访客模式"这样的通常PC端支持的多用户模式。其特点设备可以支持多个人在不同时段登陆, 独享设备。比如上班时登陆A用户,回家给小孩玩,登陆B用户. A/B用户数据完全独立, 互不影响。 单纯为了"应用多开",原生的...

求变,从思想开始 1万+

Android多用户实现和原理

Android多用户实现和原理,用户添加/切换、删除等详解。

手把手拆解DGX GB200 NVL72:1个机柜如何塞进72块GPU?详解NVLink Switch铜缆背板设计

本文深度解析英伟达DGX GB200 NVL72系统如何通过NVLink Switch铜缆背板设计,在标准42U机柜中集成72块B200 GPU,实现1800GB/s的超高带宽。文章详细介绍了模块化架构、铜缆互连技术、液冷散热方案和高密度供电系统等核心创新,为AI算力需求提供突破性解决方案。

red88的博客 1574

android切换账户功能,安卓9系统开启多用户 android 9支持多用户切换吗?

android 9支持多用户切换吗?消息说是支持的呢Android中的APP如何实现多帐号切换?这个并不难,你可以同时在服务器的数据库中关联一个主键下的多个账户,每一个账户都有唯一的主键,用主键可以便利所有的账户,在客户端便可以实现时时切换。华为P9多用户模式怎么用?在手机“设置”里,点击“用户”进入多用户管理界面哦。然后你可以选择添加用户或者切换用户或者授权用户使用电话、信息及共享通讯记录哦。1...

weixin_26950847的博客 2874

故障处理:Oracle一体机磁盘故障时磁盘组重平衡失败的故障处理

摘要: Oracle Exadata一体机出现磁盘故障案例,存储节点故障灯未亮但磁盘已产生大量坏块,导致重平衡失败。文章强调了一体机硬件巡检需检查6个关键维度:节点messages日志、存储cell告警日志、CellCLI日志、ASM告警日志、ILOM日志及服务器信号灯。故障分析显示硬盘物理故障导致磁盘组冗余受损,自动修复流程受阻。文中详细记录了通过CellCLI工具排查损坏磁盘的过程,并提供了完整的故障处理流程和命令输出。文章同步发布于微信公众号"IT民工的龙马人生"和博客网站(www

提供数据库技术支持(系统优化,故障处理,安装升级,数据恢复等) 1230

[Android 5.1] 多用户管理UserManager相关整理

1. 概述当前版本(Android 5.1)下,多用户管理适用于平板模式(手机版本可能受限于版本等原因)。 多用户模式下,不同用户运行在不同的用户空间,共享具体的应用实例(即不同的用户下,其应用版本是一致的),但拥有各自不同的配置。 本文不会对具体的代码进行解读,只会对相关概念、关联性较强的部分加以说明,目的在于对Android下多用户管理进行整体描述。 主要相关的代码路径如下: fram

vivisoul的专栏 8844

算法-基于MACD的Adaboost股价涨跌预测模型

MACD是一个指标,具体用法是MACD>0看涨,反之看跌,果真是这样的吗?由于所有的技术指标都基于对历史数据的统计,指标的滞后性也就难免,有时候MACD明明大于0,股价仍然跌,有时候macd小于0,股价仍然涨。 本文基于Adaboost算法提出了一种基于macd的线性阀值分类器作为若分类器,通过在限定的解空间内寻找使得错误率最小的阀值和偏置,这样做避免了训练若分类器的复杂性,经验证,这种方法有效。

mtaxot的博客 8022

嵌入式学习(29)-ASM330LHH驱动程序

ASM330LHH是一种系统级封装器件,具有3D数字加速度计和3D数字陀螺仪,扩展温度范围上限为+105 °C,适用于汽车非安全领域的应用。ASM330LHH 的满量程加速范围为±2 g、±4 g、±8 g 和 ±16 g,角速度范围扩展至 ±125 dps、±250 dps、±500 dps、±1000 dps、± 2000 dps 和 ±4000 dps,适合各种汽车应用。

剑藏锋的博客 1175

Universal Android Debloater多用户模式使用教程:工作配置文件完全指南

想要**完全掌握**Android设备的**多用户模式**和**工作配置文件管理**?Universal Android Debloater Next Generation(UAD)提供了强大的**跨平台GUI工具**,通过ADB帮助你轻松管理非root Android设备上的所有用户配置。本**完整指南**将教你如何利用UAD的**多用户模式功能**,提升设备隐私、安全和电池寿命。 ## 🚀

gitblog_01133的博客 921

Python爬虫实战:5分钟搞定Web of Science文献批量导出与解析(附完整代码)

本文提供了一套利用Python爬虫技术,快速实现Web of Science文献批量提取与解析的完整方案。通过解析官方导出的HTML文件,结合BeautifulSoup和pandas库,可将文献数据自动转换为结构化的Excel/CSV表格,帮助科研人员高效管理文献信息,节省大量手动整理时间。

weixin_29050513的博客 1163

Android多用户模式的特性

虽说Android在4.2就开始支持多用户了,但是我手头上4.2系统的手机,并没有找到新用户创建的菜单。只好拿6.0系统的nexus5来做测试了。      一、手机状态信息不一样                              Owner用户

燕十二的BLOG 9763

【调优】OpenClaw调优之模型(tokens)篇

OpenClaw用户常遇四大痛点:规则被无视、API账单暴涨、低效模型选择、记忆丢失。通过/context list命令可解析上下文组成,包括工作区配置、引导参数限制、系统提示词等关键信息。重点需关注Token消耗来源,如核心配置文件、技能工具说明等,避免静默截断。建议精简非必要内容,优化记忆机制,平衡功能与成本。该分析帮助用户减少无效操作,合理控制API支出。

海兰 990

R语言基于机器学习三分类结果、计算三分类混淆矩阵、计算每一个类别对应的特异度(specificity)、灵敏度(sensitivity)、PPV、NPV

R语言基于机器学习三分类结果、计算三分类混淆矩阵、计算每一个类别对应的特异度(specificity)、灵敏度(sensitivity)、PPV、NPV

data+scenario+science+insight 644

android 多用户功能,Android多用户模式的特性

虽说Android在4.2就开始支持多用户了,但是我手头上4.2系统的手机,并没有找到新用户创建的菜单。只好拿6.0系统的nexus5来做测试了。一、手机状态信息不一样 Owner用户 其它用户手机状态差别:其它用...

weixin_31001739的博客 2586

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

android多用户在前台,Android 多用户模式下的特性

虽说 Android 在4.2就开始支持多用户了,但是我手头上4.2系统的手机,并没有找到新用户创建的菜单。只好拿6.0系统的 nexus5 来做测试了。一、手机状态信息不一样Owner 用户 其它用户手机状态差别 (其它用户获取信息受限):1.无SIM卡状态2.无IMEI信息3.wifi m...

weixin_39627144的博客 257

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

android多用户怎么管理系统应用,Android多用户模式下开发不得不知的注意事项

虽说 Android 在4.2就开始支持多用户了,但是笔者手头上4.2系统的手机,并没有找到新用户创建的菜单。只好拿6.0系统的 nexus5 来做测试了。一、手机状态信息不一样 Owner 用户 其它用户手机状态差别 (其它用户获取信息受限):1.无SIM卡状态2.无IMEI信息3.wi...

weixin_42509868的博客 758

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

android 多用户指定进程名字,Android多用户模式下开发不得不知的注意事项

虽说 Android 在4.2就开始支持多用户了,但是笔者手头上4.2系统的手机,并没有找到新用户创建的菜单。只好拿6.0系统的 nexus5 来做测试了。一、手机状态信息不一样 Owner 用户 其它用户手机状态差别 (其它用户获取信息受限):1.无SIM卡状态2.无IMEI信息3.wi...

weixin_42298881的博客 274

Android10.0-多用户切换流程源码分析

多用户切换流程 代码路径:packages/apps/Settings/src/com/android/settings/users/UserSettings.java 切换新用户流程: 1.多用户下点击事件 public boolean onPreferenceClick(Preference pref) 2.这段代码机主用户下切换新用户: else if (pref instanceof UserPrefe...

吉吉的博客 4354

Android-0.UMS简介及VA中多用户的实现

多用户涉及的基本类和知识点,及UMS创建,删除用户流程源码简介 VA中的多用户管理

hgy413的专栏 1857

Android4.2多用户模式的结构与实现

原文地址:Android4.2多用户模式的结构与实现在Android 4.2中的一个比较显著的改变就是加入了多用户的支持。因多用户手机专利早已被Symbian雇员注册,故 android官方的多用户切换目前仅支持平板设备。多用户模式的启用系统判断当前设备是否支持多用户模式的依据是配置文件config.xml中的config_multiuserMaximumUsers配置项。 其取值为整型,决定着当前

zlp1992的专栏 696

Android4.2的多用户管理中关于SD卡的实现改动

http://zzqhost.com/%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1_Android_%E4%BB%A3%E7%A0%81%E7%A0%94%E7%A9%B6_Android4.2%E7%9A%84%E5%A4%9A%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86%E4%B8%AD%E5%85%B3%E4%BA%8ESD%E5%8D

浩海天空 3756

免Root清理安卓预装应用:5分钟给安卓手机减重

三星S10出厂带着近百个预装组件,吃掉5GB存储,还在后台偷电。免Root清理安卓预装应用,是这类设备最省事的减负动作,工具名叫UAD。 ## 它是什么:Rust编写的免Root清理工具 Universal Android Debloater(UAD)是跨平台桌面程序,用Rust编写。它通过ADB(Android调试桥,电脑连接手机的控制通道)下发指令,全程不需要Root。同类方案多是命令行脚

gitblog_00663的博客 760
上一篇: 删除文件及文件夹
下一篇: Android--SparseArray<E>详细介绍
Kitty_London
博客等级 码龄12年 104粉丝 69原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值