分类: u盘挂载2012-03-29 22:25 3215人阅读 评论(0) 收藏 举报

MountService启动之后 ,一切准备工作都 做好了,就等待碰上u盘插上了,

这里要讲的是内核发信息给vold,我们在 vold启动这篇曾讲到过注册了一个到内核的UEVENT事件,当有u盘插入的时候,我们就能从这个套接字上收到内核所发出的消息了,这样就开始了vold的消息处理。

先看下消息处理的流程:

在SocketListener::runListener()函数 中,我们一直在select,等待某个连接的到来或者已经的套接字上数据的到来,看下代码:

  1. if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
  2. SLOGE("select failed (%s)", strerror(errno));
  3. sleep(1);
  4. continue;
  5. } else if (!rc)
  6. continue;
  7. if (FD_ISSET(mCtrlPipe[0], &read_fds))
  8. break;
  9. if (mListen && FD_ISSET(mSock, &read_fds)) {
  10. struct sockaddr addr;
  11. socklen_t alen = sizeof(addr);
  12. int c;
  13. if ((c = accept(mSock, &addr, &alen)) < 0) {
  14. SLOGE("accept failed (%s)", strerror(errno));
  15. sleep(1);
  16. continue;
  17. }
  18. pthread_mutex_lock(&mClientsLock);
  19. mClients->push_back(new SocketClient(c));
  20. pthread_mutex_unlock(&mClientsLock);
  21. }
  22. do {
  23. pthread_mutex_lock(&mClientsLock);
  24. for (it = mClients->begin(); it != mClients->end(); ++it) {
  25. int fd = (*it)->getSocket();
  26. if (FD_ISSET(fd, &read_fds)) {
  27. pthread_mutex_unlock(&mClientsLock);
  28. if (!onDataAvailable(*it)) {
  29. close(fd);
  30. pthread_mutex_lock(&mClientsLock);
  31. delete *it;
  32. it = mClients->erase(it);
  33. pthread_mutex_unlock(&mClientsLock);
  34. }
  35. FD_CLR(fd, &read_fds);
  36. pthread_mutex_lock(&mClientsLock);
  37. continue;
  38. }
  39. }
  40. pthread_mutex_unlock(&mClientsLock);
  41. } while (0);

当某个套接字上有数据到来时,首先看这个套接字是不是listen的那个套接字,如果是则接收 连接并加到mClients链表中,否则说明某个套接字上有数据到来,这时里是我们注册到内核的那个套接字,调用onDataAvailable函数,这里由于多态调用的是NetlinkListener::onDataAvailable中的这个函数:

  1. bool NetlinkListener::onDataAvailable(SocketClient *cli)
  2. {
  3. int socket = cli->getSocket();
  4. int count;
  5. if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) {
  6. SLOGE("recv failed (%s)", strerror(errno));
  7. return false;
  8. }
  9. NetlinkEvent *evt = new NetlinkEvent();
  10. if (!evt->decode(mBuffer, count)) {
  11. SLOGE("Error decoding NetlinkEvent");
  12. goto out;
  13. }
  14. onEvent(evt);
  15. out:
  16. delete evt;
  17. return true;
  18. }

调用recv接收数据,接着new一个NetlinkEvent并调用它的 decode函数对收到的数据进行解析:

  1. bool NetlinkEvent::decode(char *buffer, int size) {
  2. char *s = buffer;
  3. char *end;
  4. int param_idx = 0;
  5. int i;
  6. int first = 1;
  7. end = s + size;
  8. while (s < end) {
  9. if (first) {
  10. char *p;
  11. for (p = s; *p != '@'; p++);
  12. p++;
  13. mPath = strdup(p);
  14. first = 0;
  15. } else {
  16. if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
  17. char *a = s + strlen("ACTION=");
  18. if (!strcmp(a, "add"))
  19. mAction = NlActionAdd;
  20. else if (!strcmp(a, "remove"))
  21. mAction = NlActionRemove;
  22. else if (!strcmp(a, "change"))
  23. mAction = NlActionChange;
  24. } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
  25. mSeq = atoi(s + strlen("SEQNUM="));
  26. else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
  27. mSubsystem = strdup(s + strlen("SUBSYSTEM="));
  28. else
  29. mParams[param_idx++] = strdup(s);
  30. }
  31. s+= strlen(s) + 1;
  32. }
  33. return true;
  34. }

这里会对消息进行解析,解析出ACTION、DEVPATH、SUBSYSTEM等等,下面看一下我抓的 一个u盘插入抓的log:

  1. D/NetlinkEvent(  946): s = add@/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host1/target1:0:0/1:0:0:0/block/sda
  2. D/NetlinkEvent(  946): s = ACTION=add
  3. D/NetlinkEvent(  946): s = DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host1/target1:0:0/1:0:0:0/block/sda
  4. D/NetlinkEvent(  946): s = SUBSYSTEM=block
  5. D/NetlinkEvent(  946): s = MAJOR=8
  6. D/NetlinkEvent(  946): s = MINOR=0
  7. D/NetlinkEvent(  946): s = DEVNAME=sda
  8. D/NetlinkEvent(  946): s = DEVTYPE=disk
  9. D/NetlinkEvent(  946): s = NPARTS=1
  10. D/NetlinkEvent(  946): s = SEQNUM=1058
  11. D/NetlinkEvent( 1206): s = DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host1/target1:0:0/1:0:0:0/block/sda/sda1
  12. D/NetlinkEvent( 1206): s = SUBSYSTEM=block
  13. D/NetlinkEvent( 1206): s = MAJOR=8
  14. D/NetlinkEvent( 1206): s = MINOR=1
  15. D/NetlinkEvent( 1206): s = DEVNAME=sda1
  16. D/NetlinkEvent( 1206): s = DEVTYPE=partition
  17. D/NetlinkEvent( 1206): s = PARTN=1
  18. D/NetlinkEvent( 1206): s = SEQNUM=1059

这个u盘只有一个分区,下面是有两个分区的log(一部分):

  1. D/NetlinkEvent( 1207): s = ACTION=add
  2. D/NetlinkEvent( 1207): s = DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host2/target2:0:0/2:0:0:0/block/sdb
  3. D/NetlinkEvent( 1207): s = SUBSYSTEM=block
  4. D/NetlinkEvent( 1207): s = MAJOR=8
  5. D/NetlinkEvent( 1207): s = MINOR=16
  6. D/NetlinkEvent( 1207): s = DEVNAME=sdb
  7. D/NetlinkEvent( 1207): s = DEVTYPE=disk
  8. D/NetlinkEvent( 1207): s = NPARTS=2
  9. D/NetlinkEvent( 1207): s = SEQNUM=1086

可以看到,从内核收到的消息中我们能获得很多的信息。

解析完后,就调用onEvent函数对消息进行处理,这里调用的是NetlinkHandler的onEvent函数:

  1. void NetlinkHandler::onEvent(NetlinkEvent *evt) {
  2. VolumeManager *vm = VolumeManager::Instance();
  3. const char *subsys = evt->getSubsystem();
  4. if (!subsys) {
  5. SLOGW("No subsystem found in netlink event");
  6. return;
  7. }
  8. if (!strcmp(subsys, "block")) {
  9. vm->handleBlockEvent(evt);
  10. } else if (!strcmp(subsys, "switch")) {
  11. vm->handleSwitchEvent(evt);
  12. } else if (!strcmp(subsys, "usb_composite")) {
  13. vm->handleUsbCompositeEvent(evt);
  14. } else if (!strcmp(subsys, "battery")) {
  15. } else if (!strcmp(subsys, "power_supply")) {
  16. }
  17. }

从上面 的log可以看出这里获取的subsys是block,所以调用handleBlockEvent函数

  1. void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
  2. const char *devpath = evt->findParam("DEVPATH");
  3. /* Lookup a volume to handle this device */
  4. VolumeCollection::iterator it;
  5. bool hit = false;
  6. for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
  7. if (!(*it)->handleBlockEvent(evt)) {
  8. #ifdef NETLINK_DEBUG
  9. SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
  10. #endif
  11. hit = true;
  12. break;
  13. }
  14. }
  15. if (!hit) {
  16. #ifdef NETLINK_DEBUG
  17. SLOGW("No volumes handled block event for '%s'", devpath);
  18. #endif
  19. }
  20. }

mVolumes中我们在初始化的时候往里面add了 个DirectVolume,所以这里调用DirectVolume::handleBlockEvent

  1. int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
  2. const char *dp = evt->findParam("DEVPATH");
  3. PathCollection::iterator  it;
  4. for (it = mPaths->begin(); it != mPaths->end(); ++it) {
  5. if (!strncmp(dp, *it, strlen(*it))) {
  6. /* We can handle this disk */
  7. int action = evt->getAction();
  8. const char *devtype = evt->findParam("DEVTYPE");
  9. if (action == NetlinkEvent::NlActionAdd) {
  10. int major = atoi(evt->findParam("MAJOR"));
  11. int minor = atoi(evt->findParam("MINOR"));
  12. char nodepath[255];
  13. snprintf(nodepath,
  14. sizeof(nodepath), "/dev/block/vold/%d:%d",
  15. major, minor);
  16. if (createDeviceNode(nodepath, major, minor)) {
  17. SLOGE("Error making device node '%s' (%s)", nodepath,
  18. strerror(errno));
  19. }
  20. if (!strcmp(devtype, "disk")) {
  21. handleDiskAdded(dp, evt);
  22. } else {
  23. handlePartitionAdded(dp, evt);
  24. }
  25. } else if (action == NetlinkEvent::NlActionRemove) {
  26. if (!strcmp(devtype, "disk")) {
  27. handleDiskRemoved(dp, evt);
  28. } else {
  29. handlePartitionRemoved(dp, evt);
  30. }
  31. } else if (action == NetlinkEvent::NlActionChange) {
  32. if (!strcmp(devtype, "disk")) {
  33. handleDiskChanged(dp, evt);
  34. } else {
  35. handlePartitionChanged(dp, evt);
  36. }
  37. } else {
  38. SLOGW("Ignoring non add/remove/change event");
  39. }
  40. return 0;
  41. }
  42. }
  43. errno = ENODEV;
  44. return -1;
  45. }

mPaths我们在parse vold.fstab把相应的解析到的路径添加进去了,我们看下这个脚本:

  1. ev_mount sdcard /mnt/sdcard auto /devices/platform/hiusb-ehci.0 /devices/platform/hi_godbox-ehci.0

这里add的路径正好和上面 log打出来的路径相匹配,首先执行的handleDiskAdded,也就是在收到这样的消息的时候,提示有磁盘插入:

  1. void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
  2. mDiskMajor = atoi(evt->findParam("MAJOR"));
  3. mDiskMinor = atoi(evt->findParam("MINOR"));
  4. const char *tmp = evt->findParam("NPARTS");
  5. if (tmp) {
  6. mDiskNumParts = atoi(tmp);
  7. } else {
  8. SLOGW("Kernel block uevent missing 'NPARTS'");
  9. mDiskNumParts = 1;
  10. }
  11. char msg[255];
  12. int partmask = 0;
  13. int i;
  14. for (i = 1; i <= mDiskNumParts; i++) {
  15. partmask |= (1 << i);
  16. }
  17. mPendingPartMap = partmask;
  18. if (mDiskNumParts == 0) {
  19. #ifdef PARTITION_DEBUG
  20. SLOGD("Dv::diskIns - No partitions - good to go son!");
  21. #endif
  22. setState(Volume::State_Idle);
  23. } else {
  24. #ifdef PARTITION_DEBUG
  25. SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
  26. mDiskNumParts, mPendingPartMap);
  27. #endif
  28. setState(Volume::State_Pending);
  29. }
  30. snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
  31. getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
  32. mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
  33. msg, false);
  34. }

mDiskNumParts 不为0,将Volume的状态设置为State_Pending并向FrameWork层广播VolumeDiskInserted的消息,在setState函数中也会广播VolumeStateChange的消息给上层,接着就是handlePartitionAdded 这里是处理add /block/sda/sda*这样的消息的

  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. const char *tmp = evt->findParam("PARTN");
  6. if (tmp) {
  7. part_num = atoi(tmp);
  8. } else {
  9. SLOGW("Kernel block uevent missing 'PARTN'");
  10. part_num = 1;
  11. }
  12. if (part_num > mDiskNumParts) {
  13. mDiskNumParts = part_num;
  14. }
  15. if (major != mDiskMajor) {
  16. SLOGE("Partition '%s' has a different major than its disk!", devpath);
  17. return;
  18. }
  19. #ifdef PARTITION_DEBUG
  20. SLOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
  21. #endif
  22. mPartMinors[part_num -1] = minor;
  23. mPendingPartMap &= ~(1 << part_num);
  24. if (!mPendingPartMap) {
  25. #ifdef PARTITION_DEBUG
  26. SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
  27. #endif
  28. if (getState() != Volume::State_Formatting) {
  29. setState(Volume::State_Idle);
  30. }
  31. } else {
  32. #ifdef PARTITION_DEBUG
  33. SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
  34. #endif
  35. }
  36. }

当mPendingPartMap减为0时,这时Volume的状态不为State_Formatting,将广播一条VolumeStateChange的消息。
到这里,内核的消息基本就处理完了,当然这里讲的只是add的消息,还有remove,change消息等。。。这里就不做介绍了。

 

android usb挂载分析---vold处理内核消息的更多相关文章

  1. android usb挂载分析----vold启动

    http://blog.csdn.net/new_abc/article/details/7396733 前段时间做了下usb挂载的,现在出了几个bug,又要把流程给梳理下,顺便也把相关的知识总结下, ...

  2. android usb挂载分析---MountService启动

    android usb挂载分析---MountService启动 分类: android框架 u盘挂载2012-03-27 23:00 11799人阅读 评论(4) 收藏 举报 androidsock ...

  3. android usb挂载分析

    http://blog.csdn.net/new_abc/article/details/7409018

  4. android文件系统挂载分析(1)---正常开机挂载

    未完,更新中 ... "android"系列分为三部分: 1.正常开机挂载 2.encryption 3.dm-verity 我们知道android有很多分区,如"sys ...

  5. Android USB驱动源码分析(-)

    Android USB驱动中,上层应用协议里最重要的一个文件是android/kernel/drivers/usb/gadget/android.c.这个文件实现USB的上层应用协议. 首先包含了一些 ...

  6. Android源码分析-消息队列和Looper

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17361775 前言 上周对Android中的事件派发机制进行了分析,这次博主 ...

  7. Chromium on Android: Android在系统Chromium为了实现主消息循环分析

    总结:刚开始接触一个Chromium on Android时间.很好奇Chromium主消息循环是如何整合Android应用. 为Android计划,一旦启动,主线程将具有Java消息层循环处理系统事 ...

  8. Android USB Host框架

    Android 下的usb框架及功能点:https://blog.csdn.net/tianruxishui/article/details/379029591.Android framework中* ...

  9. 【TencentOS tiny】深度源码分析(4)——消息队列

    消息队列 在前一篇文章中[TencentOS tiny学习]源码分析(3)--队列 我们描述了TencentOS tiny的队列实现,同时也点出了TencentOS tiny的队列是依赖于消息队列的, ...

随机推荐

  1. servlet第1讲初识

  2. how computer boot up?

    The power button activates the power supply in the PC, sending power to the motherboard and other co ...

  3. DefaultHttpClient is deprecated 【Api 弃用]】

    最近在使用Apache的httpclient的时候,maven引用了最新版本4.3,发现Idea提示DefaultHttpClient等常用的类已经不推荐使用了,之前在使用4.2.3版本的时候,还没有 ...

  4. string 转 int,int 转 string

    string str="12345"; int b=atoi(str.c_str()); 可以配合atof,转为double char buf[10]; sprintf(buf,  ...

  5. PHP字符串函数试题

    Ctrl+A查看答案 1.把ASCII字符的字符串转换为十六进制值的函数是什么?答:bin2hex($string),例如bin2hex('ab') = 6162 2.ASCII码转字符,字符转ASC ...

  6. launchMode传递参数注意startActivityForResult

    Activity1 到Activity2 用startActivityForResult 如果Activity2的launchMode为 singleInstance 和 singleTask 都会启 ...

  7. select用法

    每一次操作select的时候,总是要出来翻一下资料,不如自己总结一下,以后就翻这里了. 比如<select class="selector"></select&g ...

  8. 【转】Git代码行统计命令集

    http://blog.csdn.NET/dwarven/article/details/46550117 http://blog.csdn.net/hshl1214/article/details/ ...

  9. 四种xml的解析方式

    这篇文章是我上网找资料,加上自己总结了一些而得 资料来源: http://www.cnblogs.com/allenzheng/archive/2012/12/01/2797196.html http ...

  10. Beam me out!

    Beam me out! 题目描述 King Remark, first of his name, is a benign ruler and every wrongdoer gets a second ...