您的位置:首页 > 移动开发 > Android开发

Android Multi_User 添加新用户源码分析

2016-03-31 15:28 489 查看
PackageManagerService

1,首先看UserManager中的createUser函数,他是创建新用户的入口

      public UserInfo createUser(String name, int flags) {

            try {

                //调用UserManagerService的createUser方法

                return mService.createUser(name, flags);

            } catch (RemoteException re) {

                Log.w(TAG, "Could not create a user", re);

                return null;

            }

        }

复制代码
由上可知,该段代码主要功能就是调用了UserManagerService的对应的createUser函数。

2,下面我们重点分析一下UserManagerService的createUser方法

@Override

    public UserInfo createUser(String name, int flags) {

        //权限检查

        checkManageUsersPermission("Only the system can create users");

        final long ident = Binder.clearCallingIdentity();

        final UserInfo userInfo;

        try {

            synchronized (mInstallLock) {

                synchronized (mPackagesLock) {

                    //如果当前用户数,已经到达支持数目,则停止创建新用户

                    if (isUserLimitReachedLocked()) return null;

                    //生成用户ID

                    int userId = getNextAvailableIdLocked();

                    //创建一个userInfo实例

                    userInfo = new UserInfo(userId, name, null, flags);

                    //指向“data/user/userId”文件夹

                    File userPath = new File(mBaseUserPath, Integer.toString(userId));

                    //mNextSerialNumber就是UserManagerService的构造函数中读取到的

                    userInfo.serialNumber = mNextSerialNumber++;

                    long now = System.currentTimeMillis();

                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;

                    //该值为true,表明用户尚未创建成功

                    userInfo.partial = true;

                    //为新用户创建“data/system/users/userId”目录

                    Environment.getUserSystemDirectory(userInfo.id).mkdirs();

                    //将新用户添加到mUsers 中

                    mUsers.put(userId, userInfo);

                    //将新用户信息写入到“data/system/users/userlist.xml”之中

                    writeUserListLocked();

                    //将新用户信息写入到“data/system/users/userId.xml”之中

                    writeUserLocked(userInfo);

                    mPm.createNewUserLILPw(userId, userPath);

                    //新用户创建成功

                    userInfo.partial = false;

                    //更新新用户的partial属性为false,更新“data/system/users/userId.xml”

                    writeUserLocked(userInfo);

                    //更新mUserIds数组

                    updateUserIdsLocked();

                    //这是用户限制“restrictions”相关的内容,将在其他地方讲解

                    Bundle restrictions = new Bundle();

                    mUserRestrictions.append(userId, restrictions);

                }

            }

            if (userInfo != null) {

                //发送广播Intent.ACTION_USER_ADDED

                //同时此广播附带Intent.EXTRA_USER_HANDLE类型的userInfo.id消息,

                //便于接收到广播的组件读取用户ID

                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;

    }

复制代码
由上面代码可知,UserManagerService的createUser方法主要工作如下:

A,检查当前用户数是否已达上限,如是则停止创建新用户

B,创建新用户userInfo

C,准备创建“data/user/userId”文件夹

D,为新用户创建“data/system/users/userId”文件夹

E,将新用户信息写入到“data/system/users/userlist.xml”文件中

F,将新用户信息写入到“data/system/users/userId.xml”文件之中

G,调用PackageManagerService的createNewUserLILPw,此方法很重要,将在后面重点讲解。此方法首先创建“data/user/userId”文件夹,然后将新用户可以使用的App从“system/app”等地方的APP复制一份到“data/user/userId”文件夹下

H,用户正式创建成功,userInfo.partial值为false,更新“data/system/users/userId.xml”

I,发送Intent.ACTION_USER_ADDED广播,附带了新用户的userId


收藏0

 回复

举报

  
西夏





250
主题
7
好友
1万
积分
超级版主





发消息
沙发


 发表于
2014-1-16 18:29:03 |只看该作者

3,下面看看writeUserListLocked函数所写的“data/system/users/userlist.xml”的格式

/**

             *   <?xml version="1.0" encoding="utf-8" standalone="yes" ?> 

             *  - <users nextSerialNumber="11" version="2">

             *       <user id="0" /> 

             *       <user id="10" /> 

             *   </users>

             */

复制代码
具体,可自行从你的安卓设备中下载查看。

4,下面再看看writeUserLocked函数所写的“data/system/users/userId.xml”文件的格式

/**

     *  <?xml version="1.0" encoding="utf-8" standalone="yes" ?> 

     * - <user id="11" serialNumber="14" flags="16" created="1388534774982" 

     *     lastLoggedIn="1388534863493" icon="/data/system/users/11/photo.png">

     *      <name>新用户</name> 

     *      <restrictions /> 

     *   </user>

     */

复制代码
5,下面重点分析PackageManagerService的createNewUserLILPw方法

    void createNewUserLILPw(int userHandle, File path) {

        if (mInstaller != null) {

            mSettings.createNewUserLILPw(this, mInstaller, userHandle, path);

        }

    }

复制代码
A,该方法调用了Settings中的createNewUserLILPw方法
 
 回复

举报

  
西夏





250
主题
7
好友
1万
积分
超级版主





发消息
板凳


 发表于
2014-1-16 18:32:17 |只看该作者

6,下面看一下Settings的createNewUserLILPw函数

void createNewUserLILPw(PackageManagerService service, Installer installer,

            int userHandle, File path) {

        //创建“data/user/userId”文件夹

        path.mkdir();

        FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG

                | FileUtils.S_IXOTH, -1, -1);

        for (PackageSetting ps : mPackages.values()) {

            // Only system apps are initially installed.

            //存放在"system/app"文件夹下的app都属于系统应用

            //他们都具有这样的属性:(ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0

            //将所有"system/app"文件夹下的app的PackageUserState的installed属性置为true

            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);

        }

        // 从”etc/preferred-apps”目录下的XML文件

        readDefaultPreferredAppsLPw(service, userHandle);

        // 写当前用户的“data/system/users/userId/package-restrictions.xml”文件

        writePackageRestrictionsLPr(userHandle);

    }

复制代码
A,该方法首先将所有“system/app”文件夹下的系统应用的installed属性设置为true

B,然后调用Installer.java中的createUserData函数

C,读取”etc/preferred-apps”目录下的XML文件

D,写入当前用户的“data/system/users/userId/package-restrictions.xml”文件

7,下面我们重点看看writePackageRestrictionsLPr函数,该函数源码如下

void writePackageRestrictionsLPr(int userId) {

        if (DEBUG_MU) {

            Log.i(TAG, "Writing package restrictions for user=" + userId);

        }

        // Keep the old stopped packages around until we know the new ones have

        // been successfully written.

        //指向文件“data/system/users/userId/package-restrictions.xml”

        File userPackagesStateFile = getUserPackagesStateFile(userId);

        //指向文件“data/system/users/userId/package-restrictions-backup.xml”

        File backupFile = getUserPackagesStateBackupFile(userId);

        new File(userPackagesStateFile.getParent()).mkdirs();

        if (userPackagesStateFile.exists()) {

            // Presence of backup settings file indicates that we failed

            // to persist packages earlier. So preserve the older

            // backup for future reference since the current packages

            // might have been corrupted.

            if (!backupFile.exists()) {

                if (!userPackagesStateFile.renameTo(backupFile)) {

                    Log.wtf(PackageManagerService.TAG, "Unable to backup user packages state file, "

                            + "current changes will be lost at reboot");

                    return;

                }

            } else {

                userPackagesStateFile.delete();

                Slog.w(PackageManagerService.TAG, "Preserving older stopped packages backup");

            }

        }

        try {

            final FileOutputStream fstr = new FileOutputStream(userPackagesStateFile);

            final BufferedOutputStream str = new BufferedOutputStream(fstr);

            final XmlSerializer serializer = new FastXmlSerializer();

            serializer.setOutput(str, "utf-8");

            serializer.startDocument(null, true);

            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

            //XML标记:package-restrictions

            serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);

            for (final PackageSetting pkg : mPackages.values()) {

                PackageUserState ustate = pkg.readUserState(userId);

                if (ustate.stopped || ustate.notLaunched || !ustate.installed

                        || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT

                        || ustate.blocked

                        || (ustate.enabledComponents != null

                                && ustate.enabledComponents.size() > 0)

                        || (ustate.disabledComponents != null

                                && ustate.disabledComponents.size() > 0)) {

                    //XML标记:pkg

                    serializer.startTag(null, TAG_PACKAGE);

                    //XML标记:name

                    serializer.attribute(null, ATTR_NAME, pkg.name);

                    if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);

                    if (!ustate.installed) {

                        //XML标记:inst

                        serializer.attribute(null, ATTR_INSTALLED, "false");

                    }

                    if (ustate.stopped) {

                        //XML标记:stopped

                        serializer.attribute(null, ATTR_STOPPED, "true");

                    }

                    if (ustate.notLaunched) {

                        //XML标记:nl

                        serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");

                    }

                    if (ustate.blocked) {

                        //XML标记:

                        serializer.attribute(null, ATTR_BLOCKED, "true");

                    }

                    if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {

                        //XML标记:enabled

                        serializer.attribute(null, ATTR_ENABLED,

                                Integer.toString(ustate.enabled));

                        if (ustate.lastDisableAppCaller != null) {

                            //XML标记:enabledCaller

                            serializer.attribute(null, ATTR_ENABLED_CALLER,

                                    ustate.lastDisableAppCaller);

                        }

                    }

                    if (ustate.enabledComponents != null

                            && ustate.enabledComponents.size() > 0) {

                        //XML标记:enabled-components

                        serializer.startTag(null, TAG_ENABLED_COMPONENTS);

                        for (final String name : ustate.enabledComponents) {

                            //XML标记:item

                            serializer.startTag(null, TAG_ITEM);

                            //XML标记:name

                            serializer.attribute(null, ATTR_NAME, name);

                            //XML标记:item

                            serializer.endTag(null, TAG_ITEM);

                        }

                        //XML标记:enabled-components

                        serializer.endTag(null, TAG_ENABLED_COMPONENTS);

                    }

                    if (ustate.disabledComponents != null

                            && ustate.disabledComponents.size() > 0) {

                        //XML标记:disabled-components

                        serializer.startTag(null, TAG_DISABLED_COMPONENTS);

                        for (final String name : ustate.disabledComponents) {

                            serializer.startTag(null, TAG_ITEM);

                            serializer.attribute(null, ATTR_NAME, name);

                            serializer.endTag(null, TAG_ITEM);

                        }

                        serializer.endTag(null, TAG_DISABLED_COMPONENTS);

                    }

                    //XML标记:pkg

                    serializer.endTag(null, TAG_PACKAGE);

                }

            }

            writePreferredActivitiesLPr(serializer, userId, true);

            serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);

            serializer.endDocument();

            str.flush();

            FileUtils.sync(fstr);

            str.close();

            // New settings successfully written, old ones are no longer

            // needed.

            backupFile.delete();

            FileUtils.setPermissions(userPackagesStateFile.toString(),

                    FileUtils.S_IRUSR|FileUtils.S_IWUSR

                    |FileUtils.S_IRGRP|FileUtils.S_IWGRP,

                    -1, -1);

            // Done, all is good!

            return;

        } catch(java.io.IOException e) {

            Log.wtf(PackageManagerService.TAG,

                    "Unable to write package manager user packages state, "

                    + " current changes will be lost at reboot", e);

        }

        // Clean up partially written files

        if (userPackagesStateFile.exists()) {

            if (!userPackagesStateFile.delete()) {

                Log.i(PackageManagerService.TAG, "Failed to clean up mangled file: "

                        + mStoppedPackagesFilename);

            }

        }

    }

复制代码
A,该函数主要工作就是写入当前用户的“data/system/users/userId/package-restrictions.xml”
 
 回复

举报

  
西夏





250
主题
7
好友
1万
积分
超级版主





发消息
地板


 发表于
2014-1-16 18:35:18 |只看该作者

8,下面我们看看Installer.java中的createUserData函数

   public int createUserData(String name, int uid, int userId) {

        //发送一个记号为“mkuserdata”的socket通信

        StringBuilder builder = new StringBuilder("mkuserdata");

        builder.append(' ');

        builder.append(name);

        builder.append(' ');

        builder.append(uid);

        builder.append(' ');

        builder.append(userId);

        return execute(builder.toString());

    }

复制代码
A,该方法发送了一个记号为“mkuserdata”的socket通信

9,该通信的接收处理方是一个C文件installd.c,我们看看该文件的源码

struct cmdinfo cmds[] = {

    { "ping",                 0, do_ping },

    { "install",              4, do_install },

    { "dexopt",               3, do_dexopt },

    { "movedex",              2, do_move_dex },

    { "rmdex",                1, do_rm_dex },

    { "remove",               2, do_remove },

    { "rename",               2, do_rename },

    { "fixuid",               3, do_fixuid },

    { "freecache",            1, do_free_cache },

    { "rmcache",              2, do_rm_cache },

    { "getsize",              6, do_get_size },

    { "rmuserdata",           2, do_rm_user_data },

    { "movefiles",            0, do_movefiles },

    { "linklib",              3, do_linklib },

    { "mkuserdata",           3, do_mk_user_data },

    { "rmuser",               1, do_rm_user },

};

static int do_mk_user_data(char **arg, char reply[REPLY_MAX])

{

    return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, userid */

}

复制代码
A,该文件定义了若干socket通信命令的处理函数

B,由上我们可知,是调用了installd.c中的do_mk_user_data这个函数,而在这个函数中则调用了commands.c中的make_user_data函数
 
 回复

举报

  
西夏





250
主题
7
好友
1万
积分
超级版主





发消息
5#


 发表于
2014-1-16 18:38:15 |只看该作者

10,我们接着看看commands.c中的make_user_data函数源码

int make_user_data(const char *pkgname, uid_t uid, userid_t userid)

{

        //形如"data/user/userId/包名"的路径

    char pkgdir[PKG_PATH_MAX];

    //形如"data/app-lib"的路径

    char applibdir[PKG_PATH_MAX];

    //形如"data/user/userId/包名/lib"的路径

    char libsymlink[PKG_PATH_MAX];

    struct stat libStat;

    // Create the data dir for the package

    //PKG_DIR_POSTFIX 定义是空

    //#define PKG_DIR_POSTFIX        ""

    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) {

        return -1;

    }

    //#define PKG_LIB_POSTFIX        "/lib"

    if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userid)) {

        ALOGE("cannot create package lib symlink origin path\n");

        return -1;

    }

    //形如"data/app-lib"的路径

    if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {

        ALOGE("cannot create package lib symlink dest path\n");

        return -1;

    }

    //创建新的文件夹,权限-rwxr-x--x

    if (mkdir(pkgdir, 0751) < 0) {

        ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));

        return -errno;

    }

    //将新的文件夹权限设置为-rwxr-x--x

    if (chmod(pkgdir, 0751) < 0) {

        ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));

        unlink(pkgdir);

        return -errno;

    }

    if (lstat(libsymlink, &libStat) < 0) {

        if (errno != ENOENT) {

            ALOGE("couldn't stat lib dir for non-primary: %s\n", strerror(errno));

            unlink(pkgdir);

            return -1;

        }

    } else {

        if (S_ISDIR(libStat.st_mode)) {

            if (delete_dir_contents(libsymlink, 1, 0) < 0) {

                ALOGE("couldn't delete lib directory during install for non-primary: %s",

                        libsymlink);

                unlink(pkgdir);

                return -1;

            }

        } else if (S_ISLNK(libStat.st_mode)) {

            if (unlink(libsymlink) < 0) {

                ALOGE("couldn't unlink lib directory during install for non-primary: %s",

                        libsymlink);

                unlink(pkgdir);

                return -1;

            }

        }

    }

    //让新用户的App中的lib文件指向"data/app-lib"文件中的lib

    if (symlink(applibdir, libsymlink) < 0) {

        ALOGE("couldn't symlink directory for non-primary '%s' -> '%s': %s\n", libsymlink,

                applibdir, strerror(errno));

        unlink(pkgdir);

        return -1;

    }

    if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {

        ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));

        unlink(libsymlink);

        unlink(pkgdir);

        return -errno;

    }

    if (chown(pkgdir, uid, uid) < 0) {

        ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));

        unlink(libsymlink);

        unlink(pkgdir);

        return -errno;

    }

    return 0;

}

复制代码
A,该函数中首先创建形如"data/user/userId/包名"的路径

B,然后创建形如"data/user/userId/包名/lib"的路径

C,在data/app-lib目录下创建相应目录

D,设置"data/user/userId/包名"目录的权限是0751,-rwxr-x--x

E,让新用户的"data/user/userId/包名/lib"文件指向"data/app-lib"文件中的lib
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: