整体流程图

大概意思就是UsbHostManager启动监控线程,monitorUsbHostBus会调用usb_host_run函数(使用inotify来监听USB设备的插拔)不停的读取bus总线,读取到以后,当

1、设备插入:发送 广播ACTION_USB_DEVICE_ATTACHED
2、设备拔出: 发送广播ACTION_USB_DEVICE_DETACHED

本篇只分析插入广播的发送,拔出广播类似,读者可自行分析。

UsbHostManager的初始化

UsbHostManager是在UsbService中新建的。

    public UsbService(Context context) {mContext = context;mAlsaManager = new UsbAlsaManager(context);final PackageManager pm = mContext.getPackageManager();if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {mHostManager = new UsbHostManager(context, mAlsaManager);}..........

然后在UsbService的systemReady中调用了UsbHostManager的systemReady函数。

    public void systemReady() {mAlsaManager.systemReady();if (mDeviceManager != null) {mDeviceManager.systemReady();}if (mHostManager != null) {mHostManager.systemReady();}if (mPortManager != null) {mPortManager.systemReady();}}

UsbHostManager的构造函数就是新建一些对象,我们直接看其systemReady函数。这个函数在新的线程中调用了monitorUsbHostBus函数。

public void systemReady() {synchronized (mLock) {// Create a thread to call into native code to wait for USB host events.// This thread will call us back on usbDeviceAdded and usbDeviceRemoved.Runnable runnable = this::monitorUsbHostBus;new Thread(null, runnable, "UsbService host thread").start();}}

这里重点看monitorUsbHostBus,monitorUsbHostBus函数是一个JNI函数。

 private native void monitorUsbHostBus();

UsbHostManager的HAL层

monitorUsbHostBus对应的JNI函数是在com_android_server_UsbHostManager.cpp的android_server_UsbHostManager_monitorUsbHostBus函数,

static const JNINativeMethod method_table[] = {{ "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },{ "nativeOpenDevice",  "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",(void*)android_server_UsbHostManager_openDevice },
};

在这个函数调用了usb_host_init函数,创建了一个INotify的fd,以及创建了一个usb_host_context对象。usb_host_run函数就是循环读取INotify的fd的事件,我们把usb_device_added, usb_device_removed两个回调函数也传入了usb_host_run函数了。

static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
{struct usb_host_context* context = usb_host_init();if (!context) {ALOGE("usb_host_init failed");return;}// this will never return so it is safe to pass thiz directlyusb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}

关于inotify机制相关的东西,这里简单介绍一下。

inotify机制

Inotify 是一个 Linux 内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。还可以跟踪活动的源头和目标等细节。使用 inotify 很简单:创建一个文件描述符,附加一个或多个监视器(一个监视器是一个路径和一组事件),然后使用 read() 方法从描述符获取事件信息。read() 并不会用光整个周期,它在事件发生之前是被阻塞的。更好的是,因为 inotify 通过传统的文件描述符工作,可以利用传统的 select() 系统调用来被动地监控监视器和许多其他输入源。两种方法 — 阻塞文件描述符和使用 select() 都避免了繁忙轮询。

usb_host_run

usb_host_run()进来之后直接就调用了usb_host_load函数,同时将两个函数指针传了进去(discovery_done_cb为NULL),我们接着看usb_host_load函数,也是在usbhost.c中,如下:

190  int usb_host_load(struct usb_host_context *context,
191                    usb_device_added_cb added_cb,
192                    usb_device_removed_cb removed_cb,
193                    usb_discovery_done_cb discovery_done_cb,
194                    void *client_data)
195  {
196      int done = 0;
197      int i;
198
199      context->cb_added = added_cb;
200      context->cb_removed = removed_cb;
201      context->data = client_data;
202
203      D("Created device discovery thread\n");
204
205      /* watch for files added and deleted within USB_FS_DIR */
206      context->wddbus = -1;
207      for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
208          context->wds[i] = -1;
209      /* 查看根目录是否有新的子目录  #define DEV_DIR "/dev"  */
210      /* watch the root for new subdirectories */
211      context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
212      if (context->wdd < 0) {
213          fprintf(stderr, "inotify_add_watch failed\n");
214          if (discovery_done_cb)
215              discovery_done_cb(client_data);
216          return done;
217      }
218
219      watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
220
221      /* check for existing devices first, after we have inotify set up */
222      done = find_existing_devices(added_cb, client_data);
223      if (discovery_done_cb)
224          done |= discovery_done_cb(client_data);
225
226      return done;
227  } /* usb_host_load() */

在usb_host_load中,首先将两个函数指针赋值给struct usb_host_context中的成员变量,client_data是在JNI中传入的thiz指针,discovery_done_cb传入的值为NULL。

接着是inotify_add_watch函数,这里添加了一个目录watch("/dev"),其事件掩码设置为IN_CREATE | IN_DELETE(即观察创建和删除事件),并将这个watch的描述符储存在usb_host_context的成员变量wdd中。后面紧接着就调用watch_existing_subdirs函数,该函数也在usbhost.c中,如下所示:

144  static void watch_existing_subdirs(struct usb_host_context *context,
145                                     int *wds, int wd_count)
146  {
147      char path[100];
148      int i, ret;
149      //#define USB_FS_DIR          "/dev/bus/usb"
150      wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
151      if (wds[0] < 0)
152          return;
153      /* 查看USB_FS_DIR的现有子目录   #define USB_FS_DIR   "/dev/bus/usb" */
154      /* watch existing subdirectories of USB_FS_DIR */
155      for (i = 1; i < wd_count; i++) {
156          snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i);
157          ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
158          if (ret >= 0)
159              wds[i] = ret;
160      }
161  }

wds是usb_host_context中的一个int数组,其长度为MAX_USBFS_WD_COUNT。这里又添加了一个目录watch("/dev/usb"),其事件掩码也设置为IN_CREATE | IN_DELETE,然后将watch描述符储存在wds[0]的位置。紧接着就是一个for循环,该for循环将"/dev/bus/usb"目录下的设备目录(从001开始到MAX_USBFS_WD_COUNT),都添加到inotify中去,作为目录watch进行观察,然后各个watch描述符依次存放在wds数组剩余的位置中。

watch_existing_subdirs函数返回之后,在usb_host_load函数中接着又调用了find_existing_devices函数。(usbhost.c)如下所示:

120  /* returns true if one of the callbacks indicates we are done */
121  static int find_existing_devices(usb_device_added_cb added_cb,
122                                    void *client_data)
123  {
124      char busname[32];
125      DIR *busdir;
126      struct dirent *de;
127      int done = 0;
128  //#define USB_FS_DIR          "/dev/bus/usb"
129      busdir = opendir(USB_FS_DIR);
130      if(busdir == 0) return 0;
131
132      while ((de = readdir(busdir)) != 0 && !done) {
133          if(badname(de->d_name)) continue;
134
135          snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name);
136          done = find_existing_devices_bus(busname, added_cb,
137                                           client_data);
138      } //end of busdir while
139      closedir(busdir);
140
141      return done;
142  }

该函数首先打开"/dev/bus/usb"目录,然后对该目录下的目录调用find_existing_devices_bus函数(usbhost.c),如下:

97  static int find_existing_devices_bus(char *busname,
98                                       usb_device_added_cb added_cb,
99                                       void *client_data)
100  {
101      char devname[32];
102      DIR *devdir;
103      struct dirent *de;
104      int done = 0;
105
106      devdir = opendir(busname);
107      if(devdir == 0) return 0;
108
109      while ((de = readdir(devdir)) && !done) {
110          if(badname(de->d_name)) continue;
111
112          snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name);
113          done = added_cb(devname, client_data);
114      } // end of devdir while
115      closedir(devdir);
116
117      return done;
118  }

该函数会打开之前传入的目录,例如:"/dev/bus/usb/001",然后会遍历该目录下的所有文件,将文件名和之前的目录路径组合到一起就可以得到准确的文件位置,这个文件就代表了一个USB设备,该USB设备是在UsbHostManager运行之前就挂载好了的,所以此时上层还不知道该USB设备的存在,所以需要对该USB设备向上层做补充的通知,即通过added_cb函数指针调用usb_device_added函数。

这里我们还是先不分析usb_device_added函数,只需要知道find_existing_devices函数的作用是,将"/dev/bus/usb"子目录下,在UsbHostManager运行之前就挂载好了的USB设备向上做补充的通知,让上层感知到这些USB设备的存在。

这样usb_host_load函数就返回了,该函数返回之后,usb_host_run会进入一个while循环去执行usb_host_read_event函数(usbhost.c),usb_host_read_event函数非常的长如下:

229  int usb_host_read_event(struct usb_host_context *context)
230  {
231      struct inotify_event* event;
232      char event_buf[512];
233      char path[100];
234      int i, ret, done = 0;
235      int offset = 0;
236      int wd;
237
238      ret = read(context->fd, event_buf, sizeof(event_buf));
239      if (ret >= (int)sizeof(struct inotify_event)) {
240          while (offset < ret && !done) {
241              event = (struct inotify_event*)&event_buf[offset];
242              done = 0;
243              wd = event->wd;
244              if (wd == context->wdd) {
245                  if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
246                      context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
247                      if (context->wddbus < 0) {
248                          done = 1;
249                      } else {
250                          watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
251                          done = find_existing_devices(context->cb_added, context->data);
252                      }
253                  }
254              } else if (wd == context->wddbus) {
255                  if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
256                      watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
257                      done = find_existing_devices(context->cb_added, context->data);
258                  } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
259                      for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
260                          if (context->wds[i] >= 0) {
261                              inotify_rm_watch(context->fd, context->wds[i]);
262                              context->wds[i] = -1;
263                          }
264                      }
265                  }
266              } else if (wd == context->wds[0]) {
267                  i = atoi(event->name);
268                  snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name);
269                  D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ?
270                          "new" : "gone", path, i);
271                  if (i > 0 && i < MAX_USBFS_WD_COUNT) {
272                      int local_ret = 0;
273                      if (event->mask & IN_CREATE) {
274                          local_ret = inotify_add_watch(context->fd, path,
275                                  IN_CREATE | IN_DELETE);
276                          if (local_ret >= 0)
277                              context->wds[i] = local_ret;
278                          done = find_existing_devices_bus(path, context->cb_added,
279                                  context->data);
280                      } else if (event->mask & IN_DELETE) {
281                          inotify_rm_watch(context->fd, context->wds[i]);
282                          context->wds[i] = -1;
283                      }
284                  }
285              } else {
286                  for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {
287                      if (wd == context->wds[i]) {
288                          snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name);
289                          if (event->mask == IN_CREATE) {
290                              D("new device %s\n", path);
291                              done = context->cb_added(path, context->data);
292                          } else if (event->mask == IN_DELETE) {
293                              D("gone device %s\n", path);
294                              done = context->cb_removed(path, context->data);
295                          }
296                      }
297                  }
298              }
299
300              offset += sizeof(struct inotify_event) + event->len;
301          }
302      }
303
304      return done;
305  } /* usb_host_read_event() */

大致就是使用read调用去读取context->fd中的数据,这个fd就是一开始创建的inotify的fd,可以对这个fd做select、poll()等操作。read读到的数据可以转换为struct inotify_event类型的结构体,通过这个结构体中的一些字段来走不同的分支,这里先关注最后一个分支,即是wds数组中,索引0之外的watch描述符发生了变化就会走该分支,根据mask来判断是发生了IN_CREATE事件还是IN_DELETE事件,前者调用usb_device_added函数,后者调用usb_device_removed函数。

usb_device_added函数

当mask为IN_CREATE会调用该回调函数(frameworks/base/services/core/jni/com_android_server_UsbHostManager.cpp)。

56  static int usb_device_added(const char *devAddress, void* clientData) {
57      struct usb_device *device = usb_device_open(devAddress);
58      if (!device) {
59          ALOGE("usb_device_open failed\n");
60          return 0;
61      }
62
63      const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
64      int classID = deviceDesc->bDeviceClass;
65      int subClassID = deviceDesc->bDeviceSubClass;
66
67      // get the raw descriptors
68      int numBytes = usb_device_get_descriptors_length(device);
69      if (numBytes > 0) {
70          JNIEnv* env = AndroidRuntime::getJNIEnv();
71          jobject thiz = (jobject)clientData;
72          jstring deviceAddress = env->NewStringUTF(devAddress);
73
74          jbyteArray descriptorsArray = env->NewByteArray(numBytes);
75          const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
76          env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
77
78          env->CallBooleanMethod(thiz, method_usbDeviceAdded,
79                  deviceAddress, classID, subClassID, descriptorsArray);
80
81          env->DeleteLocalRef(descriptorsArray);
82          env->DeleteLocalRef(deviceAddress);
83
84          checkAndClearExceptionFromCallback(env, __FUNCTION__);
85      } else {
86          // TODO return an error code here?
87          ALOGE("error reading descriptors\n");
88      }
89
90      usb_device_close(device);
91
92      return 0;
93  }

参数devAddress为发生变化的文件的路径,clientData为android_server_UsbHostManager_monitorUsbHostBus时设置的jobject thiz指针。该函数通过JNI调用UsbHostManager.java中的usbDeviceAdded方法:

343      private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
344              byte[] descriptors) {
345          if (DEBUG) {
346              Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
347          }
348
349          if (isBlackListed(deviceAddress)) {
350              if (DEBUG) {
351                  Slog.d(TAG, "device address is black listed");
352              }
353              return false;
354          }
355          UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
356          logUsbDevice(parser);
357
358          if (isBlackListed(deviceClass, deviceSubclass)) {
359              if (DEBUG) {
360                  Slog.d(TAG, "device class is black listed");
361              }
362              return false;
363          }
364
365          synchronized (mLock) {
366              if (mDevices.get(deviceAddress) != null) {
367                  Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
368                  //TODO If this is the same peripheral as is being connected, replace
369                  // it with the new connection.
370                  return false;
371              }
372
373              UsbDevice newDevice = parser.toAndroidUsbDevice();
374              if (newDevice == null) {
375                  Slog.e(TAG, "Couldn't create UsbDevice object.");
376                  // Tracking
377                  addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
378                          parser.getRawDescriptors());
379              } else {
380                  mDevices.put(deviceAddress, newDevice);
381                  Slog.d(TAG, "Added device " + newDevice);
382
383                  // It is fine to call this only for the current user as all broadcasts are
384                  // sent to all profiles of the user and the dialogs should only show once.
385                  ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
386                  if (usbDeviceConnectionHandler == null) {
387                      getCurrentUserSettings().deviceAttached(newDevice);
388                  } else {
389                      getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
390                              usbDeviceConnectionHandler);
391                  }
392
393                  mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
394
395                  // Tracking
396                  addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
397                          parser.getRawDescriptors());
398              }
399          }
400
401          if (DEBUG) {
402              Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
403          }
404
405          return true;
406      }

在UsbDeviceAdded方法中可以看到Slog.d(TAG, "Added device " + newDevice); 可以看到实际log输出如下:

UsbHostManager: Added device UsbDevice[mName=/dev/bus/usb/001/002,mVendorId=1121,mProductId=20052,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=PixArt,mProductName=Tiger USB Optical Mouse,mVersion=1.00,mSerialNumber=null,mConfigurations=[

继续执行 getCurrentUserSettings().deviceAttached(newDevice);

getCurrentUserSettings返回的是UsbProfileGroupSettingsManager的一个对象,对应文件是

frameworks\base\services\usb\java\com\android\server\usb\UsbProfileGroupSettingsManager.java

deviceAttached

655      public void deviceAttached(UsbDevice device) {
656          final Intent intent = createDeviceAttachedIntent(device);
657
658          // Send broadcast to running activities with registered intent
659          mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
660
661          resolveActivity(intent, device, true /* showMtpNotification */);
662      }

首先看createDeviceAttachedIntent函数

1165      private static Intent createDeviceAttachedIntent(UsbDevice device) {
1166          Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1167          intent.putExtra(UsbManager.EXTRA_DEVICE, device);
1168          intent.addFlags(
1169                  Intent.FLAG_ACTIVITY_NEW_TASK |
1170                  Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1171          return intent;
1172      }
1173  }

至此已经很明显了,createDeviceAttachedIntent创建了UsbManager.ACTION_USB_DEVICE_ATTACHED的Intent,后面再去sendBroadcastAsUser取发出广播,app就可以接收到广播去做相应的逻辑处理即可!

Android usb广播 ACTION_USB_DEVICE_ATTACHED流程源码分析相关推荐

  1. Android之View绘制流程源码分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于稍有自定义View经验的安卓开发者来说,onMeasure,onLayout,onDraw这三个方法都不会陌生,起码多少都有所接触吧. 在安卓中 ...

  2. android系统加载主题的流程,详解Android布局加载流程源码

    一.首先看布局层次 看这么几张图 我们会发现DecorView里面包裹的内容可能会随着不同的情况而变化,但是在Decor之前的层次关系都是固定的.即Activity包裹PhoneWindow,Phon ...

  3. nimble源码学习——广播流程源码分析1

    广播流程源码分析1 在controller层有多种状态:广播.空闲.连接等等,这次分析的是广播这个状态或者叫 做角色.在前面controller层循环的分析中,可以明确controller层也有eve ...

  4. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  5. Activity启动流程源码分析-浅析生命周期函数

    源码分析 接着上一篇 Activity启动流程源码分析-setContentView源码阅读 的讲解,本节介绍一下Activity的生命周期函数何时被调用 要看Activity的生命周期函数何时被调用 ...

  6. SpringBoot2 | SpringBoot启动流程源码分析(一)

    首页 博客 专栏·视频 下载 论坛 问答 代码 直播 能力认证 高校 会员中心 收藏 动态 消息 创作中心 SpringBoot2 | SpringBoot启动流程源码分析(一) 置顶 张书康 201 ...

  7. OkHttp原理流程源码分析

    OkHttp已经是非常流行的android客户端的网络请求框架,我其实在项目中使用也已经好几年了,之前一直把重心放在如何快速的搞定业务上.迭代的效率上,这一点来讲,对于一个公司优秀员工是没有毛病的.但 ...

  8. 【源码分析】storm拓扑运行全流程源码分析

    [源码分析]storm拓扑运行全流程源码分析 @(STORM)[storm] 源码分析storm拓扑运行全流程源码分析 一拓扑提交流程 一stormpy 1storm jar 2def jar 3ex ...

  9. Android服务查询完整过程源码分析

    Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...

最新文章

  1. 一年新增457万例,用 Python 揭示癌症的可怕之处
  2. 自定义UIViewController的过渡效果
  3. JVM-虚拟机栈详解 附面试高频题 (手画多图)!!!深入浅出,绝对值得收藏哈!!!
  4. opengl glad.h和 glu.h
  5. 使用代码创建ABAP transparent table
  6. java包引入顺序_多个相同jar存在时的引用顺序
  7. csv 逗号数量不一样_MySQL Workbeach导入CSV时的大坑,一直都是UTF-8问题,绕不过去了~。~...
  8. 拓端tecdat|约会数据动态可视化分析:R语言使用ggplot和ganimate制作的动画图
  9. 码农们:完美主义也是一种错
  10. cudnn下载与安装
  11. MFC绘制bmp图片背景
  12. 实现音乐播放器歌词显示效果
  13. springboot webService调用
  14. P1957 口算练习题[c++版]
  15. 美国卡内基梅隆大学计算机排名,美国卡内基梅隆大学世界排名情况
  16. 【开发心得】如何免费用python刷网站的百度排名(已实测含源码)
  17. 一 Django模型层简介
  18. oracle 表名 添加注释
  19. 恢复chrome书签
  20. 视频教程-.NET MVC5—60分钟快速入门学会《增删改查》-.NET

热门文章

  1. Keil MDK终于免费了,并且没有代码大小限制~
  2. css 横线中间添加文字
  3. PL/SQL Developer 14 配置
  4. C语言试题123之有 5 个人坐在一起,问第五个人多少岁?他说比第 4 个人大 2 岁。问第 4 个人岁数,他说比第 3 个人大 2 岁。问第三个人,又说比第 2 人大两岁。问第 2 个人,说比第一个
  5. html盒子整体向下移动,盒子上下左右居中方法全(持续更新中)
  6. 锐化pdf文件(图片形式)
  7. 【计算机网络】TCP/IP协议(DNS协议、IP协议、TCP协议、UDP协议、三次握手、四次挥手)
  8. [Codeforces Round #516][Codeforces 1063C/1064E. Dwarves, Hats and Extrasensory Abilities]
  9. zookeeper入门到实战-阶段二(常用命令的使用)
  10. VMware的下载和安装(最详细)