0%

Usb Framework层框架整理

结构

  • UsbPortManager

  • UsbHostManager

    • UsbAlsaManager
  • UsbDeviceManager

    usb//images/usb2.png

1. UsbPortManager-usb role管理

1.1. USB主机和USB设备

USB通信中居于核心地位的是主机(Host),常见的USB主机是PC机。

  • 任何一次USB的数据传输都必须由主机发起和控制

  • 所有的USB外设都只能和主机建立连接

  • 任何二个外设之间或是二个主机之间都无法直接通信

USB主机和USB设备的功能是不同的。

USB主机的功能:

  • 通过USB接口给外设提供电源(外设也可以自带电源)
  • 检测和配置设备(即设备的枚举)
  • 错误检查和管理数据的传输
  • 根据设定的传输方式与外设交换数据

USB设备的功能:

  • 管理电源。设备可以由USB接口获取电源,也可能有自己的电源

  • 检测通信

    每一个设备都要检测通信信息包中的地址是否和本设备的地址相符,如果不符,设备就会忽略本次通信,这由USB接口硬件自动进行处理。在设备一开始连上USB接口时,使用固定的默认地址0,然后USB主机在检测阶段会给设备分配一个地址,以后的通信都按这个地址进行

  • 通信数据的错误检查。由USB接口硬件保证,不必编程处理

  • 响应请求
    主机在检测到有设备连接上以后,会按USB协议发送相应的设备请求来了解设备的类型和能力,并对设备进行一些配置(如设定地址和配置描述符),设备应能响应这些请求,并返回相应的应答数据

  • 根据设定的传输方式与主机交换数据

1.2. Usb设备演进

USB Type A:该标准一般适用于个人电脑PC中,是应用于最广泛的接口标准

USB Type B:一般用于3.5寸移动硬盘、以及打印机、显示器等连接

usb_device

mini_usb

USB Type C:USB Type C这个接口名称是在USB 3.1时代之后出现的,该接口的亮点在于更加纤薄的设计、更快的传输速度(最高10Gbps)以及更强悍的电力传输(最高100W)。Type-C双面可插接口最大的特点是支持USB接口双面插入,主要面向更轻薄、更纤细的设备(未来可能统一手机平板的接口,取代Micro USB接口)。

配备Type-C连接器的标准规格连接线可通过3A电流,同时还支持超出现有USB供电能力的“USB PD”,可以提供最大100W 的电力。

typec

1.3. typec设备 role管理

Type-C与既有的USB Type-A与USB Type-B相比,具备无方向性的插拔、并能同时进行数据传输、影音输出及电源传递。消费者无法从Type-C的物理外观判断何者是主控端(例如计算机或笔电)或设备端(例如手机或随身碟),这点与传统接口有显著差异(传统上Type-A即是主控端,USB Micro-B是设备端):因此,Type-C与USB Power Delivery(以下简称USB PD)规范定义了数种双模式的角色,用以解决各种设备相互连接时可能产生的问题。

USB Power Delivery(USB PD):

  • POWER_ROLE_SOURCE
  • POWER_ROLE_SINK

source 和 sink 对应 主控端和受控端,source提供电力的一方,sink接收电力的一方。

设备需要支持两种模式,与计算机相连时应作为设备端,与随身碟或耳机相连时则转换角色作为主控端

1
2
<string name="usb_supplying_notification_title">USB supplying power to attached device</string>
<string name="usb_charging_notification_title">USB charging this device</string>

相关的模式,见上层设置界面:

1
2
3
4
5
6
7
public static final int[] DEFAULT_MODES = {
UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_NONE,
UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_NONE,
UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MTP,
UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_PTP,
UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI
};

1.3.1. power 模式

  • MODE_DFP dfp
    DFP 是一种在host或hub上的USB Type-C端口,与device相连接

  • MODE_UFP ufp
    UFP 是一种在device或hub上的USB Type-C端口,与host或hub的DFP相连接

  • MODE_DUAL dual
    DRP 是一种既可作为DFP或UFP进行工作的USB Type-C端口

    DRP指的是作为Power Source(提供者)和Sink(消费者)的电源端口。例如,笔记本电脑上的USB Type-C端口支持USB-PD DRP,既可以作为Power Source(连接U盘或手机时),也可以作为Sink(连接显示器或电源适配器时)

1.3.2. 数据模式

  • DATA_ROLE_HOST host

    手机作为usb主机使用

  • DATA_ROLE_DEVICE device

    手机作为USB设备使用

1.3.3. 相关底层节点

1
2
3
4
5
6
7
8
9
10
11
/sys/class/dual_role_usb
sp7731e_1h10:/sys/class/dual_role_usb/sprd_dual_role_usb # ls -l
total 0
-r--r--r-- 1 root root 4096 1970-01-01 17:49 data_role
lrwxrwxrwx 1 root root 0 1970-01-01 17:49 device -> ../../../403c0000.spi:pmic@0:typec@380
-rw-r--r-- 1 root root 4096 1970-01-01 17:49 mode
drwxr-xr-x 2 root root 0 1970-01-01 00:00 power
-r--r--r-- 1 root root 4096 1970-01-01 17:49 power_role
lrwxrwxrwx 1 root root 0 1970-01-01 17:49 subsystem -> ../../../../../../../../../../../class/dual_role_usb
-r--r--r-- 1 root root 4096 1970-01-01 17:49 supported_modes
-rw-r--r-- 1 root root 4096 1970-01-01 00:00 uevent
  • supportedModes 对应支持的[power模式](#power 模式), 这一项一般是固定的(不可改变)

    MODE_DFP | MODE_UFP | MODE_DUAL

  • mode

    none/dfp/ufp

  • power_role

    no-power/source/sink

  • data_role

    no-data/host/device

1
./android/0000:7008:A111-09 14:17:01.953  3432  3450 I UsbPortManager: USB port changed: port=UsbPort{id=sprd_dual_role_usb, supportedModes=dual}, status=UsbPortStatus{connected=false, currentMode=none, currentPowerRole=source, currentDataRole=host, supportedRoleCombinations=[source:host]}, canChangeMode=true, canChangePowerRole=false, canChangeDataRole=false

1.3.4. 状态切换与通知

Hal层通过uevent机制监控上述底层节点的变化, 通过回调PortManager注册的callback的notifyPortStatusChange函数,使portManager更新port. 同时,portManager也可以通过queryPortStatus函数向hal层查询port的信息.

  • DISPOSITION_ADDED

    底层上报的portinfo在上层的mPorts中没有找到

  • DISPOSITION_CHANGED

    底层上报的portinfo在上层的mPorts中找到了,且对应的 currentMode | currentPowerRole | currentDataRole | supportedRoleCombinations 几个值中任一个改变了

  • DISPOSITION_REMOVED

    上层mPorts中有的项没有在底层上报的所有portinfo中, 该项设置为Removed

  • DISPOSITION_READY

    底层上报的portinfo 在上层的mPorts中,且 currentMode | currentPowerRole | currentDataRole | supportedRoleCombinations 都没变.

PortManager对上层提供的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* Returns a list of physical USB ports on the device.
* <p>
* This list is guaranteed to contain all dual-role USB Type C ports but it might
* be missing other ports depending on whether the kernel USB drivers have been
* updated to publish all of the device's ports through the new "dual_role_usb"
* device class (which supports all types of ports despite its name).
* </p>
*
* @return The list of USB ports, or null if none.
*
* @hide
*/
UsbPort[] getPorts()

/**
* Gets the status of the specified USB port.
*
* @param port The port to query.
* @return The status of the specified USB port, or null if unknown.
*
* @hide
*/
UsbPortStatus getPortStatus(UsbPort port)

/**
* Sets the desired role combination of the port.
* <p>
* The supported role combinations depend on what is connected to the port and may be
* determined by consulting
* {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
* </p><p>
* Note: This function is asynchronous and may fail silently without applying
* the requested changes. If this function does cause a status change to occur then
* a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
* </p>
*
* @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
* or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
* @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
* or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
*
* @hide
*/
void setPortRoles(UsbPort port, int powerRole, int dataRole)

在有状态变更时, 发送ACTION_USB_PORT_CHANGED的广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /** @hide */
public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
int supportedRoleCombinations) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
mCurrentDataRole = currentDataRole;
mSupportedRoleCombinations = supportedRoleCombinations;
}
// 接收广播.
if (UsbManager.ACTION_USB_PORT_CHANGED.equals(intent.getAction())) {
UsbPortStatus portStatus = intent.getExtras()
.getParcelable(UsbManager.EXTRA_PORT_STATUS);
if (portStatus != null) {
mDataRole = portStatus.getCurrentDataRole();
mPowerRole = portStatus.getCurrentPowerRole();
}
}

2. USB host 管理

2.1. Usb host 添加和删除设备.

UsbHostManager的初始化

1
2
3
4
5
   // 音频管理相关
mAlsaManager = new UsbAlsaManager(context);
if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
mHostManager = new UsbHostManager(context, mAlsaManager, mSettingsManager);
}

UsbService systemReady时,调用UsbHostManager的systemReady

1
2
3
4
5
6
7
8
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();
}
}

这里启动了一个线程,调用了 jni 方法 monitorUsbHostBus.

对应 android_server_UsbHostManager_monitorUsbHostBus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
{
// 在usb_host_init 中 , 新建一个usb_host_context对象,还有新建了一个INotify,并且usb_host_context的fd就是INotify的fd。
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);
}

void usb_host_run(struct usb_host_context *context,
usb_device_added_cb added_cb,
usb_device_removed_cb removed_cb,
usb_discovery_done_cb discovery_done_cb,
void *client_data)
{
int done;
// 先调用了usb_host_load函数,这个函数主要把add和remove的回调,放到context相应的成员变量中,然后增加了dev目录放入INotify的观察。下面循环调用usb_host_read_event函数去读取INotify fd的事件。
1. done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);

while (!done) {

done = usb_host_read_event(context);
}
} /* usb_host_run() */

int usb_host_load(struct usb_host_context *context,
usb_device_added_cb added_cb,
usb_device_removed_cb removed_cb,
usb_discovery_done_cb discovery_done_cb,
void *client_data)
{
int done = 0;
int i;
// 添加 add remove的回调
context->cb_added = added_cb;
context->cb_removed = removed_cb;
// 此处client_data 指向 jni 类的thiz
context->data = client_data;

D("Created device discovery thread\n");

/* watch for files added and deleted within USB_FS_DIR */
context->wddbus = -1;
for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
context->wds[i] = -1;

/* watch the root for new subdirectories */
// 监控 root 根节点 /dev 目录的 CREATE 和 DELETE 事件, 返回 watch 描述符
context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
// ..监控失败
....
// 监控存在的/dev/下的特定的子目录 /dev/bus/usb 同样是 CREATE 和 DELETE 事件
// MAX_USBFS_WD_COUNT = 10; 监控 最多10个 目录
// /dev/bus/usb/001 ... /dev/bus/usb/002 .. /dev/bus/usb/009
// 监控成功, 描述符放在对应的 wds数组中.
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);

/* check for existing devices first, after we have inotify set up */
// 最后查找已经存在的/dev/bus/usb 下的目录, 找到的设备调用added_cb函数.

done = find_existing_devices(added_cb, client_data);

{
snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name);
// devname ex: /dev/bus/usb/001/001
done = added_cb(devname, client_data);
}

// 此处 discovery_done_cb 为空.
if (discovery_done_cb)
done |= discovery_done_cb(client_data);

return done;
} /* usb_host_load() */

2.1.1. usb_host_load小结

  • 启动一个线程,初始化了iNotify实例( inotify_init ), 返回fd 赋给 usb_host_context的 fd 成员
  • 绑定 add remove的回调函数, added_cb\removed_cb. 赋给 usb_host_context的 cb_added | cb_removed 成员
  • 监控 root 根节点 /dev 目录的 CREATE 和 DELETE 事件, 返回 watch 描述符 给 usb_host_context 的 wdd成员
  • 监控/dev/bus/usb下存在的子目录 的CREATE 和 DELETE 事件, 返回 watch描述符给 usb_host_context的wds 数组成员
  • 查找/dev/bus/usb/<00i>/ 下的设备是否存在, 如果存在这样的设备, 调用added_cb

2.1.2. usb_host_read_event 监听节点

通过INotify中的fd读取相关的事件,usb_host_run函数会在while循环中一直循环。一直运行该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 读取 inotify_init 实例的fd, 读出对应的事件
ret = read(context->fd, event_buf, sizeof(event_buf));
while (offset < ret && !done) {
event = (struct inotify_event*)&event_buf[offset];
wd = event->wd;
// watch描述符为 /dev 目录的描述符
if (wd == context->wdd) {
// 如果是CREATE事件, 且创建的目录为 /dev/bus
if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
// 添加对 /dev/bus 目录的监控, 返回watch描述符赋给 context->wddbus
context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
// 监控失败, 返回done 为 1, 结束循环,退出usb_host_run函数
if (context->wddbus < 0) {
done = 1;
} else {
// 监控成功, 监控/dev/bus下的子目录, 下面的过程和 usb_host_load 过程一致.
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
// 查找存在的/deb/bus/usb 节点, 并调用added_cb添加设备. 此处done返回0, 仍然处于循环中
done = find_existing_devices(context->cb_added, context->data);
}
// watch 描述符为 /dev/bus 目录的 描述符
} else if (wd == context->wddbus) {
// 如果为 CREATE 事件, 查找存在的 /dev/bus/usb 子目录节点....
if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
done = find_existing_devices(context->cb_added, context->data);
// 为DELETE事件, 且为usb子目录被删除了, 则删除对子目录的监控
} else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
if (context->wds[i] >= 0) {
inotify_rm_watch(context->fd, context->wds[i]);
context->wds[i] = -1;
}
}
}
// 如果为 /dev/bus/usb/ 目录
else if (wd == context->wds[0]) {
i = atoi(event->name);
snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name);
D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ?
"new" : "gone", path, i);
if (i > 0 && i < MAX_USBFS_WD_COUNT) {
int local_ret = 0;
// 新建了该目录 (001-00MAX_USBFS_WD_COUNT的新建)
if (event->mask & IN_CREATE) {
// 添加对该目录的监控
local_ret = inotify_add_watch(context->fd, path,
IN_CREATE | IN_DELETE);
if (local_ret >= 0)
context->wds[i] = local_ret;
// 查找该目录下的文件, 并调用added_cb添加设备
done = find_existing_devices_bus(path, context->cb_added,
context->data);
// 删除了该目录
} else if (event->mask & IN_DELETE) {
// 对该目录取消监控
inotify_rm_watch(context->fd, context->wds[i]);
context->wds[i] = -1;
}
}
}
// 如果为 /dev/bus/usb/<001-00MAX_USBFS_WD_COUNT> 目录
else {
for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {
if (wd == context->wds[i]) {
snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name);
if (event->mask == IN_CREATE) {
D("new device %s\n", path);
done = context->cb_added(path, context->data);
} else if (event->mask == IN_DELETE) {
D("gone device %s\n", path);
done = context->cb_removed(path, context->data);
}
}
}
}
// 读取下一个 inotify_event 事件
offset += sizeof(struct inotify_event) + event->len;
}
}
}

2.1.2.1. usb_host_read_event 小结

该函数即读取此次的inotify实例的fd, 放在event_buf数组中(包含多个inotify_event 事件.), 每个事件都有一个watch fd,

将watch fd 与之前建立的 watch fd比较, 判断是哪一个根目录的事件, 过滤出CREATE 和 DELETE 事件, 再由目录的层级添加对其子目录的监控,如果是CREATE事件,则查找目录下可能存在的usb设备节点, 找到调用added_cb添加解析设备,通知上层添加usb设备. 如果是 /dev/bus/usb/<001-00MAX_USBFS_WD_COUNT> 目录下的CREATE| DELETE 事件, 则直接添加删除设备,并通知到上层.只有在监控/dev目录失败的情况下,才会退出循环,也就是终止线程的运行.其他情况都是返回0,不退出循环,线程一直执行.

2.1.3. 两个回调函数

绑定的两个回调函数为 usb_device_added\usb_device_removed, 这两个函数在jni层.

着重介绍usb_device_added函数.

参数:

1
2
const char *devname   //native 层通过inotify找到的设备名称  ex: /dev/bus/usb/001/001
void* client_data // jni 层的thiz

2.1.4. usb_device_added方法添加设备

先调用usb_device_open 方法打开该设备文件, 返回usb_device结构体.

1
2
3
4
5
6
7
struct usb_device {
char dev_name[64];
unsigned char desc[4096];
int desc_length;
int fd;
int writeable;
};

dev_file

usb_dev_file

该结构体中 desc为 设备描述内容(包含一个设备描述符/n个配置描述符/n个接口描述符/n个端点描述符) , desc最大大小为4096, len中保存了实际的desc的大小, 即 /dev/bus/usb/001/001 的文件大小.

1
2
3
4
jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
manufacturerName, productName, version, serialNumber);

在USB设备枚举过程中,主机端的协义软件需要解析从USB设备读取的所有描述符信息。在USB主向设备发送读取描述符的请求后,USB设备将所有的描述符以连续的数据流方式传输给USB主机。主机从第一个读到的字符开始,根据双方规定好的数据格式,顺序地解析读到的数据流

设备描述符:给出了USB设备的一般信息,一个设备描述符可以有多个配置描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct usb_device_descriptor {  
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01

__le16 bcdUSB;//USB 规范发布号.表示了本设备能适用于那种协议,如2.0=0200,1.1=0110等.
__u8 bDeviceClass;//USB设备类代码,由USB-IF分配,如果该字段为0x00,表示由接口描述符来指定(有可能该USB设备是一个复合设备,USB设备的各个接口相互独立,分别属于不同的设备类)。如果是0x01~0xfe,表示为USB-IF定义的设备类,例如0x03为HID设备,0x09为HUB设备。如果是0xff,表示由厂商自定义设备类型
//0x00不是在设备描述符中定义的,如HID
__u8 bDeviceSubClass;//子类型代码(由USB分配).如果bDeviceClass值是0,一定要设置为0.其它情况就跟据USB-IF组织定义的编码.
__u8 bDeviceProtocol;// 协议代码(由USB分配).如果使用USB-IF组织定义的协议,就需要设置这里的值,否则直接设置为0。如果厂商自己定义的可以设置为FFH.
__u8 bMaxPacketSize0;//端点0最大分组大小(只有8,16,32,64有效)
__le16 idVendor;//供应商ID(由USB分配).
__le16 idProduct;//产品ID(由厂商分配).由供应商ID和产品ID,就可以让操作系统加载不同的驱动程序.
__le16 bcdDevice;//设备出产编码.由厂家自行设置.
__u8 iManufacturer;//厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有.
__u8 iProduct;//描述产品字符串的索引
__u8 iSerialNumber;//描述设备序列号字符串的索引
__u8 bNumConfigurations;//可能的配置数.指配置字符串的个数
} __attribute__ ((packed));

设备描述符给出了USB设备的一般信息,包括对设备及在设备配置中起全程作用的信息,包括制造商标识号ID、产品序列号、所属设备类号、默认端点的最大包长度和配置描述符的个数等。一个USB设备必须有且仅有一个设备描述符。设备描述符是设备连接到总线上时USB主机所读取的第一个描述符.

配置描述符:配置描述符定义了设备的配置信息

1
2
3
4
5
6
7
8
9
10
11
typedef struct _USB_CONFIGURATION_DESCRIPTOR_
{
BYTE bLength, // 描述符大小.固定为0x09.
BYTE bDescriptorType, // 配置描述符类型.固定为0x02.
WORD wTotalLength, // 返回整个数据的长度.指此配置返回的配置描述符,接口描述符以及端点描述符的全部大小.
BYTE bNumInterfaces, // 配置所支持的接口数.指该配置配备的接口数量,也表示该配置下接口描述符数量.
BYTE bConfigurationValue, // 作为Set Configuration的一个参数选择配置值.
BYTE iConfiguration, //用于描述该配置字符串描述符的索引.
BYTE bmAttributes, // 供电模式选择.Bit4-0保留,D7:总线供电,D6:自供电,D5:远程唤醒.
BYTE MaxPower //总线供电的USB设备的最大消耗电流.以2mA为单位.
}USB_CONFIGURATION_DESCRIPTOR;

配置描述符中包括了描述符的长度(属于此描述符的所有接口描述符和端点描述符的长度的和)、供电方式(自供电/总线供电)、最大耗电量等。主果主机发出USB标准命令Get_Descriptor要求得到设备的某个配置描述符,那么除了此配置描述符以外,此配置包含的所有接口描述符与端点描述符都将提供给USB主机。

接口描述符:接口描述符说明了接口所提供的配置,一个配置所拥有的接口数量通过配置描述符的bNumInterfaces决定

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _USB_INTERFACE_DESCRIPTOR_
{
BYTE bLength, // 描述符大小.固定为0x09.
BYTE bDescriptorType, // 接口描述符类型.固定为0x04.
BYTE bInterfaceNumber, // 该接口的编号.
BYTE bAlternateSetting, // 用于为上一个字段选择可供替换的位置.即备用的接口描述符标号.
BYTE bNumEndpoint, // 使用的端点数目.端点0除外.
BYTE bInterfaceClass, // 类型代码(由USB分配).
BYTE bInterfaceSubClass, // 子类型代码(由USB分配).
BYTE bInterfaceProtocol, // 协议代码(由USB分配).
BYTE iInterface // 字符串描述符的索引
}USB_INTERFACE_DESCRIPTOR;

配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为功能更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。

  如果一个配置描述符不止支持一个接口描述符,并且每个接口描述符都有一个或多个端点描述符,那么在响应USB主机的配置描述符命令时,USB设备的端点描述符总是紧跟着相关的接口描述符后面,作为配置描述符的一部分被返回。接口描述符不可直接用Set_Descriptor和Get_Descriptor来存取。

  如果一个接口仅使用端点0,则接口描述符以后就不再返回端点描述符,并且此接口表现的是一个控制接口的特性,它使用与端点0相关联的默认管道进行数据传输。在这种情况下bNumberEndpoints域应被设置成0。接口描述符在说明端点个数并不把端点0计算在内。

端点描述符:USB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量

1
2
3
4
5
6
7
8
9
typedef struct _USB_ENDPOINT_DESCRIPTOR_
{
BYTE bLength, //描述符大小.固定为0x07.
BYTE bDescriptorType, // 接口描述符类型.固定为0x05.
BYTE bEndpointAddress, // USB设备的端点地址.Bit7,方向,对于控制端点可以忽略,1/0:IN/OUT.Bit6-4,保留.BIt3-0:端点号.
BYTE bmAttributes, // 端点属性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中断.
WORD wMaxPacketSize, // 本端点接收或发送的最大信息包大小.
BYTE bInterval // 轮循数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255.对于全速/低速的中断端点,取值范围为 1~255,对于高速中断端点,取值范围为1~16,详细定义可以参考USB协议
}USB_ENDPOINT_DESCRIPTOR;

端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。  

除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。

在将设备描述符的信息发到上层后, 接着遍历设备的各种描述符,之后调用endUsbDeviceAdded结束添加设备

2.1.5. 描述符解析

设备描述符/配置描述符/接口描述符/端点描述符继承父类UsbDescriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
abstract class UsbDescriptor{
-int mLength
-byte mType
---
+postParse(ByteStream stream)
+<b>parseRawDescriptors(ByteStream stream)
}

class UsbDeviceDescriptor extends UsbDescriptor
class UsbConfigDescriptor extends UsbDescriptor
class UsbEndpointDescriptor extends UsbDescriptor
class UsbInterfaceDescriptor extends UsbDescriptor
class UsbUnknown extends UsbDescriptor

abstract class UsbACInterface extends UsbDescriptor
class UsbHIDDescriptor extends UsbDescriptor
class UsbInterfaceAssoc extends UsbDescriptor


abstract class UsbACEndpoint extends UsbDescriptor
package UsbACEndpoint {
class UsbACAudioControlEndpoint extends UsbACEndpoint
class UsbACAudioStreamEndpoint extends UsbACEndpoint
class UsbACMidiEndpoint extends UsbACEndpoint
}


abstract class UsbACInterface extends UsbDescriptor

package UsbACInterface {
class Usb10ASGeneral extends UsbACInterface
class Usb20ASGeneral extends UsbACInterface
class UsbACFeatureUnit extends UsbACInterface
class UsbACMixerUnit extends UsbACInterface
class UsbMSMidiInputJack extends UsbACInterface
class UsbMSMidiOutputJack extends UsbACInterface
class ... extends UsbACInterface

abstract class UsbACTerminal extends UsbACInterface
abstract class UsbACHeaderInterface extends UsbACInterface

package UsbACHeaderInterface{
class Usb10ACHeader extends UsbACHeaderInterface
class Usb20ACHeader extends UsbACHeaderInterface
}

package UsbACTerminal {
class Usb10ACOutputTerminal extends UsbACTerminal
class Usb10ACInputTerminal extends UsbACTerminal
class Usb20ACInputTerminal extends UsbACTerminal
class Usb20ACOutputTerminal extends UsbACTerminal
}

}

继承parseRawDescriptors方法, descriptor前两个字节中存放了长度和类型.解析时根据长度和类型最终将一个device中存放的descriptor的全部描述符全部解析出来.

2.1.5.1.1. USB 端点、管道和接口的关系

USB 系统中数据的传输,宏观的看来是在HOST 和 USB 功能设备之间进行;微观的看是在应用软件的 Buffer 和 USB 功能设备的端点之间进行。一般来说端点都有 Buffer,可以认为USB通讯就是应用软件Buffer和设备端点Buffer之间的数据交换,交换的通道称为管道。应用软件通过和设备之间的数据交换来完成设备的控制和数据传输。通常需要多个管道来完成数据交换,因为同一管道只支持一种类型的数据传输。用在一起来对设备进行控制的若干管道称为设备的接口;

一个 USB 设备可以包括若干个端点,不同的端点以端点编号和方向区分。不同端点可以支持不同的传输类型、访问间隔以及最大数据包大小。除端点 0外,所有的端点只支持一个方向的数据传输。端点 0是一个特殊的端点,它支持双向的控制传输。管道和端点关联,和关联的端点有相同的属性,如支持的传输类型、最大包长度、传输方向等。

usb_interface

2.1.6. added_cb 回调小结

  • 将设备描述符的参数通过beginUsbDeviceAdded会传给上层
  • 将配置描述符的参数通过addUsbConfiguration回传给上层
  • 将接口描述符的参数通过addUsbInterface回传给上层
  • 将端口描述符的参数通过addUsbEndpoint回传给上层
  • 最后调用endUsbDeviceAdded方法回调上层结束该设备的添加过程.

beginUsbDeviceAdded:

1
2
3
4
5
6
7
8
// 创建一个 UsbDevice 对象, 并初始化和其绑定的 mNewConfigurations| mNewInterfaces | mNewEndpoints集合
mNewDevice = new UsbDevice(deviceName, vendorID, productID,
deviceClass, deviceSubclass, deviceProtocol,
manufacturerName, productName, versionString, serialNumber);

mNewConfigurations = new ArrayList<>();
mNewInterfaces = new ArrayList<>();
mNewEndpoints = new ArrayList<>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 新建一个  UsbConfiguration对象,并保存到   mNewConfigurations 集合中
private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
if (mNewConfiguration != null) {
mNewConfiguration.setInterfaces(
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
mNewInterfaces.clear();
}

mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
mNewConfigurations.add(mNewConfiguration);
}
// 新建一个 UsbInterface,并保存到 mNewInterfaces 集合中
private void addUsbInterface(int id, String name, int altSetting,
int Class, int subClass, int protocol) {
if (mNewInterface != null) {
mNewInterface.setEndpoints(
mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
mNewEndpoints.clear();
}

mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
mNewInterfaces.add(mNewInterface);
}
// 新建一个 UsbEndpoint,并保存到 mNewEndpoints 集合中
private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private void endUsbDeviceAdded() {
if (mNewInterface != null) {
// 接口描述符需要绑定端口描述符集合
mNewInterface.setEndpoints(
mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
}
if (mNewConfiguration != null) {
// 配置描述符需要绑定接口描述符集合
mNewConfiguration.setInterfaces(
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
}


synchronized (mLock) {
if (mNewDevice != null) {
// 设备描述符需要绑定配置描述符集合
mNewDevice.setConfigurations(
mNewConfigurations.toArray(
new UsbConfiguration[mNewConfigurations.size()]));
mDevices.put(mNewDevice.getDeviceName(), mNewDevice);

// It is fine to call this only for the current user as all broadcasts are sent to
// all profiles of the user and the dialogs should only show once.
ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
if (usbDeviceConnectionHandler == null) {
getCurrentUserSettings().deviceAttached(mNewDevice);
} else {
getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
usbDeviceConnectionHandler);
}
// deviceName is something like: "/dev/bus/usb/001/001"
UsbDescriptorParser parser = new UsbDescriptorParser();
boolean isInputHeadset = false;
boolean isOutputHeadset = false;
if (parser.parseDevice(mNewDevice.getDeviceName())) {
isInputHeadset = parser.isInputHeadset();
isOutputHeadset = parser.isOutputHeadset();
Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+ " , out: " + isOutputHeadset + "]");
}
// 如果为音频设备,通过UsbAlsaManager处理
mUsbAlsaManager.usbDeviceAdded(mNewDevice,
isInputHeadset, isOutputHeadset);
}
}
}

描述符的相关信息可参考这里

其中几个参数描述了这几个描述符的关系:

  1. 设备描述符中的 bNumConfigurations : 可能的配置数.指配置字符串的个数
  2. 配置描述符中的bNumInterfaces, 指接口描述符的个数
  3. 接口描述符中的bNumEndpoint, 指定端口描述符的个数

除上述几种必须的描述符外, 还有字符串描述符,HID描述符,报告描述符等等.

2.1.7. usb_device_removed方法销毁设备

调用 usbDeviceRemoved 销毁deviceName对应的设备.

1
2
3
4
5
6
7
8
9
10
private void usbDeviceRemoved(String deviceName) {
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
mUsbAlsaManager.usbDeviceRemoved(device);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
}
}
}

2.2. 上层获取USB 设备并进行传输

UsbManager提供了getDeviceList函数,获取当前的usb设备列表,

最终调用到UsbHostManager中,从其保存的mDevices中拿到设备列表.

拿到设备(UsbDevice)后,可以通过openDevice打开这个设备. 返回 UsbDeviceConnection 对象.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public UsbDeviceConnection openDevice(UsbDevice device) {
try {
String deviceName = device.getDeviceName();
ParcelFileDescriptor pfd = mService.openDevice(deviceName);
if (pfd != null) {
UsbDeviceConnection connection = new UsbDeviceConnection(device);
// open中调到了 native_open 为native方法, 调用到了jni层打开该设备.
boolean result = connection.open(deviceName, pfd, mContext);
pfd.close();
if (result) {
return connection;
}
}
}
return null;
}

这里涉及到了另一个jni类(android_hardware_UsbDeviceConnection.cpp)

native_open中主要调用了usb_device_new函数(见这里open_device), 返回了 native层 usb_device结构. 并将device跟field_context关联起来.

1
2
3
4
struct usb_device* device = usb_device_new(deviceNameStr, fd);
if (device) {
env->SetLongField(thiz, field_context, (jlong)device);
}

最后用户进程获取的对象就是UsbDeviceConnection,使用UsbDeviceConnection访问usb设备时,最终都会通过field_context找到device.

2.2.1. USB传输通信

google提供了控制传输的两个方法: 官方资料

  • bulkTransfer (块传输) 在bulk endpoint上进行传输
    bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout);
    在给定的端点执行一个bulk transaction;
    参数:

    1
    2
    3
    4
    endpoint: OUT or IN(Host to Device用OUT,Device to Host 用IN);
    buffer : 将要发送/接收的指令或数据,当endpoint为OUT,buffer为你定义好的指令或数据,将下发给device,当endpoint为IN,buffer则是一个容器,用来存储device返回的应答指令或数据,注意buffer的大小,以足够存储所有的数据;
    length : 即发送/接收指令或数据的大小;
    timeout : 即指令或数据的最长通讯时间,在通讯出现问题时,若超时还未通讯完成,视为通讯失败;

    每个数据包长度高速的时候为512字节,低速设备最大64个字节;用于主机与USB设备之间的批量数据传输,通常一次块传输需要分解成若干个块传输事务。

    一次块传输的方向是单一的,对主机而言,要么是输入,要么是输出。因此,一次块传输是由若干个IN事务或由若干个OUT事务组成的。

    对于要进行输入的块传输,一般要执行若干个IN事务。每执行一个IN事务时,主机都首先发出IN令牌包。设备端点收到后做出响应,一般是回送一个数据包。如果不能回送数据,则回送NAK包或STALL包。NAK表示设备暂时不能回送数据;STALL表示端点一直停着或需要IJSB系统软件进行干预;如果主机收到合法数据包,则回以ACK握手包;如果主机在接收数据时发现有错,则不给设备任何回音。

    对于要进行输出的块传输,一般要执行若干个 OUT事务。每执行一个OUT事务时,主机都首先发出OUT令牌包,接着发出数据包。设备在收到数据包后,根据情况回以握手包;回以ACK表示数据已接收 无误,并通知主机可开始下一个0UT事务,以便传送下一个数据包;回以NAK表示数据已接收无误,但是主机不要再送数据,因为设备暂时不能接收(如缓冲区 满);如果端点已终止,则回以STALL,通知主机不要再重发数据,因为设备出现了故障;如果接收时出现CRC校验错,则不发任何握手包。如果需要输入、输出同时进行,则需要使用2个端点。

  • controlTransfer (控制传输)
    controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
    零点传输执行一个control transaction,即所有的通讯都是通过endpoint 0;
    参数:

    1
    2
    3
    4
    5
    6
    7
    requestType,  request type for this transaction 设置通信的方向
    request ,request ID for this transaction 设置的是访问的类型
    value ,value field for this transaction
    index ,index field for this transaction
    buffer ,同bulkTransfer()
    length ,同bulkTransfer()
    timeout ,同bulkTransfer()

该函数可以设置设备信息,可以看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void makeThisDeviceAnAccessory(@NonNull UsbDeviceConnection connection) {
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MANUFACTURER,
"Android");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MODEL,
"Android device");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_DESCRIPTION,
"Android device running CTS verifier");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_VERSION, "1");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_URI,
"https://source.android.com/compatibility/cts/verifier.html");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_SERIAL, "0");
// 发送ACCESSORY_START 指令开始usb数据传输
AoapInterface.sendAoapStart(connection);
}

public static void sendString(UsbDeviceConnection conn, int index, String string) {
byte[] buffer = (string + "\0").getBytes();
int len = conn.controlTransfer(
// host to device
UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
AoapInterface.ACCESSORY_SEND_STRING, 0, index,
buffer, buffer.length, 10000);
if (len != buffer.length) {
throw new RuntimeException("Failed to send string " + index + ": \"" + string + "\"");
} else {
Log.i(TAG, "Sent string " + index + ": \"" + string + "\"");
}
}

定制了设备的 MANUFACTURER / DESCRIPTION / VERSION 等的信息. 设置了相关的String信息后,可以从device设备上读取相应的信息. 同样通过 controlTransfer 函数, 不过requestType变为了 USB_DIR_IN.

USB 体系四种传输类型:

  • 控制传输:主要用于在设备连接时对设备进行枚举以及其他因设备而异的特定操作。

  • 中断传输:用于对延迟要求严格、小数据的可靠传输,如键盘、游戏手柄等。

  • 批量传输:用于对延迟要求宽松,游戏手柄等大量数据的可靠传输,如U盘等, 该模式即为块传输。

  • 同步传输:用于对可靠性要求不高的实时数据传输,如摄像头、USB音响等.

    不同的传输类型在物理上并没有太大的区别,只是在传输机制、主机安排传输任务、可占用USB 带宽的限制以及最大包长度有一定的差异。

2.3. UsbAlsaManager 音频管理

2.3.1. UsbAlsaManager初始化

进行初始化扫描, 调用了AlsaCardsParser类对"/proc/asound/cards" 文件进行扫描

1
2
// AlsaCardsParser的scan方法
mCardsParser.scan();

扫描结果存放到AlsaCardRecord数组中 (mCardRecords) , 查看AlsaCardRecord的结构如下:

1
2
3
4
5
public int     mCardNum = -1;
public String mField1 = "";
public String mCardName = "";
public String mCardDescription = ""; //只有是usb audio设备该字段才有意义
public boolean mIsUsb = false;

其中mIsUsb标明该音频设备是否是Usb设备. 判断标准是 包含 at usb- 关键字

1
2
3
bullhead:/proc/asound # cat cards
0 [msm8994tomtomsn]: msm8994-tomtom- - msm8994-tomtom-snd-card
msm8994-tomtom-snd-card

2.3.2. UsbAlsaManager systemReady

systemReady时添加对 /dev/snd/ 下的文件监控, 监控新建和删除.

同时对其下面的文件调用alsaFileAdded方法, 添加已经存在的设备.

新建文件时,回调alsaFileAdded方法, 删除了文件时回调 alsaFileRemoved 方法

对于alsaFileAdded方法, 过滤目录下文件名:

  • 文件名以pcmC开头的,以p结束的为TYPE_PLAYBACK设备(播放设备)
  • 文件名以pcmC开头的,以c结束的为TYPE_CAPTURE设备(录音设备)
  • 文件名以midiC开头的,为TYPE_MIDI设备.

对于播放设备和录音设备, 创建AlsaDevice,并以filename为key存放到mAlsaDevices 的map中保存起来.

1
2
3
4
ex:     /dev/snd/pcmC0D34c
public int mCard; //对应C后的一位
public int mDevice; //对应D- c|p中间的位
public int mType; //对应其类型

对于alsaFileRemoved方法, 只需要将filename对应key的 AlsaDevice 从mAlsaDevices中删除即可.

2.3.3. usbDeviceAdded 事件回调

在前一章中介绍了通过inotify监控/dev/bus/usb下设备的机制.

这里在UsbHostManager中, 在接收到usbDeviceAdded后, 处理后(endUsbDeviceAdded

), 将事件转发给了UsbAlsaManager.在转发给UsbAlsaManager之前, 借助UsbDescriptorParser类对新添加usb设备的描述符进行了解析. 涉及到了 新增的 descriptors文件夹下的多个类的处理.(这里涉及的类是UsbACTerminal) 最终判断出该设备是否是 input设备和 output设备.(含有麦克|含有外放)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* package */ void usbDeviceAdded(UsbDevice usbDevice,
// 传入的两个参数是通过上述解析过程解析出来的
boolean isInputHeadset, boolean isOutputHeadset) {
// 进一步检查设备中是否含有audio的接口
int interfaceCount = usbDevice.getInterfaceCount();
for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < interfaceCount;
ntrfaceIndex++) {
UsbInterface ntrface = usbDevice.getInterface(ntrfaceIndex);
// interface 的 Class 为 USB_CLASS_AUDIO 说明是AUDIO设备
if (ntrface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
isAudioDevice = true;
}
}
if (!isAudioDevice) {
return;
}
// 再次调用AlsaCardsParser类对 /proc/asound/cards 文件进行解析.
// 规则, 对比这次解析前与解析后文件内容变化, 如果出现新的usb设备, 则返回其在mCardRecords中的index,
// 如果没有新添加的usb设备, 则从旧的记录中找出对应的usb设备,返回其index
int addedCard = mCardsParser.getDefaultUsbCard();
// 返回了usb设备, 才继续往下执行, 否则什么都不做
if (mCardsParser.isCardUsb(addedCard)) {
// 切换音频设备? 这个函数比较长, 后面进行分析, 主要和AUDIO_SERVICE进行了通信
UsbAudioDevice audioDevice = selectAudioCard(addedCard);
if (audioDevice != null) {
// 将新添加的usb audio 设备 以 usbDevice 添加到 mAudioDevices map中保存
mAudioDevices.put(usbDevice, audioDevice);
Slog.i(TAG, "USB Audio Device Added: " + audioDevice);
}
// 如果 /proc/asound/devices 中有 midi 的device, 且 android.software.midi 开关打开, 继续添加 midi device. 下面的不是重点.
boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard);
if (hasMidi && mHasMidiFeature) {
int device = mDevicesParser.getDefaultDeviceNum(addedCard);
// 等着 /dev/snd 下出现相应的 midi设备文件, 如果超时,直接返回null, 跳过后面的添加过程
AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
... // 省略设备描述相关内容
UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
alsaDevice.mCard, alsaDevice.mDevice);
if (usbMidiDevice != null) {
// 以 usbDevice为key, 存放到 mMidiDevices map中
mMidiDevices.put(usbDevice, usbMidiDevice);
}
}
}

通过上面的过程分析, 可见UsbAlsaManager的流程:

对于USB audio类型的设备, 首先扫描 /proc/asound/cards 文件, 找出usb设备, 在inotify检测到/dev/bus/usb下有新增的usb设备时, UsbHostManager将相应的usbDeviceAdded回调转发给UsbAlsaManager, 转发前解析了设备的desciptor, 判断其中是否含有麦克/外放设备. Alsa处理该转发时, 还需要去查找设备的接口中是否有USB_CLASS_AUDIO Class. 上述条件都满足时, 还要再次对/proc/asound/cards 文件进行扫描. 查找最近添加的usb audio设备. 查到了, 则调用selectAudioCard函数, 连接音频设备.

关键函数是 selectAudioCard 函数. 下面对该函数进行进一步分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/* package */ UsbAudioDevice selectAudioCard(int card) {
...
// 继续扫描 "/proc/asound/devices" 文件
mDevicesParser.scan();
// device 为 0
int device = mDevicesParser.getDefaultDeviceNum(card);
// device的数据结构:
/* int mCardNum
int mDeviceNum
int mDeviceType
int mDeviceDir */
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
// Playback device file needed/present?
// audio playback 设备, 需要等 /dev/snd 下生成了对应的pcmC<card>D<device>p 设备节点生成, 注意此时的 device 为 0. card为相应的usb设备的卡. 等待超时时长 2.5s
if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) {
return null;
}
// Capture device file needed/present?
// audio capture 设备, 需要等 /dev/snd下 pcmC<card>D<device>c 设备节点生成, 等待超时时长 2.5s
if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) {
return null;
}
// 生成 UsbAudioDevice 对象, 对应的数据结构:
/*
mCard = card;
mDevice = device;
mHasPlayback = hasPlayback;
mHasCapture = hasCapture;
mDeviceClass = deviceClass; usb设备为kAudioDeviceClass_External
*/
UsbAudioDevice audioDevice =
new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass);
// 通过 card 索引 从 mCardRecords 索引到 记录的 AlsaCardRecord
AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
//proc/asound/cards 查看这些信息
audioDevice.setDeviceNameAndDescription(cardRecord.mCardName, cardRecord.mCardDescription);
// Notifies AudioService when a device is added or removed
// audioDevice - the AudioDevice that was added or removed
// enabled - if true, we're connecting a device (it's arrived), else disconnecting
// 与 AUDIO_SERVICE 进行交互, 连接当前audio设备.
1. notifyDeviceState(audioDevice, true /*enabled*/);

return audioDevice;
}
// 还需要详细看一下 notifyDeviceState 函数:
// 分别连接录音设备和 播放设备.
1. private void notifyDeviceState(UsbAudioDevice audioDevice, boolean enabled) {
int state = (enabled ? 1 : 0);
if (alsaCard < 0 || alsaDevice < 0) {
Slog.e(TAG, "Invalid alsa card or device alsaCard: " + alsaCard +
" alsaDevice: " + alsaDevice);
return;
}
// "card=" + card + ";device=" + device + ";";
String address = AudioService.makeAlsaAddressString(alsaCard, alsaDevice);
try {
// Playback Device //对应播放设备
if (audioDevice.mHasPlayback) {
int device;
// 这个是通过解析usb设备的描述符得到的
if (mIsOutputHeadset) {
device = AudioSystem.DEVICE_OUT_USB_HEADSET;
} else {
device = (audioDevice == mAccessoryAudioDevice
? AudioSystem.DEVICE_OUT_USB_ACCESSORY
: AudioSystem.DEVICE_OUT_USB_DEVICE);
}
mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG);
}

// Capture Device //对应录音设备
if (audioDevice.mHasCapture) {
int device;
if (mIsInputHeadset) {
device = AudioSystem.DEVICE_IN_USB_HEADSET;
} else {
device = (audioDevice == mAccessoryAudioDevice
? AudioSystem.DEVICE_IN_USB_ACCESSORY
: AudioSystem.DEVICE_IN_USB_DEVICE);
}
mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG);
}
}
}

关于AudioService的setWiredDeviceConnectionState:

  1. 判断是否需要发送拔出耳机的intent; (拔出耳机时,需要将当前播放的音频停掉)
  2. 通过底层AudioSystem处理设备的连接和断开连接
  3. 发送Intent广播(ex: ACTION_HEADSET_PLUG)通知外设状态变化

关于"/proc/asound/devices" 文件内容:

1
2
3
4
5
6
7
8
sp9832e_1h10:/proc/asound # cat devices                                        
0: [ 0] : control
16: [ 0- 0]: digital audio playback
17: [ 0- 1]: digital audio playback
20: [ 0- 4]: digital audio playback
21: [ 0- 5]: digital audio playback
24: [ 0- 0]: digital audio capture
25: [ 0- 1]: digital audio capture

可见对应整个流程,需要四个条件准备就绪:

  1. /dev/bus/usb/ 下出现了新的设备, 且解析设备描述符为 HEADSET相关的设备:(desciptor, 判断其中是否含有 麦克/外放设备. 还需要去查找设备的接口中是否有USB_CLASS_AUDIO Class)
  2. /dev/snd/下出现对应的音频设备
  3. “/proc/asound/cards” 中出现对应的声卡
  4. “/proc/asound/devices” 中出现对应的设备描述

上述过程对应Alsa架构的音频设备管理.

Alsa 架构声卡相关知识

alsa-jiagou

3. Usb device管理

手机作为usb设备,主要是和pc端的交互.pc作为usb主机, 与手机进行usb通信.

3.1. Usb枚举过程

usb枚举

切换function涉及到的逻辑:

switch_function

Client 对应app或者其他服务.

一个USB接口扩展出多个设备功能的实现方法有两种:

  • 在设备外部或内部加Hub扩展

  • 以Usb Composite Device方式实现(一般称为复合设备)。

    复合设备其实只是一个USB设备,只有一个USB设备地址,它实现多个功能的原因主要在于它扩展实现了多个USB接口,每个接口具有不同的设备类型。Android下采用了USB Composite Device这种方式来实现一个USB口的情况下扩展出多个功能设备,这种情况下一个USB接口(Interface)便对应一种类型的功能设备,需要实现与之对应的功能驱动。

    ![usb composite](/images/usb composite.png)

3.2. 传统Usb device与accessory(配件)模式

Android设备作为传统device设备, host端主要是pc(也可以是手机). 如mtp/ptp/mass_storage/虚拟光驱/tethering模式等.

Android设备还可以作为accessory设备, 以USB Device的角色与一些具有USB Host功能,但却扮演着配件角色的设备相连.

这些设备可能是机器人控制器、Dock(基座)、诊断设备、音响设备、配电设备、读卡器等等。

Google引入USB Accessory概念的原因应该主要有如下:

  • 非常多的Android设备不具有USB Host的功能而只具有USB Device功能,或者即使具备USB Host的功能,也承担不起对USB外设供电的任务,因为便携式Android设备本身的电池容量就很有限。
  • 原来的Android设备,作为USB Device所实现的功能相对比较简单,内置的功能只有U盘或ADB调试设备等,Google希望提供应用层的USB开发库,让更多的软硬件厂商来开发新的功能,比如说安装一个APK应用,然后通过USB连接到一个与电视机配套的Dock上,就可以让一台Android手机变身为一个电视机遥控器。

当Android设备作为Host模式时为USB总线供电;当Android设备连接到一个USB Accessory设备时,USB Accessory设备以Host身份为USB总线供电。

usb_device_mode

USB Accessory设备: 配件设备, 以host方式供电

Android 设备: device角色, usb function切换为accessory.

两台手机互联时, 在打开accessory mode情况下, 连接adapter的一端为USB Accessory设备, 直接microusb或typec接口的一端为device设备.

3.2.1. accessory模式下的枚举过程:

USB Accessory设备和Android设备两者双方整个枚举识别工作过程如下:

  • USB Accessory设备发起USB控制传输进行正常的USB设备枚举,获取设备描述符和配置描述符等信息,此时大部分Android设备上报的还只是普通的U盘或MTP设备;
  • USB Accessory设备发起Vendor类型,request值为51(0x33)的控制传输命令(ACCESSORY_GET_PROTOCOL),看看该Android设备是否支持USB Accessory功能,如果支持的话会返回所支持的AOA协议版本
  • USB Accessory设备判断到该Android设备支持Accessory功能后,发起request值为52(0x34)的控制传输命令(ACCESSORY_SEND_STRING),并把该Accessory设备的相关信息(包括厂家,序列号等)告知Android设备
  • 如果是USB Audio Accessory设备,还会发起request值为58(0x3A)的控制传输命令(SET_AUDIO_MODE命令),通知Android设备进入到Audio Accessory模式
  • USB Accessory设备发起request值为53(0x35)的控制传输命令(ACCESSORY_START),通知Android设备切换到Accessory功能模式开始工作。Android设备收到此uevent信息后,会先把sys.usb.config设置为包含accessory功能,此后Android设备工作在accessory 模式下. 厂商可以通过该模式开发出适用各种场景的usb配件.

3.2.2. accessory模式框架

accessory_mode_control

在Android设备切换为accessory function成功后, 会触发configured的流程, 回调accessoryAttached函数.

通过匹配android.hardware.usb.action.USB_ACCESSORY_ATTACHEDintent找到厂商的apk.apk在onCreate时会接收到的主要参数为UsbManager.EXTRA_ACCESSORY, 携带了usb accessory设备的描述信息. 也可以通过getAccessoryList接口拿到当前已经连接的accessory配件.

1
2
3
4
5
6
private final  String  mManufacturer;
private final String mModel;
private final String mDescription;
private final String mVersion;
private final String mUri;
private final String mSerial;
1
2
3
4
5
6
7
8
9
<activity android:name="UsbAccessoryActivity" android:label="DemoKit"
android:taskAffinity="" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>

<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" />
</activity>

通过openAccessory方法拿到fd, 通过fd拿到输入输出流进行传输控制

1
2
3
4
UsbManager usbManager = getContext().getSystemService(UsbManager.class);
ParcelFileDescriptor fd = usbManager.openAccessory(accessory);
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(fd))
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(fd))

而host端传输方式(假设host是手机)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
UsbDeviceConnection connection = mUsbManager.openDevice(device);
UsbInterface iface = null;
for (int i = 0; i < device.getConfigurationCount(); i++) {
if (device.getInterface(i).getName().equals("Android Accessory Interface")) {
iface = device.getInterface(i);
break;
}
}
//拿到输入流 输出流端口
final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
// 往device端发送
int numSent = connection.bulkTransfer(out, sizeBuffer, 1, 0);
// 接收device端的回复
byte[] recBytes = new byte[1];
int numReceived = connection.bulkTransfer(in, recBytes,
recBytes.length, timeout);

3.2.2.1. 申请accessory权限

打开accessory配件需要权限, 需要应用进行动态申请.

1
2
//uidList中保存了已经为该accessory申请权限的应用的uid列表.
mAccessoryPermissionMap.put(accessory, uidList);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
//权限名称任意,仅是标识作用
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
// 通过hasPermission方法查询是否已经有该配件的权限, 没有则申请权限, 启动UsbPermissionActivity弹框提示是否需要授权该类配件的权限.点击授予会调用PendingIntent.send方法发送该广播.
if(!mUsbManager.hasPermission(accessory))
mUsbManager.requestPermission(accessory, mPermissionIntent);

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
// 权限被授予
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(accessory != null){
//call method to set up accessory communication
// 权限授予后, 可以进行通信.
}
}
else {
Log.d(TAG, "permission denied for accessory " + accessory);
}
}
}
}
};

Accessory的google官方文档

accessory模式的框架与host框架开发类似. host模式下device设备开发文档见host overview

3.3. Usb 网络共享

usb模式涉及到usb网络共享. (usb网络绑定与逆绑定)

网络共享服务基于微软开发的rndis协议. 主要用于windows平台中usb网络设备的驱动开发.当手机通过usb连接到主机,启用usb绑定.需要将手机上的usb设置为rndis.主机上识别到新的网卡.要使用通过USB绑定的网卡,需要给主机分配IP地址.

在USB绑定中,主机是DHCP的客户端,手机是DHCP的服务器端.Android中使用了DNSmasq充当DHCP服务器.

切换为网络共享的流程和其他的模式是一样的, 都是通过UsbDeviceManager设置属性, init写入driver的相关节点, 开启rndis模式.开启后通过driver上报的uevent事件, 判断驱动是否可以正常工作.当收到configured状态时,开始真正的配置网络.

3.3.1. 配置网卡

等rndis切换成功后, Android设备网卡的节点会出现rndis0/usb0的接口.

1
2
3
4
5
6
7
8
9
sp9832a_2h11:/sys/class/net # ls -l
total 0
lrwxrwxrwx 1 root root 0 2012-01-07 00:54 dummy0 -> ../../devices/virtual/net/dummy0
lrwxrwxrwx 1 root root 0 2012-01-07 00:54 ip6tnl0 -> ../../devices/virtual/net/ip6tnl0
lrwxrwxrwx 1 root root 0 2012-01-07 00:54 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 2012-01-07 00:56 rndis0 -> ../../devices/20200000.usb/gadget/net/rndis0
lrwxrwxrwx 1 root root 0 2018-07-11 10:38 usb0 -> ../../devices/platform/soc/soc:ap-ahb/e2500000.usb2/gadget/net/usb0
lrwxrwxrwx 1 root root 0 2012-01-07 00:54 seth_lte0 -> ../../devices/virtual/net/seth_lte0
...

3.3.2. StateMachine

3.3.2.1. state 模式

将对象的状态封装成一个对象,在不同的状态下同样的调用执行不同的操作

对象内部状态决定行为方式,对象状态改变则行为方式改变.封装对象的状态是将对象的状态与行为封装在一起;可以解决庞大的分支语句带来程序阅读性差和不便于进行扩展问题,使整个结构变得更加清晰明了,降低程序管理的复杂性提高灵活度。

stateMachine

StateMachine的构造函数都是protected类型,不能实例化;都是由其子类进行初始化操作;

1
2
3
4
5
6
7
protected StateMachine(String name) {
mSmThread = new HandlerThread(name);
mSmThread.start();
Looper looper = mSmThread.getLooper();

initStateMachine(name, looper);
}

3.3.2.2. State Machine各个模块作用

  • State

状态的基类,stateMachine中的状态都是由State派生而来,构造函数protected,不能实例化;只能由子类继承进行实例化.

1
2
3
4
5
6
7
8
public class State implements IState
{
  protected State() {}
  public void enter() {}
  public void exit() {}
  public boolean processMessage(Message msg) {}
  public String getName() {}
}
  • SmHandler

    SmHandler的内部类

    • StateInfo

      存储当前State,和其parentState,以及是否激活状态;用来构建树形层次结构模型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      private class StateInfo 
      {
        /** the state */
        State state;
        /** The parent of this state, null if there is no parent */
        StateInfo parentStateInfo;
        /** True when the state has been entered and on the stack */
        boolean active;
      }
    • HaltingState与QuittingState

      都是State的 派生类,用于在状态停止和放弃之后处理的一些事情;都重写了ProcessMessage方法,

      在StateMachine没有实际行动仅仅保留用于扩展。

    整个SmHandle是消息处理派发和状态控制切换的核心,运行在单独的线程上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static class SmHandler extends Handler 
{
  /** The current message */
  private Message mMsg;

  /** Stack used to manage the current hierarchy of states */
  private StateInfo mStateStack[];

  /** The map of all of the states in the state machine */
  private HashMap<State, StateInfo> mStateInfo =
    new HashMap<State, StateInfo>();

  /** The initial state that will process the first message */
  private State mInitialState;

  /** The destination state when transitionTo has been invoked */
  private State mDestState;
}
3.3.2.2.1. Smhandler

SmHandle是构建StateMachine的核心,运行在独立的线程上,有三个功能:

  • 建立树形层次结构存储State;

    在构成一个状态机前需要确定当前都多少状态,需要将这些状态集中起来进行管理。

    StateMachine提供了这样一个protected类型方法 addState来将状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /****************************************************
    * state:    加入state machine的State
    * parent:   the parent of state, patent的状态节点可以为空
    ****************************************************/
    private final StateInfo addState(State state, State parent) {
    StateInfo parentStateInfo = null;
    if (parent != null) {
    //获取当前状态parent详细信息 StateInfo
    parentStateInfo = mStateInfo.get(parent);
    if (parentStateInfo == null) {
    //当前状态父状态未加入到StateMachine中,
          //递归先加入其Parent State
    parentStateInfo = addState(parent, null);
    }
    }
    //判断当前状态是否加入到 StateMachine层次结构中
    StateInfo stateInfo = mStateInfo.get(state);
    if (stateInfo == null) {
    //创建State详细信息对象,将其加入到StateMachine层次结构中
    stateInfo = new StateInfo();
    mStateInfo.put(state, stateInfo);
    }

    //验证我们有没有加入相同的状态,在两个不同层次,否则异常
    if ((stateInfo.parentStateInfo != null)
    && (stateInfo.parentStateInfo != parentStateInfo)) {
    throw new RuntimeException("state already added");
    }
    stateInfo.state = state;
    stateInfo.parentStateInfo = parentStateInfo;
    stateInfo.active = false;
    return stateInfo;
    }
    1
    private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();

    mStateInfo是按照tree层次组织State的.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      SmHandle sm;
      sm.addState(S0,null);
      sm.addState(S1,S0);
      sm.addState(S2,S0);
      sm.addState(S3,S1);
      sm.addState(S4,S1);
      sm.addState(S5,S2);
      sm.addState(S6,S2);
      sm.addState(S7,S2);
    //设置初始状态
      setInitialState(S4);

    state_tree

  • 状态机的StateStack建立和状态切换;

    各状态State加入到StateMachine,各条件初始化OK后,就可以启动状态机了。

    启动状态机:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
      public void start() 
      {
        /** Send the complete construction message */
    1.    mSmHandler.completeConstruction();
      }
    1. private final void completeConstruction()
    {
    //计算State tree的最大深度以便创建运行State Stack
    int maxDepth = 0;
    for (StateInfo si : mStateInfo.values()) {
    int depth = 0;
    for (StateInfo i = si; i != null; depth++) {
    i = i.parentStateInfo;
    }
    if (maxDepth < depth) {
    maxDepth = depth;
    }
    }

    //创建State Stack
    mStateStack = new StateInfo[maxDepth];
    mTempStateStack = new StateInfo[maxDepth];

    //根据当前mDestState(S5)按照其层次结构沿着其父子关系,
    //保存此条路径上的StateInfo 存储到State Stack中于是
    //例如:S0--S2—S5 存储到mStateStack中
    setupInitialStateStack();

    //层次结构状态构建完成调用mStateStack中State的enter方法
    //使mStateStack中的State 处于active状态
    mIsConstructionCompleted = true;
    mMsg = obtainMessage(SM_INIT_CMD);
    invokeEnterMethods(0);

    //Perform any transitions requested by the enter methods
    1.1 performTransitions(); //待下面分析
    }

    State Stack里面的元素结构是根据父子关系组成链式结构:S0——S2——S5;S5是mDestState,

    S2,S0都是其parentState;状态是一种父子关系;

    状态切换:

    StateMachine中提供了方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    protected final void transitionTo(IState destState) 
    {
      mSmHandler.transitionTo(destState);
    }
    private final void transitionTo(IState destState)
    {
      // mDestState保存当前状态 来处理消息;
      mDestState = (State) destState;
    }

    transitionTo仅仅是改变了当前状态mDestState,从StateStack建立这里可以看到和这个mDestState相关的还有mStateStack,如果改变了mDestState,mStateStack也需要改变,使mStateStack仍然是链式层次式结构。

    • 需要改变mStateStack:

      mDestState改变时,没有同时改变 mStateStack,而是等到消息处理派发状态Handle的时候,当前的状态行为处理完,切换到下一个状态,即消息处理完毕然后才进行mStateStack的更新。目的是使状态切换和mStateStack的更新独立开来.

      ex:

      ​ mStateStack中存储:S0——S2——S5 mDestState为S5 (栈顶)

      ​ 现在状态切换为S3,mDestState为S3

      ​ 按照父子关系,mStateStack应该存储有:S0——S1——S3

      ​ 那么此时S5,S2都要出栈pop from mStateStack

      ​ 那我们就是要找到一个点,让S5,S2出栈;S3,S1进栈;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1.1 //Do any transitions
private synchronized void performTransitions()
{
...
State destState = mDestState;
while (true) {
{
//当前状态切换了 存在于mStateStack中的State需要改变
//仍然按照链式父子关系来存储
//先从当前状态S3找到 最近的被激活的parent状态S0
//未被激活的全部保存起来(S3,S1) 返回S0
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);

//将mStateStack中 不属于当前状态(S3),
//关系链上的State(S5,S2)退出(执行exit方法)
invokeExitMethods(commonStateInfo);

//将S3关系链 加入到栈中(S3,S1)
int stateStackEnteringIndex = moveTempStateStackToStateStack();

//将新加入到mStateStack中 未被激活的State激活(S3,S1)
invokeEnterMethods(stateStackEnteringIndex);

//将延迟的消息移动到消息队列的前面,以便快速得到处理
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
}
  • 消息处理和派发;

StateMachine处理的核心就是SmHandler,就是一个Handler,运行在单独线程中。

Handler是用来异步处理派发消息,这里使用Handler管理各个状态,派发消息处理到各个状态中去执行。

  状态机准备OK后(状态加入和状态栈构建完成)就可以执行某些行为,接收消息进行处理,派发到当前状态去执行。

看一下SmHandler中handleMessage是如何进行消息处理的。

消息接收:

​ StateMachine提供了sendMessage等方法将消息加入到消息队列中,当然都是交给SmHandler去处理的。

消息处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final void handleMessage(Message msg) 
{
  //处理当前消息到state中去处理
  1. processMsg(msg);
  //消息处理完毕状态切换 更新mStateStack
  2. performTransitions();
}
1. private final void processMsg(Message msg)
{
  //派发消息到state中去处理
  StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
  while (!curStateInfo.state.processMessage(msg))
  {
    //当前状态mDestState 未处理该消息,交给其parent state处理
    curStateInfo = curStateInfo.parentStateInfo;
    if (curStateInfo == null){
      //此消息未被当前层次状态链处理
    }
  }
}

到这里看到建立状态栈mStateStack的作用,用来支持进行链式的消息处理;(Chain of Responsibility)

3.3.3. Tethering网络状态机

Tethering-state

Tethering对象使用了状态模式来实现共享连接机制的实现,为每个状态创建一个状态对象,一个状态对象根据不同情景可以切换到另一个状态对象。

3.3.3.1. TetherInterfaceSM(TetherInterfaceStateMachine)

Tethering类图如上, Tethering对象为每一个使用共享连接的物理接口维护一个TetherInterfaceSM类型的状态机,管理Tethering接口的状态。TetherInterfaceSM(TetherInterfaceStateMachine的简写)状态机在NetworkManagementService服务触发的interfaceAdded回调中实例化。

TetherInterfaceSM类型的状态机状态:

  • InitialState(初始状态)
  • LocalHotspotState
  • TetheredState(共享状态)
  • UnavailableState(连接不可用状态)
1
2
3
4
5
6
//TetherState
public class IControlsTethering {
public static final int STATE_UNAVAILABLE = 0;
public static final int STATE_AVAILABLE = 1;
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;

TetherInterfaceSM通过isAvailable、isTethered、isErrored、getLastError等函数对外提供Tethering接口的状态信息,从而使Tethering的getTetherableIfaces、getTetheredIfaces、getTetheredIfacePairs、getTetheringErroredIfaces、getLastTetherError等函数可以从接口对应的状态机中获得Tethering接口的状态。TetherInterfaceSM状态机在正常共享工作情况下应该处于TetheredState状态,在TetheredState状态通过NetworkManagementService的tetherInterface的函数来添加使用共享连接的接口。

在底层配置好rndis function后, 添加rndis0/usb0的网卡接口,会回调interfaceAdded方法, 这时会新建TetherState实例, 同时会实例化一个TetherInterfaceStateMachine对象,作为TetherState的成员, 最后以接口名称为索引,加到mTetherStates的map中保存.

3.3.3.1.1. TetherInterfaceSM 初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
        public TetherState(TetherInterfaceStateMachine sm) {
stateMachine = sm;
// Assume all state machines start out available and with no errors.
// 初始状态为 STATE_AVAILABLE
lastState = IControlsTethering.STATE_AVAILABLE;
lastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
public TetherInterfaceStateMachine(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
IControlsTethering tetherController) {
...
mServingMode = IControlsTethering.STATE_AVAILABLE;
// 初始化四个状态
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
mTetheredState = new TetheredState();
mUnavailableState = new UnavailableState();
// 添加了四个state,
1.addState(mInitialState);
addState(mLocalHotspotState);
addState(mTetheredState);
addState(mUnavailableState);
// 设置SmHandler的mInitialState为 InitialState
setInitialState(mInitialState);
}
mTetherStates.put(iface, tetherState);
2. tetherState.stateMachine.start();

1.// addState, 每个State的parentState都为null
public final void addState(State state) {
mSmHandler.addState(state, null);
}
2.// 启动状态机. 调用completeConstruction方法.
public void start() {
SmHandler smh = mSmHandler;
if (smh == null) return;
smh.completeConstruction();
}
private final void completeConstruction() {
...
// 为InitialState更新stack. 这个上面只有它自己
setupInitialStateStack();
// smhandler 发送SM_INIT_CMD消息, 通过handleMessage处理
2.1 sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
2.1
else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
/** Initial one time path. */
mIsConstructionCompleted = true;
2.1.1. // 调用InitialState的enter方法
invokeEnterMethods(0);
}
2.1.2 // 更新DestState, 调用DestState的enter方法,并更新DestState的Stack, 这个地方因DestState为null, 所以这个地方可以忽略
performTransitions(msgProcessedState, msg);
2.1.1 //调用InitialState的enter方法
public void enter() {
sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
}
private void sendInterfaceState(int newInterfaceState) {
mServingMode = newInterfaceState;
mTetherController.updateInterfaceState(
TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
sendLinkProperties();
}
//回头看mTetherController, 这个来自makeControlCallback函数. 注册回调的方式通知到TetherMasterSM主控状态机,
// 关于makeControlCallback回调中的notifyInterfaceStateChange和notifyLinkPropertiesChanged后面再分析.
new TetherInterfaceStateMachine(
iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(iface)));
private IControlsTethering makeControlCallback(String ifname) {
return new IControlsTethering() {
@Override
public void updateInterfaceState(
TetherInterfaceStateMachine who, int state, int lastError) {
// 通知到TetherMasterSM主控状态机,后面分析
notifyInterfaceStateChange(ifname, who, state, lastError);
}

@Override
public void updateLinkProperties(
TetherInterfaceStateMachine who, LinkProperties newLp) {
// 通知到TetherMasterSM主控状态机
notifyLinkPropertiesChanged(ifname, who, newLp);
}
};
}
  • 初始化工作
    • 构建tree, 添加了TISM的四个状态.
    • 初始状态为TISM的initialState, 并调用了其enter方法, 通过回调通知到主控状态机更新state.
    • 将mIsConstructionCompleted标记为true, 后面可以处理各个state的processmessage方法.

3.3.3.2. TetherMasterSM

Tethering对象还提供了一个TetherMasterSM类型的主控状态机,提供共享连接的启动、停止等管理及连接状态事件的监控并向TetherInterfaceSM状态机发送事件通知

TetherMasterSM状态机的状态包括:

  • InitialState(初始状态)

  • TetherModeAliveState(共享模式激活状态)

  • ErrorState

    ErrorState类型的状态

    • SetIpForwardingEnabledErrorState
    • SetIpForwardingDisabledErrorState
    • StartTetheringErrorState
    • StopTetheringErrorState
    • SetDnsForwardersErrorState等出错状态

正常共享工作情况下TetherMasterSM状态机处于TetherModeAliveState状态,在TetherModeAliveState状态打开共享连接,并调用NetworkManagementService服务的setIpForwardingEnabled、setDnsForwarders、startTethering函数启动共享连接服务。

3.3.3.2.1. 主控状态机初始化

在Tethering的构造函数中, 进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
//启动主控状态机
mTetherMasterSM.start();
TetherMasterSM(String name, Looper looper) {
super(name, looper);
// 构建tree, 每一个的parentnode也是null
addState(mInitialState);
addState(mTetherModeAliveState);
addState(mSetIpForwardingEnabledErrorState);
addState(mSetIpForwardingDisabledErrorState);
addState(mStartTetheringErrorState);
addState(mStopTetheringErrorState);
addState(mSetDnsForwardersErrorState);

mNotifyList = new ArrayList<>();
mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
mOffload = new OffloadWrapper();
// 初始State为 TetherMasterSM的InitialState
setInitialState(mInitialState);
}

TetherMasterSM的InitialState没有enter方法, 所以在启动主控状态机时并没有执行.只是更新了InitialState的stack,并将mIsConstructionCompleted标记为true

3.3.3.2.2. 接收interface状态机的回调

接着TISM的初始化工作讲, 将回调interfaceAdd或interfaceChanged后, 初始化并启动了TISM状态机. 回调了notifyInterfaceStateChange方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
         // TISM initalState
public void enter() {
sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
}
...
// TODO: Move into TetherMasterSM.
// state = STATE_AVAILABLE
private void notifyInterfaceStateChange(
String iface, TetherInterfaceStateMachine who, int state, int error) {
synchronized (mPublicSync) {
// 在TISM初始化时, 加到mTetherStates中的
final TetherState tetherState = mTetherStates.get(iface);
if (tetherState != null && tetherState.stateMachine.equals(who)) {
// 更新lastState为STATE_AVAILABLE
tetherState.lastState = state;
tetherState.lastError = error;
}
}

try {
// data_saver相关, 暂不用关注
mPolicyManager.onTetheringChanged(iface, state == IControlsTethering.STATE_TETHERED);
}
...
int which;
switch (state) {
case IControlsTethering.STATE_UNAVAILABLE:
case IControlsTethering.STATE_AVAILABLE:
// handler 处理 EVENT_IFACE_SERVING_STATE_INACTIVE消息.
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
break;
case IControlsTethering.STATE_TETHERED:
case IControlsTethering.STATE_LOCAL_ONLY:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
break;
default:
Log.wtf(TAG, "Unknown interface state: " + state);
return;
}
mTetherMasterSM.sendMessage(which, state, 0, who);
sendTetherStateChangedBroadcast();
}
// StateMachine的handleMessage进行处理
if (mIsConstructionCompleted) {
/** Normal path */
1. msgProcessedState = processMsg(msg);
}
2. performTransitions(msgProcessedState, msg);
3. 进到TetherMasterSm的InitialState处理
class InitialState extends State {
@Override
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
transitionTo(mTetherModeAliveState);
break;
// 处理inactive消息,这个地方在这时什么也没做
case EVENT_IFACE_SERVING_STATE_INACTIVE:
who = (TetherInterfaceStateMachine) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
break;
case EVENT_IFACE_UPDATE_LINKPROPERTIES:
// Silently ignore these for now.
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
1. // 没有转换state, 因此performTransitions也没有实际的工作.

在仅仅有interfaceAdd回调时, 此时的TMSM主控状态机的状态保留在InitialState. 而TISM 接口状态机的状态也保存在InitialState. 同时TetherState的lastState为STATE_AVAILABLE

3.3.3.3. 收到configured状态后, 开启网络共享

在切换rndis成功后, 首先有网卡接口消息的上报, 然后收到configured uevent消息, 并通过广播拿到该状态后, 开启网络共享.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED,
ConnectivityManager.TETHERING_USB);

private void tetherMatchingInterfaces(int requestedState, int interfaceType) {
if (VDBG) {
Log.d(TAG, "tetherMatchingInterfaces(" + requestedState + ", " + interfaceType + ")");
}

String[] ifaces = null;
try {
// 下发netd找到所有网卡接口
ifaces = mNMService.listInterfaces();
}
String chosenIface = null;
if (ifaces != null) {
for (String iface : ifaces) {
// 找到usb0/rndis0
if (ifaceNameToType(iface) == interfaceType) {
chosenIface = iface;
break;
}
}
}
changeInterfaceState(chosenIface, requestedState);
}
private void changeInterfaceState(String ifname, int requestedState) {
final int result;
switch (requestedState) {
case IControlsTethering.STATE_UNAVAILABLE:
case IControlsTethering.STATE_AVAILABLE:
result = untether(ifname);
break;
// 处理STATE_TETHERED
case IControlsTethering.STATE_TETHERED:
case IControlsTethering.STATE_LOCAL_ONLY:
result = tether(ifname, requestedState);
break;
}
}
private int tether(String iface, int requestedState) {
if (DBG) Log.d(TAG, "Tethering " + iface);
synchronized (mPublicSync) {
TetherState tetherState = mTetherStates.get(iface);
// 检查lastState是否是STATE_AVAILABLE, 不是的话, 状态出错
if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
// 通知到TISM 进入TISM的当前的状态机处理CMD_TETHER_REQUESTED消息. 消息参数为STATE_TETHERED
tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
}

在开启网络共享时, 先通过TISM的状态机切换状态.

TISM的InitialState处理CMD_TETHER_REQUESTED消息.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
switch (message.arg1) {
case IControlsTethering.STATE_LOCAL_ONLY:
transitionTo(mLocalHotspotState);
break;
// 处理CMD_TETHER_REQUESTED消息. 消息参数为STATE_TETHERED
case IControlsTethering.STATE_TETHERED:
// TISM 状态机切换到TetherdState
transitionTo(mTetheredState);
break;
default:
mLog.e("Invalid tethering interface serving state specified.");
}
break;

在基类StateMachine handleMessage的末尾处理performTransitions函数.

调用切换前State的Exit方法, 更新DestState的Stack, 对更新后的Stack未激活的stack执行其enter方法.

TISM的InitialState没有exit方法. 只执行TetherState的enter方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    class TetheredState extends BaseServingState {
@Override
public void enter() {
// 执行BaseServingState的enter方法
super.enter();
if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
transitionTo(mInitialState);
}
3. // 通知到主控状态机
sendInterfaceState(IControlsTethering.STATE_TETHERED);
// SPRD: set sys.ril.internet_tethering prop when USB internet connected
if(mInterfaceType == ConnectivityManager.TETHERING_USB) {
SystemProperties.set("sys.ril.internet_tethering", "1");
}
}

class BaseServingState extends State {
@Override
public void enter() {
1. // 配置ipv4.
if (!startIPv4()) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
return;
}

try {
2. // 开启网卡usb网络共享状态, 下发netd.
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
mLog.e("Error Tethering: " + e);
mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
return;
}

if (!isTetherIpv6SprdDesigned()) {
if (!startIPv6()) {
mLog.e("Failed to startIPv6");
// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
return;
}
}
}
3. //sendInterfaceState(IControlsTethering.STATE_TETHERED);
// TODO: Move into TetherMasterSM.
private void notifyInterfaceStateChange(
String iface, TetherInterfaceStateMachine who, int state, int error) {
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
if (tetherState != null && tetherState.stateMachine.equals(who)) {
// tetherState 的lastState更新为 STATE_TETHERED
tetherState.lastState = state;
tetherState.lastError = error;
}
}
...

int which;
switch (state) {
// 处理STATE_TETHERED
case IControlsTethering.STATE_TETHERED:
case IControlsTethering.STATE_LOCAL_ONLY:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
break;
}
// 主控状态机切换状态. InitialState处理EVENT_IFACE_SERVING_STATE_ACTIVE消息.
mTetherMasterSM.sendMessage(which, state, 0, who);
sendTetherStateChangedBroadcast();
}
class InitialState extends State {
@Override
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
handleInterfaceServingStateActive(message.arg1, who);
3.1 // 主控状态机切换为 TetherModeAliveState
transitionTo(mTetherModeAliveState);
break;
3.2 // sendMessage的末尾处理InitialState.exit方法(这里没有实现),并更新新的State的Stack, 最后执行TetherModeAliveState 的enter方法. 激活的意思.
private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
if (mode == IControlsTethering.STATE_TETHERED) {
// No need to notify OffloadController just yet as there are no
// "offload-able" prefixes to pass along. This will handled
// when the TISM informs Tethering of its LinkProperties.
mForwardedDownstreams.add(who);
}
}
3.2 class TetherModeAliveState extends State {
boolean mUpstreamWanted = false;
boolean mTryCell = true;

@Override
// 执行enter方法.
public void enter() {
// If turning on master tether settings fails, we have already
// transitioned to an error state; exit early.
3.2.1 turnOnMasterTetherSettings
if (!turnOnMasterTetherSettings()) {
return;
}

mSimChange.startListening();
mUpstreamNetworkMonitor.start();

// TODO: De-duplicate with updateUpstreamWanted() below.
if (upstreamWanted()) {
mUpstreamWanted = true;
mOffload.start();
3.2.2 chooseUpstreamType
chooseUpstreamType(true);
mTryCell = false;
}
}

在开启网络共享后, 先是TISM行动, 切换并激活了TetherState, 然后tetherState lastState变更到STATE_TETHERD, 并通知到主控状态机TMSM切换到TetherModeAliveState 并激活.

在上面两个状态机激活绑定状态时,即在enter时, 涉及到了usb网卡的配置及激活共享等操作. 这些命令抛开state的流程单独描述.

3.3.3.3.1. 网络配置激活等
  1. startIPv4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
private boolean startIPv4() { return configureIPv4(true); }
private boolean configureIPv4(boolean enabled) {
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
// TODO: Replace this hard-coded information with dynamically selected
// config passed down to us by a higher layer IP-coordinating element.
String ipAsString = null;
int prefixLen = 0;
if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
// "192.168.42.129"
ipAsString = USB_NEAR_IFACE_ADDR;
// 24
prefixLen = USB_PREFIX_LENGTH;
}

final LinkAddress linkAddr;
try {
// 获得usb0网卡的初始配置
// SND -> {63 interface getcfg usb0}
// RCV <- {213 63 9e:84:73:52:ac:0a 0.0.0.0 0 down broadcast multicast}
// mHwAddr mAddr prefixLen flag flag flag
final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
/*

private String mHwAddr;
private LinkAddress mAddr;
private HashSet<String> mFlags = Sets.newHashSet();
*/
InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
// 更新网口config, 更新后的参数
// mAddr 192.168.42.129 lenth为24
linkAddr = new LinkAddress(addr, prefixLen);
ifcg.setLinkAddress(linkAddr);
// enabled为true
if (enabled) {
// mFlags.remove(FLAG_DOWN); mFlags.add(FLAG_UP);
ifcg.setInterfaceUp();
} else {
ifcg.setInterfaceDown();
}
// mFlags.remove("running");
ifcg.clearFlag("running");
// SND -> {64 interface setcfg usb0 192.168.42.129 24 broadcast up multicast}
// 更新后的usb0网卡参数, up表示会启动该网卡
// 9e:84:73:52:ac:0a 192.168.42.129 24 up broadcast multicast
// 设置更新完成后, 会收到回复
// RCV <- {614 Address updated 192.168.42.129/24 usb0 128 0}
// RCV <- {600 Iface linkstate usb0 up}
// RCV <- {616 Route updated fe80::/64 dev usb0}
// RCV <- {614 Address updated fe80::9c84:73ff:fe52:ac0a/64 usb0 196 253}
mNMService.setInterfaceConfig(mIfaceName, ifcg);
}
// Directly-connected route.
final RouteInfo route = new RouteInfo(linkAddr);
if (enabled) {
mLinkProperties.addLinkAddress(linkAddr);
mLinkProperties.addRoute(route);
} else {
mLinkProperties.removeLinkAddress(linkAddr);
mLinkProperties.removeRoute(route);
}
return true;
}
  1. 开启网卡usb网络共享状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    mNMService.tetherInterface(mIfaceName);
    @Override
    public void tetherInterface(String iface) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    try {
    // SND -> {65 tether interface add usb0}
    mConnector.execute("tether", "interface", "add", iface);
    }
    List<RouteInfo> routes = new ArrayList<>();
    // SND -> {66 interface getcfg usb0}
    // RCV <- {213 66 9e:84:73:52:ac:0a 192.168.42.129 24 up broadcast multicast}
    routes.add(new RouteInfo(getInterfaceConfig(iface).getLinkAddress(), null, iface));
    addInterfaceToLocalNetwork(iface, routes);
    }
    public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
    // SND -> {67 network interface add local usb0}
    modifyInterfaceInNetwork("add", "local", iface);

    for (RouteInfo route : routes) {
    if (!route.isDefaultRoute()) {
    // SND -> {68 network route add local usb0 192.168.42.0/24}
    modifyRoute("add", "local", route);
    }
    }
    }
  2. turnOnMasterTetherSettings

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    3.2.1. // 跳转turnOnMasterTetherSettings 函数. 启动共享连接服务
    protected boolean turnOnMasterTetherSettings() {
    final TetheringConfiguration cfg = mConfig;
    try {
    1. mNMService.setIpForwardingEnabled(true);
    } catch (Exception e) {
    mLog.e(e);
    transitionTo(mSetIpForwardingEnabledErrorState);
    return false;
    }
    // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
    try {
    // TODO: Find a more accurate method name (startDHCPv4()?).
    2.1 // TetheringConfiguration最初的信息来自于TetheringConfiguration的构造函数.
    2.2 mNMService.startTethering(cfg.dhcpRanges);
    } catch (Exception e) {
    try {
    mNMService.stopTethering();
    mNMService.startTethering(cfg.dhcpRanges);
    } catch (Exception ee) {
    mLog.e(ee);
    transitionTo(mStartTetheringErrorState);
    return false;
    }
    }
    mLog.log("SET master tether settings: ON");
    return true;
    }
    1. setIpForwardingEnabled
    public void setIpForwardingEnabled(boolean enable) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    try {
    // SND -> {70 ipfwd enable tethering}
    mConnector.execute("ipfwd", enable ? "enable" : "disable", "tethering");
    } catch (NativeDaemonConnectorException e) {
    throw e.rethrowAsParcelableException();
    }
    }
    2.1 TetheringConfiguration初始化
    // Tethering构造函数中
    updateConfiguration();
    private void updateConfiguration() {
    mConfig = new TetheringConfiguration(mContext, mLog);
    mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
    }

    public TetheringConfiguration(Context ctx, SharedLog log) {
    final SharedLog configLog = log.forSubComponent("config");

    tetherableUsbRegexs = ctx.getResources().getStringArray(
    com.android.internal.R.array.config_tether_usb_regexs);

    69 <string-array translatable="false" name="config_tether_usb_regexs">
    70 <item>"usb\\d"</item>
    71 <item>"rndis\\d"</item>
    72 </string-array>

    // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
    // us an interface name. Careful consideration needs to be given to
    // implications for Settings and for provisioning checks.
    tetherableWifiRegexs = ctx.getResources().getStringArray(
    com.android.internal.R.array.config_tether_wifi_regexs);
    tetherableBluetoothRegexs = ctx.getResources().getStringArray(
    com.android.internal.R.array.config_tether_bluetooth_regexs);

    dunCheck = checkDunRequired(ctx);
    configLog.log("DUN check returned: " + dunCheckString(dunCheck));

    preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
    isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);

    2.1.1 dhcpRanges = getDhcpRanges(ctx);
    defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);

    configLog.log(toString());
    }

    2.1.1 private static String[] getDhcpRanges(Context ctx) {
    // 这个里面没改的话是空的,
    final String[] fromResource = ctx.getResources().getStringArray(
    com.android.internal.R.array.config_tether_dhcp_range);
    if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
    return fromResource;
    }
    // 前面的为空, 返回下面这个写死的值
    /*
    private static final String[] DHCP_DEFAULT_RANGE = {
    "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
    "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
    "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
    "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
    "192.168.137.2", "192.168.137.254", "192.168.0.2", "192.168.0.254",
    };
    */
    return copy(DHCP_DEFAULT_RANGE);
    }

    2.2 mNMService.startTethering(cfg.dhcpRanges);
    public void startTethering(String[] dhcpRange) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    final Command cmd = new Command("tether", "start");
    for (String d : dhcpRange) {
    cmd.appendArg(d);
    }
    try {
    //SND -> {71 tether start 192.168.42.2 192.168.42.254 192.168.43.2 192.168.43.254 192.168.44.2 192.168.44.254 192.168.45.2 192.168.45.254 192.168.46.2 192.168.46.254 192.168.47.2 192.168.47.254 192.168.48.2 192.168.48.254 192.168.49.2 192.168.49.254 192.168.137.2 192.168.137.254 192.168.0.2 192.168.0.254}
    mConnector.execute(cmd);
    } catch (NativeDaemonConnectorException e) {
    throw e.rethrowAsParcelableException();
    }
    }

    turnOnMasterTetherSettings 主要工作就是下发了两个命令:

    1
    2
    ipfwd enable tethering
    tether start 192.168.42.2 192.168.42.254 192.168.43.2 192.168.43.254 192.168.44.2 192.168.44.254 192.168.45.2 192.168.45.254 192.168.46.2 192.168.46.254 192.168.47.2 192.168.47.254 192.168.48.2 192.168.48.254 192.168.49.2 192.168.49.254 192.168.137.2 192.168.137.254 192.168.0.2 192.168.0.254
  3. chooseUpstreamType

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
      3.2.2 //  跳转chooseUpstreamType函数, 进行网络通路优化 tryCell true
    protected int chooseUpstreamType(boolean tryCell) {
    // We rebuild configuration on ACTION_CONFIGURATION_CHANGED, but we
    // do not currently know how to watch for changes in DUN settings.
    // 可能更新了配置信息, 即上面的TetheringConfiguration
    maybeUpdateConfiguration();
    // preferredUpstreamIfaceTypes是在TetheringConfiguration传入的, 注意系统中只定义了数据流量模式, wifi模式和蓝牙网络可以作为优选网络, 1/7/0. 在数据流量模式时, 需要检查TelephoneManager的dunCheck, dunCheck为DUN_REQUIRED时忽略数据流量模式, 即不能作为优选网络. 网络通路.
    final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
    mConfig.preferredUpstreamIfaceTypes);
    if (ns == null) {
    if (tryCell) {
    mUpstreamNetworkMonitor.registerMobileNetworkRequest();
    // We think mobile should be coming up; don't set a retry.
    } else {
    sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
    }
    }
    mUpstreamNetworkMonitor.setCurrentUpstream((ns != null) ? ns.network : null);
    // 如果找到了优选网络通路. 设置dns转发
    3.2.2.1 setUpstreamNetwork(ns);

    ...//ipv6 相关
    return upV6Type;
    }
    3.2.2.1
    protected void setUpstreamNetwork(NetworkState ns) {
    String iface = null;
    if (ns != null && ns.linkProperties != null) {
    // Find the interface with the default IPv4 route. It may be the
    // interface described by linkProperties, or one of the interfaces
    // stacked on top of it.
    mLog.i("Finding IPv4 upstream interface on: " + ns.linkProperties);
    // 选择最佳路由
    RouteInfo ipv4Default = RouteInfo.selectBestRoute(
    ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
    if (ipv4Default != null) {
    iface = ipv4Default.getInterface();
    mLog.i("Found interface " + ipv4Default.getInterface());
    } else {
    mLog.i("No IPv4 upstream interface, giving up.");
    }
    }

    if (iface != null) {
    1. setDnsForwarders(ns.network, ns.linkProperties);
    }
    // 设置桥接转发
    2. notifyDownstreamsOfNewUpstreamIface(iface);
    if (ns != null && pertainsToCurrentUpstream(ns)) {
    // If we already have NetworkState for this network examine
    // it immediately, because there likely will be no second
    // EVENT_ON_AVAILABLE (it was already received).
    handleNewUpstreamNetworkState(ns);
    } else if (mCurrentUpstreamIface == null) {
    // There are no available upstream networks, or none that
    // have an IPv4 default route (current metric for success).
    handleNewUpstreamNetworkState(null);
    }
    }

    发现优选网络通路后, 设置dns server地址, 并进行网络地址转换和ip转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
1. // SND -> {72 tether dns set 100 192.168.1.1}
setDnsForwarders(ns.network, ns.linkProperties);
2. notifyDownstreamsOfNewUpstreamIface(iface)
protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
mLog.log("Notifying downstreams of upstream=" + ifaceName);
mCurrentUpstreamIface = ifaceName;
for (TetherInterfaceStateMachine sm : mNotifyList) {
// TISM此时处于TetherState, 因此由TetherState处理该消息
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
ifaceName);
}
}
..//TetherState的processMessage函数
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
cleanupUpstream();
if (newUpstreamIfaceName != null) {
try {

2.1 // 网络地址转换
mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
2.2 // 启动ip转发
mNMService.startInterfaceForwarding(mIfaceName,
newUpstreamIfaceName);
} catch (Exception e) {
mLog.e("Exception enabling NAT: " + e);
cleanupUpstreamInterface(newUpstreamIfaceName);
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
transitionTo(mInitialState);
return true;
}
}
mMyUpstreamIfaceName = newUpstreamIfaceName;
break;

2.1 public void enableNat(String internalInterface, String externalInterface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
// SND -> {74 nat enable usb0 wlan0 1 192.168.42.0/24}
// 网络地址转换
modifyNat("enable", internalInterface, externalInterface);
} catch (SocketException e) {
throw new IllegalStateException(e);
}
}
2.2 private void modifyInterfaceForward(boolean add, String fromIface, String toIface) {
// SND -> {75 ipfwd add usb0 wlan0}
// RCV <- {614 Address updated fe80::9c84:73ff:fe52:ac0a/64 usb0 128 253}
final Command cmd = new Command("ipfwd", add ? "add" : "remove", fromIface, toIface);
try {
mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}

在调用turnOnMasterTetherSettings时, 执行了tether start命令, 该命令会启动dnsmasq服务.

dnsmasq启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
M00AE64 06-29 08:31:02.445  7182  7182 I dnsmasq : started, version 2.51 cachesize 150
M00AE65 06-29 08:31:02.445 7182 7182 I dnsmasq : compile time options: IPv6 GNU-getopt no-I18N DHCP no-scripts
M00AE66 06-29 08:31:02.445 7182 7182 W dnsmasq : warning: no upstream servers configured
M00AE67 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.0.2 -- 192.168.0.254, lease time 1h
M00AE68 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.137.2 -- 192.168.137.254, lease time 1h
M00AE69 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.49.2 -- 192.168.49.254, lease time 1h
M00AE6A 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.48.2 -- 192.168.48.254, lease time 1h
M00AE6B 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.47.2 -- 192.168.47.254, lease time 1h
M00AE6C 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.46.2 -- 192.168.46.254, lease time 1h
M00AE6D 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.45.2 -- 192.168.45.254, lease time 1h
M00AE6E 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.44.2 -- 192.168.44.254, lease time 1h
M00AE6F 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.43.2 -- 192.168.43.254, lease time 1h
M00AE70 06-29 08:31:02.446 7182 7182 I dnsmasq : DHCP, IP range 192.168.42.2 -- 192.168.42.254, lease time 1h
M00AE71 06-29 08:31:02.447 7182 7182 I dnsmasq : read /etc/hosts - 2 addresses
3.3.3.3.2. 涉及到的网络知识
  • tether interface add usb0

    将interface写到dnsmasq update_ifaces更新列表中, 使dnsmasq监听该新加的interface. 并在后面分配ip地址.

  • network interface add local usb0

    添加usb0 dev网卡设备到 local路由表中

  • network route add local usb0 192.168.42.0/24

    为usb0网卡在local表中添加路由规则

    1
    2
    3
    4
    5
    sp9853i_1h10:/ # busybox route -n 
    Kernel IP routing table
    Destination Gateway Genmask Flags Metric Ref Use Iface
    192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 wlan0
    192.168.42.0 0.0.0.0 255.255.255.0 U 0 0 0 usb0
  • ipfwd enable tethering

    出于安全考虑,Linux系统默认是禁止数据包转发的。

    所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的ip地址将数据包发往本机另一块网卡,该网卡根据路由表继续发送数据包。这通常是路由器所要实现的功能。

    启用ip转发功能, echo 1 > IPV4_FORWARDING_PROC_FILE

    1
    const char IPV4_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv4/ip_forward";
  • tether start 192.168.42.2 192.168.42.254 192.168.43.2 192.168.43.254 192.168.44.2 192.168.44.254 192.168.45.2 192.168.45.254 192.168.46.2 192.168.46.254 192.168.47.2 192.168.47.254 192.168.48.2 192.168.48.254 192.168.49.2 192.168.49.254 192.168.137.2 192.168.137.254 192.168.0.2 192.168.0.254

    启动dnsmasq服务, DNSmasq是一个小巧且方便地用于配置DNSDHCP的工具,适用于小型网络,它提供了DNS功能和可选择的DHCP功能。它服务那些只在本地适用的域名,这些域名是不会在全球的DNS服务器中出现的。DHCP服务器和DNS服务器结合,并且允许DHCP分配的地址能在DNS中正常解析,而这些DHCP分配的地址和相关命令可以配置到每台主机中,也可以配置到一台核心设备中(比如路由器),DNSmasq支持静态和动态两种DHCP配置方式。

  • tether dns set 100 192.168.1.1

    设置dnsmasq的dns服务器地址, 缓存DNS

  • nat enable usb0 wlan0 1 192.168.42.0/24

    网络地址转换, pc地址在该网段内

    usb0输入设备 wlan0为输出设备 1代表后面的地址组合只有一个 192.168.42.0/24代表ip地址和子网掩码

    目的是修改来自源设备的数据包,使它看起来是目标设备发出的数据包

    借助于NAT,私有(保留)地址的”内部”网络通过路由器发送数据包时,私有地址被转换成合法的IP地址,一个局域网只需使用少量IP地址即可实现私有地址网络内所有计算机与Internet的通信需求。

    NAT将自动修改IP报文的源IP地址和目的IP地址,Ip地址校验则在NAT处理过程中自动完成。有些应用程序将源IP地址嵌入到IP报文的数据部分中,所以还需要同时对报文的数据部分进行修改,以匹配IP头中已经修改过的源IP地址。否则,在报文数据部分嵌入IP地址的应用程序就不能正常工作

  • ipfwd add usb0 wlan0

    新加一条路由规则. 从usb0来的网转到wlan0去处理.

    路由规则相关案例1

    路由规则相关案例2

3.4. Usb pc-share pc互联网

1
2
3
4
5
6
~$ sudo adb shell busybox route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 usb0
~$ sudo adb shell ip route
192.168.0.0/24 dev usb0 proto kernel scope link src 192.168.0.129

由pc端开启网络共享,给手机使用. pc端对端地址(xp 192.168.0.1), 手机端地址(xp 192.168.0.129),

0000.log|79018| S01155E 07-23 18:12:44.495   732   945 D ConnectivityService: Switching to new default network: NetworkAgentInfo{ ni{[type: USBETHER[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), failover: false, available: true, roaming: false]}  network{100}  nethandle{432902426637}  lp{{InterfaceName: usb0 LinkAddresses: [192.168.0.129/24,]  Routes: [0.0.0.0/0 -> 192.168.0.1 usb0,192.168.0.0/24 -> 0.0.0.0 usb0,] DnsAddresses: [192.168.0.1,] UsePrivateDns: false PrivateDnsServerName: null Domains: null MTU: 0}}  nc{[ Transports: ETHERNET Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN&FOREGROUND&NOT_SUSPENDED Unwanted: ]}  Score{11}  everValidated{false}  lastValidated{false}  created{true} lingering{false} explicitlySelected{false} acceptUnvalidated{false} everCaptivePortalDetected{false} lastCaptivePortalDetected{fals[} clat{null} ]
``: