SD卡 挂载分析Framework

http://blog.csdn.net/gzshun/article/details/7172389


本系列主要走了一下Android挂载SD卡的流程,从Vold到Framework,再从Framework到UI。也熟悉了谷歌写的C/C++源代码,提高一下自己的类的设计与代码的技巧,很感谢谷歌提供这么一个手机的开源操作系统Android,让鄙人也有机会来了解一下智能终端系统的实现原理。


只要有C/C++的基础,就能够很流畅地分析完这一块代码,比较重要的还是Android的实现架构,代码倒没什么难题。
Android系统也出来好几年,这系列文章讲解的是Android-2.2源码,那时候还没有足够的成熟,通过这种没有足够的成熟
的系统,分析起来也相对容易一些,一个系统都会慢慢地发展,越来越规范,代码质量越高,但分析起来可能越吃力。

Android磁盘管理-系统源码分析(1)

作者:gzshun. 原创作品,转载请标明出处!


本系列文章,将会详细讲解Android系统的磁盘管理部分,可以通过这个突破口,熟悉整个Android的系统架构与实现原理。


Android磁盘管理总共涉及到四大部分源码:
1.Linux kernel: Android建立在Linux内核的基础上,最底层的部分是由Linux kernel来负责的,用于检测热插拔事件;
2.Vold:Android没有使用Linux平台下的udev来处理,于是Google写了一个类似udev功能的vold,充当了kernel与framework之间的桥梁;
3.Framework:Android的核心框架,(仅仅磁盘管理这部分)负责操作vold,给vold下发操作命令;
4.UI:Androidd的系统应用,与Framework进行交互,用于挂载/卸载SD卡。

Android挂载SD卡,流程如下:
1.用户在“设置”页面的“SD卡和手机内存”中,点击“挂载”;
2.UI从Framework获取操作磁盘的函数(向Framework注册,才能使用的函数),然后调用挂载的处理函数;
3.该处理函数通过广播机制发送挂载命令“volume mount sdcard”,vold接受命令并挂载SD卡后,用广播通知Framework;
4.Framework收到挂载SD卡的回复,通知UI的处理结果;
5.界面显示挂载成功/挂载失败。

从这里可以看出,Android的磁盘管理涉及到整个系统框架,这是Android系统很重要的一个子系统,通过磁盘管理的子系统来熟悉Android系统架构,能够了解到Android的多方面知识。从SD卡的挂载流程可以看出,Android系统大量地使用IPC,从而做到了模块独立的效果。从磁盘管理的那四大部分来看,四个部分之间的相互联系均是使用socket进行通信,没有使用到传统的API调用,整个系统就显得非常的独立。

源码的位置:
Vold:system/vold
Framework: frameworks/base/services/java/com/android/server

UI: android-2.2r2/packages/apps/Settings/src/com/android/settings/deviceinfo/

Android磁盘管理-之vold源码分析(2)

Vold是Android系统处理磁盘的核心部分,取代了原来Linux系统中的udev,主要用来处理Android系统的热插拔存储设备。在Android2.2以后的系统中,vold源码已经移到了system目录下,vold目录包含以下源码:
├── Android.mk
├── Asec.h
├── CleanSpec.mk
├── CommandListener.cpp
├── CommandListener.h
├── Devmapper.cpp
├── Devmapper.h
├── DirectVolume.cpp
├── DirectVolume.h
├── Fat.cpp
├── Fat.h
├── hash.h
├── logwrapper.c
├── Loop.cpp
├── Loop.h
├── main.cpp
├── NetlinkHandler.cpp
├── NetlinkHandler.h
├── NetlinkManager.cpp
├── NetlinkManager.h
├── Process.cpp
├── Process.h
├── ResponseCode.cpp
├── ResponseCode.h
├── vdc.c
├── VoldCommand.cpp
├── VoldCommand.h
├── Volume.cpp
├── Volume.h
├── VolumeManager.cpp
├── VolumeManager.h
├── Xwarp.cpp
└── Xwarp.h


先简要说明一下类的继承关系,vold中比较重要的有以下几个类:
三大管理类:VolumeManager,CommandListener,NetlinkManager
其他处理类:Volume,DirectVolume,NetlinkHandler,Fat,ResponseCode
其他相关的类:NetlinkListener,SocketListener

1.VolumeManager管理Volume类;
2.DirectVolume类继承于Volume类,保存着磁盘信息与操作函数;
3.NetlinkManager类负责与内核uevent事件通信,期间,使用到了NetlinkListener和SocketListener类的函数;
4.Fat是格式化sd卡的函数;
5.ResponseCode保存着vold向framework反馈的值。

本文讲解main.cpp文件的源代码:

  1. int main() {  
  2.     /********************************************************************************** 
  3.     **以下三个类声明三个指针对象: 
  4.     **VolumeManager     :管理所有存储设备(volume对象); 
  5.     **CommandListener   :监听Framework下发的消息,并分析命令,调用响应的操作函数; 
  6.     **NetlinkManager  :监听Linux内核的热插拔事件,uevent事件 
  7.     **********************************************************************************/  
  8.     VolumeManager *vm;  
  9.     CommandListener *cl;  
  10.     NetlinkManager *nm;  
  11.   
  12.     SLOGI("Vold 2.1 (the revenge) firing up");  
  13.     /********************************************************************************** 
  14.     **在Linux系统,如scsi硬盘,U盘的设备节点默认生成在/dev/目录下,Android把这些设备 
  15.     **节点改到了/dev/block/目录下。但随着热插拔事件的产生,设备节点(如sda,sdb)经常变换, 
  16.     **对于vold来说,可能有点麻烦,所以在/dev/block/下新建了一个名为vold的目录,存放sda, 
  17.     **sdb对应的设备节点,形如"8:0"。 
  18.     **eg:sda 的主次设备号分别为8,0,于是vold就会在vold目录下创建名为"8:0"的节点,基于主次设备号 
  19.     **命名,便于程序操作,增加了灵活性。 
  20.     **********************************************************************************/  
  21.     mkdir("/dev/block/vold", 0755);  
  22.   
  23.     /********************************************************************************** 
  24.     **实例化vm对象,VolumeManager类调用自身的Instance函数,new了一个对象给vm。 
  25.     **源码: 
  26.     VolumeManager *VolumeManager::Instance() { 
  27.         if (!sInstance) 
  28.             sInstance = new VolumeManager(); 
  29.         return sInstance; 
  30.     } 
  31.     **********************************************************************************/  
  32.     if (!(vm = VolumeManager::Instance())) {  
  33.         SLOGE("Unable to create VolumeManager");  
  34.         exit(1);  
  35.     };  
  36.       
  37.     /********************************************************************************** 
  38.     **实例化nm对象,NetlinkManager类调用自身的Instance函数,new了一个对象给nm。 
  39.     **源码: 
  40.     NetlinkManager *NetlinkManager::Instance() { 
  41.         if (!sInstance) 
  42.             sInstance = new NetlinkManager(); 
  43.         return sInstance; 
  44.     } 
  45.     **********************************************************************************/  
  46.     if (!(nm = NetlinkManager::Instance())) {  
  47.         SLOGE("Unable to create NetlinkManager");  
  48.         exit(1);  
  49.     };  
  50.   
  51.     /********************************************************************************** 
  52.     **实例化cl对象; 
  53.     **vm->setBroadcaster((SocketListener *) cl); 
  54.     **setBroadcaster函数将VolumeManager的成员变量mBroadcaster设置成cl,这两个变量都是 
  55.     **SocketListener的指针类型,命令执行状态广播函数就会调用这个SocketListener指针来调用 
  56.     **SocketListener类的广播函数; 
  57.     **为什么SocketListener类能强制转换CommandListener类呢? 
  58.     **原因:继承关系:CommandListener(子类) --> FrameworkListener(子类) --> SocketListener(父类) 
  59.     **将子类强制转换为父类是没错的。 
  60.     **********************************************************************************/  
  61.     cl = new CommandListener();  
  62.     vm->setBroadcaster((SocketListener *) cl);  
  63.     nm->setBroadcaster((SocketListener *) cl);  
  64.       
  65.     /********************************************************************************** 
  66.     **调用start函数启动存储设备的管理类,看了源码,这函数没干什么事,估计去哪打酱油了。 
  67.     **源码: 
  68.     int VolumeManager::start() { 
  69.         return 0; 
  70.     } 
  71.     **********************************************************************************/  
  72.     if (vm->start()) {  
  73.         SLOGE("Unable to start VolumeManager (%s)", strerror(errno));  
  74.         exit(1);  
  75.     }  
  76.   
  77.     /********************************************************************************** 
  78.     **process_config函数用来解析/etc/vold.fstab的配置文件,从代码可以看出,配置文件的参数 
  79.     **以空格和制表格(Tab键)分隔;系统启动起来,分析该配置文件,挂载相应的分区,相当于 
  80.     **Linux系统的/etc/fstab文件。 
  81.     **********************************************************************************/  
  82.     if (process_config(vm)) {  
  83.         SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));  
  84.     }  
  85.       
  86.     /********************************************************************************** 
  87.     **nm对象调用start函数开启了一个线程,用来监听底层的uevent事件;这start函数干的事就 
  88.     **多了,主要是打开一个udp套接字,循环监听底层事件。线程里面使用了Select函数来处理 
  89.     **套接字,这设计到fd_set结构体等等的使用; 
  90.     **当捕获到uevent事件,vold会将该事件通知给Framework层,Framework进行判断,然后再 
  91.     **下发操作命令。 
  92.     **********************************************************************************/  
  93.     if (nm->start()) {  
  94.         SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));  
  95.         exit(1);  
  96.     }  
  97.       
  98.     coldboot("/sys/block");  
  99.   
  100.     /********************************************************************************** 
  101.     **下面是判断Android系统是否处于ums状态,ums是大容量存储的意思,这是Android系统 
  102.     **的OTG功能。OTG是on-the-go的简称,主要提供与pc机的连接; 
  103.     **notifyUmsConnected函数将ums的状态通知给Framework层,于是Framework与UI配合,弹出 
  104.     **一个与pc机连接的交互界面。 
  105.     **********************************************************************************/  
  106.     {  
  107.         FILE *fp;  
  108.         char state[255];  
  109.           
  110.         if ((fp = fopen("/sys/devices/virtual/switch/usb_mass_storage/state","r"))) {  
  111.             if (fgets(state, sizeof(state), fp)) {  
  112.                 if (!strncmp(state, "online", 6)) {  
  113.                     vm->notifyUmsConnected(true);  
  114.                 } else {  
  115.                     vm->notifyUmsConnected(false);  
  116.                 }  
  117.                 } else {  
  118.                     SLOGE("Failed to read switch state (%s)", strerror(errno));  
  119.                 }  
  120.           
  121.             fclose(fp);  
  122.         } else {  
  123.             SLOGW("No UMS switch available");  
  124.         }  
  125.     }  
  126.   
  127.     /********************************************************************************** 
  128.     **上面的准备工作已做完,现在是vold比较重要的一个处理线程; 
  129.     **startListener是CommandListener类的父类的函数,该函数用于开启监听线程,监听 
  130.     **Framework层下发给vold的命令,然后调用相应的命令操作存储设备。 
  131.     **********************************************************************************/  
  132.     if (cl->startListener()) {  
  133.         SLOGE("Unable to start CommandListener (%s)", strerror(errno));  
  134.         exit(1);  
  135.     }  
  136.   
  137.     /********************************************************************************** 
  138.     **进入一个循环,让vold保持守护进程的状态; 
  139.     **vold的主要工作是由:nm->start()和cl->startListener()两个线程共同完成;这两个处理线程 
  140.     **中间需要Framework来充当桥梁与boss的身份,Framework是管理这些磁盘的boss。 
  141.     **********************************************************************************/  
  142.     while(1) {  
  143.         sleep(1000);  
  144.     }  
  145.       
  146.     SLOGI("Vold exiting");  
  147.     exit(0);  
  148.     }  
  149.   
  150. /********************************************************************************** 
  151. **以下这两个函数不重要,也就是打开/sys/block目录处理一些事情;这俩函数用来给vold打杂, 
  152. **社会阶级比较低,o(∩_∩)o 哈哈。 
  153. **里面有几个函数是bionic库提供的,用得比较少。 
  154. **********************************************************************************/  
  155. static void do_coldboot(DIR *d, int lvl)  
  156. {  
  157.     struct dirent *de;  
  158.     int dfd, fd;  
  159.   
  160.     dfd = dirfd(d);  
  161.   
  162.     fd = openat(dfd, "uevent", O_WRONLY);  
  163.     if(fd >= 0) {  
  164.             write(fd, "add\n", 4);  
  165.             close(fd);  
  166.     }  
  167.   
  168.     while((de = readdir(d))) {  
  169.         DIR *d2;  
  170.   
  171.         if (de->d_name[0] == '.')  
  172.                 continue;  
  173.   
  174.         if (de->d_type != DT_DIR && lvl > 0)  
  175.                 continue;  
  176.   
  177.         fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);  
  178.         if(fd < 0)  
  179.                 continue;  
  180.   
  181.         d2 = fdopendir(fd);  
  182.         if(d2 == 0)  
  183.                 close(fd);  
  184.         else {  
  185.                 do_coldboot(d2, lvl + 1);  
  186.                 closedir(d2);  
  187.         }  
  188.     }  
  189. }  
  190.   
  191. static void coldboot(const char *path)  
  192. {  
  193.         DIR *d = opendir(path);  
  194.         if(d) {  
  195.                 do_coldboot(d, 0);  
  196.                 closedir(d);  
  197.         }  
  198. }  
  199.   
  200. /********************************************************************************** 
  201. **该函数用来解析/etc/vold.fstab配置文件,文本的处理; 
  202. **可能不同的源码版本,有点差异; 
  203. **strsep是字符串的分割函数,可以看出该函数是以" \t"来分割(\t前面有一空格),分割空格 
  204. **或制表格,所以配置文件里面空格与tab键来分割都行; 
  205. **strsep不是ANSI C的函数,但它用来取代strtok函数,strtok是线程不安全的函数。 
  206. **********************************************************************************/  
  207. static int process_config(VolumeManager *vm) {  
  208.     FILE *fp;  
  209.     int n = 0;  
  210.     char line[255];  
  211.   
  212.     if (!(fp = fopen("/etc/vold.fstab""r"))) {  
  213.             return -1;  
  214.     }  
  215.   
  216.     while(fgets(line, sizeof(line), fp)) {  
  217.         char *next = line;  
  218.         char *type, *label, *mount_point;  
  219.   
  220.         n++;  
  221.         line[strlen(line)-1] = '\0';  
  222.   
  223.         if (line[0] == '#' || line[0] == '\0')  
  224.                 continue;  
  225.   
  226.         if (!(type = strsep(&next, " \t"))) {  
  227.                 SLOGE("Error parsing type");  
  228.                 goto out_syntax;  
  229.         }  
  230.         if (!(label = strsep(&next, " \t"))) {  
  231.                 SLOGE("Error parsing label");  
  232.                 goto out_syntax;  
  233.         }  
  234.         if (!(mount_point = strsep(&next, " \t"))) {  
  235.                 SLOGE("Error parsing mount point");  
  236.                 goto out_syntax;  
  237.         }  
  238.   
  239.         if (!strcmp(type, "dev_mount")) {  
  240.             DirectVolume *dv = NULL;  
  241.             char *part, *sysfs_path;  
  242.   
  243.             if (!(part = strsep(&next, " \t"))) {  
  244.                     SLOGE("Error parsing partition");  
  245.                     goto out_syntax;  
  246.             }  
  247.             if (strcmp(part, "auto") && atoi(part) == 0) {  
  248.                     SLOGE("Partition must either be 'auto' or 1 based index instead of '%s'", part);  
  249.                     goto out_syntax;  
  250.             }  
  251.             /********************************************************************************** 
  252.             **如果配置文件指定为auto,则为自动挂载存储设备,在实例化DirectVolume的对象,传递-1 
  253.             **进去,否则将分区序数part传进去; 
  254.             **********************************************************************************/  
  255.             if (!strcmp(part, "auto")) {  
  256.                     dv = new DirectVolume(vm, label, mount_point, -1);  
  257.             } else {  
  258.                     dv = new DirectVolume(vm, label, mount_point, atoi(part));  
  259.             }  
  260.   
  261.             while((sysfs_path = strsep(&next, " \t"))) {  
  262.                 /********************************************************************************** 
  263.                 **将存储设备在/sys/对应的路径添加进PathCollection容器,该容器为“char *”类型; 
  264.                 **在/sys/里面可以获取到存储设备的热插拔事件,所以DirectVolume类的主要工作就是针对 
  265.                 **这里去获取uevent事件的; 
  266.                 **DirectVolume::handleBlockEvent(NetlinkEvent *evt)函数去得到这些事件,主要还是 
  267.                 **NetlinkListener类从内核捕获到的。 
  268.                 **********************************************************************************/  
  269.                 if (dv->addPath(sysfs_path)) {  
  270.                         SLOGE("Failed to add devpath %s to volume %s", sysfs_path,  
  271.                                  label);  
  272.                         goto out_fail;  
  273.                 }  
  274.             }  
  275.             /********************************************************************************** 
  276.             **如果在配置文件有找到正确的挂载参数,那么就会将DirectVolume的对象添加到VolumeCollection 
  277.             **容器中,该容器存放着Volume*类型的数据,VolumeManager的对象vm是用来管理这些存储设备的; 
  278.             **一块存储设备就会实例化一个Volume对象,但对于手机来说,一般只能识别到一张SD卡。 
  279.             **********************************************************************************/  
  280.             vm->addVolume(dv);  
  281.         } else if (!strcmp(type, "map_mount")) {  
  282.         } else {  
  283.                 SLOGE("Unknown type '%s'", type);  
  284.                 goto out_syntax;  
  285.         }  
  286.     }  
  287.   
  288.     fclose(fp);  
  289.     return 0;  
  290.   
  291. /********************************************************************************** 
  292. **从这个main函数的出错处理可以看出,系统源码经常使用到这种高效性的goto技巧,goto在 
  293. **系统中的出错处理用得很频繁,可以说几乎每个文件都使用到了goto跳转函数; 
  294. **很多文章或者教材,经常反面性的批判goto的不规则,但从这些外国的开源代码可以看出, 
  295. **那些牛人都很喜欢用goto,利用了goto来处理出错情况的技巧,显得很漂亮; 
  296. **我觉得,要从实用性的角度来评论这些语言的优缺点,并不能用否认的说法来解释,这样才能 
  297. **不断地进步; 
  298. **所以,如果在出错处理非常多的情况下,使用goto是使代码更可读,减少重复的出错判断的 
  299. **代码量。 
  300. **********************************************************************************/  
  301. out_syntax:  
  302.         SLOGE("Syntax error on config line %d", n);  
  303.         errno = -EINVAL;  
  304. out_fail:  
  305.         fclose(fp);  
  306.         return -1;     

Android磁盘管理-之vold源码分析(3)

NetlinkManager类负责管理捕获内核的uevent事件,这里使用了Netlink套接字。

Netlink的概念:
Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。Netlink套接字可以使用标准的套接字APIs来创建。socket(), bind(), sendmsg(), recvmsg() 和 close()很容易地应用到 netlink socket。netlink包含于头文件linux/netlink.h中。
平时的应用层一般都不会用到Netlink这个套接字,了解就行。

在Main.cpp文件中的main函数里面,有一个准备工作是用来开启监听内核uevent事件的线程,源码如下:

  1. if (nm->start()) {  
  2.     SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));  
  3.     exit(1);  
  4. }  

nm是NetlinkManager类实例化的一个对象,以下是start()函数的源码:
  1. /********************************************************************************** 
  2. **file:system/vold/NetlinkManager.cpp 
  3. **以下一些socket的初始化,跟linux的应用层的tcp/udp使用差不多。 
  4. **********************************************************************************/  
  5. int NetlinkManager::start() {  
  6.     struct sockaddr_nl nladdr;  
  7.     int sz = 64 * 1024;  
  8.       
  9.     memset(&nladdr, 0, sizeof(nladdr));  
  10.     nladdr.nl_family = AF_NETLINK;  
  11.     nladdr.nl_pid = getpid();  
  12.     nladdr.nl_groups = 0xffffffff;  
  13.       
  14.     if ((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {  
  15.         SLOGE("Unable to create uevent socket: %s", strerror(errno));  
  16.         return -1;  
  17.     }  
  18.       
  19.     if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {  
  20.         SLOGE("Unable to set uevent socket options: %s", strerror(errno));  
  21.         return -1;  
  22.     }  
  23.       
  24.     if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {  
  25.         SLOGE("Unable to bind uevent socket: %s", strerror(errno));  
  26.         return -1;  
  27.     }  
  28.     /********************************************************************************** 
  29.     **这里先说明NetlinkHandler类的继承关系: 
  30.     **NetlinkHandler --> NetlinkListener --> SocketListener(父类) 
  31.     **NetlinkHandler类的start()函数调用了SocketListener::startListener()函数,源码如下。 
  32.     **********************************************************************************/  
  33.     mHandler = new NetlinkHandler(mSock);  
  34.     if (mHandler->start()) {  
  35.         SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));  
  36.         return -1;  
  37.     }  
  38.     return 0;  
  39. }  
  40. /********************************************************************************** 
  41. **file:system/vold/NetlinkHandler.cpp 
  42. **该函数使用this指针调用自身的startListener函数,可以发现,在NetlinkHandler没有 
  43. **startListener()这个函数,这函数是它的父类的函数SocketListener::startListener; 
  44. **********************************************************************************/  
  45. int NetlinkHandler::start() {  
  46.     return this->startListener();  
  47. }  
  48. /********************************************************************************** 
  49. **file:system/core/libsysutils/src/SocketListener.cpp 
  50. **以下这个函数就涉及到其他方面的内容了,不在vold部分。 
  51. **********************************************************************************/  
  52. int SocketListener::startListener() {  
  53.     if (!mSocketName && mSock == -1) {  
  54.         SLOGE("Failed to start unbound listener");  
  55.         errno = EINVAL;  
  56.         return -1;  
  57.     } else if (mSocketName) {  
  58.         if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
  59.             SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
  60.             mSocketName, strerror(errno));  
  61.             return -1;  
  62.         }  
  63.     }  
  64.       
  65.     if (mListen && listen(mSock, 4) < 0) {  
  66.         SLOGE("Unable to listen on socket (%s)", strerror(errno));  
  67.         return -1;  
  68.     } else if (!mListen)  
  69.         mClients->push_back(new SocketClient(mSock));  
  70.       
  71.     if (pipe(mCtrlPipe)) {  
  72.         SLOGE("pipe failed (%s)", strerror(errno));  
  73.         return -1;  
  74.     }  
  75.     /********************************************************************************** 
  76.     **该函数开启了一个监听线程。 
  77.     **********************************************************************************/  
  78.     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
  79.         SLOGE("pthread_create (%s)", strerror(errno));  
  80.         return -1;  
  81.     }  
  82.       
  83.     return 0;  
  84. }  
  85.   
  86. /********************************************************************************** 
  87. **该线程函数在类里面声明为静态函数:static void *threadStart(void *obj); 
  88. **所以不能调用this指针来指向自身的函数,所以通过pthread_create线程的创建函数来传递 
  89. **一个参数,将this指针传递给它; 
  90. **这里使用reinterpret_cast运算符是用来处理无关类型之间的转换, 
  91. **它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。 
  92. **********************************************************************************/  
  93. void *SocketListener::threadStart(void *obj) {  
  94.     SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  95.       
  96.     me->runListener();  
  97.     pthread_exit(NULL);  
  98.     return NULL;  
  99. }  
  100. /********************************************************************************** 
  101. **该函数才是真正的处理函数了,使用select集合,结合fd_set结构体,可以判断套接字有无 
  102. **信息可读,如果没有,立即返回,不阻塞; 
  103. **还使用了管道,仅仅判断该套接字的读端是否有数据可读。 
  104. **********************************************************************************/  
  105. void SocketListener::runListener() {  
  106.     while(1) {  
  107.         SocketClientCollection::iterator it;  
  108.         fd_set read_fds;  
  109.         int rc = 0;  
  110.         int max = 0;  
  111.           
  112.         FD_ZERO(&read_fds);  
  113.           
  114.         if (mListen) {  
  115.             max = mSock;  
  116.         FD_SET(mSock, &read_fds);  
  117.         }  
  118.       
  119.         FD_SET(mCtrlPipe[0], &read_fds);  
  120.         if (mCtrlPipe[0] > max)  
  121.             max = mCtrlPipe[0];  
  122.           
  123.         pthread_mutex_lock(&mClientsLock);  
  124.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  125.             FD_SET((*it)->getSocket(), &read_fds);  
  126.             if ((*it)->getSocket() > max)  
  127.             max = (*it)->getSocket();  
  128.         }  
  129.         pthread_mutex_unlock(&mClientsLock);  
  130.           
  131.         if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {  
  132.             SLOGE("select failed (%s)", strerror(errno));  
  133.             sleep(1);  
  134.             continue;  
  135.         } else if (!rc)  
  136.             continue;  
  137.           
  138.         if (FD_ISSET(mCtrlPipe[0], &read_fds))  
  139.             break;  
  140.         if (mListen && FD_ISSET(mSock, &read_fds)) {  
  141.             struct sockaddr addr;  
  142.             socklen_t alen = sizeof(addr);  
  143.             int c;  
  144.               
  145.             if ((c = accept(mSock, &addr, &alen)) < 0) {  
  146.                 SLOGE("accept failed (%s)", strerror(errno));  
  147.                 sleep(1);  
  148.                 continue;  
  149.             }  
  150.             pthread_mutex_lock(&mClientsLock);  
  151.             mClients->push_back(new SocketClient(c));  
  152.             pthread_mutex_unlock(&mClientsLock);  
  153.         }  
  154.           
  155.         do {  
  156.             pthread_mutex_lock(&mClientsLock);  
  157.             for (it = mClients->begin(); it != mClients->end(); ++it) {  
  158.                 int fd = (*it)->getSocket();  
  159.                 if (FD_ISSET(fd, &read_fds)) {  
  160.                     pthread_mutex_unlock(&mClientsLock);  
  161.                     /********************************************************************************** 
  162.                     **onDataAvailable是SocketListener类声明的一个纯虚函数,在其子类NetlinkListener实现 
  163.                     **onDataAvailable函数,函数里面调用了NetlinkHandler类的onEvent函数,该函数是 
  164.                     **在NetlinkListener类中定义的纯虚函数,在vold中的NetlinkHandler类中实现。 
  165.                     **********************************************************************************/  
  166.                     if (!onDataAvailable(*it)) {  
  167.                         close(fd);  
  168.                         pthread_mutex_lock(&mClientsLock);  
  169.                         delete *it;  
  170.                         it = mClients->erase(it);  
  171.                         pthread_mutex_unlock(&mClientsLock);  
  172.                     }  
  173.                     FD_CLR(fd, &read_fds);  
  174.                     continue;  
  175.                 }  
  176.             }  
  177.             pthread_mutex_unlock(&mClientsLock);  
  178.         } while (0);  
  179.     }  
  180. }  
  181.   
  182. /********************************************************************************** 
  183. **file:system/core/libsysutils/src/NetlinkListener.cpp 
  184. **该函数用来处理内核的uevent事件,然后调用onEvent函数,让onEvent函数去捕获这些事件 
  185. **的信息。 
  186. **********************************************************************************/  
  187. bool NetlinkListener::onDataAvailable(SocketClient *cli)  
  188. {  
  189.     int socket = cli->getSocket();  
  190.     int count;  
  191.   
  192.     if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) {  
  193.         SLOGE("recv failed (%s)", strerror(errno));  
  194.         return false;  
  195.     }  
  196.   
  197.     NetlinkEvent *evt = new NetlinkEvent();  
  198.     if (!evt->decode(mBuffer, count)) {  
  199.         SLOGE("Error decoding NetlinkEvent");  
  200.         goto out;  
  201.     }  
  202.         /*下一篇文章介绍该函数*/  
  203.     onEvent(evt);  
  204. out:  
  205.     delete evt;  
  206.     return true;  

Android磁盘管理-之vold源码分析(4)

上篇文章分析到了NetlinkHandler类中的onEvent函数,该函数由NetlinkListener::onDataAvailable函数调用,当SocketListener类监听到内核的uevent事件,调用该函数,之后的事情交给onEvent来负责。


file:system/vold/NetlinkHandler.cpp
现在来说onEvent函数,在vold中,磁盘的热插拔事件都是通过上述那些间接的过程来调用到
该函数,该函数再调用vold中的处理事件的函数,这样vold就能得到最新的磁盘热插拔事件;
  1. void NetlinkHandler::onEvent(NetlinkEvent *evt) {  
  2.     VolumeManager *vm = VolumeManager::Instance();  
  3.     const char *subsys = evt->getSubsystem();  
  4.       
  5.     if (!subsys) {  
  6.         SLOGW("No subsystem found in netlink event");  
  7.         return;  
  8.     }  
  9.       
  10.     if (!strcmp(subsys, "block")) {  
  11.         vm->handleBlockEvent(evt);  
  12.     } else if (!strcmp(subsys, "switch")) {  
  13.         vm->handleSwitchEvent(evt);  
  14.     } else if (!strcmp(subsys, "battery")) {  
  15.     } else if (!strcmp(subsys, "power_supply")) {  
  16.     }  
  17. }  
file:system/vold/VolumeManager.cpp
vm->handleSwitchEvent(evt)函数涉及的比较少,先来分析;
该函数是用来处理大容量存储设备,也就是otg功能,NetlinkEvent类提供的findParam函数
可以获取到该事件的具体信息,比如设备路径,设备名称,状态,分区数量等等。。
如果判断是“online”状态,那么就向framework发送状态消息。使用notifyUmsConnected函数
进行广播。

  1. void VolumeManager::handleSwitchEvent(NetlinkEvent *evt) {  
  2.     const char *devpath = evt->findParam("DEVPATH");  
  3.     const char *name = evt->findParam("SWITCH_NAME");  
  4.     const char *state = evt->findParam("SWITCH_STATE");  
  5.       
  6.     if (!name || !state) {  
  7.         SLOGW("Switch %s event missing name/state info", devpath);  
  8.         return;  
  9.     }  
  10.       
  11.     if (!strcmp(name, "usb_mass_storage")) {  
  12.         if (!strcmp(state, "online"))  {  
  13.             notifyUmsConnected(true);  
  14.         } else {  
  15.             notifyUmsConnected(false);  
  16.         }  
  17.         } else {  
  18.             SLOGW("Ignoring unknown switch '%s'", name);  
  19.         }  
  20. }  

notifyUmsConnected函数将otg的状态发送给framework,
命令为:Share method ums now available/unavailable;
getBroadcaster()->sendBroadcast()广播函数,这在main函数分析中就涉及到了,
源码:
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
将CommandListener对象强制转换成SocketListener类型,这样cl对象就能够调用SocketListener
类的setBroadcaster广播函数;
getBroadcaster()的源码:
SocketListener *getBroadcaster() { return mBroadcaster; }
直接将刚才被强制转换成SocketListener类型返回,相当于如下:
(SocketListener *)cl->sendBroadcast(xxx);
  1. void VolumeManager::notifyUmsConnected(bool connected) {  
  2.     char msg[255];  
  3.       
  4.     if (connected) {  
  5.         mUsbMassStorageConnected = true;  
  6.     } else {  
  7.         mUsbMassStorageConnected = false;  
  8.     }  
  9.     snprintf(msg, sizeof(msg), "Share method ums now %s",   (connected ? "available" : "unavailable"));  
  10.       
  11.     getBroadcaster()->sendBroadcast(ResponseCode::ShareAvailabilityChange, msg, false);  
  12. }  

file:system/vold/ResponseCode.cpp
该类没做什么事,也就是提供一些出错标志,下面将写出出错原因;
这些标志都会在不同的操作(命令)反馈不同的值。
  1. class ResponseCode {  
  2. public:  
  3.     // 100 series - Requestion action was initiated; expect another reply  
  4.     // before proceeding with a new command.  
  5.     static const int ActionInitiated  = 100;  
  6.       
  7.     static const int VolumeListResult         = 110;  
  8.     static const int AsecListResult           = 111;  
  9.     static const int StorageUsersListResult   = 112;  
  10.       
  11.     // 200 series - Requested action has been successfully completed  
  12.     static const int CommandOkay              = 200;  
  13.     static const int ShareStatusResult        = 210;  
  14.     static const int AsecPathResult           = 211;  
  15.     static const int ShareEnabledResult       = 212;  
  16.     static const int XwarpStatusResult        = 213;  
  17.       
  18.     // 400 series - The command was accepted but the requested action  
  19.     // did not take place.//这几个标志有点重复性,其实vold里面也没有这几个标志。  
  20.     static const int OperationFailed          = 400;  
  21.     static const int OpFailedNoMedia          = 401;   
  22.     static const int OpFailedMediaBlank       = 402;   
  23.     static const int OpFailedMediaCorrupt     = 403;   
  24.     static const int OpFailedVolNotMounted    = 404;   
  25.     static const int OpFailedStorageBusy      = 405;   
  26.     static const int OpFailedStorageNotFound  = 406;   
  27.       
  28.     // 500 series - The command was not accepted and the requested  
  29.     // action did not take place.  
  30.     static const int CommandSyntaxError = 500;//  
  31.     static const int CommandParameterError = 501;//  
  32.       
  33.     // 600 series - Unsolicited broadcasts  
  34.     static const int UnsolicitedInformational       = 600;   
  35.     static const int VolumeStateChange              = 605;   
  36.     static const int VolumeMountFailedBlank         = 610;   
  37.     static const int VolumeMountFailedDamaged       = 611;   
  38.     static const int VolumeMountFailedNoMedia       = 612;   
  39.       
  40.     static const int ShareAvailabilityChange        = 620;   
  41.       
  42.     static const int VolumeDiskInserted            = 630;   
  43.     static const int VolumeDiskRemoved             = 631;   
  44.     static const int VolumeBadRemoval              = 632;   
  45.       
  46.     static int convertFromErrno();  
  47. };  

file:system/vold/VolumeManager.cpp
该函数用来捕获磁盘的热插拔事件信息。
  1. void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {  
  2.     const char *devpath = evt->findParam("DEVPATH");  
  3.       
  4.     VolumeCollection::iterator it;  
  5.     bool hit = false;  
  6.     /********************************************************************************** 
  7.     **mVolumes是一个存放volume*的容器,类型如下: 
  8.     **typedef android::List<Volume *> VolumeCollection; 
  9.     **mVolumes是在main函数中添加进磁盘的,在mian函数中,使用process_config函数分析 
  10.     **/etc/vold.fstab配置文件,然后将磁盘的信息添加进该容器,以便后续的操作。  
  11.     **********************************************************************************/  
  12.     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {  
  13.         if (!(*it)->handleBlockEvent(evt)) {  
  14.             #ifdef NETLINK_DEBUG  
  15.             SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());  
  16.             #endif  
  17.             hit = true;  
  18.             break;  
  19.         }  
  20.     }  
  21.     if (!hit) {  
  22.         #ifdef NETLINK_DEBUG  
  23.         SLOGW("No volumes handled block event for '%s'", devpath);  
  24.         #endif  
  25.     }  
  26. }  

这里使用(*it)调用handleBlockEvent函数,可以看出handleBlockEvent在Volume类对象实现,
源码在system/vold/Volume.cpp
可以发现,该函数并没有做什么事情,其实handleBlockEvent函数在Volume的子类DirectVolume
中重写了。
  1. int Volume::handleBlockEvent(NetlinkEvent *evt) {  
  2.     errno = ENOSYS;  
  3.     return -1;  
  4. }  

file:system/vold/DirectVolume.cpp
这函数是处理热插拔事件最重要的函数,Volume若要操作磁盘,肯定先要从这个函数获取到
磁盘事件和信息。
  1. int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {  
  2.     const char *dp = evt->findParam("DEVPATH");  
  3.       
  4.     PathCollection::iterator  it;  
  5.     for (it = mPaths->begin(); it != mPaths->end(); ++it) {  
  6.         size_t len = strlen(*it);  
  7.         if (!strncmp(dp, *it, len) && (dp[len] == '\0' || dp[len] == '/')) {  
  8.             /* We can handle this disk */  
  9.             int action = evt->getAction();  
  10.             const char *devtype = evt->findParam("DEVTYPE");  
  11.             /********************************************************************************** 
  12.             **NetlinkEvent提供4个uevent状态,代码: 
  13.             **const int NetlinkEvent::NlActionUnknown = 0; 
  14.                 const int NetlinkEvent::NlActionAdd = 1; //增加硬盘或分区的事件 
  15.                 const int NetlinkEvent::NlActionRemove = 2;//移除硬盘或分区的事件 
  16.                 const int NetlinkEvent::NlActionChange = 3;//改变硬盘或分区的事件 
  17.             **这里的事件是增加存储设备 
  18.             **********************************************************************************/  
  19.             if (action == NetlinkEvent::NlActionAdd) {  
  20.                 /*从这里获取到主次设备号*/  
  21.                 int major = atoi(evt->findParam("MAJOR"));  
  22.                 int minor = atoi(evt->findParam("MINOR"));  
  23.                 char nodepath[255];  
  24.                   
  25.                 /********************************************************************************** 
  26.                 **之前有提到过,vold在/dev/block/vold创建了相应的设备节点,诸如8:0形式的节点,这里 
  27.                 **就是创建节点的位置,createDeviceNode函数。 
  28.                 **********************************************************************************/  
  29.                 snprintf(nodepath, sizeof(nodepath), "/dev/block/vold/%d:%d",   major, minor);  
  30.                 if (createDeviceNode(nodepath, major, minor)) {  
  31.                         SLOGE("Error making device node '%s' (%s)", nodepath, strerror(errno));  
  32.                 }  
  33.                 /*磁盘类型,指的就是一块存储设备*/  
  34.                 if (!strcmp(devtype, "disk")) {  
  35.                     handleDiskAdded(dp, evt);  
  36.                 } else {/*分区类型,指的是一块存储设备的某个分区*/  
  37.                     handlePartitionAdded(dp, evt);  
  38.                 }  
  39.             }  
  40.             /*移除存储设备*/  
  41.             else if (action == NetlinkEvent::NlActionRemove) {  
  42.                     if (!strcmp(devtype, "disk")) {  
  43.                         handleDiskRemoved(dp, evt);  
  44.                 } else {  
  45.                     handlePartitionRemoved(dp, evt);  
  46.                 }  
  47.             }  
  48.             /*改变存储设备*/  
  49.             else if (action == NetlinkEvent::NlActionChange) {  
  50.                     if (!strcmp(devtype, "disk")) {  
  51.                     handleDiskChanged(dp, evt);  
  52.                 } else {  
  53.                     handlePartitionChanged(dp, evt);  
  54.                 }  
  55.             } else {  
  56.                 SLOGW("Ignoring non add/remove/change event");  
  57.             }  
  58.             return 0;  
  59.         }  
  60.     }  
  61.     errno = ENODEV;  
  62.     return -1;  
  63. }  

该函数主要对存储设备或分区处理,每个存储设备或分区都有增加、删除和改变的事件,下一篇文章介绍
每个不同事件和函数的处理,有以下6个函数:
  1. void handleDiskAdded(const char *devpath, NetlinkEvent *evt);  
  2. void handleDiskRemoved(const char *devpath, NetlinkEvent *evt);  
  3. void handleDiskChanged(const char *devpath, NetlinkEvent *evt);  
  4. void handlePartitionAdded(const char *devpath, NetlinkEvent *evt);  
  5. void handlePartitionRemoved(const char *devpath, NetlinkEvent *evt);  
  6. void handlePartitionChanged(const char *devpath, NetlinkEvent *evt);  

下一篇文章继续。。。

Android-vold源码分析之handleBlockEvent(5)

上篇文章分析到了handleBlockEvent函数,该函数包含了以下6个处理函数:

  1. void handleDiskAdded(const char *devpath, NetlinkEvent *evt);  
  2. void handleDiskRemoved(const char *devpath, NetlinkEvent *evt);  
  3. void handleDiskChanged(const char *devpath, NetlinkEvent *evt);  
  4. void handlePartitionAdded(const char *devpath, NetlinkEvent *evt);  
  5. void handlePartitionRemoved(const char *devpath, NetlinkEvent *evt);  
  6. void handlePartitionChanged(const char *devpath, NetlinkEvent *evt);  

以下是精简版的handleBlockEvent函数:
  1. if (action == NetlinkEvent::NlActionAdd) {  
  2.     ...  
  3.     if (!strcmp(devtype, "disk")) {  
  4.         handleDiskAdded(dp, evt);  
  5.     } else {  
  6.         handlePartitionAdded(dp, evt);  
  7.     }  
  8. }  
  9. else if (action == NetlinkEvent::NlActionRemove) {  
  10.     if (!strcmp(devtype, "disk")) {  
  11.         handleDiskRemoved(dp, evt);  
  12.     } else {  
  13.         handlePartitionRemoved(dp, evt);  
  14.     }  
  15. }  
  16. else if (action == NetlinkEvent::NlActionChange) {  
  17.     if (!strcmp(devtype, "disk")) {  
  18.         handleDiskChanged(dp, evt);  
  19.     }  
  20.     else {  
  21.         handlePartitionChanged(dp, evt);  
  22.     }  
  23. }  

这样看起来就比较清楚每个函数的作用了,贴源码其实是比较直接的方法,程序员对代码都比较敏感,一看就明白意思,好,开始分析。
  1. void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {  
  2.     mDiskMajor = atoi(evt->findParam("MAJOR"));  
  3.     mDiskMinor = atoi(evt->findParam("MINOR"));  
  4.       
  5.     const char *tmp = evt->findParam("NPARTS");  
  6.     if (tmp) {  
  7.         mDiskNumParts = atoi(tmp);  
  8.     } else {  
  9.         SLOGW("Kernel block uevent missing 'NPARTS'");  
  10.         mDiskNumParts = 0;  
  11.     }  
  12.     /********************************************************************************** 
  13.     **mPendingPartsCount是一个全局变量,用来保存该存储设备的分区数量;这里需要说明一个 
  14.     **存储设备识别的顺序: 
  15.     **当插入一块5个分区的硬盘,首先会调用handleDiskAdded函数获取该存储设备的事件信息, 
  16.     **随后会调用若干次handlePartitionAdded函数来识别该存储设备的多个分区的事件信息, 
  17.     **当然,一般5个分区的硬盘肯定有一个扩展节点(因为mbr最多支持4个主分区); 
  18.     **调用顺序是这样: 
  19.     **handleDiskAdded函数调用1次; 
  20.     **handlePartitionAdded函数调用6次; 
  21.     **该变量的作用是这样:插入一块硬盘,此时mPendingPartsCount变量为分区数量, 
  22.     **开始用handlePartitionAdded函数识别分区,每识别一个分区, 
  23.     **mPendingPartsCount自减一次,当mPendingPartsCount==0时,结束该存储设备事件的捕获。 
  24.     **********************************************************************************/  
  25.     mPendingPartsCount = mDiskNumParts;  
  26.       
  27.     if (mDiskNumParts == 0) {  
  28.         /********************************************************************************** 
  29.         **broadcastDiskAdded函数的作用是通知framework,系统插入一块存储设备,源码如下:       
  30.         void DirectVolume::broadcastDiskAdded() 
  31.         { 
  32.             setState(Volume::State_Idle); 
  33.             char msg[255]; 
  34.             snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)", 
  35.                     getLabel(), getMountpoint(), mDiskMajor, mDiskMinor); 
  36.             mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false); 
  37.         } 
  38.         **********************************************************************************/  
  39.         broadcastDiskAdded();  
  40.     } else {  
  41.         /********************************************************************************** 
  42.         **setState函数起到的作用很大,相当于一只信鸽,每次sd卡发现状态改变,该函数马上就将 
  43.         **最新的状态广播给framework,后面会详细介绍该函数与framework的通信。 
  44.         **********************************************************************************/  
  45.         setState(Volume::State_Pending);  
  46.     }  
  47. }  

随后就是handlePartitionAdded函数了,这个函数要识别插入的设备的所有分区,源码如下:
  1. void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     int part_num;  
  5.       
  6.     const char *tmp = evt->findParam("PARTN");  
  7.       
  8.     if (tmp) {  
  9.         part_num = atoi(tmp);  
  10.     } else {  
  11.         SLOGW("Kernel block uevent missing 'PARTN'");  
  12.         part_num = 1;  
  13.     }  
  14.       
  15.     if (part_num > mDiskNumParts) {  
  16.         mDiskNumParts = part_num;  
  17.     }  
  18.       
  19.     if (major != mDiskMajor) {  
  20.         SLOGE("Partition '%s' has a different major than its disk!", devpath);  
  21.         return;  
  22.     }  
  23.     /********************************************************************************** 
  24.     **上面就是做一下其他的判断,不重要; 
  25.     **MAX_PARTITIONS定义在system/vold/DirectVolume.h文件中,声明如下: 
  26.     static const int MAX_PARTITIONS = 4; 
  27.     Android系统支持太有限,谷歌太懒,呵呵,最多就识别4个分区,当然如果有些厂商想多实现 
  28.     **分区的识别数量,需要修改源码; 
  29.     **我觉得,Android系统是做得不错,但磁盘管理方面不太完善,自从分析修改了vold源码, 
  30.     **vold支持得太少,也许谷歌以前只想到应用于手机,要是哥想识别一块10个分区的硬盘, 
  31.     **咋办?修改源码咯。。。 
  32.     **********************************************************************************/  
  33.     if (part_num > MAX_PARTITIONS) {  
  34.         SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS);  
  35.     } else {  
  36.         /*全局数组,用来存放磁盘分区的此设备号*/  
  37.         mPartMinors[part_num -1] = minor;  
  38.     }  
  39.     /*看到了吧,上面那个函数说到mPendingPartsCount每识别一个分区要自减一次,就在这里*/  
  40.     --mPendingPartsCount;  
  41.     /*这里就在判断mPendingPartsCount变量了,如果mPendingPartsCount==0时,再向framework广播 
  42.     一次该设备的插入,所以framework总共需要收到磁盘的插入广播2次,才会下发操作命令。*/  
  43.     if (!mPendingPartsCount) {  
  44.         /*判断了磁盘的状态,如果正在格式化,将不做操作*/  
  45.         if (getState() != Volume::State_Formatting) {  
  46.             broadcastDiskAdded();  
  47.         }  
  48.     } else {  
  49.     }  
  50. }  

磁盘在被系统识别完后,可能发生改变,这种改变的例子如下:
在Linux系统,大家格式化硬盘就会使用到了,就是fdisk命令,该命令会修改磁盘的一些分区参数,
当然,fdisk只是把分区信息写到存储设备的第一个设备节点或扩展节点。
fdisk里面有一个操作是修改分区类型id,按“t”就能修改,当修改完成后,保存退出fdisk,磁盘的设备节点
将会重新生成。
这里是Android系统,也可能遇到这种情况,以下是源码:
  1. void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.       
  5.     if ((major != mDiskMajor) || (minor != mDiskMinor)) {  
  6.         return;  
  7.     }  
  8.       
  9.     SLOGI("Volume %s disk has changed", getLabel());  
  10.     const char *tmp = evt->findParam("NPARTS");  
  11.     if (tmp) {  
  12.         mDiskNumParts = atoi(tmp);  
  13.     } else {  
  14.         SLOGW("Kernel block uevent missing 'NPARTS'");  
  15.         mDiskNumParts = 0;  
  16.     }  
  17.     mPendingPartsCount = mDiskNumParts;  
  18.       
  19.     if (getState() != Volume::State_Formatting) {  
  20.         if (mDiskNumParts == 0) {  
  21.             /*这里类似于fdisk将删除存储设备的所有分区,这样存储设备的分区数量mDiskNumParts 
  22.             就等于0,此时广播磁盘的空闲状态*/  
  23.             setState(Volume::State_Idle);  
  24.         } else {  
  25.             setState(Volume::State_Pending);  
  26.         }  
  27.     }  
  28. }  

分区的改变:
  1. void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);  
  5. }  

以上两个参数基本没涉及到什么重要的内容,看下源码就行。
刚才上面分析了handleDiskAdded和handlePartitionAdded,这两个增加磁盘或分区的函数,当然也需要
对应移除磁盘或分区的函数,是handleDiskRemoved和handlePartitionRemoved函数。
  1. void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     char msg[255];  
  5.       
  6.     SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);  
  7.     snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",  
  8.                 getLabel(), getMountpoint(), major, minor);  
  9.     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved, msg, false);  
  10.     /*设备移除后,广播通知framework*/  
  11.     setState(Volume::State_NoMedia);  
  12. }  

移除一块存储设备比较方便,当移除分区涉及的比较多,移除分区需要卸载分区,并且删除设备节点,以下是删除分区的源码:
  1. void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     char msg[255];  
  5.     int state;  
  6.       
  7.     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);  
  8.       
  9.     state = getState();  
  10.     if (state != Volume::State_Mounted && state != Volume::State_Shared) {  
  11.         return;  
  12.     }  
  13.       
  14.     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {  
  15.         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",  
  16.                 getLabel(), getMountpoint(), major, minor);  
  17.         /*mCurrentlyMountedKdev变量保存着目前正挂载在系统的存储设备的设备号, 
  18.         这里的判断是这样:如果目前正在移除的分区等于挂载的存储设备的设备号,说明 
  19.         该存储设备没有被安全删除,也就是没有先卸载后移除*/  
  20.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, msg, false);  
  21.           
  22.         /*卸载挂载在asec目录的分区*/  
  23.         if (mVm->cleanupAsec(thistrue)) {  
  24.             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");  
  25.         }  
  26.         /*卸载该分区挂载的所有挂载点,这里为什么用所有来形容了,因为Android 
  27.         系统挂载一个分区的期间,重复挂载在好几个目录,将分区挂载在/mnt/asec目录,也挂载 
  28.         在/mnt/secure/asec目录,也挂载在/mnt/sdcard目录下,总共三次挂载,谷歌不知为什么搞这么复杂? 
  29.         待深究。。*/  
  30.         if (Volume::unmountVol(true)) {  
  31.             SLOGE("Failed to unmount volume on bad removal (%s)",   
  32.             strerror(errno));  
  33.         } else {  
  34.             SLOGD("Crisis averted");  
  35.         }  
  36.     } else if (state == Volume::State_Shared) {  
  37.         snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",  
  38.         getLabel(), major, minor);  
  39.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, msg, false);  
  40.         /*这种情况是这样:如果手机跟电脑连接在一起,电脑正在使用sd卡,你把sd卡取出, 
  41.         就会广播该错误信息给framework*/  
  42.         if (mVm->unshareVolume(getLabel(), "ums")) {  
  43.             SLOGE("Failed to unshare volume on bad removal (%s)",  
  44.             strerror(errno));  
  45.         } else {  
  46.             SLOGD("Crisis averted");  
  47.         }  
  48.     }  
  49. }  

这几章介绍了磁盘事件的处理,总算可以告一段落,这些工作就是在main函数中的nm->start()函数负责的,下一篇文章可以分析其他处理函数了,但这些事件的处理起着至关重要的作用,如果没有做这些工作,framework也就根本不理会也不知道底层发生了什么事情。
下一篇继续磁盘操作部分。。

Android-vold源码分析之startListener(6)

vold处理完磁盘事件,就要开始接受framework的操作命令,在main函数里面,开启了一个线程来监听framework的信息,当收到操作命令,vold进行解析,分析出命令,然后调用相应的磁盘操作函数,待操作完成后,再将操作结果的状态值反馈给framework,中间均使用了广播机制,使用了UDP协议。

在main函数中,有以下函数的调用:

  1. if (cl->startListener()) {  
  2.     SLOGE("Unable to start CommandListener (%s)", strerror(errno));  
  3.     exit(1);  
  4. }  
cl是CommandListener类实例化的一个对象,该对象专门负责与framework的通信,首先说明与CommandListener类相关的一些继承关系。
CommandListener --> FrameworkListener --> SocketListener(父类)
在CommandListener类中,声明了6个类,这6个类继承了VoldCommand类,VoldCommand类继承了FrameworkCommand,关系如下:
VoldCommand --> FrameworkCommand(父类)
以下是CommandListener类的声明:
  1. class CommandListener : public FrameworkListener {  
  2. public:  
  3.     CommandListener();  
  4.     virtual ~CommandListener() {}  
  5.   
  6. private:  
  7.     static void dumpArgs(int argc, char **argv, int argObscure);  
  8.   
  9.     class DumpCmd : public VoldCommand {  
  10.     public:  
  11.         DumpCmd();  
  12.         virtual ~DumpCmd() {}  
  13.         int runCommand(SocketClient *c, int argc, char ** argv);  
  14.     };  
  15.   
  16.     class VolumeCmd : public VoldCommand {  
  17.     public:  
  18.         VolumeCmd();  
  19.         virtual ~VolumeCmd() {}  
  20.         int runCommand(SocketClient *c, int argc, char ** argv);  
  21.     };  
  22.   
  23.     class ShareCmd : public VoldCommand {  
  24.     public:  
  25.         ShareCmd();  
  26.         virtual ~ShareCmd() {}  
  27.         int runCommand(SocketClient *c, int argc, char ** argv);  
  28.     };  
  29.   
  30.     class AsecCmd : public VoldCommand {  
  31.     public:  
  32.         AsecCmd();  
  33.         virtual ~AsecCmd() {}  
  34.         int runCommand(SocketClient *c, int argc, char ** argv);  
  35.     };  
  36.   
  37.     class StorageCmd : public VoldCommand {  
  38.     public:  
  39.         StorageCmd();  
  40.         virtual ~StorageCmd() {}  
  41.         int runCommand(SocketClient *c, int argc, char ** argv);  
  42.     };  
  43.   
  44.     class XwarpCmd : public VoldCommand {  
  45.     public:  
  46.         XwarpCmd();  
  47.         virtual ~XwarpCmd() {}  
  48.         int runCommand(SocketClient *c, int argc, char ** argv);  
  49.     };  
  50. };  
在这个类中,VoldCommand主要写了一个纯虚函数runCommand,声明如下:
virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
为了就是在CommandListener类中的实现,这里总共实现了6个版本的runCommand函数,下一篇文章只讨论其中一个比较重要的VolumeCmd类的实现,其余的跟这个类的实现差不多。
开始深入startListener线程:
进入startListener函数,创建了以下线程:
  1. if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
  2.     SLOGE("pthread_create (%s)", strerror(errno));  
  3.     return -1;  
  4. }  
这里跟前几章线程的创建几乎一样,
  1. void *SocketListener::threadStart(void *obj) {  
  2.     SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  3.   
  4.     me->runListener();  
  5.     pthread_exit(NULL);  
  6.     return NULL;  
  7. }  
线程真正实现的函数在这里:
  1. void SocketListener::runListener() {  
  2.     while(1) {  
  3.         SocketClientCollection::iterator it;  
  4.         fd_set read_fds;  
  5.         int rc = 0;  
  6.         int max = 0;  
  7.   
  8.         FD_ZERO(&read_fds);  
  9.   
  10.         if (mListen) {  
  11.             max = mSock;  
  12.             FD_SET(mSock, &read_fds);  
  13.         }  
  14.   
  15.         FD_SET(mCtrlPipe[0], &read_fds);  
  16.         if (mCtrlPipe[0] > max)  
  17.             max = mCtrlPipe[0];  
  18.   
  19.         pthread_mutex_lock(&mClientsLock);  
  20.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  21.             FD_SET((*it)->getSocket(), &read_fds);  
  22.             if ((*it)->getSocket() > max)  
  23.                 max = (*it)->getSocket();  
  24.         }  
  25.         pthread_mutex_unlock(&mClientsLock);  
  26.   
  27.         if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {  
  28.             SLOGE("select failed (%s)", strerror(errno));  
  29.             sleep(1);  
  30.             continue;  
  31.         } else if (!rc)  
  32.             continue;  
  33.   
  34.         if (FD_ISSET(mCtrlPipe[0], &read_fds))  
  35.             break;  
  36.         if (mListen && FD_ISSET(mSock, &read_fds)) {  
  37.             struct sockaddr addr;  
  38.             socklen_t alen = sizeof(addr);  
  39.             int c;  
  40.   
  41.             if ((c = accept(mSock, &addr, &alen)) < 0) {  
  42.                 SLOGE("accept failed (%s)", strerror(errno));  
  43.                 sleep(1);  
  44.                 continue;  
  45.             }  
  46.             pthread_mutex_lock(&mClientsLock);  
  47.             mClients->push_back(new SocketClient(c));  
  48.             pthread_mutex_unlock(&mClientsLock);  
  49.         }  
  50.   
  51.         do {  
  52.             pthread_mutex_lock(&mClientsLock);  
  53.             for (it = mClients->begin(); it != mClients->end(); ++it) {  
  54.                 int fd = (*it)->getSocket();  
  55.                 if (FD_ISSET(fd, &read_fds)) {  
  56.                     pthread_mutex_unlock(&mClientsLock);  
  57.                     /*当收到framework的操作命令,执行该函数*/  
  58.                     if (!onDataAvailable(*it)) {  
  59.                         close(fd);  
  60.                         pthread_mutex_lock(&mClientsLock);  
  61.                         delete *it;  
  62.                         it = mClients->erase(it);  
  63.                         pthread_mutex_unlock(&mClientsLock);  
  64.                     }  
  65.                     FD_CLR(fd, &read_fds);  
  66.                     continue;  
  67.                 }  
  68.             }  
  69.             pthread_mutex_unlock(&mClientsLock);  
  70.         } while (0);  
  71.     }  
  72. }  
将收到的命令交给onDataAvailable函数来处理,onDataAvailable函数是类的纯虚函数,在FrameworkListener类实现了该函数:
  1. bool FrameworkListener::onDataAvailable(SocketClient *c) {  
  2.     char buffer[255];  
  3.     int len;  
  4.   
  5.     if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {  
  6.         SLOGE("read() failed (%s)", strerror(errno));  
  7.         return errno;  
  8.     } else if (!len)  
  9.         return false;  
  10.   
  11.     int offset = 0;  
  12.     int i;  
  13.   
  14.     for (i = 0; i < len; i++) {  
  15.         if (buffer[i] == '\0') {  
  16.             /*命令的处理函数*/  
  17.             dispatchCommand(c, buffer + offset);  
  18.             offset = i + 1;  
  19.         }  
  20.     }  
  21.     return true;  
  22. }  
dispatchCommand函数用来选择不同的分支函数,因为在类有6个分支的实现版本,包括DumpCmd,VolumeCmd,ShareCmd,AsecCmd,StorageCmd和XwarpCmd。以下是dispatchCommand函数的源码:
  1. void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
  2.     FrameworkCommandCollection::iterator i;  
  3.     int argc = 0;  
  4.     /*static const int CMD_ARGS_MAX = 16; 
  5.     说明framework发送的命令最多只能容纳16个子串*/  
  6.     char *argv[FrameworkListener::CMD_ARGS_MAX];  
  7.       
  8.     /*中间省略了字符串的处理部分,将每个子串保存到了argv数组中, 
  9.     所以在out标记的位置,对argv[i]的数组进行释放, 
  10.     因为使用了strdup函数复制字符串*/  
  11.           
  12.     /*下面这个循环用来循环选择不同的分支实现函数, 
  13.     mCommands容器保存着6个新声明的类对象*/  
  14.     for (i = mCommands->begin(); i != mCommands->end(); ++i) {  
  15.         FrameworkCommand *c = *i;  
  16.         /*当第一个参数与FrameworkCommand类中的mCommand参数相等时, 
  17.         才去调用该对象新实现的runCommand函数*/  
  18.         if (!strcmp(argv[0], c->getCommand())) {  
  19.             if (c->runCommand(cli, argc, argv)) {  
  20.                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
  21.             }  
  22.             goto out;  
  23.         }  
  24.     }  
  25.   
  26.     cli->sendMsg(500, "Command not recognized"false);  
  27. out:  
  28.     int j;  
  29.     for (j = 0; j < argc; j++)  
  30.         free(argv[j]);  
  31.     return;  
  32. }  
这里从代码可能有点疑问,这个循环为什么可以选择不同的分支新实现的函数呢?
仔细看下代码会发现,在FrameworkListener中有一个注册函数,将新的派生类的名字注册到mCommands容器中,每个类的mCommand参数各代表一个派生类的名字。
  1. void FrameworkListener::registerCmd(FrameworkCommand *cmd) {  
  2.     mCommands->push_back(cmd);  
  3. }  
在CommandListener类的构造函数中,分别对6个分支的派生类进行了注册,每次注册都实例化了一个新的对象存放到mCommands容器中。
  1. CommandListener::CommandListener() :  
  2.                  FrameworkListener("vold") {  
  3.     registerCmd(new DumpCmd());  
  4.     registerCmd(new VolumeCmd());  
  5.     registerCmd(new AsecCmd());  
  6.     registerCmd(new ShareCmd());  
  7.     registerCmd(new StorageCmd());  
  8.     registerCmd(new XwarpCmd());  
  9. }  
那这6个派生类的名称保存在FrameworkCommand类的一个私有变量mCommand中,该类的构造函数就是给mCommand赋值,源码:
  1. FrameworkCommand::FrameworkCommand(const char *cmd) {  
  2.     mCommand = cmd;  
  3. }  
在刚才的FrameworkListener::dispatchCommand函数里,有这么一个比较:
  1. if (!strcmp(argv[0], c->getCommand())){}  
getCommand函数的源码:
  1. const char *getCommand() { return mCommand; }  

这里来看mCommand的赋值,我们知道以下关系:
DumpCmd --> VoldCommand --> FrameworkCommand
VolumeCmd --> VoldCommand --> FrameworkCommand
ShareCmd --> VoldCommand --> FrameworkCommand
AsecCmd --> VoldCommand --> FrameworkCommand
StorageCmd --> VoldCommand --> FrameworkCommand
XwarpCmd --> VoldCommand --> FrameworkCommand
所以在CommandListener类中的6个派生类中的构造函数中,必须初始化const char *cmd这个参数,以下是初始化代码:

  1. CommandListener::DumpCmd::DumpCmd() :  
  2.                  VoldCommand("dump") {  
  3. }  
  4. CommandListener::VolumeCmd::VolumeCmd() :  
  5.                  VoldCommand("volume") {  
  6. }  
  7. CommandListener::ShareCmd::ShareCmd() :  
  8.                  VoldCommand("share") {  
  9. }  
  10. CommandListener::StorageCmd::StorageCmd() :  
  11.                  VoldCommand("storage") {  
  12. }  
  13. CommandListener::AsecCmd::AsecCmd() :  
  14.                  VoldCommand("asec") {  
  15. }  
  16. CommandListener::XwarpCmd::XwarpCmd() :  
  17.                  VoldCommand("xwarp") {  
  18. }  

6个构造函数均初始化不同的cmd参数,分别为dump,volume,share,storage,asec,xwarp。
在VoldCommand类的构造函数中,将cmd的值初始化FrameworkCommand类的构造函数。
  1. VoldCommand::VoldCommand(const char *cmd) :  
  2.               FrameworkCommand(cmd)  {  
  3. }  

所以这个比较,就是将framework发下来的命令的第一个参数与mCommands容器中的6个对象的mCommand参数进行了比较,从而选择正确的处理分支函数。

以上的内容涉及到好几个类,谷歌真是花了很大的力气啊。。

现在流程就走到了runCommand函数,该函数就声明在最上面那个CommandListener类里面,下一篇文章将介绍runCommand函数的实现。

Android-vold源码分析之runCommand(7)

说了这么多,该到执行命令的函数了,要不黄花菜都凉了。上一篇文章讲了vold如何开启接收framework下发命令的线程,最终到了runCommand函数的实现,总共有6个版本,由于重复性大,只讲VolumeCmd类的runCommand函数的实现。
VolumeCmd可以说是最重要的,该类的实现处理了list,debug,mount,unmount,format,unshare,shared等等操作,详细说明:
list: 在启动vold之后,接收的第一条命令就是list,这里是获取系统的所有磁盘对象,一般只有sd卡。
debug: 设置USB的调试模式
mount: 挂载磁盘
unmount: 卸载磁盘
format: 格式化磁盘
unshare: 关闭USB的大容量存储模式,相当于断开手机与电脑的连接
shared: 开启USB的大容量存储模式,相当于手机与电脑连接,并挂载在电脑

下面把该函数的源码贴出来,一目了然,程序员,代码说话:

  1. int CommandListener::VolumeCmd::runCommand(SocketClient *cli,  
  2.                                                       int argc, char **argv) {  
  3.     dumpArgs(argc, argv, -1);  
  4.   
  5.     if (argc < 2) {  
  6.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument"false);  
  7.         return 0;  
  8.     }  
  9.   
  10.     VolumeManager *vm = VolumeManager::Instance();  
  11.     int rc = 0;  
  12.   
  13.     if (!strcmp(argv[1], "list")) {  
  14.         return vm->listVolumes(cli);  
  15.     } else if (!strcmp(argv[1], "debug")) {  
  16.         if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {  
  17.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>"false);  
  18.             return 0;  
  19.         }  
  20.         vm->setDebug(!strcmp(argv[2], "on") ? true : false);  
  21.     } else if (!strcmp(argv[1], "mount")) {  
  22.         if (argc != 3) {  
  23.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>"false);  
  24.             return 0;  
  25.         }  
  26.         rc = vm->mountVolume(argv[2]);  
  27.     } else if (!strcmp(argv[1], "unmount")) {  
  28.         if (argc < 3 || argc > 4 || (argc == 4 && strcmp(argv[3], "force"))) {  
  29.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force]"false);  
  30.             return 0;  
  31.         }  
  32.   
  33.         bool force = false;  
  34.         if (argc >= 4 && !strcmp(argv[3], "force")) {  
  35.             force = true;  
  36.         }  
  37.         rc = vm->unmountVolume(argv[2], force);  
  38.     } else if (!strcmp(argv[1], "format")) {  
  39.         if (argc != 3) {  
  40.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>"false);  
  41.             return 0;  
  42.         }  
  43.         rc = vm->formatVolume(argv[2]);  
  44.     } else if (!strcmp(argv[1], "share")) {  
  45.         if (argc != 4) {  
  46.             cli->sendMsg(ResponseCode::CommandSyntaxError,  
  47.                     "Usage: volume share <path> <method>"false);  
  48.             return 0;  
  49.         }  
  50.         rc = vm->shareVolume(argv[2], argv[3]);  
  51.     } else if (!strcmp(argv[1], "unshare")) {  
  52.         if (argc != 4) {  
  53.             cli->sendMsg(ResponseCode::CommandSyntaxError,  
  54.                     "Usage: volume unshare <path> <method>"false);  
  55.             return 0;  
  56.         }  
  57.         rc = vm->unshareVolume(argv[2], argv[3]);  
  58.     } else if (!strcmp(argv[1], "shared")) {  
  59.         bool enabled = false;  
  60.         if (argc != 4) {  
  61.             cli->sendMsg(ResponseCode::CommandSyntaxError,  
  62.                     "Usage: volume shared <path> <method>"false);  
  63.             return 0;  
  64.         }  
  65.   
  66.         if (vm->shareEnabled(argv[2], argv[3], &enabled)) {  
  67.             cli->sendMsg(  
  68.                     ResponseCode::OperationFailed, "Failed to determine share enable state"true);  
  69.         } else {  
  70.             cli->sendMsg(ResponseCode::ShareEnabledResult,  
  71.                     (enabled ? "Share enabled" : "Share disabled"), false);  
  72.         }  
  73.         return 0;  
  74.     } else {  
  75.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd"false);  
  76.     }  
  77.   
  78.     if (!rc) {  
  79.         cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded"false);  
  80.     } else {  
  81.         int erno = errno;  
  82.         rc = ResponseCode::convertFromErrno();  
  83.         cli->sendMsg(rc, "volume operation failed"true);  
  84.     }  
  85.   
  86.     return 0;  
  87. }  

每次操作完成后,都要将操作结果汇报给framework,阶级负责工作,一定要把工作结果汇报给领导,这里使用了sendMsg函数。
1.搜索需要挂载的磁盘对象信息列表。先从listVolumes函数开始,贴源码:
  1. int VolumeManager::listVolumes(SocketClient *cli) {  
  2.     VolumeCollection::iterator i;  
  3.   
  4.     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {  
  5.         char *buffer;  
  6.         asprintf(&buffer, "%s %s %d",  
  7.                  (*i)->getLabel(), (*i)->getMountpoint(),  
  8.                  (*i)->getState());  
  9.         cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);  
  10.         free(buffer);  
  11.     }  
  12.     cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed."false);  
  13.     return 0;  
  14. }  

这函数扫描了mVolumes容器,里面保存着从配置文件/etc/vold.fstab解析出来的磁盘。
该容器是在main函数中添加的:vm->addVolume(dv);
这函数先将每个磁盘的标签,挂载点,状态汇报给framework,然后再循环工作结束后,再次向领导汇报工作结束。
2.设置调试模式。贴源码:
  1. void VolumeManager::setDebug(bool enable) {  
  2.     mDebug = enable;  
  3.     VolumeCollection::iterator it;  
  4.     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {  
  5.         (*it)->setDebug(enable);  
  6.     }  
  7. }  

每个Volume对象都有一个setDebug函数设置调试状态:
  1. void Volume::setDebug(bool enable) {  
  2.     mDebug = enable;  
  3. }  

挂载磁盘涉及到很多内容和源码,将在下一篇文章继续说明,待续。。。

Android-vold源码分析之挂载SD卡(8)

这里续上一篇文章,讲解挂载磁盘的操作:

3.挂载磁盘。这里都有一个const char *类型的参数,这参数保存着每个磁盘的标签信息,比如sd卡的label是sdcard。

  1. int VolumeManager::mountVolume(const char *label) {  
  2.     Volume *v = lookupVolume(label);  
  3.   
  4.     if (!v) {  
  5.         errno = ENOENT;  
  6.         return -1;  
  7.     }  
  8.   
  9.     return v->mountVol();  
  10. }  

lookupVolume函数寻找与label匹配的对象:
  1. Volume *VolumeManager::lookupVolume(const char *label) {  
  2.     VolumeCollection::iterator i;  
  3.   
  4.     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {  
  5.         if (label[0] == '/') {  
  6.             if (!strcmp(label, (*i)->getMountpoint()))  
  7.                 return (*i);  
  8.         } else {  
  9.             if (!strcmp(label, (*i)->getLabel()))  
  10.                 return (*i);  
  11.         }  
  12.     }  
  13.     return NULL;  
  14. }  

如果找到,直接返回磁盘对象Volume*,挂载操作在mountVol函数里面,该函数内容有点多,贴源码:
  1. int Volume::mountVol() {  
  2.     dev_t deviceNodes[4];  
  3.     int n, i, rc = 0;  
  4.     char errmsg[255];  
  5.   
  6.     if (getState() == Volume::State_NoMedia) {  
  7.         snprintf(errmsg, sizeof(errmsg),  
  8.                  "Volume %s %s mount failed - no media",  
  9.                  getLabel(), getMountpoint());  
  10.         mVm->getBroadcaster()->sendBroadcast(  
  11.                                          ResponseCode::VolumeMountFailedNoMedia,  
  12.                                          errmsg, false);  
  13.         errno = ENODEV;  
  14.         return -1;  
  15.     } else if (getState() != Volume::State_Idle) {  
  16.         errno = EBUSY;  
  17.         return -1;  
  18.     }  
  19.     /*判断该挂载点是否已经挂载,若已经挂载,则直接返回。*/  
  20.     if (isMountpointMounted(getMountpoint())) {  
  21.         SLOGW("Volume is idle but appears to be mounted - fixing");  
  22.         /*这里的setState函数用得很频繁,这函数就是将状态通知给framework*/  
  23.         setState(Volume::State_Mounted);  
  24.         // mCurrentlyMountedKdev = XXX  
  25.         return 0;  
  26.     }  
  27.     /*获取磁盘的设备号与分区数量,在下面说明*/  
  28.     n = getDeviceNodes((dev_t *) &deviceNodes, 4);  
  29.     if (!n) {  
  30.         SLOGE("Failed to get device nodes (%s)\n", strerror(errno));  
  31.         return -1;  
  32.     }  
  33.     /*将循环挂载n个分区,但从代码上看,只适用于挂载一个分区*/  
  34.     for (i = 0; i < n; i++) {  
  35.         char devicePath[255];  
  36.         /*这里看到了吧,用这种方式来使用磁盘的设备节点很方便,直接用主次设备号来命令*/  
  37.         sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),  
  38.                 MINOR(deviceNodes[i]));  
  39.   
  40.         SLOGI("%s being considered for volume %s\n", devicePath, getLabel());  
  41.   
  42.         errno = 0;  
  43.         setState(Volume::State_Checking);  
  44.         /*挂载之前先检测一下该分区是否是fat分区,如果不是fat格式,返回-1*/  
  45.         if (Fat::check(devicePath)) {  
  46.             if (errno == ENODATA) {  
  47.                 SLOGW("%s does not contain a FAT filesystem\n", devicePath);  
  48.                 continue;  
  49.             }  
  50.             errno = EIO;  
  51.             /* Badness - abort the mount */  
  52.             SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));  
  53.             setState(Volume::State_Idle);  
  54.             return -1;  
  55.         }  
  56.   
  57.         /* 
  58.          * Mount the device on our internal staging mountpoint so we can 
  59.          * muck with it before exposing it to non priviledged users. 
  60.          */  
  61.         errno = 0;  
  62.         /*将设备节点挂载在/mnt/secure/staging目录*/  
  63.         if (Fat::doMount(devicePath, "/mnt/secure/staging"falsefalse, 1000, 1015, 0702, true)) {  
  64.             SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));  
  65.             continue;  
  66.         }  
  67.   
  68.         SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());  
  69.   
  70.         protectFromAutorunStupidity();  
  71.         /*挂载一个只有root用户能够访问的目录,这函数挂载了两次 
  72.         将/mnt/secure/staging/.android_secure  挂载在 /mnt/secure/asec, 
  73.         将tmpfs 挂载在 /mnt/secure/staging/.android_secure*/  
  74.         if (createBindMounts()) {  
  75.             SLOGE("Failed to create bindmounts (%s)", strerror(errno));  
  76.             umount("/mnt/secure/staging");  
  77.             setState(Volume::State_Idle);  
  78.             return -1;  
  79.         }  
  80.   
  81.         /* 
  82.          * Now that the bindmount trickery is done, atomically move the 
  83.          * whole subtree to expose it to non priviledged users. 
  84.          */  
  85.         /*将挂载点挂载的目录再挂载到最终的目录/mnt/sdcard*/  
  86.         if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {  
  87.             SLOGE("Failed to move mount (%s)", strerror(errno));  
  88.             umount("/mnt/secure/staging");  
  89.             setState(Volume::State_Idle);  
  90.             return -1;  
  91.         }  
  92.         setState(Volume::State_Mounted);  
  93.         mLastMountedKdev = mCurrentlyMountedKdev = deviceNodes[i];  
  94.         return 0;  
  95.     }  
  96.   
  97.     SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());  
  98.     setState(Volume::State_Idle);  
  99.   
  100.     return -1;  
  101. }  

这个挂载函数看起来,会发现很繁琐,好几个目录的挂载关系,有以下挂载目录:
/dev/block/vold/8:1 挂载在-> /mnt/secure/staging
/mnt/secure/staging/.android_secure  挂载在-> /mnt/secure/asec
tmpfs 挂载在-> /mnt/secure/staging/.android_secure
/mnt/secure/staging 挂载在-> /mnt/sdcard
从程序的注释看,这样的目的是挂载一个只有root用户能查看的目录,具体还是没搞清楚谷歌为什么要这样挂载,
还是有疑问,希望有清楚的高手指点一下。
sd卡的挂载比较清楚,中间多了一个中介,将设备节点8:1挂载在/mnt/secure/staging,最后又将该目录挂载在/mnt/sdcard,
这目录就是最终用户能够看到文件的目录。
函数里面涉及到几个函数:
getDeviceNodes函数获取挂载设备的设备号与分区数量,是Volume类的一个纯虚函数,在子类DirectVolume中实现,源码:
  1. int DirectVolume::getDeviceNodes(dev_t *devs, int max) {  
  2.     if (mPartIdx == -1) {  
  3.         // If the disk has no partitions, try the disk itself  
  4.         if (!mDiskNumParts) {  
  5.             devs[0] = MKDEV(mDiskMajor, mDiskMinor);  
  6.             return 1;  
  7.         }  
  8.   
  9.         int i;  
  10.         for (i = 0; i < mDiskNumParts; i++) {  
  11.             if (i == max)  
  12.                 break;  
  13.             devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);  
  14.         }  
  15.         return mDiskNumParts;  
  16.     }  
  17.     devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);  
  18.     return 1;  
  19. }  

下面贴一些mountVol里面挂载的源码:
  1. int Fat::doMount(const char *fsPath, const char *mountPoint,  
  2.                  bool ro, bool remount, int ownerUid, int ownerGid,  
  3.                  int permMask, bool createLost) {  
  4.     int rc;  
  5.     unsigned long flags;  
  6.     char mountData[255];  
  7.   
  8.     flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;  
  9.   
  10.     flags |= (ro ? MS_RDONLY : 0);  
  11.     flags |= (remount ? MS_REMOUNT : 0);  
  12.   
  13.     /* 
  14.      * Note: This is a temporary hack. If the sampling profiler is enabled, 
  15.      * we make the SD card world-writable so any process can write snapshots. 
  16.      * 
  17.      * TODO: Remove this code once we have a drop box in system_server. 
  18.      */  
  19.     char value[PROPERTY_VALUE_MAX];  
  20.     property_get("persist.sampling_profiler", value, "");  
  21.     if (value[0] == '1') {  
  22.         SLOGW("The SD card is world-writable because the"  
  23.             " 'persist.sampling_profiler' system property is set to '1'.");  
  24.         permMask = 0;  
  25.     }  
  26.   
  27.     sprintf(mountData,  
  28.             "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",  
  29.             ownerUid, ownerGid, permMask, permMask);  
  30.   
  31.     rc = mount(fsPath, mountPoint, "vfat", flags, mountData);  
  32.   
  33.     if (rc && errno == EROFS) {  
  34.         SLOGE("%s appears to be a read only filesystem - retrying mount RO", fsPath);  
  35.         flags |= MS_RDONLY;  
  36.         rc = mount(fsPath, mountPoint, "vfat", flags, mountData);  
  37.     }  
  38.   
  39.     if (rc == 0 && createLost) {  
  40.         char *lost_path;  
  41.         asprintf(&lost_path, "%s/LOST.DIR", mountPoint);  
  42.         if (access(lost_path, F_OK)) {  
  43.             /* 
  44.              * Create a LOST.DIR in the root so we have somewhere to put 
  45.              * lost cluster chains (fsck_msdos doesn't currently do this) 
  46.              */  
  47.             if (mkdir(lost_path, 0755)) {  
  48.                 SLOGE("Unable to create LOST.DIR (%s)", strerror(errno));  
  49.             }  
  50.         }  
  51.         free(lost_path);  
  52.     }  
  53.   
  54.     return rc;  
  55. }  
  56.   
  57. int Volume::createBindMounts() {  
  58.     unsigned long flags;  
  59.   
  60.     /* 
  61.      * Rename old /android_secure -> /.android_secure 
  62.      */  
  63.     if (!access("/mnt/secure/staging/android_secure", R_OK | X_OK) &&  
  64.          access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {  
  65.         if (rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) {  
  66.             SLOGE("Failed to rename legacy asec dir (%s)", strerror(errno));  
  67.         }  
  68.     }  
  69.   
  70.     /* 
  71.      * Ensure that /android_secure exists and is a directory 
  72.      */  
  73.     if (access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {  
  74.         if (errno == ENOENT) {  
  75.             if (mkdir(SEC_STG_SECIMGDIR, 0777)) {  
  76.                 SLOGE("Failed to create %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
  77.                 return -1;  
  78.             }  
  79.         } else {  
  80.             SLOGE("Failed to access %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
  81.             return -1;  
  82.         }  
  83.     } else {  
  84.         struct stat sbuf;  
  85.   
  86.         if (stat(SEC_STG_SECIMGDIR, &sbuf)) {  
  87.             SLOGE("Failed to stat %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
  88.             return -1;  
  89.         }  
  90.         if (!S_ISDIR(sbuf.st_mode)) {  
  91.             SLOGE("%s is not a directory", SEC_STG_SECIMGDIR);  
  92.             errno = ENOTDIR;  
  93.             return -1;  
  94.         }  
  95.     }  
  96.   
  97.     /* 
  98.      * Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll 
  99.      * have a root only accessable mountpoint for it. 
  100.      */  
  101.     if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {  
  102.         SLOGE("Failed to bind mount points %s -> %s (%s)",  
  103.                 SEC_STG_SECIMGDIR, SEC_ASECDIR, strerror(errno));  
  104.         return -1;  
  105.     }  
  106.   
  107.     /* 
  108.      * Mount a read-only, zero-sized tmpfs  on <mountpoint>/android_secure to 
  109.      * obscure the underlying directory from everybody - sneaky eh? ;) 
  110.      */  
  111.     if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=000,uid=0,gid=0")) {  
  112.         SLOGE("Failed to obscure %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
  113.         umount("/mnt/asec_secure");  
  114.         return -1;  
  115.     }  
  116.   
  117.     return 0;  
  118. }  
  119.   
  120. int Volume::doMoveMount(const char *src, const char *dst, bool force) {  
  121.     unsigned int flags = MS_MOVE;  
  122.     int retries = 5;  
  123.   
  124.     while(retries--) {  
  125.         if (!mount(src, dst, "", flags, NULL)) {  
  126.             if (mDebug) {  
  127.                 SLOGD("Moved mount %s -> %s sucessfully", src, dst);  
  128.             }  
  129.             return 0;  
  130.         } else if (errno != EBUSY) {  
  131.             SLOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno));  
  132.             return -1;  
  133.         }  
  134.         int action = 0;  
  135.   
  136.         if (force) {  
  137.             if (retries == 1) {  
  138.                 action = 2; // SIGKILL  
  139.             } else if (retries == 2) {  
  140.                 action = 1; // SIGHUP  
  141.             }  
  142.         }  
  143.         SLOGW("Failed to move %s -> %s (%s, retries %d, action %d)",  
  144.                 src, dst, strerror(errno), retries, action);  
  145.         Process::killProcessesWithOpenFiles(src, action);  
  146.         usleep(1000*250);  
  147.     }  
  148.   
  149.     errno = EBUSY;  
  150.     SLOGE("Giving up on move %s -> %s (%s)", src, dst, strerror(errno));  
  151.     return -1;  
  152. }  

篇幅有点长了,挂载点也挂载了好几个,mountVol处理的事情最多的,也最繁琐,但都是简单的,主要是有点摸不着头脑,
谷歌为啥要这样挂载,也许自己太菜了。。
下一篇文章继续。。

Android-vold源码分析之卸载SD卡(9)

本文分析卸载sd卡的源码,这些涉及的内容比较少,走过就行。在Android系统,系统设置里面,
提供了挂载,卸载与格式化的功能。在这里,我们可以回忆一下,Android在挂载sd卡有几个途径,
卸载又是有几种方法?
一、挂载sd卡,有2种方式:
1.用户自己挂载sd卡;
2.自动挂载sd卡,这时候是在Android系统启动完成后挂载的,所以可以看成是自动挂载;

二、卸载sd卡,有2种方式:
1.用户自己卸载sd卡;
2.用户移除sd卡;
卸载sd卡,1和2看起来有点矛盾,但这相当于平时我们在电脑使用U盘一样,
一种是先安全删除再移除U盘;
一种是不安全删除直接移除U盘;
所以在Android系统中,卸载sd卡有两个途径:
1.Framework下发卸载命令(volume unmount sdcard force),卸载sd卡;
2.vold捕获到移除事件,如果是非安全删除sd卡,那么就卸载sd卡。

所以挂载与卸载sd卡的两种方法不同,挂载sd卡都是由Framework下发命令,而卸载sd卡则不同。

一、先来分析Framework下发卸载命令:

截取CommandListener类其中的源码:

  1. else if (!strcmp(argv[1], "unmount")) {  
  2.     if (argc < 3 || argc > 4 || (argc == 4 && strcmp(argv[3], "force"))) {  
  3.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force]"false);  
  4.         return 0;  
  5.     }  
  6.       
  7.     bool force = false;  
  8.     if (argc >= 4 && !strcmp(argv[3], "force")) {  
  9.         force = true;  
  10.     }  
  11.     rc = vm->unmountVolume(argv[2], force);  
  12. }  
这条卸载命令的格式是:volume unmount sdcard true/false.
这里有一个强制卸载的标志,也就是force参数,如果命令包含“force”字符串,那么就强制
卸载sd卡,接下来看unmountVolume卸载函数:
  1. int VolumeManager::unmountVolume(const char *label, bool force) {  
  2.     Volume *v = lookupVolume(label);   
  3.     ......  
  4.     /*这函数清理了/mnt/asec目录的一些内容,其实看不见*/  
  5.     cleanupAsec(v, force);  
  6.   
  7.     return v->unmountVol(force);  
  8. }  

开始看卸载函数,以下函数均做简化,不再贴出大量源码,Android系统的源码实在太多:
  1. int Volume::unmountVol(bool force) {  
  2.     /*广播sd卡正在卸载的状态*/  
  3.     setState(Volume::State_Unmounting);  
  4.   
  5.     if (doMoveMount(getMountpoint(), SEC_STGDIR, force)) {  
  6.         SLOGE("Failed to move mount %s => %s (%s)", getMountpoint(), SEC_STGDIR, strerror(errno));  
  7.         setState(Volume::State_Mounted);  
  8.         return -1;  
  9.     }  
  10.   
  11.     if (doUnmount(Volume::SEC_STG_SECIMGDIR, force)) {  
  12.         SLOGE("Failed to unmount tmpfs on %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
  13.         goto fail_republish;  
  14.     }  
  15.   
  16.     if (doUnmount(Volume::SEC_ASECDIR, force)) {  
  17.         SLOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR, strerror(errno));  
  18.         goto fail_remount_tmpfs;  
  19.     }  
  20.     if (doUnmount(Volume::SEC_STGDIR, force)) {  
  21.         SLOGE("Failed to unmount %s (%s)", SEC_STGDIR, strerror(errno));  
  22.         goto fail_recreate_bindmount;  
  23.     }  
  24.     /*以上doMoveMount, doUnmount等函数做些清理工作,就是之前挂载到系统的目录 
  25.     清理干净*/  
  26.     /*卸载工作做完,告诉framework,该磁盘处于空闲状态*/  
  27.     setState(Volume::State_Idle);  
  28.     /*用来保存正在挂载的设备号的全局变量,现在赋为-1*/  
  29.     mCurrentlyMountedKdev = -1;  
  30.     return 0;  
  31. }  

二、vold捕获移除事件,调用卸载函数:

  1. void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     char msg[255];  
  5.     int state;  
  6.   
  7.     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);  
  8.   
  9.     /* 
  10.      * The framework doesn't need to get notified of 
  11.      * partition removal unless it's mounted. Otherwise 
  12.      * the removal notification will be sent on the Disk 
  13.      * itself 
  14.      */  
  15.     state = getState();  
  16.     if (state != Volume::State_Mounted && state != Volume::State_Shared) {  
  17.         return;  
  18.     }  
  19.     /*判断,如果移除事件的设备号与正在挂载系统的设备号相等, 
  20.     说明此时是没有安全删除,所以下面会广播VolumeBadRemoval反馈码*/  
  21.     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {  
  22.         /* 
  23.          * Yikes, our mounted partition is going away! 
  24.          */  
  25.   
  26.         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",  
  27.                  getLabel(), getMountpoint(), major, minor);  
  28.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,  
  29.                                              msg, false);  
  30.         /*清理asec目录*/  
  31.         if (mVm->cleanupAsec(thistrue)) {  
  32.             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");  
  33.         }  
  34.         /*在这里调用了卸载函数,跟framework下发命令卸载一样。*/  
  35.         if (Volume::unmountVol(true)) {  
  36.             SLOGE("Failed to unmount volume on bad removal (%s)",   
  37.                  strerror(errno));  
  38.             // XXX: At this point we're screwed for now  
  39.         } else {  
  40.             SLOGD("Crisis averted");  
  41.         }  
  42.     } else if (state == Volume::State_Shared) {  
  43.         /*这里的判断是,如果此时手机与电脑正在连接着使用时,移除事件的情况*/  
  44.         /* removed during mass storage */  
  45.         snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",  
  46.                  getLabel(), major, minor);  
  47.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,  
  48.                                              msg, false);  
  49.         /*卸载大容量存储*/  
  50.         if (mVm->unshareVolume(getLabel(), "ums")) {  
  51.             SLOGE("Failed to unshare volume on bad removal (%s)",  
  52.                 strerror(errno));  
  53.         } else {  
  54.             SLOGD("Crisis averted");  
  55.         }  
  56.     }  
  57. }  


卸载函数基本就这些,下一篇文章继续,格式化sd卡。。。

Android-vold源码分析之格式化SD卡(10)

本文开始讨论sd卡的格式化功能,平时使用windows操作系统,也经常格式化磁盘。涉及到的
操作有这几步:
1.将分区信息写到硬盘的第一个设备节点的MBR结构中的分区表;
2.格式化分区到指定的文件系统类型。

MBR中存放分区表的位置在446-509,占用了64字节,MBR结构只支持4个主分区,所以

有4个16字节的区域,先简要说明一下MBR的分区表的结构:


从这个表格可以看出,相对于446-509的分区表区域,每个主分区的第5个字节存放的是文件
系统标志位,用来识别什么分区,用fdisk工具查看一下,有如下文件系统对应的十六进制标志:


若需要读取这些文件系统标志,只需读取MBR的450个位置,占用一个字节大小。

扯得太远了,回到正题,本文是要分析Android格式化sd卡的功能,在格式化部分,涉及到
了系统的一些函数,与vold无关,简单的说明一下即可。
Android系统在格式化sd卡的时候,首先会判断sd卡是否存在分区,如果sd卡不存在分区,
那么需要重新初始化MBR区域,所以上面简要的介绍了MBR结构中分区表的区域。

  1. int Volume::formatVol() {  
  2.     if (getState() == Volume::State_NoMedia) {  
  3.         errno = ENODEV;  
  4.         return -1;  
  5.     } else if (getState() != Volume::State_Idle) {  
  6.         errno = EBUSY;  
  7.         return -1;  
  8.     }  
  9.     /*如果该分区为卸载,那么格式化失败,返回错误*/  
  10.     if (isMountpointMounted(getMountpoint())) {  
  11.         SLOGW("Volume is idle but appears to be mounted - fixing");  
  12.         setState(Volume::State_Mounted);  
  13.         // mCurrentlyMountedKdev = XXX  
  14.         errno = EBUSY;  
  15.         return -1;  
  16.     }  
  17.   
  18.     char devicePath[255];  
  19.     dev_t diskNode = getDiskDevice();  
  20.     dev_t partNode;  
  21.   
  22.     if (mDebug) {  
  23.         SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);  
  24.     }  
  25.     setState(Volume::State_Formatting);  
  26.   
  27.     if (!mLastMountedKdev) {  
  28.         dev_t deviceNodes[2];  
  29.         int n = getDeviceNodes(deviceNodes, 2);  
  30.         // initialize MBR if no partition, or has multiple partitions  
  31.         // but none is selected  
  32.         if ((diskNode == deviceNodes[0]) || (n > 1)) {  
  33.             sprintf(devicePath, "/dev/block/vold/%d:%d",  
  34.                     MAJOR(diskNode), MINOR(diskNode));  
  35.             if (initializeMbr(devicePath)) {  
  36.                 SLOGE("Failed to initialize MBR (%s)", strerror(errno));  
  37.                 partNode = diskNode; // try to use whole disk  
  38.             } else {  
  39.                 partNode = MKDEV(MAJOR(diskNode), MINOR(diskNode) + 1);  
  40.             }  
  41.         } else {  
  42.             partNode = deviceNodes[0];  
  43.         }  
  44.     } else {  
  45.         partNode = mLastMountedKdev;  
  46.     }  
  47.   
  48.     sprintf(devicePath, "/dev/block/vold/%d:%d",  
  49.             MAJOR(partNode), MINOR(partNode));  
  50.   
  51.     int ret = Fat::format(devicePath, 0);  
  52.     SLOGE_IF(ret, "Failed to format (%s)", strerror(errno));  
  53.   
  54.     setState(Volume::State_Idle);  
  55.     return ret;  
  56. }  

格式化函数两个主要工作交给了initializeMbr和Fat::format函数:
1.initializeMbr函数负责初始化MBR;
2.Fat::format函数负责格式化分区。
先来看initializeMbr函数的初始化工作:
  1. int Volume::initializeMbr(const char *deviceNode) {  
  2.     struct disk_info dinfo;  
  3.   
  4.     memset(&dinfo, 0, sizeof(dinfo));  
  5.   
  6.     if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {  
  7.         SLOGE("Failed to malloc prt_lst");  
  8.         return -1;  
  9.     }  
  10.   
  11.     memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));  
  12.     dinfo.device = strdup(deviceNode);  
  13.     dinfo.scheme = PART_SCHEME_MBR;  
  14.     dinfo.sect_size = 512;  
  15.     dinfo.skip_lba = 2048;  
  16.     dinfo.num_lba = 0;  
  17.     dinfo.num_parts = 1;  
  18.   
  19.     struct part_info *pinfo = &dinfo.part_lst[0];  
  20.   
  21.     pinfo->name = strdup("android_sdcard");  
  22.     pinfo->flags |= PART_ACTIVE_FLAG;  
  23.     pinfo->type = PC_PART_TYPE_FAT32;  
  24.     pinfo->len_kb = -1;  
  25.   
  26.     int rc = apply_disk_config(&dinfo, 0);  
  27.   
  28.     if (rc) {  
  29.         SLOGE("Failed to apply disk configuration (%d)", rc);  
  30.         goto out;  
  31.     }  
  32.   
  33.  out:  
  34.     free(pinfo->name);  
  35.     free(dinfo.device);  
  36.     free(dinfo.part_lst);  
  37.   
  38.     return rc;  
  39. }  

这里贴出一些重要的结构体:
  1. struct part_info {  
  2.     char *name;  
  3.     uint8_t flags;  
  4.     uint8_t type;  
  5.     uint32_t len_kb;       /* in 1K-bytes */  
  6.     uint32_t start_lba;    /* the LBA where this partition begins */  
  7. };  
  8. struct disk_info {  
  9.     char *device;  
  10.     uint8_t scheme;  
  11.     int sect_size;       /* expected sector size in bytes. MUST BE POWER OF 2 */  
  12.     uint32_t skip_lba;   /* in sectors (1 unit of LBA) */  
  13.     uint32_t num_lba;    /* the size of the disk in LBA units */  
  14.     struct part_info *part_lst;  
  15.     int num_parts;  
  16. };  

初始化完成后,将该结构体变量pinfo通过apply_disk_config函数进行设置:
  1. int apply_disk_config(struct disk_info *dinfo, int test)  
  2. {  
  3.     int fd;  
  4.     struct write_list *wr_lst = NULL;  
  5.     int rv;  
  6.   
  7.     if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {  
  8.         LOGE("Configuration is invalid.");  
  9.         goto fail;  
  10.     }  
  11.   
  12.     if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)  
  13.         rv = test ? 0 : sync_ptable(fd);  
  14.   
  15.     close(fd);  
  16.     wlist_free(wr_lst);  
  17.     return rv;  
  18.   
  19. fail:  
  20.     close(fd);  
  21.     if (wr_lst)  
  22.         wlist_free(wr_lst);  
  23.     return 1;  
  24. }  

该函数先打开sd卡的设备节点,然后将MBR的初始化信息写到第一个block(512B)中,
这里涉及到非常多函数,不在vold的讨论范围。
写完MBR后,就要对分区进行格式化,要格式化成FAT32格式,Fat::format函数直接调用
系统命令newfs_msdos来格式化新分区,检测磁盘是,Fat::check函数直接调用系统命令
fsck_msdos来检测分区。

最后格式化完成功,通知Framework,sd卡处于空闲状态:
  1. setState(Volume::State_Idle);  

然后,Framework又要重复挂载和卸载的操作了。
下篇文章介绍Android系统与电脑的连接,OTG功能,全称on-the-go。
待续。。

Android-vold源码分析之连接电脑OTG(11)

OTG是on-the-go的简称,是2001年由USB Implementers Forum公布,主要应用于各种不同的设备或移动设备间的联接,进行数据交换。特别是PDA、移动电话、消费类设备。改变如数码照相机、摄像机、打印机等设备间多种不同制式连接器,多达7种制式的存储卡间数据交换的不便。
对OTG介绍比较详细的在USB的官网,详细请见英文文档: http://www.usb.org/developers/onthego/。以下是官网的一个简要的介绍,贴出来看看:
USB On-The-Go and Embedded Host

Virtually every portable device now uses USB for PC connectivity. As these products increase in popularity, there is a growing need for them to communicate both with USB peripherals and directly with each other when a PC is not available. There is also an increase in the number of other, non-PC hosts (Embedded Hosts) which support USB in order to connect to USB peripherals.

The USB On-The-Go and Embedded Host Supplements addresses these scenarios by allowing portable devices and non-PC hosts to have the following enhancements:

Targeted host capability to communicate with selected other USB peripherals
Support for direct connections between OTG devices
Power saving features to preserve battery life
Revision 2.0 of the USB On-The-Go and Embedded Host Supplement to the USB 2.0 Specification applies to products operating at low-speed, full-speed and high-speed and is released, including applicable ECNs and errata, as part of the USB 2.0 Specification package. The corresponding OTG Adopters Agreement is also available.

Revision 1.0 of the USB On-The-Go and Embedded Host Supplement to the USB 3.0 Specification enhances these scenarios by adding SuperSpeed capability to USB On-The-Go and is released as part of the USB 3.0 Specification package. The corresponding Adopters Agreement for USB OTG 3.0 is the USB 3.0 Adopters Agreement.
这里写得很清楚,OTG主要是实现可移动设备点对点的数据共享,不需要再依赖于PC机第三方的识别或者操作。平时经常从电脑下载MP3格式的音乐到phone or Pad,都是经过USB线来连接电脑,电脑可以识别到设备里面的存储设备,或者手机与手机的连接,这就是OTG功能。

这是一个在线浏览Linux内核源码的网站:http://lxr.linux.no/

Linux内核早就提供了OTG的驱动,在http://lxr.linux.no/linux+v3.1.6/drivers/usb/gadget/目录下,Linux将usb与otg两个功能模块给独立开了,主要的驱动是file_storage.c,该驱动主要负责终端设备的存储设备节点的操作,比如,从电脑,在该终端的磁盘上面创建文件,都会通过这个驱动来完成存储任务。另外一个驱动主要是负责USB通信的,是结尾为"_udc.c"的驱动源码,该目录下有好几个以"_udc.c"结尾的文件,这是针对不同型号的处理器而增加的,不同厂家提供不同的驱动。

这里只是简单的介绍,Android在vold中处理otg,就是将要共享的磁盘设备写到一个标志文件里面,再广播一下大容量存储连接的状态。

一、连接电脑;先来看看源码:

  1. else if (!strcmp(argv[1], "share")) {  
  2.     if (argc != 4) {  
  3.     cli->sendMsg(ResponseCode::CommandSyntaxError,  
  4.     "Usage: volume share <path> <method>"false);  
  5.     return 0;  
  6.     }  
  7.     rc = vm->shareVolume(argv[2], argv[3]);  
  8. }  

进入shareVolume函数中,该函数判断一下sd卡的状态,如果不存在设备,或者sd卡不是出于空闲状态,那么就返回失败。
  1. int VolumeManager::shareVolume(const char *label, const char *method) {  
  2.     Volume *v = lookupVolume(label);  
  3.   
  4.     if (!v) {  
  5.         errno = ENOENT;  
  6.         return -1;  
  7.     }  
  8.   
  9.     /* 
  10.      * Eventually, we'll want to support additional share back-ends, 
  11.      * some of which may work while the media is mounted. For now, 
  12.      * we just support UMS 
  13.      */  
  14.     if (strcmp(method, "ums")) {  
  15.         errno = ENOSYS;  
  16.         return -1;  
  17.     }  
  18.   
  19.     if (v->getState() == Volume::State_NoMedia) {  
  20.         errno = ENODEV;  
  21.         return -1;  
  22.     }  
  23.   
  24.     if (v->getState() != Volume::State_Idle) {  
  25.         // You need to unmount manually befoe sharing  
  26.         errno = EBUSY;  
  27.         return -1;  
  28.     }  
  29.     /*getDiskDevice函数直接返回sd卡的设备号。*/  
  30.     dev_t d = v->getDiskDevice();  
  31.     if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {  
  32.         // This volume does not support raw disk access  
  33.         errno = EINVAL;  
  34.         return -1;  
  35.     }  
  36.   
  37.     int fd;  
  38.     char nodepath[255];  
  39.     snprintf(nodepath,  
  40.              sizeof(nodepath), "/dev/block/vold/%d:%d",  
  41.              MAJOR(d), MINOR(d));  
  42.     /*比较重要的在这里,想要实现让电脑识别到Android系统中的sd卡, 
  43.     那么就必须将sd卡的设备节点路径写到以下文件中去,这样才能打开 
  44.     大容量存储的功能,也就是OTG*/  
  45.     if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",  
  46.                    O_WRONLY)) < 0) {  
  47.         SLOGE("Unable to open ums lunfile (%s)", strerror(errno));  
  48.         return -1;  
  49.     }  
  50.   
  51.     if (write(fd, nodepath, strlen(nodepath)) < 0) {  
  52.         SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));  
  53.         close(fd);  
  54.         return -1;  
  55.     }  
  56.   
  57.     close(fd);  
  58.     /*handleVolumeShared函数在Volume中,是一个虚函数,在子类DirectVolume中 
  59.     实现,也就是广播一些ums的状态。*/  
  60.     v->handleVolumeShared();  
  61.     return 0;  
  62. }  

如果有Android手机的用户,可以看到系统中存在/sys/devices/platform/usb_mass_storage/lun0/file这么一个文件,Java层可能没有权限访问该文件。最近发现,Java想要访问系统的文件,或者操作命令都要赋以最高权限(0777)才能访问或执行,真是麻烦。
  1. void DirectVolume::handleVolumeShared() {  
  2.     setState(Volume::State_Shared);  
  3. }  

二、断开连接;
  1. int VolumeManager::unshareVolume(const char *label, const char *method) {  
  2.     Volume *v = lookupVolume(label);  
  3.   
  4.     if (!v) {  
  5.         errno = ENOENT;  
  6.         return -1;  
  7.     }  
  8.   
  9.     if (strcmp(method, "ums")) {  
  10.         errno = ENOSYS;  
  11.         return -1;  
  12.     }  
  13.   
  14.     if (v->getState() != Volume::State_Shared) {  
  15.         errno = EINVAL;  
  16.         return -1;  
  17.     }  
  18.   
  19.     int fd;  
  20.     if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {  
  21.         SLOGE("Unable to open ums lunfile (%s)", strerror(errno));  
  22.         return -1;  
  23.     }  
  24.   
  25.     char ch = 0;  
  26.     /*如果要断开连接,可以在电脑安全删除,然后Android系统会提示用户点击挂载sd卡,因为 
  27.     在电脑使用sd卡的情况下,手机是不能对sd卡操作的,多处操作磁盘,会造成数据同步错误。 
  28.     就在断开电脑,进入手机使用模式时,将/sys/devices/platform/usb_mass_storage/lun0/file 
  29.     写为空了*/  
  30.     if (write(fd, &ch, 1) < 0) {  
  31.         SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));  
  32.         close(fd);  
  33.         return -1;  
  34.     }  
  35.   
  36.     close(fd);  
  37.     /*再广播otg断开状态*/  
  38.     v->handleVolumeUnshared();  
  39.     return 0;  
  40. }  
  41. void DirectVolume::handleVolumeUnshared() {  
  42.     setState(Volume::State_Idle);  
  43. }  

这里已经分析完在CommandListener::VolumeCmd::runCommand函数的实现的所有操作了,然后程序将命令的执行结果通知Framework,源码:
  1. if (!rc) {  
  2.         cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded"false);  
  3.     } else {  
  4.         int erno = errno;  
  5.         rc = ResponseCode::convertFromErrno();  
  6.         cli->sendMsg(rc, "volume operation failed"true);  
  7. }  

磁盘操作的命令圆满结束,下一篇文章继续分析Framework与vold的通信。待续。

Android-Vold, Framework和UI的通信-大结局(12)

写了好几篇文章,花儿都快谢了,终于轮到Framework发挥领导的身份了,Framework作为界面与vold之间的桥梁,用户是Framework的大爷,那么Framework就是Vold的大哥大,Framework发出一个命令,Vold不敢不遵从,只能照着Framework的意思照办,千万不能出差错,不然Android就要丢大脸了,登不上三国争霸的舞台,iOS VS Android VS WP7?
在Framework里面,有一个目录是用来存放一些Java的系统服务,这些都在后台跑着,在:/android-2.2r2/frameworks/base/services/java/com/android/server目录下,比较重要的是这两个源文件:MountService.java和NativeDaemonConnector.java。
这里先列出在Vold中,VolumeCmd类处理的一些磁盘操作命令,这些命令均是有Framework下发的:
1.volume list:Framework先得到系统目前存在几个Volume对象,需要获取到这些对象的标签;
2.volume debug:设置USB调试模式
3.volume mount sdcard:挂载SD卡
4.volume unmount force:卸载SD卡
5.volume format sdcard:格式化SD卡
6.volume share sdcard ums:开启SD卡的OTG功能(大容量存储),也就是连接电脑
7.volume unshare sdcard ums:关闭SD卡的OTG功能(大容量存储)
8.volume shared sdcard ums:获取目前OTG的开启状态,就是是否连接电脑的状态。

以下分别列出每个命令的下发函数,对Java不熟,但看得懂程序的流程,真是惭愧啊。


一、Framework磁盘管理服务的开启?

在NativeDaemonConnector服务里面,开始监听底层Vold发送过来的磁盘热插拔事件的状态信息,当收到底层广播上来的状态,调用MountService服务中的onDaemonConnected函数进行处理,当然这是开机第一次去获取信息的,也就是下发"volume list"命令。

  1. public void run() {  
  2.     while (true) {  
  3.         try {  
  4.             /*开始监听底层广播信息*/  
  5.             listenToSocket();  
  6.         } catch (Exception e) {  
  7.             Slog.e(TAG, "Error in NativeDaemonConnector", e);  
  8.             SystemClock.sleep(5000);  
  9.         }  
  10.     }  
  11. }  
  12. private void listenToSocket() throws IOException {  
  13.     /*函数太长,以下是执行顺序*/  
  14.     //连接SOCKET  
  15.     socket.connect(address);  
  16.     ->  
  17.     /*调用该函数来处理下发"volume list"命令的反馈结果*/  
  18.     mCallbacks.onDaemonConnected();  
  19.     ->  
  20.     /*处理磁盘的状态*/  
  21.     mCallbacks.onEvent(code, event, tokens);  
  22. }  

以上两个比较重要的函数在MountService当中,处理相当多的内容,源码太长。
下发volume list命令,Framework收到反馈值,将调用onDaemonConnected函数获取到了磁盘的标签,挂载点与状态,然后调用doGetShareMethodAvailable函数判断现在是否连接OTG,若连接OTG,那么调用doShareUnshareVolume函数下发otg连接命令(volume share sdcard ums)。

二、Vold与Framework如何通信?
onEvent主要是处理状态信息的解析,将每一种状态进行判断,并调用相应的操作函数。比如此时vold发送一个VolumeDiskInserted状态,意味着系统插入一个磁盘,于是onEvent就调用doMountVolume挂载函数进行下发命令(volume mount sdcard)。
在系统使用当中,用户可能会插入,移除,挂载,卸载,格式化磁盘,那么这儿多状态如何告诉Framework呢?之前已经说过,vold使用了setState函数来广播磁盘的状态消息,使得Framework能够及时地判断下发什么命令与操作。该函数在Volume.cpp源文件中,先贴出setState源码看看:

  1. void Volume::setState(int state) {  
  2.     char msg[255];  
  3.     int oldState = mState;  
  4.   
  5.     if (oldState == state) {  
  6.         SLOGW("Duplicate state (%d)\n", state);  
  7.         return;  
  8.     }  
  9.   
  10.     mState = state;  
  11.   
  12.     SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,  
  13.          oldState, stateToStr(oldState), mState, stateToStr(mState));  
  14.     snprintf(msg, sizeof(msg),  
  15.              "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),  
  16.              getMountpoint(), oldState, stateToStr(oldState), mState,  
  17.              stateToStr(mState));  
  18.   
  19.     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,  
  20.                                          msg, false);  
  21. }  

可以看到,setState函数将磁盘的label,mountpoint,oldstate,statestr,newstate,statestr消息通知给Framework,这样Framework就知道vold中SD卡的新旧状态。然后调用通过SocketListener类继承下来的sendBroadcast函数广播消息,反馈码是ResponseCode::VolumeStateChange,代表状态改变的消息。
这里可能有个疑问,vold广播这么多消息,Framework是如何分清哪条消息是代表哪一类的反馈消息呢?
广播消息在开头使用了ResponseCode类提供的一些状态反馈码,每一类消息都用一个反馈码,这样Framework的MountService服务能够很快的判断出类型。之前的文章说过了,这里列出几个重要的反馈码:
  1. static const int VolumeStateChange         = 605;//磁盘状态改变的反馈码  
  2. static const int ShareAvailabilityChange   = 620;//OTG状态改变的反馈码  
  3. static const int VolumeDiskInserted        = 630;//插入磁盘的反馈码  
  4. static const int VolumeDiskRemoved         = 631;//移除磁盘的反馈码  
  5. static const int VolumeBadRemoval          = 632;//没有安全删除地移除磁盘的反馈码。  

setState函数只负责磁盘状态改变的广播,其他插入磁盘或者移除磁盘的都是直接调用sendBroadcast函数来广播,这里贴出插拔事件的广播函数:
  1. void DirectVolume::broadcastDiskAdded()  
  2. {  
  3.     setState(Volume::State_Idle);  
  4.   
  5.     char msg[255];  
  6.   
  7.     snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",  
  8.              getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);  
  9.     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false);  
  10. }  

三、下发操作命令?

volume mount sdcard:

  1. public int mountVolume(String path) {  
  2.     validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);  
  3.   
  4.     waitForReady();  
  5.     return doMountVolume(path);  
  6. }  

volume unmount force:
  1. public void unmountVolume(String path, boolean force) {  
  2.     validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);  
  3.     waitForReady();  
  4.   
  5.     String volState = getVolumeState(path);  
  6.     if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);  
  7.     if (Environment.MEDIA_UNMOUNTED.equals(volState) ||  
  8.             Environment.MEDIA_REMOVED.equals(volState) ||  
  9.             Environment.MEDIA_SHARED.equals(volState) ||  
  10.             Environment.MEDIA_UNMOUNTABLE.equals(volState)) {  
  11.         // Media already unmounted or cannot be unmounted.  
  12.         // TODO return valid return code when adding observer call back.  
  13.         return;  
  14.     }  
  15.     UnmountCallBack ucb = new UnmountCallBack(path, force);  
  16.     mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));  
  17. }  

volume format sdcard:
  1. public int formatVolume(String path) {  
  2.     validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);  
  3.     waitForReady();  
  4.   
  5.     return doFormatVolume(path);  
  6. }  

volume share sdcard ums
volume unshare sdcard ums:

  1. private void doShareUnshareVolume(String path, String method, boolean enable) {  
  2.     // TODO: Add support for multiple share methods  
  3.     if (!method.equals("ums")) {  
  4.         throw new IllegalArgumentException(String.format("Method %s not supported", method));  
  5.     }  
  6.   
  7.     try {  
  8.         mConnector.doCommand(String.format(  
  9.                 "volume %sshare %s %s", (enable ? "" : "un"), path, method));  
  10.     } catch (NativeDaemonConnectorException e) {  
  11.         Slog.e(TAG, "Failed to share/unshare", e);  
  12.     }  
  13. }  

volume shared sdcard ums:
  1. private boolean doGetVolumeShared(String path, String method) {  
  2.     String cmd = String.format("volume shared %s %s", path, method);  
  3.     ArrayList<String> rsp;  
  4.   
  5.     try {  
  6.         rsp = mConnector.doCommand(cmd);  
  7.     } catch (NativeDaemonConnectorException ex) {  
  8.         Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);  
  9.         return false;  
  10.     }  
  11.   
  12.     for (String line : rsp) {  
  13.         String[] tok = line.split(" ");  
  14.         if (tok.length < 3) {  
  15.             Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");  
  16.             return false;  
  17.         }  
  18.   
  19.         int code;  
  20.         try {  
  21.             code = Integer.parseInt(tok[0]);  
  22.         } catch (NumberFormatException nfe) {  
  23.             Slog.e(TAG, String.format("Error parsing code %s", tok[0]));  
  24.             return false;  
  25.         }  
  26.         if (code == VoldResponseCode.ShareEnabledResult) {  
  27.             return "enabled".equals(tok[2]);  
  28.         } else {  
  29.             Slog.e(TAG, String.format("Unexpected response code %d", code));  
  30.             return false;  
  31.         }  
  32.     }  
  33.     Slog.e(TAG, "Got an empty response");  
  34.     return false;  
  35. }  

在Framework中的磁盘管理部分,也涉及到很多代码,源码太多,只贴出比较重要的功能模块代码。

四、UI的处理
从vold走到了Framework,最后一层就是UI,是用户操作磁盘的界面。有Android手机的哥们儿都知道,在设置里面可以挂载,卸载与格式化SD卡。当然这图形界面不是咱擅长的,需要Java的功底与XML,那边主要就是界面的实现。
UI的源码路径是:/android-2.2r2/packages/apps/Settings/src/com/android/settings/deviceinfo/Memory.java
我们可以发现,在Android手机的设置界面,或者主页面,只要插入SD卡或者移除SD卡,都会有相应的提示,

1.这些磁盘状态是如何与UI通信的呢?
在MountService服务中,每次改变状态,都会调用updatePublicVolumeState函数,从意思上看,可以理解成:更新一个公用的磁盘状态。这个函数就是起到了这么一个作用,这会设置到Environment类中的一个变量中,这样UI就能够取到磁盘的状态。

2.UI是如何调用Framework中的函数的?
前面有提到过,UI想要调用Framework中的MountService服务中的函数,比如以注册的方式来得到调用操作磁盘函数的权限。以下贴出UI中,Memory.java源码中注册MountService磁盘操作函数的方法:

  1. private synchronized IMountService getMountService() {  
  2.    if (mMountService == null) {  
  3.        /*调用了getService函数来注册"mount"服务的操作权限*/  
  4.        IBinder service = ServiceManager.getService("mount");  
  5.        if (service != null) {  
  6.            mMountService = IMountService.Stub.asInterface(service);  
  7.        } else {  
  8.            Log.e(TAG, "Can't get mount service");  
  9.        }  
  10.    }  
  11.    return mMountService;  
  12. }  

来看一个挂载SD卡的操作函数,就知道如何来调用Framework系统服务的函数:
  1. private void mount() {  
  2.     /*先注册*/  
  3.     IMountService mountService = getMountService();  
  4.     try {  
  5.         if (mountService != null) {  
  6.             /*挂载SD卡*/  
  7.             mountService.mountVolume(Environment.getExternalStorageDirectory().toString());  
  8.         } else {  
  9.             Log.e(TAG, "Mount service is null, can't mount");  
  10.         }  
  11.     } catch (RemoteException ex) {  
  12.     }  
  13. }  

3.UI获取SD卡的当前状态
在第一点已经解释过了,这里再仔细的说明下:
SD卡的状态是这样获取到的:
Vold  (setState)--> 
MountService  (onEvent)--> 
MountService  (updatePublicVolumeState)--> 
UI  (getExternalStorageState)

Vold调用setState函数广播SD卡的状态,Framework的MountService服务通过onEvent函数收到该状态消息,调用updatePublicVolumeState函数设置到Environment中的一个变量中,UI再通过Environment.getExternalStorageState函数获取到最新状态,于是UI调用updateMemoryStatus函数将最新状态设置到界面,这样用户就能看到状态的改变。
(用户都会发现,挂载或卸载SD卡,状态由挂载变为卸载需要一小段时间,这中间就是经过这些处理来得到的,包括上层发送命令->底层解析命令调用相应函数->操作完成后发送操作结果->最后将SD卡的最新状态广播给Framework,并设置到UI。这中间涉及到很多东西,再加上Java虚拟机的速度,于是界面就有一个停顿的时间)。
以下贴出UI状态的更新代码:

  1. private void updateMemoryStatus() {  
  2.     String status = Environment.getExternalStorageState();  
  3.     String readOnly = "";  
  4.     if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {  
  5.         status = Environment.MEDIA_MOUNTED;  
  6.         readOnly = mRes.getString(R.string.read_only);  
  7.     }  
  8.   
  9.     mSdFormat.setEnabled(false);  
  10.   
  11.     if (status.equals(Environment.MEDIA_MOUNTED)) {  
  12.         try {  
  13.             File path = Environment.getExternalStorageDirectory();  
  14.             StatFs stat = new StatFs(path.getPath());  
  15.             long blockSize = stat.getBlockSize();  
  16.             long totalBlocks = stat.getBlockCount();  
  17.             long availableBlocks = stat.getAvailableBlocks();  
  18.               
  19.             mSdSize.setSummary(formatSize(totalBlocks * blockSize));  
  20.             mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);  
  21.   
  22.             mSdMountToggle.setEnabled(true);  
  23.             mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));  
  24.             mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));  
  25.   
  26.         } catch (IllegalArgumentException e) {  
  27.             // this can occur if the SD card is removed, but we haven't received the   
  28.             // ACTION_MEDIA_REMOVED Intent yet.  
  29.             status = Environment.MEDIA_REMOVED;  
  30.         }  
  31.           
  32.     } else {  
  33.         mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));  
  34.         mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));  
  35.   
  36.   
  37.         if (status.equals(Environment.MEDIA_UNMOUNTED) ||  
  38.             status.equals(Environment.MEDIA_NOFS) ||  
  39.             status.equals(Environment.MEDIA_UNMOUNTABLE) ) {  
  40.             mSdFormat.setEnabled(true);  
  41.             mSdMountToggle.setEnabled(true);  
  42.             mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  43.             mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));  
  44.         } else {  
  45.             mSdMountToggle.setEnabled(false);  
  46.             mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  47.             mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));  
  48.         }  
  49.     }  
  50.   
  51.     File path = Environment.getDataDirectory();  
  52.     StatFs stat = new StatFs(path.getPath());  
  53.     long blockSize = stat.getBlockSize();  
  54.     long availableBlocks = stat.getAvailableBlocks();  
  55.     findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));  
  56. }  

五、好聚好散
终于将Android系统磁盘管理的这部分给over了,现在我深深地敬佩系统设计人员,他们都是非凡的人才,再次感谢谷歌对Android社区的贡献。先不说谷歌有没有对Android的开源,或者一些专利的事,但在我眼里,Google是非常强大的公司,向他学习,没错的。
三争IT天下:    Google   VS  Microsoft  VS  Apple.
三分智能领域:Android  VS  WP7         VS  iOS.



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值