Android存储系统-MountService 和vold 对外置存储的管理(3)

        Android 4.4就实现了多用户功能,但是一直没有正式开发,直到Android 7.0添加了基于文件的加密能力,每个用户可以使用自己的加密空间,这才完全开放,今天我们就来分析下Android的多用户目录管理。在Android存储系统-MountService 和vold 对外置存储的管理(1) 一文中我们看到Android的MountService启动后会发送user_added 和 user_started命令。 来回顾一下:

frameworks/base/services/core/java/com/android/server/MountService.java

    private void resetIfReadyAndConnectedLocked() {
        Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
                + ", mDaemonConnected=" + mDaemonConnected);
        if (mSystemReady && mDaemonConnected) {
            killMediaProvider();

            mDisks.clear();
            mVolumes.clear();

            addInternalVolume();

            try {
                mConnector.execute("volume", "reset");

                // Tell vold about all existing and started users
                final UserManager um = mContext.getSystemService(UserManager.class);
                final List<UserInfo> users = um.getUsers();
                for (UserInfo user : users) {
                    mConnector.execute("volume", "user_added", user.id, user.serialNumber);
                }
                for (int userId : mStartedUsers) {
                    mConnector.execute("volume", "user_started", userId);
                }
            } catch (NativeDaemonConnectorException e) {
                Slog.w(TAG, "Failed to reset vold", e);
            }
        }
    }

        resetIfReadyAndConnectedLocked函数在systemReady时被调用,首先发送volume reset命令触发磁盘的重新挂载。之后发送user_added命令,来添加用户, 最后发送user_started命令来启动用户。 磁盘挂载过程我们已经在Android存储系统-MountService 和vold 对外置存储的管理(1)中进行了分析,这里只关注多用户相关的命令。

system/vold/CommandListener.cpp

int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
                                           int argc, char **argv) {
    ......
    } else if (cmd == "user_added" && argc > 3) {
        // user_added [user] [serial]
        return sendGenericOkFail(cli, vm->onUserAdded(atoi(argv[2]), atoi(argv[3])));

    }
    ......
}

        对于user_added命令的处理,直接调用vm->onUserAdded(atoi(argv[2]), atoi(argv[3]))来完成。

system/vold/VolumeManager.cpp

int VolumeManager::onUserAdded(userid_t userId, int userSerialNumber) {
    mAddedUsers[userId] = userSerialNumber;
    return 0;
}

        addUser的处理很简单,只是将user的序列号按照userId保存起来。 再来看下user_started命令的处理:

system/vold/CommandListener.cpp

int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
                                           int argc, char **argv) {
    ......
    } else if (cmd == "user_started" && argc > 2) {
        // user_started [user]
        return sendGenericOkFail(cli, vm->onUserStarted(atoi(argv[2])));

    } 
    ......
}

         同样调用vm->onUserStarted(atoi(argv[2])来处理。

system/vold/VolumeManager.cpp

static const char* kUserMountPath = "/mnt/user";
int VolumeManager::onUserStarted(userid_t userId) {
    // Note that sometimes the system will spin up processes from Zygote
    // before actually starting the user, so we're okay if Zygote
    // already created this directory.
    std::string path(StringPrintf("%s/%d", kUserMountPath, userId));
    fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);

    mStartedUsers.insert(userId);
    if (mPrimary) {
        linkPrimary(userId);
    }
    return 0;
}

         onUserStarted函数首先创建/mnt/user/${userid} 目录,然后将userId插入到以启动的user集合里面。最后如果已经确定主存储,调用linkPrimary(userId) 来链接主存储上user的目录空间。

system/vold/VolumeManager.cpp

 421 int VolumeManager::linkPrimary(userid_t userId) {
 422     std::string source(mPrimary->getPath());
 423     if (mPrimary->getType() == android::vold::VolumeBase::Type::kEmulated) {
 424         source = StringPrintf("%s/%d", source.c_str(), userId);
 425         fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
 426     }
 427 
 428     std::string target(StringPrintf("/mnt/user/%d/primary", userId));
 429     if (TEMP_FAILURE_RETRY(unlink(target.c_str()))) {
 430         if (errno != ENOENT) {
 431             SLOGW("Failed to unlink %s: %s", target.c_str(), strerror(errno));
 432         }
 433     }
 434     LOG(DEBUG) << "Linking " << source << " to " << target;
 435     if (TEMP_FAILURE_RETRY(symlink(source.c_str(), target.c_str()))) {
 436         SLOGW("Failed to link %s to %s: %s", source.c_str(), target.c_str(),
 437                 strerror(errno));
 438         return -errno;
 439     }
 440     return 0;
 441 }

         如果主存储是一个PublicVolume, 则主存储只有挂载它的用户可见,不用区分多用户,否则在主存储的目录下创建 ${userid}目录,然后将 /mnt/user/${userid}/primary 用软连接指向 主存储下的${userid}目录,给该用户在主存储上划出一个专用目录。 所以在我们在Android存储系统-MountService 和vold 对外置存储的管理(1) 一文中看到Pixel的多用户目录如下

/mnt/user/0/primary -> /storage/emulated/0

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值