Android热插拔事件处理详解

一、Android热插拔事件处理流程图

Android热插拔事件处理流程如下图所示:

二、组成

1. NetlinkManager:

全称是NetlinkManager.cpp位于Android 4.x 源码位置/system/vold/NetlinkManager.cpp。该类的主要通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。

2. VolumeManager:

全称是VolumeManager.cpp位于Android 4.x源码位置/system/vold/VolumeManager.cpp。该类的主要作用是接收经过NetlinkManager处理过后的事件消息。因为我们这里是SD的挂载,因此经过NetlinkManager处理过后的消息会分为五种,分别是:block,switch,usb_composite,battery,power_supply。这里SD卡挂载的事件是block。

3. DirectVolume:

位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:Add,Removed,Change,Noaction这四种。后文通过介绍Add事件展开。

4. Volume:

位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cpp主要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。

5. CommandListener:

该类位于位于/system/vold/CommandListener.cpp。通过vold socket与NativeDaemonConnector通信。

6. NativeDaemonConnector:

该类位于frameworks/base/services/java/com.android.server/NativeDaemonConnector.java。该类用于接收来自Volume.cpp 发来的SD卡挂载消息并向上传递。

7.  MountService:

位于frameworks/base/services/java/com.android.server/MountService.java。MountService是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等。在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用。在Android系统中这是一个非常重要的类。

8. StorageManaer:

位于frameworks/base/core/java/andriod/os/storage/StorageManager.java。在该类的说明中有提到,该类是系统存储服务的接口。在系统设置中,有Storage相关项,同时Setting也注册了该类的监听器。而StorageManager又将自己的监听器注册到了MountService中,因此该类主要用于上层应用获取SD卡状态。

三、典型流程描述 (SD卡挂载流程)

整个过程从Kernel检测到SD卡插入事件开始,之前的一些硬件中断的触发以及driver的加载这里并不叙述,一直到SD卡挂载消息更新到“Android——系统设置——存储”一项中。

1.    Kernel发出SD卡插入uevent。

2.    NetlinkHandler::onEvent()接收内核发出的uevent并进行解析。

3.    VolumeManager::handlBlockEvent()处理经过第二步处理后的事件。

4.    接下来调用DirectVolume:: handleBlockEvent()。

在该方法中主要有两点需要注意:

第一,程序首先会遍历mPath容器,寻找与event对应的sysfs_path是否存在与mPath容器中。

第二,针对event中的action有4种处理方式:Add,Removed,Change,Noaction 。

例如:在Add action中会有如下操作(因为我们这里所讲的是SD卡的挂载流程,因此以Add来说明),首先创建设备节点,其次对disk和partition两种格式的设备分别进行处理。SD卡属于disk类型。

5.    经过上一步之后会调用DirectVolume::handleDiskAdded()方法,在该方法中会广播disk insert消息。

6.    SocketListener::runListener会接收DirectVolume::handleDiskAdded()广播的消息。该方法主要完成对event中数据的获取,通过Socket。(PS:这里的SocketListener.cpp位于Android源码/system/core/libsysutils/src/中,后文的FramworkListener.cpp也是,之前自己找了很久 T_T)

7.    调用FrameworkListener::onDataAvailable()方法处理接收到的消息内容。

8.    FrameworkListener::dispatchCommand()该方法用于分发指令。

9.    在FrameworkListener::dispatchCommand()方法中,通过runCommand()方法去调用相应的指令。

10.   在/system/vold/CommandListener.cpp中有runCommand()的具体实现。在该类中可以找到这个方法:CommandListener::VolumeCmd::runCommand(),从字面意思上来看这个方法就是对Volume分发指令的解析。该方法中会执行“mount”函数:vm->mountVolume(arg[2])。

11.    mountVolume(arg[2])在VolumeManager::mountVolume()中实现,在该方法中调用v->mountVol()。

12.    mountVol()方法在Volume::mountVol()中实现,该函数是真正的挂载函数。(在该方法中,后续的处理都在该方法中,在Mount过程中会广播相应的消息给上层,通过setState()函数。)

13.    setState(Volume::Checking);广播给上层,正在检查SD卡,为挂载做准备。

14.    Fat::check();SD卡检查方法,检查SD卡是否是FAT格式。

15.    Fat::doMount()挂载SD卡。

至此,SD的挂载已算初步完成,接下来应该将SD卡挂载后的消息发送给上层,在13中也提到过,在挂载以及检查的过程中其实也有发送消息给上层的。

16.    MountService的构造函数中会开启监听线程,用于监听来自vold的socket信息。

Thread thread = new Thread(mConnector,VOLD_TAG); thread.start();

17.    mConnector是NativeDaemonConnector的对象,NativeDaemonConnector继承了Runnable并Override了run方法。在run方法中通过一个while(true)调用ListenToSocket()方法来实现实时监听。

18.    在ListenToSocket()中,首先建立与Vold通信的Socket Server端,然后调用MountService中的onDaemonConnected()方法。(PS:Java与Native通信可以通过JNI,那么Native与Java通信就需要通过Socket来实现了。Android中Native与Frameworks通信  这篇文章中有简介,感兴趣的朋友可以参考一下)

19.    onDaemonConnected()方法是在接口INativeDaemonConnectorCallbacks中定义的,MountService实现了该接口并Override了onDaemonConnected()方法。该方法开启一个线程用于更新外置存储设备的状态,主要更新状态的方法也在其中实现。

20.    然后回到ListenToSocket中,通过inputStream来获取Vold传递来的event,并存放在队列中。

21.    然后这些event会在onDaemonConnected()通过队列的”队列.take()”方法取出。并根据不同的event调用updatePublicVolumeState()方法,在该方法中调用packageManagerService中的updateExteralState()方法来更新存储设备的状态。(注:这里不太理解packageManagerService中的unloadAllContainers(args)方法)

22.    更新是通过packageHelper.getMountService().finishMediaUpdate()方法来实现的。

23.    在updatePublicVolumeState()方法中,更新后会执行如下代码:

bl.mListener.onStorageStateChanged();

在Android源码/packages/apps/Settings/src/com.android.settings.deviceinfo/Memory.java代码中,实现了StorageEventListener 的匿名内部类,并Override了onStorageStateChanged();方法。因此在updatePublicVolumeState()中调用onStorageStateChanged();方法后,Memory.java中也会收到。在Memory.java中收到以后会在Setting界面进行更新,系统设置——存储中会更新SD卡的状态。从而SD卡的挂载从底层到达了上层。

四、Vold

1. Vold简介

Vold的全称是volume daemon。主要负责系统对大容量存储设备(USB/SD)的挂载/卸载任务,它是一个守护进程,该进程支持这些存储外设的热插拔。自Android 2.2开始,Vold升级为vold 2.0,配置文件路径在Android 4.0之后变为/etc/vold.fstab。

2.Vold工作流程

Vold的工作流程大致可以分为三个部分:创建监听、引导、事件处理。

(1)创建监听

创建监听指的是创建监听链接,一方面用于监听来自内核的uevent,另一方面用于监听来自上层的控制命令,这些命令包括控制SD卡的挂载与卸载,这里所说的链接也就是socket。在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:

Service vold /system/bin/vold

Socket vold stream 0660 root mount

Iprio be 2

这样系统会在启动的时候创建与上层通信的socket,此socket name为"vold"。

在Android 4.0源码/system/vold路径下的main.cpp<NetlinkManager::start():socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT) >中创建了与内核通信的socket。在main.cpp中通过实例化VolumeManager和NetlinkManager时创建。

(2)引导

Vold进程启动时候会对现有的外部存储设备进行检查。首先加载并解析vold.fstab,并检查挂载点是否已被挂载。然后执行SD卡的挂载,最后处理USB大容量存储。因为系统是按行解析的,通过查看vold.fstab可以很清楚的知道这一点。

vold.fatab中最重要的语句:

dev_mount sdcard /mnt/sdcard auto /devices/platform/rk29_sdmmc.0/mmc_host/mmc0

dev_mount       <lable>     <mount_point>           <part>                   <sysfs_path…>

挂载命令            标签                挂载点              第几个分区              设备的sysfs paths

注:

第几个分区:如果为auto则表示第1个分区。

参数之间不能有空格,只能以tab为间隔(注意:这里为了对齐因此采用空格隔开,如果自行修改vold.fstab之后加以空格的话系统会识别不到的)。

如果vold.fstab解析无误,VolueManager将创建DirectVolume,若vold.fstab解析不存在或者打开失败,Vold将会读取Linux内核中的参数,此时如果参数中存在SDCARD(也就是SD的默认路径),VolumeManager则会创建AutoVolume,如果不存在这个默认路径那么就不会创建。

(3)事件处理

通过对两个socket的监听,完成对事件的处理以及对上层应用的响应。

a) Kernel发出uevent

NetlinkManager检测到kernel发出的uevent,解析后调用NetlinkHandler::onEvent()方法。该方法会分别处理不同的事件,这里重要的事件有:

“block”事件主要指Volume的mount、unmount、createAsec等。由VolumeManager的handleBlockEvent(evt)来处理,根据多态性最终将会调用AutoVolume或者DirectVolume的handleBlockEvent方法来处理。

“switch”事件主要指Volume的connet、disconnet等。根据相关操作,改变设备参数(设备类型、挂载点等)通过CommandListener告知FrameWork层。

b) FrameWork发出控制命令

与a)相反,CommandListener检测到FrameWork层的命令(MountService发出的命令)调用VolumeManager的函数,VolumeManager找出对应的Volume,调用Volume函数去挂载/卸载操作。而Volume类中的相关操作最终通过调用Linux函数完成。

五、Vold用户态

1. NetlinkManager

NetlinkManager负责与Kernel交互,通过PF_NETLINK来现。

Vlod启动代码如下(/system/vold/main.cpp):

[cpp] view
plain
copy

  1. int main() {
  2. VolumeManager *vm;
  3. CommandListener *cl;
  4. NetlinkManager *nm;
  5. SLOGI("Vold 2.1 (the revenge) firing up");
  6. mkdir("/dev/block/vold", 0755);
  7. /* Create our singleton managers */
  8. if (!(vm = VolumeManager::Instance())) {
  9. SLOGE("Unable to create VolumeManager");
  10. exit(1);
  11. };
  12. if (!(nm = NetlinkManager::Instance())) {
  13. SLOGE("Unable to create NetlinkManager");
  14. exit(1);
  15. };
  16. cl = new CommandListener();
  17. vm->setBroadcaster((SocketListener *) cl);
  18. nm->setBroadcaster((SocketListener *) cl);
  19. if (vm->start()) {
  20. SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
  21. exit(1);
  22. }
  23. /* 解析/etc/vold.fstab文件,
  24. 读取type, label, mount_point, part
  25. 1) 构建DirectVolume对象 :如果part为auto, 则调用dv = new DirectVolume(vm, label, mount_point, -1);
  26. 2) 添加vold.fstab中定义的某一挂载项对应的sysfs_path到 DirectVolume对象的mPaths容器  dv->addPath(sysfs_path);
  27. 3) 将这个DirectVolume 对象添加到 VolumeManager对象的容器mVolumes中   vm->addVolume(dv);
  28. */
  29. if (process_config(vm)) {
  30. SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
  31. }
  32. /*会调用NetlinkManager类的start()方法,它创建PF_NETLINK socket,
  33. 并开启线程从此socket中读取数据*/
  34. if (nm->start()) {
  35. SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
  36. exit(1);
  37. }
  38. #ifdef USE_USB_MODE_SWITCH
  39. SLOGE("Start Misc devices Manager...");
  40. MiscManager *mm;
  41. if (!(mm = MiscManager::Instance())) {
  42. SLOGE("Unable to create MiscManager");
  43. exit(1);
  44. };
  45. mm->setBroadcaster((SocketListener *) cl);
  46. if (mm->start()) {
  47. SLOGE("Unable to start MiscManager (%s)", strerror(errno));
  48. exit(1);
  49. }
  50. G3Dev* g3 = new G3Dev(mm);
  51. g3->handleUsb();
  52. mm->addMisc(g3);
  53. #endif
  54. coldboot("/sys/block"); // 冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add\n” 字符也可以触发sysfs事件,相当执行了一次热插拔。
  55. //    coldboot("/sys/class/switch");
  56. /*
  57. * Now that we‘re up, we can respond to commands
  58. */
  59. if (cl->startListener()) {
  60. SLOGE("Unable to start CommandListener (%s)", strerror(errno));
  61. exit(1);
  62. }
  63. // Eventually we‘ll become the monitoring thread
  64. while(1) {
  65. sleep(1000);
  66. }
  67. SLOGI("Vold exiting");
  68. exit(0);
  69. }

NetlinkManager的家族关系如下所示:

上图中的虚线为启动是的调用流程。

(1) class NetlinkManager(在其start函数中创建了NetlinkHandler对象,并把创建的socket作为参数)

(2)class NetlinkHandler: public NetlinkListener(实现了onEvent)

(3) class NetlinkListener : public SocketListener (实现了onDataAvailable)

(4) class SocketListener(实现了runListener,在一个线程中通过select查看哪些socket有数据,通过调用onDataAvailable来读取数据)

2. NetlinkManager::start()

[cpp] view
plain
copy

  1. int NetlinkManager::start() {
  2. struct sockaddr_nl nladdr;
  3. int sz = 64 * 1024;
  4. int on = 1;
  5. memset(&nladdr, 0, sizeof(nladdr));
  6. nladdr.nl_family = AF_NETLINK;
  7. nladdr.nl_pid = getpid();
  8. nladdr.nl_groups = 0xffffffff;
  9. // 创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件
  10. if ((mSock = socket(PF_NETLINK,
  11. SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
  12. SLOGE("Unable to create uevent socket: %s", strerror(errno));
  13. return -1;
  14. }
  15. if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
  16. SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));
  17. return -1;
  18. }
  19. if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
  20. SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
  21. return -1;
  22. }
  23. if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
  24. SLOGE("Unable to bind uevent socket: %s", strerror(errno));
  25. return -1;
  26. }
  27. // 利用新创建的socket实例化一个NetlinkHandler类对象,NetlinkHandler继承了类NetlinkListener,
  28. // NetlinkListener又继承了类SocketListener
  29. mHandler = new NetlinkHandler(mSock);
  30. if (mHandler->start()) {  //启动NetlinkHandler
  31. SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
  32. return -1;
  33. }
  34. return 0;
  35. }

把socket作为参数创建了NetlinkHandler对象,然后启动NetlinkHandler。

[cpp] view
plain
copy

  1. int NetlinkHandler::start() {
  2. return this->startListener();
  3. }
  4. int SocketListener::startListener() {
  5. if (!mSocketName && mSock == -1) {
  6. SLOGE("Failed to start unbound listener");
  7. errno = EINVAL;
  8. return -1;
  9. } else if (mSocketName) {
  10. if ((mSock = android_get_control_socket(mSocketName)) < 0) {
  11. SLOGE("Obtaining file descriptor socket ‘%s‘ failed: %s",
  12. mSocketName, strerror(errno));
  13. return -1;
  14. }
  15. }
  16. if (mListen && listen(mSock, 4) < 0) {
  17. SLOGE("Unable to listen on socket (%s)", strerror(errno));
  18. return -1;
  19. } else if (!mListen)
  20. mClients->push_back(new SocketClient(mSock, false));
  21. if (pipe(mCtrlPipe)) {
  22. SLOGE("pipe failed (%s)", strerror(errno));
  23. return -1;
  24. }
  25. if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
  26. SLOGE("pthread_create (%s)", strerror(errno));
  27. return -1;
  28. }
  29. return 0;
  30. }
  31. void *SocketListener::threadStart(void *obj) {
  32. SocketListener *me = reinterpret_cast<SocketListener *>(obj);
  33. me->runListener();
  34. pthread_exit(NULL);
  35. return NULL;
  36. }
  37. void SocketListener::runListener() {
  38. SocketClientCollection *pendingList = new SocketClientCollection();
  39. while(1) { // 死循环,一直监听
  40. SocketClientCollection::iterator it;
  41. fd_set read_fds;
  42. int rc = 0;
  43. int max = -1;
  44. FD_ZERO(&read_fds); //清空文件描述符集read_fds
  45. if (mListen) {
  46. max = mSock;
  47. FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds
  48. }
  49. FD_SET(mCtrlPipe[0], &read_fds); //添加管道的读取端文件描述符到read_fds
  50. if (mCtrlPipe[0] > max)
  51. max = mCtrlPipe[0];
  52. pthread_mutex_lock(&mClientsLock); //对容器mClients的操作需要加锁
  53. for (it = mClients->begin(); it != mClients->end(); ++it) {
  54. int fd = (*it)->getSocket();
  55. FD_SET(fd, &read_fds); ////遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds
  56. if (fd > max)
  57. max = fd;
  58. }
  59. pthread_mutex_unlock(&mClientsLock);
  60. // 等待文件描述符中某一文件描述符或者说socket有数据到来
  61. if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
  62. if (errno == EINTR)
  63. continue;
  64. SLOGE("select failed (%s)", strerror(errno));
  65. sleep(1);
  66. continue;
  67. } else if (!rc)
  68. continue;
  69. if (FD_ISSET(mCtrlPipe[0], &read_fds))
  70. break;
  71. if (mListen && FD_ISSET(mSock, &read_fds)) { //监听套接字处理
  72. struct sockaddr addr;
  73. socklen_t alen;
  74. int c;
  75. do {
  76. alen = sizeof(addr);
  77. c = accept(mSock, &addr, &alen); //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,将其添加到mClient容器
  78. } while (c < 0 && errno == EINTR);
  79. if (c < 0) {
  80. SLOGE("accept failed (%s)", strerror(errno));
  81. sleep(1);
  82. continue;
  83. }
  84. pthread_mutex_lock(&mClientsLock);
  85. mClients->push_back(new SocketClient(c, true));
  86. pthread_mutex_unlock(&mClientsLock);
  87. }
  88. /* Add all active clients to the pending list first */
  89. pendingList->clear();
  90. pthread_mutex_lock(&mClientsLock);
  91. for (it = mClients->begin(); it != mClients->end(); ++it) {
  92. int fd = (*it)->getSocket();
  93. if (FD_ISSET(fd, &read_fds)) {
  94. pendingList->push_back(*it);
  95. }
  96. }
  97. pthread_mutex_unlock(&mClientsLock);
  98. /* Process the pending list, since it is owned by the thread,
  99. * there is no need to lock it */
  100. while (!pendingList->empty()) { //非监听套接字处理
  101. /* Pop the first item from the list */
  102. it = pendingList->begin();
  103. SocketClient* c = *it;
  104. pendingList->erase(it);
  105. /* Process it, if false is returned and our sockets are
  106. * connection-based, remove and destroy it */
  107. // ****** onDataAvailable在NetlinkListener中实现*********
  108. if (!onDataAvailable(c) && mListen) {
  109. /* Remove the client from our array */
  110. pthread_mutex_lock(&mClientsLock);
  111. for (it = mClients->begin(); it != mClients->end(); ++it) {
  112. if (*it == c) {
  113. mClients->erase(it);
  114. break;
  115. }
  116. }
  117. pthread_mutex_unlock(&mClientsLock);
  118. /* Remove our reference to the client */
  119. c->decRef();
  120. }
  121. }
  122. }
  123. delete pendingList;
  124. }

SocketListener::runListener是线程真正执行的函数:mListen成员用来判定是否监听套接字,Netlink套接字属于udp套接字,非监听套接字,该函数的主要功能体现在,如果该套接字有数据到来,就调用函数onDataAvailable读取数据。

3. NetlinkListener::onDataAvailable

[cpp] view
plain
copy

  1. bool NetlinkListener::onDataAvailable(SocketClient *cli)
  2. {
  3. int socket = cli->getSocket();
  4. ssize_t count;
  5. // 从socket中读取kernel发送来的uevent消息
  6. count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer)));
  7. if (count < 0) {
  8. SLOGE("recvmsg failed (%s)", strerror(errno));
  9. return false;
  10. }
  11. NetlinkEvent *evt = new NetlinkEvent();
  12. if (!evt->decode(mBuffer, count, mFormat)) {
  13. SLOGE("Error decoding NetlinkEvent");
  14. } else {
  15. onEvent(evt); //在NetlinkHandler中实现
  16. }
  17. delete evt;
  18. return true;
  19. }

4. NetlinkHandler::onEvent

[cpp] view
plain
copy

  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. if(uEventOnOffFlag)
  10. {
  11. SLOGW("####netlink event  block ####");
  12. evt->dump();
  13. }
  14. vm->handleBlockEvent(evt);
  15. #ifdef USE_USB_MODE_SWITCH
  16. } else if (!strcmp(subsys, "usb")
  17. || !strcmp(subsys, "scsi_device")) {
  18. SLOGW("subsystem found in netlink event");
  19. MiscManager *mm = MiscManager::Instance();
  20. mm->handleEvent(evt);
  21. #endif
  22. }
  23. }

5. uevent_kernel_multicast_recv

[cpp] view
plain
copy

  1. /**
  2. * Like recv(), but checks that messages actually originate from the kernel.
  3. */
  4. ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) {
  5. struct iovec iov = { buffer, length };
  6. struct sockaddr_nl addr;
  7. char control[CMSG_SPACE(sizeof(struct ucred))];
  8. struct msghdr hdr = {
  9. &addr,
  10. sizeof(addr),
  11. &iov,
  12. 1,
  13. control,
  14. sizeof(control),
  15. 0,
  16. };
  17. ssize_t n = recvmsg(socket, &hdr, 0);
  18. if (n <= 0) {
  19. return n;
  20. }
  21. if (addr.nl_groups == 0 || addr.nl_pid != 0) {
  22. /* ignoring non-kernel or unicast netlink message */
  23. goto out;
  24. }
  25. struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
  26. if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
  27. /* ignoring netlink message with no sender credentials */
  28. goto out;
  29. }
  30. struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
  31. if (cred->uid != 0) {
  32. /* ignoring netlink message from non-root user */
  33. goto out;
  34. }
  35. return n;
  36. out:
  37. /* clear residual potentially malicious data */
  38. bzero(buffer, length);
  39. errno = EIO;
  40. return -1;
  41. }

六、与Vold相关的Kernel态

  • 用户态创建的netlink sock被kernel保存在:nl_table[sk->sk_protocol].mc_list
  • Kernel态创建的netlink sock被kernel保存在:uevent_sock_list,上面的sk->sk_protocol为uevent_sock_list的协议, 二者只有协议一致才可以发送。

1. 创建kernel态sock

  • 在用户态的socket创建方式(/system/vold/NetlinkManager.cpp):

[cpp] view
plain
copy

  1. if ((mSock = socket(PF_NETLINK,
  2. SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
  3. SLOGE("Unable to create uevent socket: %s", strerror(errno));
  4. return -1;
  5. }
  • 在Kernel的socket创建方式(/kernel/lib/kobject_uevent.c):

[cpp] view
plain
copy

  1. static int uevent_net_init(struct net *net)
  2. {
  3. struct uevent_sock *ue_sk;
  4. ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
  5. if (!ue_sk)
  6. return -ENOMEM;
  7. ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
  8. 1, NULL, NULL, THIS_MODULE);
  9. if (!ue_sk->sk) {
  10. printk(KERN_ERR
  11. "kobject_uevent: unable to create netlink socket!\n");
  12. kfree(ue_sk);
  13. return -ENODEV;
  14. }
  15. mutex_lock(&uevent_sock_mutex);
  16. list_add_tail(&ue_sk->list, &uevent_sock_list);
  17. mutex_unlock(&uevent_sock_mutex);
  18. return 0;
  19. }

从上面的代码可知,此sock被创建之后,被增加到全局变量uevent_sock_list列表中,下面的分析围绕此列表进行。

  • netlink_kernel_create函数原型:

[cpp] view
plain
copy

  1. struct sock *netlink_kernel_create(struct net *net, int unit, unsigned int groups,
  2. void (*input)(struct sk_buff *skb),
  3. struct mutex *cb_mutex, struct module *module)

1) struct net *net:是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量

2) int unit: 表示netlink协议类型,如 NETLINK_KOBJECT_UEVENT

3)  unsigned int groups: 组类型

4) void (*input)(struct sk_buff *skb):参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被调用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct
sock结构来表示。

5) struct mutex *cb_mutex: 互斥销

6) struct module *module: 一般为THIS_MODULE

  • struct sock

用户态socket在kernel中的表示。

2. 相关数据结构

相关数据结构如下图所示:

3. 发送消息给用户空间

3.1 发送消息流程图

3.2 kobject_uevent_env

[cpp] view
plain
copy

  1. /**
  2. * kobject_uevent_env - send an uevent with environmental data
  3. *
  4. * @action: action that is happening
  5. * @kobj: struct kobject that the action is happening to
  6. * @envp_ext: pointer to environmental data
  7. *
  8. * Returns 0 if kobject_uevent_env() is completed with success or the
  9. * corresponding error when it fails.
  10. */
  11. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
  12. char *envp_ext[])
  13. {
  14. struct kobj_uevent_env *env;
  15. const char *action_string = kobject_actions[action];
  16. const char *devpath = NULL;
  17. const char *subsystem;
  18. struct kobject *top_kobj;
  19. struct kset *kset;
  20. const struct kset_uevent_ops *uevent_ops;
  21. u64 seq;
  22. int i = 0;
  23. int retval = 0;
  24. #ifdef CONFIG_NET
  25. struct uevent_sock *ue_sk;
  26. #endif
  27. pr_debug("kobject: ‘%s‘ (%p): %s\n",
  28. kobject_name(kobj), kobj, __func__);
  29. /* search the kset we belong to */
  30. top_kobj = kobj;
  31. while (!top_kobj->kset && top_kobj->parent)
  32. top_kobj = top_kobj->parent;
  33. if (!top_kobj->kset) {
  34. pr_debug("kobject: ‘%s‘ (%p): %s: attempted to send uevent "
  35. "without kset!\n", kobject_name(kobj), kobj,
  36. __func__);
  37. return -EINVAL;
  38. }
  39. kset = top_kobj->kset;
  40. uevent_ops = kset->uevent_ops;
  41. /* skip the event, if uevent_suppress is set*/
  42. if (kobj->uevent_suppress) {
  43. pr_debug("kobject: ‘%s‘ (%p): %s: uevent_suppress "
  44. "caused the event to drop!\n",
  45. kobject_name(kobj), kobj, __func__);
  46. return 0;
  47. }
  48. /* skip the event, if the filter returns zero. */
  49. if (uevent_ops && uevent_ops->filter)
  50. if (!uevent_ops->filter(kset, kobj)) {
  51. pr_debug("kobject: ‘%s‘ (%p): %s: filter function "
  52. "caused the event to drop!\n",
  53. kobject_name(kobj), kobj, __func__);
  54. return 0;
  55. }
  56. /* originating subsystem */
  57. if (uevent_ops && uevent_ops->name)
  58. subsystem = uevent_ops->name(kset, kobj);
  59. else
  60. subsystem = kobject_name(&kset->kobj);
  61. if (!subsystem) {
  62. pr_debug("kobject: ‘%s‘ (%p): %s: unset subsystem caused the "
  63. "event to drop!\n", kobject_name(kobj), kobj,
  64. __func__);
  65. return 0;
  66. }
  67. /* environment buffer */
  68. env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
  69. if (!env)
  70. return -ENOMEM;
  71. /* complete object path */
  72. devpath = kobject_get_path(kobj, GFP_KERNEL);
  73. if (!devpath) {
  74. retval = -ENOENT;
  75. goto exit;
  76. }
  77. /* default keys */
  78. retval = add_uevent_var(env, "ACTION=%s", action_string);
  79. if (retval)
  80. goto exit;
  81. retval = add_uevent_var(env, "DEVPATH=%s", devpath);
  82. if (retval)
  83. goto exit;
  84. retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
  85. if (retval)
  86. goto exit;
  87. /* keys passed in from the caller */
  88. if (envp_ext) {
  89. for (i = 0; envp_ext[i]; i++) {
  90. retval = add_uevent_var(env, "%s", envp_ext[i]);
  91. if (retval)
  92. goto exit;
  93. }
  94. }
  95. /* let the kset specific function add its stuff */
  96. if (uevent_ops && uevent_ops->uevent) {
  97. retval = uevent_ops->uevent(kset, kobj, env);
  98. if (retval) {
  99. pr_debug("kobject: ‘%s‘ (%p): %s: uevent() returned "
  100. "%d\n", kobject_name(kobj), kobj,
  101. __func__, retval);
  102. goto exit;
  103. }
  104. }
  105. /*
  106. * Mark "add" and "remove" events in the object to ensure proper
  107. * events to userspace during automatic cleanup. If the object did
  108. * send an "add" event, "remove" will automatically generated by
  109. * the core, if not already done by the caller.
  110. */
  111. if (action == KOBJ_ADD)
  112. kobj->state_add_uevent_sent = 1;
  113. else if (action == KOBJ_REMOVE)
  114. kobj->state_remove_uevent_sent = 1;
  115. /* we will send an event, so request a new sequence number */
  116. spin_lock(&sequence_lock);
  117. seq = ++uevent_seqnum;
  118. spin_unlock(&sequence_lock);
  119. retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
  120. if (retval)
  121. goto exit;
  122. #if defined(CONFIG_NET)
  123. /* send netlink message */
  124. mutex_lock(&uevent_sock_mutex);
  125. list_for_each_entry(ue_sk, &uevent_sock_list, list) {
  126. struct sock *uevent_sock = ue_sk->sk;
  127. struct sk_buff *skb;
  128. size_t len;
  129. /* allocate message with the maximum possible size */
  130. len = strlen(action_string) + strlen(devpath) + 2;
  131. skb = alloc_skb(len + env->buflen, GFP_KERNEL);
  132. if (skb) {
  133. char *scratch;
  134. /* add header */
  135. scratch = skb_put(skb, len);
  136. sprintf(scratch, "%[email protected]%s", action_string, devpath); //action_string+devpath
  137. /* copy keys to our continuous event payload buffer */
  138. for (i = 0; i < env->envp_idx; i++) {
  139. len = strlen(env->envp[i]) + 1;
  140. scratch = skb_put(skb, len);
  141. strcpy(scratch, env->envp[i]);
  142. }
  143. NETLINK_CB(skb).dst_group = 1;
  144. retval = netlink_broadcast_filtered(uevent_sock, skb,
  145. 0, 1, GFP_KERNEL,
  146. kobj_bcast_filter,
  147. kobj);
  148. /* ENOBUFS should be handled in userspace */
  149. if (retval == -ENOBUFS)
  150. retval = 0;
  151. } else
  152. retval = -ENOMEM;
  153. }
  154. mutex_unlock(&uevent_sock_mutex);
  155. #endif
  156. /* call uevent_helper, usually only enabled during early boot */
  157. if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
  158. char *argv [3];
  159. argv [0] = uevent_helper;
  160. argv [1] = (char *)subsystem;
  161. argv [2] = NULL;
  162. retval = add_uevent_var(env, "HOME=/");
  163. if (retval)
  164. goto exit;
  165. retval = add_uevent_var(env,
  166. "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
  167. if (retval)
  168. goto exit;
  169. retval = call_usermodehelper(argv[0], argv,
  170. env->envp, UMH_WAIT_EXEC);
  171. }
  172. exit:
  173. kfree(devpath);
  174. kfree(env);
  175. return retval;
  176. }

[cpp] view
plain
copy

  1. /**
  2. * kobject_uevent - notify userspace by sending an uevent
  3. *
  4. * @action: action that is happening
  5. * @kobj: struct kobject that the action is happening to
  6. *
  7. * Returns 0 if kobject_uevent() is completed with success or the
  8. * corresponding error when it fails.
  9. */
  10. int kobject_uevent(struct kobject *kobj, enum kobject_action action)
  11. {
  12. return kobject_uevent_env(kobj, action, NULL);
  13. }

3.3 netlink_broadcast_filtered

[cpp] view
plain
copy

  1. int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid,
  2. u32 group, gfp_t allocation,
  3. int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
  4. void *filter_data)
  5. {
  6. struct net *net = sock_net(ssk);
  7. struct netlink_broadcast_data info;
  8. struct hlist_node *node;
  9. struct sock *sk;
  10. skb = netlink_trim(skb, allocation);
  11. info.exclude_sk = ssk;
  12. info.net = net;
  13. info.pid = pid;
  14. info.group = group;
  15. info.failure = 0;
  16. info.delivery_failure = 0;
  17. info.congested = 0;
  18. info.delivered = 0;
  19. info.allocation = allocation;
  20. info.skb = skb;
  21. info.skb2 = NULL;
  22. info.tx_filter = filter;
  23. info.tx_data = filter_data;
  24. /* While we sleep in clone, do not allow to change socket list */
  25. netlink_lock_table();
  26. // 向nl_table[ssk->sk_protocol].mc_list中的每个sock发送此netlink消息
  27. sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list)
  28. do_one_broadcast(sk, &info);
  29. consume_skb(skb);
  30. netlink_unlock_table();
  31. if (info.delivery_failure) {
  32. kfree_skb(info.skb2);
  33. return -ENOBUFS;
  34. } else
  35. consume_skb(info.skb2);
  36. if (info.delivered) {
  37. if (info.congested && (allocation & __GFP_WAIT))
  38. yield();
  39. return 0;
  40. }
  41. return -ESRCH;
  42. }

static struct netlink_table *nl_table;是全局变量,它维护了用户态创建的所有netlink sock,按协议分类,每种协议一个链表mc_list。它在函数netlink_proto_init中被初始化,向nl_table[sk->sk_protocol].mc_list中增加sock的调用流程如下(kernel/net/netlink/af_netlink.c):

3.4 do_one_broadcast

[cpp] view
plain
copy

  1. static inline int do_one_broadcast(struct sock *sk,
  2. struct netlink_broadcast_data *p)
  3. {
  4. struct netlink_sock *nlk = nlk_sk(sk);
  5. int val;
  6. if (p->exclude_sk == sk)
  7. goto out;
  8. if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups ||
  9. !test_bit(p->group - 1, nlk->groups))
  10. goto out;
  11. if (!net_eq(sock_net(sk), p->net))
  12. goto out;
  13. if (p->failure) {
  14. netlink_overrun(sk);
  15. goto out;
  16. }
  17. sock_hold(sk);
  18. if (p->skb2 == NULL) {
  19. if (skb_shared(p->skb)) {
  20. p->skb2 = skb_clone(p->skb, p->allocation);
  21. } else {
  22. p->skb2 = skb_get(p->skb);
  23. /*
  24. * skb ownership may have been set when
  25. * delivered to a previous socket.
  26. */
  27. skb_orphan(p->skb2);
  28. }
  29. }
  30. if (p->skb2 == NULL) {
  31. netlink_overrun(sk);
  32. /* Clone failed. Notify ALL listeners. */
  33. p->failure = 1;
  34. if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
  35. p->delivery_failure = 1;
  36. } else if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) {
  37. kfree_skb(p->skb2);
  38. p->skb2 = NULL;
  39. } else if (sk_filter(sk, p->skb2)) {
  40. kfree_skb(p->skb2);
  41. p->skb2 = NULL;
  42. } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
  43. netlink_overrun(sk);
  44. if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
  45. p->delivery_failure = 1;
  46. } else {
  47. p->congested |= val;
  48. p->delivered = 1;
  49. p->skb2 = NULL;
  50. }
  51. sock_put(sk);
  52. out:
  53. return 0;
  54. }

3.5 netlink_broadcast_deliver

[cpp] view
plain
copy

  1. static inline int netlink_broadcast_deliver(struct sock *sk,
  2. struct sk_buff *skb)
  3. {
  4. struct netlink_sock *nlk = nlk_sk(sk);
  5. if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
  6. !test_bit(0, &nlk->state)) {
  7. skb_set_owner_r(skb, sk);
  8. skb_queue_tail(&sk->sk_receive_queue, skb);
  9. sk->sk_data_ready(sk, skb->len);
  10. return atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf;
  11. }
  12. return -1;
  13. }

时间: 2024-08-01 10:45:32

Android热插拔事件处理详解的相关文章

Android SlidingMenu 使用详解

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/36677279 很多APP都有侧滑菜单的功能,部分APP左右都是侧滑菜单~SlidingMenu 这个开源项目可以很好帮助我们实现侧滑功能,如果对SlidingMenu 还不是很了解的童鞋,可以参考下本篇博客.将侧滑菜单引入项目的方式很多中,本博客先通过例子介绍各种引入方式,然后给大家展示个实例:主布局ViewPager,左右各一个侧滑菜单的用法,差不多已经能满足大部分应用的需求

Android WebView 开发详解

Android WebView 开发详解 1.概览: Android WebView 做为承载网页的载体控件,他在网页显示的过程中会产生一些事件,并回调给我们的应用程序,以便我们在网页加载过程中做应用程序想处理的事情.比如说客户端需要显示网页加载的进度.网页加载发生错误等等事件. WebView提供两个事件回调类给应用层,分别为WebViewClient,WebChromeClient开发者可以继承这两个类,接手相应事件处理.WebViewClient 主要提供网页加载各个阶段的通知,比如网页开

android屏幕适配详解

android屏幕适配详解 官方地址:http://developer.android.com/guide/practices/screens_support.html 一.关于布局适配建议 1.不要使用绝对布局 2.尽量使用match_parent 而不是fill_parent . 3.能够使用权重的地方尽量使用权重(android:layout_weight) 4.如果是纯色背景,尽量使用android的shape 自定义. 5.如果需要在特定分辨率下适配,可以在res目录上新建layout

Android 四大组件 详解

[置顶] Android四大组件详解 分类: Android四大组件2013-02-09 16:23 19411人阅读 评论(13) 收藏 举报 Android开发 注:本文主要来自网易的一个博主的文章,经过阅读,总结,故留下文章在此 Android四大基本组件介绍与生命周期 Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity : 应用程序中,一个

Android权限Permission详解

android.permission.EXPAND_STATUS_BAR 允许一个程序扩展收缩在状态栏,Android开发网提示应该是一个类似Windows Mobile中的托盘程序 android.permission.FACTORY_TEST 作为一个工厂测试程序,运行在root用户 android.permission.INSTALL_PACKAGES 允许一个程序安装packages android.permission.INTERNAL_SYSTEM_WINDOW 允许打开窗口使用系统

Android进阶——Preference详解之Preference系的基本应用(三)

引言 前面一篇文章Android进阶--Preference详解之Preference系的基本应用和管理(二)介绍了二级Preference的使用和特点,接下来进入系统给我提供的底级Preference的使用CheckBox选择项CheckBoxPreference.EditText编辑对话框EditTextPreference.列表选择ListPreference.多项选择MultiSelectListPreference. 开关选择SwitchPreference的应用和管理.以期大家能在学

iOS开发——实用技术OC篇&amp;事件处理详解

事件处理详解 一:事件处理 事件处理常见属性: 事件类型 @property(nonatomic,readonly) UIEventType     type; @property(nonatomic,readonly) UIEventSubtype  subtype; 事件产生的时间 @property(nonatomic,readonly) NSTimeInterval  timestamp; 事件传递 - hitTest:withEvent: SWIFT func hitTest(_ po

Android中Context详解 ---- 你所不知道的Context

转载至 :http://blog.csdn.net/qinjuning 前言:本文是我读<Android内核剖析>第7章 后形成的读书笔记 ,在此向欲了解Android框架的书籍推荐此书. 大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中 时刻的在与它打交道,例如:Service.BroadcastReceiver.Activity等都会利用到Context的相关方法 : 说它陌生,完全是 因为我们真正的不懂Context

Android相机开发详解(一)

Android相机开发详解(一) 请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客) Android相机开发能够实现打开相机,前后摄像头切换,摄像预览,保存图片,浏览已拍照图片等相机功能. Android相机开发详解(一)主要实现打开相机,摄像预览,前后置摄像头切换,保存图片等四个功能. Android相机开发详解(二)主要实现翻页浏览相片,触控缩放浏览图片,删除图片,发送图片等四个功能. Andro