Android5.1--多用户模式

2023-05-16


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列表中删除用户的相关信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android5.1--多用户模式 的相关文章

  • Spark2 Linear Regression线性回归

    本文使用SparkMllib搭建简单的线性回归例子 数据下载地址 package SparkMlibCourse3 import org span class hljs preprocessor apache span span class
  • 2011

    2011 Problem Description The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits G
  • linux 自动关机 计划任务

    设置192 168 67 232 231在21 xff1a 30自动关机 1 xff09 手动命令方式 xff03 shutdown h 21 30 2 xff09 计划任务方式 xff0c 定时关机 1 添加定时任务 root 64 se
  • 微信自动回复(python)

    前言 xff1a 自从微信禁止网页版登陆之后 xff0c itchat 库也就都不能用了 xff0c 新号不能再登录网页版 xff0c 而且itchat 库在headers里面只放了UserAgent xff0c 其他字段都没有放 所以在你
  • linux下性能监控shell脚本实现系列一(服务器整体性能监控)

    在实现监控脚本前 xff0c 我们先了解下一些已经成型的监控程序 xff0c 比如大名鼎鼎的nmon nmon官网 http nmon sourceforge net pmwiki php nmon使用简单说明 http www ibm c
  • python utf-8转汉字

    其实也不麻烦 xff0c 看下边的代码 汉字编码转换 testgbk 61 39 汉字 39 testunit 61 testgbk decode 39 gbk 39 汉字解码 testutf8 61 testgbk encode 39 u
  • linux中如何查看进程启动时间,持续时长

    root 64 gasdb2 Transsoft ps eo pid lstart etime grep 5359 5359 Tue Sep 16 14 47 22 2014 00 43 其中5359为进程号 xff0c 方便做过滤用的 T
  • 错误: 程序包org.apache.log4j不存在问题处理

    将maven中依赖修改如下 xff1a lt dependency gt lt groupId gt log4j lt groupId gt lt artifactId gt log4j lt artifactId gt lt versio
  • 构建maven项目失败解决办法

    通过eclipse的maven插件新建maven项目时 xff0c 出现如下错误 Unable to create project from archetype org apache maven archetypes maven arche
  • 关于java中的非静态代码块和静态代码块

    非静态代码块如 xff1a System out println 34 2 34 以上代码块就是非静态的代码块 xff0c 请注意这里的方法名 作用域 返回值 参数一概没有 xff0c 非静态代码块会在每次类被调用或者被实例化时就会被执行
  • jsoninclude.include.non_null 不起作用问题

    在开发过程中 xff0c 遇到在属性上加jsoninclude include non null注解 xff0c 有些属性起了作用 xff0c 有些却没有起作用 xff0c 下面使用代码简要说明一下 xff1a 64 Data public
  • 再次认识java的序列化

    首先是我们为什么要序列化 xff1f 我们可以来想想游戏的场景 xff0c 一个游戏有很多关卡 xff0c 并不是一次性能够打完的 如果我们打完一关 xff0c 这时候需要退出游戏休息了 当我们再次进入游戏之后发现这个游戏竟然需要重新打起
  • 控制actionbar中的向上键

    android中会给配置了 android parentActivityName 的activity默认加入一个向上返回键 xff0c 如下 xff1a 当点击向上返回后 xff0c 默认的行为是finish自身 xff0c startac
  • Android Studio快捷键

    Alt 43 回车 导入包 自动修正 Ctrl 43 N 查找类 Ctrl 43 Shift 43 N 查找文件 Ctrl 43 Alt 43 L 格式化代码 Ctrl 43 Alt 43 O 优化导入的类和包 Alt 43 Insert
  • Ubuntu 16.04 出现E: Problem executing scripts E: Sub-process returned an error code处理办法

    E Problem executing scripts APT Update Post Invoke Success 39 if usr bin test w var cache app info a e usr bin appstream
  • android studio “leaked window“ 错误

    昨天玩我做的app 发现app运行没问题 xff0c 但log里面出现下面的错误 xff1a Activity com example thirdversionclock MainActivity welcome has leaked wi
  • Archlinux 安装桌面环境 dwm + polybar

    Archlinux 安装桌面环境 dwm 43 polybar 关于git一 安装一些必要软件1 壁纸 状态栏等软件2 安装dwm和st 三 安装Polybar四 Fish Shell五 登录管理器六 一些个人使用的工具 2023 1 31
  • Arch Linux 安装和配置(陆续更新)

    Arch Linux 安装和配置 陆续更新 南国一年一度的回南天到来 xff0c 周末百无聊赖 xff0c 研究一下Arch Linux xff0c 整理此文 xff0c 其他使用笔记陆续补充 一 在Vmware中安装Arch Linux
  • ASP.NET Core MemoryCache 缓存

    ASP NET Core 中的缓存内存 xff08 MemoryCache xff09 ASP NET Core 中的缓存内存 ASP NET Core 中的分布式缓存 xff08 SQL Server 和 Redis 分布式缓存 xff0
  • FFmpeg In Android - 多媒体文件解封装/解码/保存Yuv

    FFMPEG视音频编解码零基础学习方法 100行代码实现最简单的基于FFMPEG 43 SDL的视频播放器 本文例子的源码 demuxing decoding cpp 修改自源码自带的例子ffmpeg源码 doc example demux

随机推荐

  • 【Java篇】多线程详解

    Java 多线程基础详解 文章目录 Java 多线程基础详解线程的相关概念一 创建线程的几种方式1 继承Thread类2 实现Runnable接口3 实现Callable接口4 推荐使用Runnable接口 二 线程安全1 线程安全问题引入
  • NotePad++ XMLTools 插件离线安装

    在使用NotePad 43 43 时 xff0c 在某些情形下 xff0c 需要格式化Xml格式内容 xff0c 可以使用Xml Tools插件 xff0c 注意下载安装包时 xff0c 需下载与NotePad 43 43 像匹配版本的插件
  • 【Windows逆向】【Qt】日志信息打印

    目录 x1f6eb 导读需求开发环境 1 示例程序Demo2 编写功能 xff08 QtCreator版本 xff09 3 编写功能 xff08 VS版本 xff09 x1f6ec 文章小结 x1f4d6 参考资料 x1f6eb 导读 需求
  • Ubuntu 18.04 安装ROS melodic文件错误问题broken packages

    反复多次尝试安装ros melodic xff0c 一直报错 xff0c 有文件损坏或者安装依赖问题 直接进入安装阶段 xff0c 前面的请看其他详细帖子 sudo apt span class token operator span ge
  • 在虚拟机安装Archlinux

    最近花了挺长一段时间练习在虚拟机安装archlinux的 xff0c 在这里跟大家分享一下经验 xff0c 如有错误 xff0c 欢迎大家指出 xff0c 谢谢大家 准备工作 archlinux镜像 43 vmware workstatio
  • Linux系统启动流程及系统裁剪

    一 内核管理简要理论 1 内核的功能 xff08 1 xff09 进程管理 xff08 2 xff09 内存管理 xff08 内核管理代码中代码量最大的部分 xff09 xff08 3 xff09 I O管理 xff1a 中断及中断处理 x
  • UNIX环境高级编程习题——第三章

    第三章习题 3 1 当读 写磁盘文件时 xff0c 本章中描述的函数确实是不带缓冲机制的吗 xff1f 请说明原因 xff1a span class hljs number 1 span 本章中描述的read和write函数都是系统调用 x
  • Ubuntu 16.04 安装Vmware Workstation12

    1 安装Vmware Workstation12 1 从官网上获取http www vmware com products workstation workstation evaluation html 2 如果觉得上面的方法下载得比较慢
  • Idea2017查看Class字节码文件

    Idea查看字节码文件的原理 1 javap命令的使用 在jdk工具包的bin目录下 xff0c 有一个java可执行文件javap xff0c 该工具可以查看java编译后的class文件 使用命令如下命令进行查看 javap span
  • Idea配置Web项目路径以及使用非默认Tomcat启动

    1 Web项目发布路径配置 1 首先点击Run gt Edit Configurations 2 点击左上角绿色的加号 xff0c 选择Tomcat gt Local 3 点击Deployment 4 点击绿色的小铅笔 5 在此处设置Out
  • emacs下org-mode导出pdf时pdflatex无法找到的问题解决方案

    配置环境 Deepin15 6 Linux emacs25 2 发现的问题 系统没有找到pdflatex命令 xff0c org mode无法导出latex的pdf 解决步骤 安装texlive2018 因为pdflatex是texlive
  • 通过Flask框架封装Tushare获取的日线股票数据

    概要介绍 概要介绍 xff08 TuShare id 282782 xff09 当我们需要进行量化交易分析 xff0c 或者通过代码进行股票的数据计算 xff0c 研究金融时 xff0c 我们需要获取最基本的股票价格 xff0c 开盘价收盘
  • IBM Was 打补丁记录

    0 拷贝解压ifph52925升级包 通过FTP工具 xff0c 把压缩包传到服务器 xff0c unzip d test01 9 0 0 0 ws was ifph52925 zip 1 停掉was 服务 ps ef grep was k
  • CoreText --- 段落样子CTParagraphStyle

    在前面一篇文章中 xff0c 介绍了属性文字的基本使用 xff0c 本章节主要针对文字的段落样式展开演示说明 先定义一段演示文字 xff08 文字中有中 xff0c 英文 xff09 cpp view plain copy NSString
  • 将自己的域名解析跳转到博客主页(GitHub中的gitpage跳转)

    最近突然迷上了博客 xff0c 突然又突发奇想 xff0c 将自己几个月前买的现在限制的域名拿来跳转到自己的csdn博客 经过一番研究 xff0c 总结 把自己的购买的域名 比如我买的circleyuan top 跳转到CSDN博客 只需要
  • Python3.4简单爬虫实现之抓取糗事百科段子

    网上的python教程大都是2 X版本的 xff0c python2 X和python3 X相比较改动比较大 xff0c 好多库的用法不太一样 xff0c 我安装的是3 4 1 xff0c 就用3 4 1实现一下网页内容抓取 首先是库 xf
  • 【C++】类和对象的关系

    概念 xff1a 对象 xff1a 将数据和对数据的操作方法放在一起 xff0c 形成一个相对独立的整体 属性和操作是对象的两大要素 类 xff1a 某一类对象所共有的 本质的属性和类行为 类和对象的关系 类是抽象的 xff0c 对象是具体
  • 美团笔试题_20220409

    前言 笔试一共五道编程题 xff08 四 43 一 xff09 xff0c 一为专项编程题 xff0c 估计不同岗位有题目不一样 xff0c 使用的是赛码网 xff0c 允许跳出界面使用自己的IDE 在此感谢筱羊冰冰提供的部分题目及题解 题
  • Android应用管理一 -- APK包的安装、卸载和优化(PackageManagerService)

    Android的应用管理主要是通过PackageManagerService来完成的 PackageManagerService服务负责各种APK包的安装 卸载 优化和查询 PackageManagerService在启动时会扫描所有的AP
  • Android5.1--多用户模式

    Android中的多用户与Windows的多用户类似 xff0c 可以支持多个用户使用系统 通常 xff0c 第一个在系统中注册的用户将默认成为系统管理员 不同用户的设置各不相同 xff0c 并且不同用户安装的应用及应用数据也不相同 但是系