Android usb广播 ACTION_USB_DEVICE_ATTACHED流程源码分析

整体流程图

大概意思就是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 directly
    usb_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就可以接收到广播去做相应的逻辑处理即可!

猜你喜欢

转载自blog.csdn.net/mdx20072419/article/details/125087114