Android6.0vold除了通信部分改动不大,其他基本改动很大,那我们就从头开始分析一下吧。
一、vold初始化
先看下main函数中下面这段代码
if (!(vm = VolumeManager::Instance())) {//new 了volumemanager,构造函数中就是一些成员变量初始化
LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
}
if (!(nm = NetlinkManager::Instance())) {//new 了NetlinkManager,构造函数中就是一些成员变量初始化
LOG(ERROR) << "Unable to create NetlinkManager";
exit(1);
}
if (property_get_bool("vold.debug", false)) {
vm->setDebug(true);
}
cl = new CommandListener();//新建一个CommandListener
ccl = new CryptCommandListener();
vm->setBroadcaster((SocketListener *) cl);//CommandListener也负责和MountService通信
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {//VolumManager的start方法
PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}
下面我们主要看下VolumManager的start方法:
int VolumeManager::start() {
// Always start from a clean slate by unmounting everything in
// directories that we own, in case we crashed.
unmountAll();//unmount所有的
// Assume that we always have an emulated volume on internal
// storage; the framework will decide if it should be mounted.
CHECK(mInternalEmulated == nullptr);
mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
new android::vold::EmulatedVolume("/data/media"));
mInternalEmulated->create();
return 0;
}
新建了一个EmulatedVolume,先看下构造函数
EmulatedVolume::EmulatedVolume(const std::string& rawPath) :
VolumeBase(Type::kEmulated), mFusePid(0) {
setId("emulated");
mRawPath = rawPath;
mLabel = "emulated";
}
把data/media赋给了mRawPath变量
二、通知MountService emulated volume建立
接下来我们再看下EmulatedVolume的create函数,因为EmulatedVolume没有create函数,我们就看VolumeBase::create
status_t VolumeBase::create() {
CHECK(!mCreated);
mCreated = true;
status_t res = doCreate();
notifyEvent(ResponseCode::VolumeCreated,
StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
setState(State::kUnmounted);
return res;
}
在create函数中给MountService发送VolumeCreated命令,然后将该volume设置成Unmounted状态
我们看下MountService的onEvent处理:
case VoldResponseCode.VOLUME_CREATED: {
final String id = cooked[1];
final int type = Integer.parseInt(cooked[2]);
final String diskId = TextUtils.nullIfEmpty(cooked[3]);
final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
final DiskInfo disk = mDisks.get(diskId);
final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
mVolumes.put(id, vol);
onVolumeCreatedLocked(vol);
break;
}
注意本来MountService就在addInternalVolume函数中,在mVolumes加了data目录的volume。和这个不同
我们再来看看onVolumeCreatedLocked函数
onVolumeCreatedLocked方法会发送H_VOLUME_MOUNT消息,我们就不详细说这个函数了。因为vold发送上来的type是emulated的,于是
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
来看看这个消息的处理吧:
case H_VOLUME_MOUNT: {
final VolumeInfo vol = (VolumeInfo) msg.obj;
if (isMountDisallowed(vol)) {
Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
break;
}
try {
mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
vol.mountUserId);
} catch (NativeDaemonConnectorException ignored) {
}
break;
}
处理是直接给vold发送的mount的命令。
下面我们看看VolumeCmd::runCommand下的这段代码是处理MountService发给来的mount命令的
} else if (cmd == "mount" && argc > 2) {
// mount [volId] [flags] [user]
std::string id(argv[2]);
auto vol = vm->findVolume(id);
if (vol == nullptr) {
return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
vol->setMountFlags(mountFlags);
vol->setMountUserId(mountUserId);
int res = vol->mount();
if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {//如果是Primary的调用volumeManager的setprimary方法
vm->setPrimary(vol);
}
return sendGenericOkFail(cli, res);
}
三、建立mnt/user下的软链接
int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
mPrimary = vol;
for (userid_t userId : mStartedUsers) {
linkPrimary(userId);
}
return 0;
}
遍历已经启动的user,再调用linkPrimary方法:
int VolumeManager::linkPrimary(userid_t userId) {
std::string source(mPrimary->getPath());
if (mPrimary->getType() == android::vold::VolumeBase::Type::kEmulated) {
source = StringPrintf("%s/%d", source.c_str(), userId);
fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
}
std::string target(StringPrintf("/mnt/user/%d/primary", userId));
if (TEMP_FAILURE_RETRY(unlink(target.c_str()))) {
if (errno != ENOENT) {
SLOGW("Failed to unlink %s: %s", target.c_str(), strerror(errno));
}
}
LOG(DEBUG) << "Linking " << source << " to " << target;
if (TEMP_FAILURE_RETRY(symlink(source.c_str(), target.c_str()))) {
SLOGW("Failed to link %s to %s: %s", source.c_str(), target.c_str(),
strerror(errno));
return -errno;
}
return 0;
}
上面这段代码就是把storage/emulated/0/ 创建软链接到mnt/user/0/primary
四、挂载emulated的volume
我们来看下volumeBase的mout函数
status_t VolumeBase::mount() {
if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
return -EBUSY;
}
setState(State::kChecking);//设置状态,没设置一个状态都会往MountService发送
status_t res = doMount();
if (res == OK) {
setState(State::kMounted);
} else {
setState(State::kUnmountable);
}
return res;
}
我们再来看看EmulatedVolume的doMount函数
status_t EmulatedVolume::doMount() {
// We could have migrated storage to an adopted private volume, so always
// call primary storage "emulated" to avoid media rescans.
std::string label = mLabel;
if (getMountFlags() & MountFlags::kPrimary) {
label = "emulated";
}
mFuseDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str());
mFuseRead = StringPrintf("/mnt/runtime/read/%s", label.c_str());
mFuseWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str());
setInternalPath(mRawPath);
setPath(StringPrintf("/storage/%s", label.c_str()));
if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << getId() << " failed to create mount points";
return -errno;
}
dev_t before = GetDevice(mFuseWrite);
if (!(mFusePid = fork())) {
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-m",
"-w",
mRawPath.c_str(),
label.c_str(),
NULL)) {
PLOG(ERROR) << "Failed to exec";
}
LOG(ERROR) << "FUSE exiting";
_exit(1);
}
if (mFusePid == -1) {
PLOG(ERROR) << getId() << " failed to fork";
return -errno;
}
while (before == GetDevice(mFuseWrite)) {
LOG(VERBOSE) << "Waiting for FUSE to spin up...";
usleep(50000); // 50ms
}
return OK;
}
这个函数中先设置了setInternalPath,setPath,这两个函数最终都会通知MountService,在下面两个地方进行处理
case VoldResponseCode.VOLUME_PATH_CHANGED: {
if (cooked.length != 3) break;
final VolumeInfo vol = mVolumes.get(cooked[1]);
if (vol != null) {
vol.path = cooked[2];
}
break;
}
case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
if (cooked.length != 3) break;
final VolumeInfo vol = mVolumes.get(cooked[1]);
if (vol != null) {
vol.internalPath = cooked[2];
}
break;
}
其实InternalPath就是一个内存存储真正的路径data/media,path就是storage/emulated,而我们再看init.rc中一段
mount none /mnt/runtime/default /storage slave bind rec
将mnt/runtime/default挂载到storage上,
symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
init.rc中还有上面一个软链接,也就是说storage/self/primary也是/mnt/user/0/primary 的一个软链接
而对storage/emulated是利用fuse文件系统,再去读取data/media的
mount结束后最后在mount函数中setState(State::kMounted);
然后在MountService做如下处理,把mVolumes新的状态保存下来,再调用onVolumeStateChangedLocked
case VoldResponseCode.VOLUME_STATE_CHANGED: {
if (cooked.length != 3) break;
final VolumeInfo vol = mVolumes.get(cooked[1]);
if (vol != null) {
final int oldState = vol.state;
final int newState = Integer.parseInt(cooked[2]);
vol.state = newState;
onVolumeStateChangedLocked(vol, oldState, newState);
}
break;
}
onVolumeStateChangedLocked函数中会去调用mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);通知各个listener状态改变