在android3.1以上的系统中,有MTP的挂载。但是我的介绍还是关于UMS大容量存储的数据访问方式,我们没有在我们新系统中采用MTP方式。
顺便提一句,在merge到android4.2.2上以后,源码中有关于多用户的特性。对挂载还是有一定的影响的。要想去掉多用户,其实改动大但是不多。
一直在做vold的开发,现在有时间刚好总结一下。
init.rc中有这么一段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Directory for putting things only root should see.
mkdir /mnt/secure 0700 root root
# Directory for staging bindmounts
mkdir /mnt/secure/staging 0700 root root
# Directory-target for where the secure container
# imagefile directory will be bind-mounted
mkdir /mnt/secure/asec 0700 root root
# Secure container public mount points.
mkdir /mnt/asec 0700 root system
mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000
mkdir /storage/sd_external 0000 system system
symlink /storage/sd_external /sd_external
# Filesystem image public mount points.
mkdir /mnt/obb 0700 root system
mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
|
会在系统启动时,创建这些文件夹。最后一项是启动vold Deamon,并且创建socket(LocalSocket)
当SD卡挂载时,/mnt/sdcard/.android_secure 目录会被映射到/mnt/asec 目录和 /mnt/secure 目录。其中/mnt/asec 目录中主要是程序的安装目录,包括其执行文件和lib文件等;而/mnt/secure 目录中就存放程序加密后的档案。(当前,前提是app是安装在sdcard上的,app安装在sdcard上时,会被分割成几块,其中asec文件夹中的.asec文件就是一部分)。
另外还要说明的是,在SDcard unmount过程中,会删除掉/mnt/asec 和 /mnt/secure的文件,因为这个时候,sdcard是mount到PC上的,所以/mnt/sdcard/.android_secure的文件不会映射到/mnt/asec 和 /mnt/secure中了。
1 2 3 4 |
vold.fstab
emmc_mount sdcard /storage/sdcard0 22 /devices/platform/msm_sdcc.1/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p22
dev_mount sdcard /mnt/usbstorage auto /devices/platform/msm_hsusb_host
dev_mount sd_external /storage/sd_external auto /devices/platform/msm_sdcc.3/mmc_host
|
emmc_mount/dev_mount代表命令,会在main.cpp创建Volume做区分
sdcard表示标签,指向挂载点
/storage/sdcard0表示挂载点
22/auto 表示子分区个数
最后一大串字符串 表示设备在sysfs文件系统下的路径
接下来详细的走一遍,来清理下思路,来了解当SDcard插拔时,从下是怎么往上的,vold是如何接受uevent的;
在vold deamon启动时,进入main.cpp的main函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
SLOGI("Vold 2.1 (the revenge) firing up");
mkdir("/dev/block/vold", 0755);
/* For when cryptfs checks and mounts an encrypted filesystem */
klog_set_level(6);
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
SLOGE("Unable to create VolumeManager");
exit(1);
};
if (!(nm = NetlinkManager::Instance())) {
SLOGE("Unable to create NetlinkManager");
exit(1);
};
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
exit(1);
}
if (process_config(vm)) {
SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
}
if (nm->start()) {
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
coldboot("/sys/block");
// coldboot("/sys/class/switch");
/*
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) {
SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
SLOGI("Vold exiting");
exit(0);
}
|
vm->start()直接返回0,不做任何处理;
process_config(vm)方法是解析上面的vold.fstab文件,然后初始Volume对象,并且交由VolumeManager管理;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
for (i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
DirectVolume *dv = NULL;
flags = 0;
dv = new DirectVolume(vm, fstab->recs[i].label,
fstab->recs[i].mount_point,
fstab->recs[i].partnum);
if (dv->addPath(fstab->recs[i].blk_device)) {
SLOGE("Failed to add devpath %s to volume %s",
fstab->recs[i].blk_device, fstab->recs[i].label);
goto out_fail;
}
/* Set any flags that might be set for this volume */
if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
flags |= VOL_NONREMOVABLE;
}
if (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= VOL_ENCRYPTABLE;
}
dv->setFlags(flags);
vm->addVolume(dv);
}
}
|
cl->startListener()主要是与上层的交互,上层的接口是NativeDaemonConnector类,vold接口类是VoldCommand,它们通过socket通讯来进行交互,这里就不做重点的介绍了;
nm->start()就是NetlinkManager::start()了,它主要是用来启动底层事件监听的,这样就能随时的了解硬件的变化,具体来看看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
int NetlinkManager::start() {
struct sockaddr_nl nladdr;
int sz = 64 * 1024;
int on = 1;
//清空nladdr
memset(&nladdr, 0, sizeof(nladdr));
//无符号整数类型
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;
//建立与Uevent之间的socket连接
if ((mSock = socket(PF_NETLINK,
SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
//设置一些socket参数
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));
return -1;
}
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
return -1;
}
//将地址信息和socket进行bind
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
return -1;
}
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
return -1;
}
return 0;
}
|
着重看看mHandler->start();而NetlinkHandler是继承与NetlinkListener的,NetlinkListener又继承与SocketListener,于是便调用到SocketListener的SocketListener::startListener()函数来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
int SocketListener::startListener() {
if (!mSocketName && mSock == -1) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
return -1;
} else if (mSocketName) {
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",
mSocketName, strerror(errno));
return -1;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
}
//建立socket的监听
if (mListen && listen(mSock, 4) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
//建立管道通讯,不是很清楚
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
//创建一个thread
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
return 0;
}
|
这里我们最关注的是threadStart进行监听线程的创建,它会调用到runListener()进行线程的创建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
void SocketListener::runListener() {
SocketClientCollection *pendingList = new SocketClientCollection();
while(1) {
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = -1;
FD_ZERO(&read_fds);
if (mListen) {
max = mSock;
FD_SET(mSock, &read_fds);
}
FD_SET(mCtrlPipe[0], &read_fds);
if (mCtrlPipe[0] > max)
max = mCtrlPipe[0];
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
int fd = (*it)->getSocket();
FD_SET(fd, &read_fds);
if (fd > max)
max = fd;
}
pthread_mutex_unlock(&mClientsLock);
SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
if (errno == EINTR)
continue;
SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
sleep(1);
continue;
} else if (!rc)
continue;
if (FD_ISSET(mCtrlPipe[0], &read_fds))
break;
if (mListen && FD_ISSET(mSock, &read_fds)) {
struct sockaddr addr;
socklen_t alen;
int c;
do {
alen = sizeof(addr);
c = accept(mSock, &addr, &alen);
SLOGV("%s got %d from accept", mSocketName, c);
} while (c < 0 && errno == EINTR);
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
sleep(1);
continue;
}
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c, true, mUseCmdNum));
pthread_mutex_unlock(&mClientsLock);
}
/* Add all active clients to the pending list first */
pendingList->clear();
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
int fd = (*it)->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList->push_back(*it);
}
}
pthread_mutex_unlock(&mClientsLock);
/* Process the pending list, since it is owned by the thread,
* there is no need to lock it */
while (!pendingList->empty()) {
/* Pop the first item from the list */
it = pendingList->begin();
SocketClient* c = *it;
pendingList->erase(it);
/* Process it, if false is returned and our sockets are
* connection-based, remove and destroy it */
if (!onDataAvailable(c) && mListen) {
/* Remove the client from our array */
SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
if (*it == c) {
mClients->erase(it);
break;
}
}
pthread_mutex_unlock(&mClientsLock);
/* Remove our reference to the client */
c->decRef();
}
}
}
delete pendingList;
}
|
不仔细去解读这个过程,重点要知道的是onDataAvailable(c)的调用,当socket有信息可读时,会调用它的子类NetlinkListener实现了的这个方法来进行后续的处理,来看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
socket, mBuffer, sizeof(mBuffer), &uid));
if (count < 0) {
if (uid > 0)
LOG_EVENT_INT(65537, uid);
SLOGE("recvmsg failed (%s)", strerror(errno));
return false;
}
NetlinkEvent *evt = new NetlinkEvent();
if (!evt->decode(mBuffer, count, mFormat)) {
SLOGE("Error decoding NetlinkEvent");
} else {
//传递一个NetlinkEvent对象
onEvent(evt);
}
delete evt;
return true;
}
|
传递一个NetlinkEvent对象,调用到其子类NetlinkHandler的onEvent()方法进行处理;
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
if (!subsys) {
SLOGW("No subsystem found in netlink event");
return;
}
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt);
}
}
|
判断如果是block,就会调用handleBlockEvent方法,仔细来看看这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
const char *devpath = evt->findParam("DEVPATH");
/* Lookup a volume to handle this device */
VolumeCollection::iterator it;
bool hit = false;
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
if (!(*it)->handleBlockEvent(evt)) {
#ifdef NETLINK_DEBUG
SLOGD("Device '%s' event handled by volume %sn", devpath, (*it)->getLabel());
#endif
hit = true;
break;
}
}
if (!hit) {
#ifdef NETLINK_DEBUG
SLOGW("No volumes handled block event for '%s'", devpath);
#endif
}
}
|
重点是(*it)->handleBlockEvent(evt)方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
const char *dp = evt->findParam("DEVPATH");
PathCollection::iterator it;
for (it = mPaths->begin(); it != mPaths->end(); ++it) {
if (!strncmp(dp, *it, strlen(*it))) {
/* We can handle this disk */
int action = evt->getAction();
const char *devtype = evt->findParam("DEVTYPE");
if (action == NetlinkEvent::NlActionAdd) {
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
char nodepath[255];
snprintf(nodepath,
sizeof(nodepath), "/dev/block/vold/%d:%d",
major, minor);
if (createDeviceNode(nodepath, major, minor)) {
SLOGE("Error making device node '%s' (%s)", nodepath,
strerror(errno));
}
if (!strcmp(devtype, "disk")) {
handleDiskAdded(dp, evt);
} else {
handlePartitionAdded(dp, evt);
}
/* Send notification iff disk is ready (ie all partitions found) */
if (getState() == Volume::State_Idle) {
char msg[255];
snprintf(msg, sizeof(msg),
"Volume %s %s disk inserted (%d:%d)", getLabel(),
getMountpoint(), mDiskMajor, mDiskMinor);
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
msg, false);
}
} else if (action == NetlinkEvent::NlActionRemove) {
if (!strcmp(devtype, "disk")) {
handleDiskRemoved(dp, evt);
} else {
handlePartitionRemoved(dp, evt);
}
} else if (action == NetlinkEvent::NlActionChange) {
if (!strcmp(devtype, "disk")) {
handleDiskChanged(dp, evt);
} else {
handlePartitionChanged(dp, evt);
}
} else {
SLOGW("Ignoring non add/remove/change event");
}
return 0;
}
}
errno = ENODEV;
return -1;
}
|
这个方法就是当volume进行各种操作时,往上报的事件,比如mount,umount,remove等;evt->findParam("MAJOR")表示的是Linux下设备的major number,表示不同的设备类型,而evt->findParam("MINOR")表示的是同一个设备的不同分区;shell进入sys/dev/block就可以明白major和minor代表的意思了;
下面就总结一下Setting下的mount/unmount,即手动mount/unmount:
NativeDaemonConnector.java与CommandListener.cpp是vold与上层的socket沟通桥梁。
NetlinkManager.cpp负责与kernel之前的 uevent socket沟通。
在Setting的Memory.java,它所呈现的是Storage Setting设置界面。
点击mount Sdcard项,调用到MountService的mountVolume()方法。
1、mountVolume()
2、doMountVolume()
3、NativeDaemonConnector.execute("volume", "mount", path);
4、CommandListener::VolumeCmd::runCommand(SocketClient cli, int argc, char *argv);
1 2 3 4 5 6 |
else if (!strcmp(argv[1], "mount")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);
return 0;
}
rc = vm->mountVolume(argv[2]);
|
经过上面的中转处理。
5、VolumeManager::mountVolume(const char *label);
6、Volume::mountVol();
在Volume中,会根据状态的改变,mVm->getBroadcaster()->sendBroadcast()会通知给上层状态的改变。
创建设备节点,等等。
7、Fat::doMount(devicePath, getMountpoint(), false, false, false,AID_SYSTEM, gid, 0700, true)
AID_SYSTEM = user id
gid = group id
0700 = 权限的掩码(0700 = gid/uid, 属主,组权,其它用户的权限)
再往下是kernel层,我不是很了解。
手动unmount流程其实差不多,步骤简单,但是中间处理很复杂。