Emulator USB Passthrough Integration Guide

This article describes how to connect two peripherals (Bluetooth and Wi-Fi) to the AAOS emulator. In this process, three areas specific to driver support are key:

  • Guest kernel
  • Guest Android
  • Linux host

First, you compile and enable the relevant USB drivers in the Guest kernel. Next, Guest Android must select the right HAL and services to bring up the drivers. Finally, the Linux host must get access to the USB driver and then transfer it to QEMU.

Dongles tested

The following dongles were tested:

Other dongles may work, however, no others were tested.

Native USB support

QEMU comes with options to pass USB to the emulator. The AAOS system image already handles a connected phone. For details, see Android Open Accessory (AOA).

Because the phone changes values for vendorID and productID upon establishing an AOA connection, the new USB interface (as well as the original USB interface) sees the device when the phone is in AOA mode.

To determine the values for vendorID and productID before and after AOA mode, use lsusb:

# Note Vendor ID and Product ID of your phone
$ lsusb
Bus 001 Device 079: ID 18d1:4ee1 Google Inc. Nexus/Pixel Device (MTP)
# Start up an emulator!
$ ./emulator @AVD_NAME -no-snapshot -qemu -device usb-ehci,id=ehci -device usb-host,bus=ehci.0,vendorid=0x18d1,productid=0x4ee1 -device usb-host,bus=ehci.0,vendorid=0x18d1,productid=0x2d00

Alternatively, pass the physical port to QEMU:

# First plug something into the interested USB port and note the Bus and Device number.
$ lsusb
Bus 001 Device 012: ID 0bda:c820 Realtek Semiconductor Corp. 802.11ac NIC
# Now figure out where the Port number is.
$ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
    |__ Port 4: Dev 12, If 1, Class=Wireless, Driver=btusb, 480M
    |__ Port 4: Dev 12, If 2, Class=Vendor Specific Class, Driver=, 480M
    |__ Port 4: Dev 12, If 0, Class=Wireless, Driver=btusb, 480M
# Launch the emulator
 ./emulator @AVD_NAME -no-snapshot -qemu -device usb-ehci,id=ehci -device usb-host,bus=ehci.0,hostbus=1,hostport=4
# Now, whatever you plug into the emulator, USB passthrough will happen on the fly.

The AAOS system image recognizes the phone, puts it in AOA mode, and recognizes it again when AOA mode is running.

To support USB passthrough, confirm you're using Emulator 30.5.0:

# Check for the emulator version
$ emulator --version

Emulator 30.5.0 includes a libusb upgrade and temporary workaround to address speed compatibility of ASUS dongle support:

Bluetooth support

To support Bluetooth passthrough, Google tested ASUS USB-BT400 USB Adapter USBBT400 and USB Wi-Fi Bluetooth Adapter by Auscomer.

First, you need to add kernel support for the dongle. To understand the Android Bluetooth stack, see Bluetooth. For HIDL, the emulator uses simulated implementation. Therefore, switch that with a native Linux implementation.

Guest kernel

To support a USB Bluetooth dongle:

  1. Add the missing btusb.ko to your kernel:

    --- a/goldfish_defconfig.fragment
    +++ b/goldfish_defconfig.fragment
    @@ -1,6 +1,7 @@
     # CONFIG_CRYPTO_DEV_VIRTIO is not set
     CONFIG_BLK_DEV_MD=m
    +CONFIG_BT_HCIBTUSB=m
     CONFIG_CPUFREQ_DUMMY=m
    

Guest Android

  1. In the vendor.mk file, include the Linux native HIDL and several permissions:

    PRODUCT_PACKAGES += \
        android.hardware.bluetooth@1.1-service.btlinux
    PRODUCT_COPY_FILES += \ frameworks/native/data/etc/android.hardware.usb.host.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.host.xml
    
  2. Create a one-way path property to switch out the HIDL such that it uses a Linux native HIDL implementation:

    selinux/common/domain.te
    
    get_prop(domain, qemu_prop)
    +get_prop(domain, vendor_build_prop)
    
    selinux/common/property_contexts
    
    qemu.cmdline            u:object_r:qemu_cmdline:s0
    +qemu.preferred.bt.service u:object_r:qemu_prop:s0
    
  3. Whenever a property qemu.preferred.bt.service is set to passthrough, you'll switch out the HIDL implementation:

    service btlinux-1.1 /vendor/bin/hw/android.hardware.bluetooth@1.1-service.btlinux
      class hal
      user bluetooth
      group bluetooth net_admin net_bt_admin
      capabilities NET_ADMIN NET_RAW SYS_NICE
      disabled
    
    on property:qemu.preferred.bt.service=passthrough
      stop vendor.bluetooth-1-1
      start btlinux-1.1
    
  4. Add a Bluetooth configuration file to get full features, such as on a real USB device:

    hal/bluetooth/bdroid_buildcfg.h
    
    #ifndef _BDROID_BUILDCFG_H
    #define _BDROID_BUILDCFG_H
    #define BTM_DEF_LOCAL_NAME "gCar Emulator"
    #define BTA_AV_SINK_INCLUDED TRUE
    /* Handsfree device */
    #define BTA_DM_COD {0x26, 0x04, 0x08}
    #endif
    
  5. Modify the BoardConfig.mk file to determine where the configuration file is saved:

    BoardConfig.mk
    
    # Bluetooth
    BOARD_HAVE_BLUETOOTH := true
    BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := vendor/auto/embedded/hal/bluetooth
    

Linux host

On the Linux host:

  1. Update udev settings to allow the user process (e.g. QEMU) to have read/write permissions:

    $ echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="0b05", ATTRS{idProduct}=="17cb", MODE="0666", GROUP="plugdev"' | sudo tee /etc/udev/rules.d/99-mynew.rules >/dev/null
    $ sudo udevadm control --reload
    $ sudo udevadm trigger
    
  2. To run the emulator, set the following command line parameters:

    # Start up an emulator!
    $ ./emulator @AVD_NAME -no-snapshot -prop qemu.preferred.bt.service=passthrough -qemu -device usb-ehci,id=ehci -device usb-host,bus=ehci.0,vendorid=0x0b05,productid=0x17cb
    # Start Bluetooth Passthrough
    

Wi-Fi support

To validate dual Bluetooth and Wi-Fi, Google tested with the USB Wi-Fi Bluetooth Adapter.

Guest kernel

This particular USB dongle uses the RTL8821CU chip, which the mainline kernel upstream does not yet support. You can find a newly developed kernel module at 8821cu.

Then, in changelist 1575108, the external kernel modules were integrated into the goldfish kernel source to get it compiled.

Finally, the kernel module compiles but with some CFI crashes. You need to patch the code manually to fix this. For details, see Control Flow Integrity in the Android kernel.

It may be helpful to enable CONFIG_CFI_PERMISSIVE and move forward with debugging the rest of the stack first:

--- a/goldfish_defconfig.fragment
+++ b/goldfish_defconfig.fragment
@@ -1,6 +1,7 @@
 CONFIG_CFI_CLANG=m
+CONFIG_CFI_PERMISSIVE=m

In any case, go to changelist 1575109 to see the proper fix for the CFI crashes.

Guest Android

To learn more about the Wi-Fi stack, see Wi-Fi Overview. The emulator comes with the settings to make Wi-Fi work.

Linux host

On the Linux host, you must update udev settings to enable the user process (for example, QEMU) to have read/write permissions.

# /lib/udev/rules.d/40-usb_modeswitch.rules
$ ATTR{idVendor}=="0bda", ATTR{idProduct}=="1a2b", RUN+="usb_modeswitch '/%k'"
$ echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="c820", MODE="0666", GROUP="plugdev"' | sudo tee /etc/udev/rules.d/99-mynew2.rules >/dev/null
$ sudo udevadm control --reload
$ sudo udevadm trigger

To pass the dongle to QEMU:

# Start up an emulator!
$ ./emulator @AVD_NAME -no-snapshot -qemu -device usb-ehci,id=ehci -device usb-host,bus=ehci.0,vendorid=0x0bda,productid=0xc820

Port changelists

Port the following changelists: