首页 > 编程语言 >Android usb广播 ACTION_USB_DEVICE_ATTACHED流程源码分析

Android usb广播 ACTION_USB_DEVICE_ATTACHED流程源码分析

时间:2024-08-18 19:26:58浏览次数:14  
标签:usb ATTACHED host 源码 done context device event

整体流程图

大概意思就是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中,如下所示:

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就可以接收到广播去做相应的逻辑处理即可!

标签:usb,ATTACHED,host,源码,done,context,device,event
From: https://www.cnblogs.com/linhaostudy/p/18365966

相关文章