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

7 篇文章 6 订阅

1.多用户涉及的基本类和知识点简介

代码参考AOSP 中 API 23 (android 6.0.1)

Android从4.2开始支持多用户模式,不同的用户运行在独立的用户空间,不同用户安装的应用和应用数据都是不一样的,但是系统中与硬件相关的设置则是共用的,比如网络设置等。

通常第一个在系统中注册的用户将成为系统管理员,可以管理手机上的其他用户。但由于专利的原因,目前手机上并没有开启多用户模式,这种模式只能用在平板上面。

手机上经常见到的一个功能就是访客模式,它是多用户模式的一种应用。访客模式下用户的所有数据(通讯录,短信,应用等)会被隐藏,访客只能查看和使用手机的基本功能,此外你还可以设置访客是否有接听电话、发送短信等权限。

1.1 基础慨念

1.1.1 userid

代表不同的用户,即手机里的主机、访客等多用户,类似于windows

1.1.2 UID

==>Linux uid
Linux是多用户系统,每个用户都拥有一个uid,这个uid由系统和用户名做映射绑定。同时,为了便于用户管理(譬如管理文档权限),Linux引入了群组的概念,可以将多个用户归于一个群组。每一个群组拥有一个群组id(gid)。

==>Android uid
Android的应用的uid的范围是从1000019999(FIRST_APPLICATION_UIDLAST_APPLICATION_UID)。
使用adb shellps命令查看到的pid,例如 u0_a94a表示FIRST_APPLICATION_UID, 后面的数字就是该应用的UID值减去FIRST_APPLICATION_UID所得的值,也就是appid, 具体字符串构造规则可参考后面介绍的formatUid函数。

应用安装后,系统重启和应用重启都不会改变uid
uid记录在data/system/packages.xml中,如查询VA的uid

generic_x86:/ # dumpsys package io.busniess.va |grep uid
    uid=10085 gids=null type=0 prot=signature

但要切记,userid用户不同,包名相同的uid也会不同,转换关系:

uid = userId * 100000  + appId

也就是真正不变的是appId

1.1.3 appId

userid用户无关的应用程序id, 取值范围 0<= appId <100000

即使是不同用户,包名相同的appid都一样。比如前面列举的 u0_a94中的94

UserHandlePER_USER_RANGE可知每个userid用户最大可以有100000个appid

1.1.4 userid区间分布

userid用户区间分布如下:

用户区间
system[1000, 9999]
application[10000, 19999]
SharedAppGid[50000, 59999]
isolated[99000, 99999]

isolated是隔离空间,也就是沙箱。

1.2 UserHandle类简介

常见函数如下:(结合前面提到的慨念,较好理解)

方法用途
public static final boolean isSameUser(int uid1, int uid2)比较两个uiduserId是否相同
public static final boolean isSameApp(int uid1, int uid2)比较两个uidappId是否相同
public static boolean isApp(int uid)appId是否属于区间[10000,19999]
public static final int getUserId(int uid)得到 userId,其实就是uid/10000
public static final int getCallingUserId()得到调用者的 userId
public static final UserHandle getCallingUserHandle()得到调用者的UserHandle类对象
public static final int getUid(int userId, int appId)得到Uid ,其实就是uid = userId * 100000 + appId
public static final int getAppId(int uid)得到appId
public int getIdentifier()得到当前UserHandle类对象所在的 userId
public static void formatUid(StringBuilder sb, int uid)uid组成字符串,如u0_a94

常见成员变量:(UserHandle的成员变量mHandle便是它所在的userId)

userId赋值含义
USER_OWNER0拥有者
USER_ALL-1所有用户
USER_CURRENT-2当前活动用户
USER_CURRENT_OR_SELF-3当前用户或者调用者所在用户
USER_NULL-1000未定义用户

类成员变量:

UserHandle OWNER = new UserHandle(USER_OWNER); // 0
UserHandle ALL = new UserHandle(USER_ALL); // -1
UserHandle CURRENT = new UserHandle(USER_CURRENT); // -2
UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF); // -3
1.2.1 UserHandle得到当前进程的userid

要得到当前进程的userIdmHandleuserId),客户端可以使用Process.myUserHandle().getIdentifier(), 代码实现如下:

    public static final UserHandle myUserHandle() {
        return new UserHandle(UserHandle.getUserId(myUid()));
    }
    --->UserHandle构造如下:
    public UserHandle(Parcel in) {
        mHandle = in.readInt();
    }
    --->UserHandle的getIdentifier如下:
    public int getIdentifier() {
        return mHandle;
    }

要得到调用者的进程userId,,服务端直接使用UserHandle.getCallingUserId()

1.2.2 UserHandle组成uid字符串规则

前面列举的 u0_a94,它的字符串组成可以参考formatUid方法:

    public static void formatUid(StringBuilder sb, int uid) {
        if (uid < Process.FIRST_APPLICATION_UID) {
            sb.append(uid);
        } else {
            sb.append('u');
            sb.append(getUserId(uid));
            final int appId = getAppId(uid);
            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
                sb.append('i');
                sb.append(appId - Process.FIRST_ISOLATED_UID);
            } else if (appId >= Process.FIRST_APPLICATION_UID) {
                sb.append('a');
                sb.append(appId - Process.FIRST_APPLICATION_UID);
            } else {
                sb.append('s');
                sb.append(appId);
            }
        }
    }

所以 u0_a94可以这样拆分,0是userId,a是Process.FIRST_APPLICATION_UID(10000)为基数,前面提到,由UserHandlePER_USER_RANGE可知每个userid用户最大可以有100000(十万)个appid,所以 u0_a94= 0*100000 + (10000 + 94)= 10094

1.3 UserInfo类简介

UserInfo代表的是一个用户的信息, 涉及到的flags及其含义,如下:

     * *************************** NOTE ***************************
     * 这些标志值无法更改,因为它们已被写入直接存储
     */

    /**
     * 主用户 仅仅只有一个user具有该标识. Meaning of this
     * flag TBD.
     */
    public static final int FLAG_PRIMARY = 0x00000001;

    /**
     * 具有管理特权的用户,例如它能创建或删除其他用户
     */
    public static final int FLAG_ADMIN   = 0x00000002;

    /**
     * 访客用户,可能是临时的
     */
    public static final int FLAG_GUEST   = 0x00000004;

    /**
     * 限制性用户,较普通用户具有更多限制,例如禁止安装app或者管理wifi等
     */
    public static final int FLAG_RESTRICTED = 0x00000008;

    /**
     * 表明用户已初始化
     */
    public static final int FLAG_INITIALIZED = 0x00000010;

    /**
     * 标志该UserInfo是否是另外一个用户的一份profile,Android中允许一个用户拥有另一份profile
     * 比如现在好多定制系统开发的应用分身,就是基于这个开发的,比如可以在同一个手机上启动两个微信帐号
	https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#ACTION_PROVISION_MANAGED_PROFILE
     */
    public static final int FLAG_MANAGED_PROFILE = 0x00000020;

    /**
     * 表明该用户处于禁用状态
     */
    public static final int FLAG_DISABLED = 0x00000040;

	 // 无效值定义
    public static final int NO_PROFILE_GROUP_ID = -1;

	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

	// 用来标记没有创建完成的用户
    public boolean partial;
    public boolean guestToRemove;

需要注意的是,用户的Id用来表示用户,如果用户被删除了它的Id会分配给下一个新建的用户,用来保持Id的连续性;
但是serialNumber是一个不会重复的数字,是不会被重复利用的,用来唯一标识一个用户。

1.4 UserState类简介

public final class UserState {
    // 用户正在启动状态
    public final static int STATE_BOOTING = 0;
    // 用户正在运行状态
    public final static int STATE_RUNNING = 1;
    // 用户正在关闭中
    public final static int STATE_STOPPING = 2;
    // 用户已经被关闭, sending Intent.ACTION_SHUTDOWN.
    public final static int STATE_SHUTDOWN = 3;

生命周期线:STATE_BOOTING -> STATE_RUNNING -> STATE_STOPPING -> STATE_SHUTDOWN

2. UMS 的启动和初始化过程

2.1 交互简介

从源码来分析:
AMS通过 UserManagerService getUserManagerLocked() 得到UMS
PMS通过sUserManager得到UMS
UMS通过mPm得到PMS

2.2. UMS 初始化流程

===>创建UMS的过程和AMSPMS一样位于startBootstrapServices中,它由PMS构造函数初始化。
启动代码位于frameworks/base/services/java/com/android/server/SystemServer.java中,代码如下:

  private void startBootstrapServices() {
  		 ...
  	   mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
         ... 
  }
  -->PackageManagerService构造函数如下:
   public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
            ....
             sUserManager = new UserManagerService(context, this,
                    mInstallLock, mPackages);
  }

===>接着看UMS的构造过程,可以看到PMS保存UMSsUserManager 成员),而UMS也保存PMSmPm 成员), 这个方法部分功能为:

  1. 保存了PMS对象到mPm 成员
  2. 创建第一个用户的目录/data/system/users/0
  3. 创建/data/system/users/userlist.xml,这个文件保存了系统中所有用户的id信息
  4. 读取userlist.xml文件,将用户的信息解析成UserInfo,并保存在mUsers列表中

代码如下:

UserManagerService(Context context, PackageManagerService pm,
            Object installLock, Object packagesLock) {
        this(context, pm, installLock, packagesLock,
                Environment.getDataDirectory(),
                new File(Environment.getDataDirectory(), "user"));
    }
 --->跳转到如下私有构造函数:
private UserManagerService(Context context, PackageManagerService pm,
            Object installLock, Object packagesLock,
            File dataDir,// dataDir = /data
             File baseUserPath) {// baseUserPath = /data/user
            mContext = context;
        	mPm = pm;
        	 mInstallLock = installLock;
    mPackagesLock = packagesLock;
    mHandler = new MainHandler();
    synchronized (mInstallLock) {
        synchronized (mPackagesLock) {
            // mUsersDir是/data/system/users目录
            mUsersDir = new File(dataDir, USER_INFO_DIR);
            mUsersDir.mkdirs();
            // 创建第一个用户的目录/data/system/users/0
            File userZeroDir = new File(mUsersDir, "0");
            userZeroDir.mkdirs();
            // 设置目录的权限
            FileUtils.setPermissions(mUsersDir.toString(),
                    FileUtils.S_IRWXU|FileUtils.S_IRWXG
                    |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                    -1, -1);
            // 创建代表/data/system/users/userlist.xml文件的对象
            mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
            // 添加一些对访客用户的默认限制,DISALLOW_OUTGOING_CALLS和DISALLOW_SMS,不允许打电话和发信息
            initDefaultGuestRestrictions();
            // 读取userlist.xml文件,将用户的信息保存在mUsers列表中
            // 如果该文件不存在则创建该文件
            readUserListLocked();
            sInstance = this;
        }
    }
    --->mUsers的定义如下:
    private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();

userlist.xml示例:

<?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>

nextSerialNumber指的是创建下一个用户时它的serialNumberversion指的是当前多用户的版本, userlist.xml 文件是可以升级的。
guestRestrictions标签指的是为访客用户设置的权限,可以通过UserManager.setDefaultGuestRestrictions()来设置。

前面userlist.xml示例里面有两个用户的信息,分别为 id 为 0 和 10 的用户 ,分别对应/data/system/users目录下0.xml10.xml(和userlist.xml同目录)以0.xml 为示例:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<user id="0" serialNumber="0" flags="19" created="0" lastLoggedIn="1493886997408">
    <name>机主</name>
    <restrictions />
</user>

标签的属性值对应了前面UserInfo里面的成员变量, readUserListLocked会根据文件的内容来创建和初始化一个UserInfo来保存到mUsers列表中去。

==>接着再看一下systemReady()函数,这里面也有一些初始化的工作,它的调用是在PackageManagerService.systemReady()中进行的。

--->PMS中的systemReady如下:
 public void systemReady() {
 	...
 	  sUserManager.systemReady();
 }
--->UMS中的systemReady如下:
void systemReady() {
    synchronized (mInstallLock) {
        synchronized (mPackagesLock) {
            // 寻找那些在userlist.xml文件中partially或者partial为true的用户
            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);
                }
            }
            // 将这些未创建完成的用户从系统中移除掉
            for (int i = 0; i < partials.size(); i++) {
                UserInfo ui = partials.get(i);
                removeUserStateLocked(ui.id);
            }
        }
    }
    // 更新一下OWNER用户的登陆时间
    onUserForeground(UserHandle.USER_OWNER);
    // 调用AppOpsManager(权限管理器)设置每个用户的权限
    mAppOpsService = IAppOpsService.Stub.asInterface(
            ServiceManager.getService(Context.APP_OPS_SERVICE));
    for (int i = 0; i < mUserIds.length; ++i) {
        try {
            mAppOpsService.setUserRestrictions(mUserRestrictions.get(mUserIds[i]), mUserIds[i]);
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
        }
    }
}

简单的说UMS 的初始化工作主要就是分析userlist.xml文件、创建用户列表mUsers以及设置用户权限。

2.3 创建用户

2.3.1 UMS

UMS 中创建用户是在createUser()方法中实现的:

    @Override
    public UserInfo createUser(String name, int flags) {
        checkManageOrCreateUsersPermission(flags);
        return createUserInternal(name, flags, UserHandle.USER_NULL);
    }
    --->
    // UID 是 system UID 或 root's UID 或APP有permission.MANAGE_USERS权限
    private static final void checkManageOrCreateUsersPermission(String message) {
        if (!hasManageOrCreateUsersPermission()) {
            throw new SecurityException(
                    "You either need MANAGE_USERS or CREATE_USERS permission to: " + message);
        }
    }

首先检查调用者的权限,UID 是 system UID 或 root’s UID 或APP有permission.MANAGE_USERS权限才有权限,否则抛出异常。然后就调用createUserInternal来执行真正的创建工作。

===>createUserInternal 这个方法部分功能为:

  1. 检查调用进程所属用户是否被限制添加用户、当前设备是否是低内存设备、当前用户数是否已达上限,如是则停止创建新用户
  2. 为新用户设置用户信息,比如Id,序列号创建时间等
  3. 将用户信息写入userlist.xml,注意,此时的userInfo.partial = true,表示正在创建
  4. 创建用户目录/data/user/或者/mnt/expand/user/
  5. 调用PMScreateNewUserLILPw方法,这个函数会在新建用户的目录下面为所有应用创建数据目录
  6. 创建用户完成,将userInfo.partial设置为false,创建用户的信息文件,比如0.xml
  7. 发送用户创建完成的广播Intent.ACTION_USER_ADDE

代码如下:

private UserInfo createUserInternal(String name, int flags, int parentId) {
    // 检查一下该用户是否被限制创建用户
    if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
            UserManager.DISALLOW_ADD_USER, false)) {
        return null;
    }
    // 检查是否是低内存设备
    if (ActivityManager.isLowRamDeviceStatic()) {
        return null;
    }
    final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
    final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
    final long ident = Binder.clearCallingIdentity();
    UserInfo userInfo = null;
    final int userId;
    try {
        synchronized (mInstallLock) {
            synchronized (mPackagesLock) {
                UserInfo parent = null;
                if (parentId != UserHandle.USER_NULL) {
                    parent = getUserInfoLocked(parentId);
                    if (parent == null) return null;
                }
                if (isManagedProfile && !canAddMoreManagedProfiles()) {
                    return null;
                }
                // 如果添加的不是Guest用户,也不是用户的profile,而且已经到达用户的上限,则不允许再添加
                if (!isGuest && !isManagedProfile && isUserLimitReachedLocked()) {
                    return null;
                }
                // 如果添加的是Guest用户,但是系统中已经存在一个,则不允许再添加
                if (isGuest && findCurrentGuestUserLocked() != null) {
                    return null;
                }
                // 得到新用户的Id
                userId = getNextAvailableIdLocked();
                // 为该新用户创建UserInfo
                userInfo = new UserInfo(userId, name, null, flags);
                // 设置序列号
                userInfo.serialNumber = mNextSerialNumber++;
                // 设置创建时间
                long now = System.currentTimeMillis();
                userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
                // 设置partial,表示正在创建
                userInfo.partial = true;
                Environment.getUserSystemDirectory(userInfo.id).mkdirs();
                // 放入mUsers列表
                mUsers.put(userId, userInfo);
                // 把用户信息写入userlist.xml
                writeUserListLocked();
                if (parent != null) {
                    if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
                        parent.profileGroupId = parent.id;
                        scheduleWriteUserLocked(parent);
                    }
                    userInfo.profileGroupId = parent.profileGroupId;
                }
                // 创建用户目录
                final StorageManager storage = mContext.getSystemService(StorageManager.class);
                for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
                    final String volumeUuid = vol.getFsUuid();
                    try {
                        final File userDir = Environment.getDataUserDirectory(volumeUuid,
                                userId);
                        // 创建userDir,如果volumeUuid为空,创建/data/user/<userId>/,不为空,创建/mnt/expand/<volumeUuid>/user/<userId>/
                        prepareUserDirectory(userDir);
                        enforceSerialNumber(userDir, userInfo.serialNumber);
                    } catch (IOException e) {
                        Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
                    }
                }
                // 调用PackageManagerService的createNewUserLILPw方法,此方法很重要,后面会单独分析
                // 这个函数会在新建用户的userDir目录下面为所有应用创建数据
                // 此方法将新用户可以使用的App在/data/user/<用户Id>文件夹下创建数据目录,目录名称为包名
                mPm.createNewUserLILPw(userId);
                // 创建成功,将partial改为false
                userInfo.partial = false;
                // 会调用writeUserLocked()创建xxx.xml,类似0.xml,xxx是用户Id
                scheduleWriteUserLocked(userInfo);
                // 更新mUserIds数组
                updateUserIdsLocked();
                Bundle restrictions = new Bundle();
                // 添加为该用户设置的限制条件
                mUserRestrictions.append(userId, restrictions);
            }
        }
        // 发送用户创建完成的广播,广播附带用户的id
        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;
}
2.3.2 PMS

===>前面函数调用了 mPm.createNewUserLILPw(userId)PMScreateNewUserLILPw用于创建用户数据,由UMS调用,代码如下:

    /** Called by UserManagerService */
    void createNewUserLILPw(int userHandle) {
        if (mInstaller != null) {
        //通过mInstaller调用守护进程installd执行mkuserconfig,创建用户配置文件。
        mInstaller.createUserConfig(userHandle);
        // 调用mSettings.createNewUserLILP为新用户中的应用创建应用数据目录
        mSettings.createNewUserLILPw(this, mInstaller, userHandle);
        // 设置默认的Browser
        applyFactoryDefaultBrowserLPw(userHandle);
        primeDomainVerificationsLPw(userHandle);
    }
    }
    ===> mSettings.createNewUserLILPw实现如下:
    void createNewUserLILPw(PackageManagerService service, Installer installer, int userHandle) {
    // 为每一个应用创建数据目录
    for (PackageSetting ps : mPackages.values()) {
        if (ps.pkg == null || ps.pkg.applicationInfo == null) {
            continue;
        }
        // Only system apps are initially installed.
        //为系统应用修改PackageUserState的installed标志
        ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
        // 通过mInstaller调用守护进程installd执行mkuserdata,为用户创建用户目录。
        installer.createUserData(ps.volumeUuid, ps.name,
                UserHandle.getUid(userHandle, ps.appId), userHandle,
                ps.pkg.applicationInfo.seinfo);
    }
    // TODO
    applyDefaultPreferredAppsLPw(service, userHandle);
    writePackageRestrictionsLPr(userHandle);
    writePackageListLPr(userHandle);
}

这个函数会在新建用户的目录下面为所有应用创建数据目录

2.4 切换用户

用户切换流程:ActivityManager.switchUser--> ActivityManagerNative.getDefault().switchUser-->ActivityManagerService.switchUser,代码如下:

--->ActivityManager.java:
    public boolean switchUser(int userid) {
        try {
            return ActivityManagerNative.getDefault().switchUser(userid);
        } catch (RemoteException e) {
            return false;
        }
    }
 --->ActivityManagerProxy.switchUser
 public boolean switchUser(int userid) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeInt(userid);
        mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
        reply.readException();
        boolean result = reply.readInt() != 0;
        reply.recycle();
        data.recycle();
        return result;
    }
-->AMS中的switchUser:
  public boolean switchUser(final int userId) {
        enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
        String userName;
        synchronized (this) {
            UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
            if (userInfo == null) {
                Slog.w(TAG, "No user info for user #" + userId);
                return false;
            }
            // 该user只是一个user的profile,无法切换
            if (userInfo.isManagedProfile()) {
                Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
                return false;
            }
            userName = userInfo.name;
             // 把该用户Id记录到mTargetUserId变量
            mTargetUserId = userId;
        }
        // 发送切换用户的消息
        mUiHandler.removeMessages(START_USER_SWITCH_MSG);
        mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));
        return true;
    }

===>Handler收到START_USER_SWITCH_MSG消息后,会调用showUserSwitchDialog()来弹出一个确认的对话框, 代码如下:

  case START_USER_SWITCH_MSG: {
        showUserSwitchDialog(msg.arg1, (String) msg.obj);
        break;
                
   ---> showUserSwitchDialog代码如下:
   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();
}            

===>点击确定后最终会调用到startUser()来执行切换用户的动作

--->UserSwitchingDialog.java
    public void onWindowShown() {
        // Slog.v(TAG, "onWindowShown called");
        startUser();
    }
--->
private boolean startUser(final int userId, final boolean foreground) {
    //切换用话需要INTERACT_ACROSS_USERS_FULL权限
    if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
            != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException(msg);
    }
    final long ident = Binder.clearCallingIdentity();
    try {
        synchronized (this) {
            // 如果当前用户已经是需要切换的用户,退出当前流程
            final int oldUserId = mCurrentUserId;
            if (oldUserId == userId) {
                return true;
            }
            mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
                    "startUser", false);
            // 如果没有需要启动的用户的信息,则直接退出
            final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
            if (userInfo == null) {
                return false;
            }
            // 如果是前台启动且是一份profile的话,则直接退出
            if (foreground && userInfo.isManagedProfile()) {
                return 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 UserState(new UserHandle(userId), false));
                updateStartedUserArrayLocked();
                needStart = true;
            }
            // 调整该用户在mUserLru列表中的位置,当前用户放到最后位置
            final Integer userIdInt = Integer.valueOf(userId);
            mUserLru.remove(userIdInt);
            mUserLru.add(userIdInt);
            if (foreground) {
                // 如果是前台切换,直接切换到需要启动的用户
                mCurrentUserId = userId;
                // 重置mTargetUserId,因为userId已经赋值给mCurrentUserId了
                mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
                // 更新与该用户相关的profile列表
                updateCurrentProfileIdsLocked();
                // 在WindowManagerService中设置要启动的用户为当前用户
                mWindowManager.setCurrentUser(userId, mCurrentProfileIds);
                // Once the internal notion of the active user has switched, we lock the device
                // with the option to show the user switcher on the keyguard.
                // 执行锁屏操作
                mWindowManager.lockNow(null);
                }
            } else {
                // 如果是后台启动
                final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
                // 更新与该用户相关的profile列表
                updateCurrentProfileIdsLocked();
                mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
                // 重新调整mUserLru
                mUserLru.remove(currentUserIdInt);
                mUserLru.add(currentUserIdInt);
            }
            final UserState uss = mStartedUsers.get(userId);
            if (uss.mState == UserState.STATE_STOPPING) {
                // 如果该用户是正在停止,这个时候还没有发送ACTION_SHUTDOWN广播,则切换为正在运行
                uss.mState = UserState.STATE_RUNNING;
                // 更新mStartedUserArray列表
                updateStartedUserArrayLocked();
                needStart = true;
            } else if (uss.mState == UserState.STATE_SHUTDOWN) {
                // 如果该用户是正在停止,这个时候已经发送ACTION_SHUTDOWN广播,则切换为正在启动状态
                uss.mState = UserState.STATE_BOOTING;
                updateStartedUserArrayLocked();
                needStart = true;
            }
            if (uss.mState == UserState.STATE_BOOTING) {
                // 如果用户的状态是正在启动,则发送一个SYSTEM_USER_START_MSG消息,该消息的处理下面会介绍
                mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
            }
            if (foreground) {
                // 发送SYSTEM_USER_CURRENT_MSG消息
                mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
                        oldUserId));
                // 分别发送REPORT_USER_SWITCH_MSG和USER_SWITCH_TIMEOUT_MSG,防止切换时间过长,后面会介绍
                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) {
                // 发送一个 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,
                        null, false, false, MY_PID, Process.SYSTEM_UID, userId);
            }
            if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
                // 当该用户还没有初始化,如果是普通用户则会发送ACTION_USER_INITIALIZE广播,如果是机主用户,则直接标记为已初始化
                if (userId != UserHandle.USER_OWNER) {
                    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,
                            null, true, false, MY_PID, Process.SYSTEM_UID, userId);
                    // 标记为正在初始化,该变量会在completeSwitchAndInitialize()重置
                    uss.initializing = true;
                } else {
                    getUserManagerLocked().makeInitialized(userInfo.id);
                }
            }
            if (foreground) {
                // 如果用户已经初始化过了,则设置为前台用户,后面会介绍这一部分
                if (!uss.initializing) {
                    moveUserToForeground(uss, oldUserId, userId);
                }
            } else {
                // 如果是后台启动用户,先加入到mStartingBackgroundUsers列表中去
                mStackSupervisor.startBackgroundUserLocked(userId, uss);
            }
            if (needStart) {
                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,
                        new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
                        null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
            }
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
    return true;
}

===>下面来分析一下moveUserToForeground()函数

void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
    //从mStackSupervisor获取newUserId用户在切换之前的stack状态,以便将原来在前台的应用推到前台
    boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss);
    if (homeInFront) {
        // 如果原来是从桌面切换的用户,则启动桌面
        startHomeActivityLocked(newUserId, "moveUserToFroreground");
    } else {
        // 如果是其他应用,则将此应用推到前台
        mStackSupervisor.resumeTopActivitiesLocked();
    }
    EventLogTags.writeAmSwitchUser(newUserId);
    getUserManagerLocked().onUserForeground(newUserId);
    // 发送ACTION_USER_BACKGROUND广播,通知和oldUserId相关的用户(包括profile)进入后台的消息
    // 发送ACTION_USER_FOREGROUND广播,通知和newUserId相关的用户(包括profile)进入前台的消息
    // 发送ACTION_USER_SWITCHED广播,通知用户切换
    sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
}

2.4 删除用户

2.4.1 UMS

UserManagerService中删除用户是在removeUser()方法中实现的:

public boolean removeUser(int userHandle) {
    // 检查该进程是否具有删除用户的权限
    checkManageUsersPermission("Only the system can remove users");
    // 检查一下该用户是否被限制删除用户
    if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
            UserManager.DISALLOW_REMOVE_USER, false)) {
        return false;
    }
    long ident = Binder.clearCallingIdentity();
    try {
        final UserInfo user;
        synchronized (mPackagesLock) {
            user = mUsers.get(userHandle);
            // 检查被删除的用户是不是管理员用户userHandle=0,检查用户列表中是否有该用户,以及该用户是否是正在被删除的用户
            if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
                return false;
            }
            // 将该用户放入mRemovingUserIds列表中,防止重复删除
            // mRemovingUserIds中的数据会一直保存直到系统重启,防止Id被重复使用
            mRemovingUserIds.put(userHandle, true);
            try {
                mAppOpsService.removeUser(userHandle);
            } catch (RemoteException e) {
                Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
            }
            // 将partial设置为true,这样如果后面的过程意外终止导致此次删除失败,系统重启后还是会继续删除工作的
            user.partial = true;
            // 设置FLAG_DISABLED,禁止该用户
            user.flags |= UserInfo.FLAG_DISABLED;
            // 将上面更新的用户文件信息写入到xml文件中去
            writeUserLocked(user);
        }
        // 如果该user是一个user的一份profile,则发出一个ACTION_MANAGED_PROFILE_REMOVED广播
        if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                && user.isManagedProfile()) {
            sendProfileRemovedBroadcast(user.profileGroupId, user.id);
        }
        // 调用AMS停止当前的用户,这部分后面会详细介绍
        int res;
        try {
            res = ActivityManagerNative.getDefault().stopUser(userHandle,
                    // 设置回调函数,调用finishRemoveUser继续后面的删除工作
                    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);
    }
}

ActivityManagerNative.getDefault().stopUser执行完后 UMS 会继续执行删除工作。

void finishRemoveUser(final int 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) {
                        new Thread() {
                            public void run() {
                                synchronized (mInstallLock) {
                                    synchronized (mPackagesLock) {
                                        removeUserStateLocked(userHandle);
                                    }
                                }
                            }
                        }.start();
                    }
                },
                null, Activity.RESULT_OK, null, null);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

根据代码可以看到finishRemoveUser方法只是发送了一个有序广播ACTION_USER_REMOVED,同时注册了一个广播接收器,这个广播接收器是最后一个接收到该广播的接收器,这样做的目的是让关心该广播的其他接收器处理完之后, UMS 才会进行删除用户的收尾工作,即调用removeUserStateLocked来删除用户的相关文件。

private void removeUserStateLocked(final int userHandle) {
    // 调用mPm.cleanUpUserLILPw来删除用户目录/data/user/<用户id>/下面的应用数据,后面会详细介绍
    mPm.cleanUpUserLILPw(this, userHandle);
    // 从mUsers列表中移除该用户信息
    mUsers.remove(userHandle);
    AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
    // 删除用户信息文件 <用户id>.xml
    userFile.delete();
    // 更新userlist.xml文件,将删除调用的用户从中移除
    writeUserListLocked();
    // 更新mUserIds列表
    updateUserIdsLocked();
    // 删除用户目录以及该用户的所有文件
    removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
}
2.4.2 AMS

UMSremoveUser()会调用AMSstopUser()来处理停止用户的一些工作,在AMS内部也会调用stopUser()。该方法在进行了权限检查之后,主要的工作都是由stopUserLocked()来完成的。

void finishUserStop(UserState 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 != UserState.STATE_SHUTDOWN) {
            stopped = false;
        } else {
            stopped = true;
            // User can no longer run.
            mStartedUsers.remove(userId);
            mUserLru.remove(Integer.valueOf(userId));
            updateStartedUserArrayLocked();
            // 杀掉和该用户相关的所有进程
            forceStopUserLocked(userId, "finish user");
        }
        // 清除用户相关的Recent Task列表
        mRecentTasks.removeTasksForUserLocked(userId);
    }
    //调用在stopUserLocked中添加的回调函数
    for (int i=0; i<callbacks.size(); i++) {
        try {
            if (stopped) callbacks.get(i).userStopped(userId);
            else callbacks.get(i).userStopAborted(userId);
        } catch (RemoteException e) {
        }
    }
    if (stopped) {
        // 回调各个SystemServer的onCleanupUser方法
        mSystemServiceManager.cleanupUser(userId);
        synchronized (this) {
            mStackSupervisor.removeUserLocked(userId);
        }
    }
}

finishUserStop()方法从mStartedUsersmUserLru列表中删除该用户,更新mStartedUserArray列表,清理和该用户有关的进程,发送Intent.ACTION_USER_STOPPED广播来通知该用户已经停止,接下来清除用户相关的Recent Task列表以及从mStackSupervisor中删除用户的信息。

private void forceStopUserLocked(int userId, String reason) {
    // 杀掉该用户相关的所有进程,具体流程会在ActivityManager相关文章中介绍
    forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason);
    // 发出Intent.ACTION_USER_STOPPED广播
    Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
    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,
            null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
2.4.3 PMS

删除用户数据cleanUpUserLILPw()

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.
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
            final String volumeUuid = vol.getFsUuid();
            mInstaller.removeUserDataDirs(volumeUuid, userHandle);
        }
    }
    mUserNeedsBadging.delete(userHandle);
    removeUnusedPackagesLILPw(userManager, userHandle);
}

删除用户的工作比较简单,删除用户的数据。同时调用mSettings.removeUserLPw(userHandle)来删除和 PMS 中和用户相关的信息。

void removeUserLPw(int userId) {
    // 删除每个应用中的该用户的信息
    Set<Entry<String, PackageSetting>> entries = mPackages.entrySet();
    for (Entry<String, PackageSetting> entry : entries) {
        entry.getValue().removeUser(userId);
    }
    mPreferredActivities.remove(userId);
    File file = getUserPackagesStateFile(userId);
    file.delete();
    file = getUserPackagesStateBackupFile(userId);
    file.delete();
    removeCrossProfileIntentFiltersLPw(userId);
    mRuntimePermissionsPersistence.onUserRemoved(userId);
    // 更新/data/system/packages.list文件
    writePackageListLPr();
}

3.VirtualApp中USM的实现

3.1 VUserHandle类简介

VUserHandle基本全参考了UserHandle,相同的方法接口

3.2 UidSystem类简介

    // packageName<-->uid映射
    private final HashMap<String, Integer> mSharedUserIdMap = new HashMap<>();
    private int mFreeUid = FIRST_APPLICATION_UID; // 10000

uid的管理类

  1. mSharedUserIdMap 记录了包名---uid的映射
  2. uid从10001(FIRST_APPLICATION_UID+1)开始依次递增,
  3. 每次创建一个新uid后,都把mSharedUserIdMap + mFreeUid 写入到/data/data/packageName/virtual/data/app/system/uid-list.ini中,同时把先前的uid-list.ini保存在/data/data/packageName/virtual/data/app/system/uid-list.ini.bak,关键方法如下:
    public int getOrCreateUid(VPackage pkg) {
        synchronized (mSharedUserIdMap) {
            String sharedUserId = pkg.mSharedUserId;
            if (sharedUserId == null) {
                sharedUserId = pkg.packageName; // eg: com.hgy.ndk
            }
            Integer uid = mSharedUserIdMap.get(sharedUserId);
            if (uid != null) {
                return uid;// 此包名对应的uid已创建
            }
            int newUid = ++mFreeUid;
            mSharedUserIdMap.put(sharedUserId, newUid);
            save();
            return newUid;
        }
    }

3.3. VUserInfo类简介

类似于系统的UserInfo

3.4 VUserManagerService类简介

类似于系统的UMS,基于IUserManager.aidlIUserManager.aidl只提供了部分接口,如setUserRestrictions等限制权限的函数是没有的。
它的构造流程基本抄了系统的UMS,可以参考前面的流程

==>同样在PMSsystemReady初始化,但它没有作为成员变量保存到PMS

    public static void systemReady() {
        new VUserManagerService(VirtualCore.get().getContext(), get(), new char[0], get().mPackages);
    }

==>构造函数和系统的UMS基本一样,这个方法部分功能为:

  1. 保存了PMS对象到mPm 成员
  2. 创建第一个用户的目录/data/data/ xxx /virtual/data/system/users/0
  3. 创建/data/data/ xxx /virtual/data/system/users/userlist.xml,这个文件保存了系统中所有用户的id信息
  4. 读取userlist.xml文件,将用户的信息解析成UserInfo,并保存在mUsers列表中
  5. userlist.xml文件移除所有部分创建或部分删除的用户记录

代码如下:

 private VUserManagerService(Context context, VPackageManagerService pm,
                                Object installLock, Object packagesLock,
                                File dataDir, File baseUserPath) { // 109
        // dataDir=/data/data/io.busniess.vatools/virtual/data
        // baseUserPath=/data/data/io.busniess.vatools/virtual/data/user
        mContext = context;
        mPm = pm;
        mInstallLock = installLock;
        mPackagesLock = packagesLock;
        synchronized (mInstallLock) {
            synchronized (mPackagesLock) {
                // mUsersDir=/data/data/io.busniess.vatools/virtual/data/system/users
                mUsersDir = new File(dataDir, USER_INFO_DIR);
                mUsersDir.mkdirs();
                // 创建第一个用户的目录/data/data/io.busniess.vatools/virtual/data/system/users/0
                File userZeroDir = new File(mUsersDir, "0");
                userZeroDir.mkdirs();
                mBaseUserPath = baseUserPath;
//                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);
                // 读取userlist.xml文件,将用户的信息保存在mUsers列表中
                readUserListLocked();

                // 去掉任何部分创建/部分删除的用户。
                ArrayList<VUserInfo> partials = new ArrayList<VUserInfo>();
                for (int i = 0; i < mUsers.size(); i++) {
                    VUserInfo ui = mUsers.valueAt(i);
                    if (ui.partial & i != 0) {
                        partials.add(ui);
                    }
                }
                for (int i = 0; i < partials.size(); i++) {
                    VUserInfo ui = partials.get(i);
                    VLog.w(LOG_TAG, "Removing partially created user #" + i
                            + " (name=" + ui.name + ")");
                    removeUserStateLocked(ui.id);
                }
                sInstance = this;
            }
        }
    }

===>writeUserListLocked写入/data/data/ xxx/virtual/data/system/users/userlist.xml
===>writeUserLockedVUserInfo写入到data/data/io.busniess.vatools/virtual/data/system/users目录下
===>fallbackToSingleUserLocked创建第一个user并调用writeUserListLocked写入userlist.xml,并调用writeUserLocked写入

3.5 UMS的hook

参考UserManagerStub.java, 主要实现:
1.将 setApplicationRestrictionsgetApplicationRestrictionsgetApplicationRestrictionsForUser的包名换成io.busniess.va
2.将 getProfileParentgetUserIcongetDefaultGuestRestrictionssetDefaultGuestRestrictionsremoveRestrictions,createUser,等都返回null

参考:
多用户管理UserManager
Android UserManager相关源码分析

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值