• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

C/C++libusb实现ADB的USB通信功能

武飞扬头像
牛顿的三棱镜
帮助1


前言

最近在查阅ADB源码,对ADB的工作原理有了些了解,于是就想利用libusb实现ADB的部分功能,加深理解。


一、ADB

1、简介

ADB(Android Debug Bridge)即是安卓调试桥,是一个专门为Android系统打造的命令行调试工具。平时我们在电脑上使用的adb.exe就是这个工具,在shell中输入命令实现对手机的各种操作。

2、工作原理

ADB采用C/S架构来设计,C即是Client,客户端,运行在我们的电脑上,S即是Server,服务端,运行在我们的Android手机中。客户端与服务端可以通过有线(USB)和无线(TCP)的方式进行通信。

3、USB通信

(1)数据帧格式

struct amessage {
    uint32_t command; //命令标识符常量
    uint32_t arg0; //参数1
    uint32_t arg1; //参数2
    uint32_t data_length; //有效数据长度(允许为 0)
    uint32_t data_check; //有效数据校验和
    uint32_t magic; //命令标识符校验
};
struct apacket {
    amessage msg; //数据头
    uint8_t data[MAX_PAYLOAD]; //数据
};

(2)设备的ADB接口类

#define ADB_CLASS              0xff
#define ADB_SUBCLASS           0x42
#define ADB_PROTOCOL           0x01

(3)ADB接口使用BULK传输模式

二、libusb

1、简介

libusb是一个用C语言编写的USB接口库,它对底层的驱动代码做了封装,用户只需调用API就能实现USB通信,具有较好的移植性,可跨Linux、Windows、macOS、Android等平台。

2、常用接口

//初始化libusb
int libusb_init(libusb_context **ctx); 
//退出libusb
void libusb_exit(libusb_context *ctx); 
//获取设备列表
ssize_t libusb_get_device_list(libusb_context*ctx, libusb_device ***list); 
//释放设备列表
void libusb_free_device_list(libusb_device **list, int unref_devices); 
//获取设备描述符
int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc); 
//获取配置描述符
int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config);
//通过vid和pid打开设备
libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id, uint16_t product_id);
//注册通信接口
int libusb_claim_interface(libusb_device_handle *dev_handle, int interface_number);
//BULK模式传输
int libusb_bulk_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout);

三、实现步骤

1、编译环境

编译环境使用的是VS2019,解决方案平台为X86

2、添加libusb库

该工程使用的是dll动态链接库
(1)下载libusb库
(2)解压libusb后复制相应环境的libusb.lib到工程目录,并添加libusb.h到工程
(3)在链接器中添加libusb.lib,以Debug为例,添加路径为相对路径,其中 “ . .\” 表示在工程文件(.vcxproj)的上一级目录,“.\”则表示和工程文件(.vcxproj)在同一级目录,如下图所示

学新通
学新通
(4)除以上操作外,还需要添加libusb.dll到输出文件目录下(与生成的.exe同目录)

3、代码分析

(1)匹配ADB接口,一个USB设备可以有多个ADB接口,可以通过ADB接口的类、子类和协议进行判断。

代码如下(示例):

/*
* 函数名         : match_with_interface
* 函数功能		 : 匹配ADB接口
* 输入           : handle:ADB结构体
* 输出         	 : 无
*/
void match_with_interface(adb_handle* handle)
{
	libusb_device** devs;
	libusb_device* dev;
	ssize_t cnt;
	uint8_t num = 0;
	int r;
	cnt = libusb_get_device_list(NULL, &devs); //获取设备列表
	if (cnt < 0) {
		printf("failed to libusb_get_device_list\n");
		return;
	}
	while ((dev = devs[num  ]) != NULL) {			//遍历设备列表
		uint8_t i, j;
		struct libusb_device_descriptor desc;
		struct libusb_config_descriptor* config;
		struct libusb_interface_descriptor interface;
		r = libusb_get_device_descriptor(dev, &desc);	//获取设备描述符
		if (r < 0) {
			printf("failed to get device descriptor\n");
			return;
		}
		r = libusb_get_config_descriptor(dev, 0, &config);	//获取配置描述符
		if (r < 0) {
			printf("failed to get config descriptor\n");
			return;
		}
		for (i = 0; i < config->bNumInterfaces; i  ) {		//遍历设备接口,找到ADB接口;一个设备可有多个不同功能的接口
			for (j = 0; j < config->interface[i].num_altsetting; j  ) {
				if (is_adb_interface(config->interface[i].altsetting[j].bInterfaceClass,
					config->interface[i].altsetting[j].bInterfaceSubClass,
					config->interface[i].altsetting[j].bInterfaceProtocol)) {
					interface = config->interface[i].altsetting[j];		
					if (2 != interface.bNumEndpoints) { //判断端点数是否满足,一个收,一个发
						return;
					}
					if (match_with_endpoint(handle, &interface)) {		//匹配读写端点
						handle->zero_mask = interface.endpoint[0].wMaxPacketSize - 1;	
						handle->dev_handle = libusb_open_device_with_vid_pid(NULL, desc.idVendor, desc.idProduct);	//通过idVendor和idProduct打开设备
						printf("idVendor:x, idProduct:x\n", desc.idVendor, desc.idProduct);
						if (handle->dev_handle != NULL) {
							r = libusb_claim_interface(handle->dev_handle, interface.bInterfaceNumber); //注册ADB接口,只有注册成功才能进行通信
							if (r < 0) {
								printf("*** libusb_claim_interface failed! \n");
								return;
							}
							handle->state = INIT_DONE;
							return;
						}
					}
				}
			}
		}
	}
}

/*
* 函数名         : is_adb_interface
* 函数功能		 : 判断是否是ADB接口
* 输入           : usb_class:USB类;usb_subclass:USB子类;usb_protocol:USB协议
* 输出         	 : 成功:0;失败:-1
*/
static int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol)
{
	if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL) {
		return 1;
	}
	return 0;
}

(2)匹配ADB接口的通信端点,UBS通信就是通过这些端点进行通信的,其实质是FIFO缓冲区,其地址高位为1是输入,否则为输出。

代码如下(示例):

/*
* 函数名         : match_with_endpoint
* 函数功能		 : 匹配端点
* 输入           : handle:ADB结构体;interface:ADB接口号
* 输出         	 : 成功:1;失败:0
*/
int match_with_endpoint(adb_handle* handle, const struct libusb_interface_descriptor* interface)
{
	uint8_t i;
	uint8_t ret = 0;
	for (i = 0; i < interface->bNumEndpoints; i  ) {
		if (interface->endpoint[i].bEndpointAddress & 0x80) {  //端点地址高位为1
			ret |= 1;
			handle->adb_read_pipe = interface->endpoint[i].bEndpointAddress; //读端点地址
		}
		else {
			ret |= 2;
			handle->adb_write_pipe = interface->endpoint[i].bEndpointAddress; //写端点地址
		}
	}
	if (ret == 3) {
		return 1;
	}
	else {
		return 0;
	}
}

(3)底层通信,通过调用libusb_bulk_transfer函数向ADB设备读或写数据,端点传输模式为BULK,块传输。

代码如下(示例):

/*
* 函数名         : usb_read
* 函数功能		 : USB读取数据
* 输入           : data:数据指针; len:数据量
* 输出         	 : 成功:0;失败:-1
*/
int usb_read(adb_handle* handle, void* data, int len)
{
	int ret;
	if (NULL != handle) {
		int xfer = (len > MAX_PAYLOAD) ? MAX_PAYLOAD : len;  //判断缓冲区大小是否超过最大负载
		ret = libusb_bulk_transfer(handle->dev_handle,
			handle->adb_read_pipe,  //读端点地址
			(uint8_t*)data,			//读数据缓冲区
			xfer,					//缓冲区大小
			NULL,
			0);
		if (ret == 0) {
			return 0;
		}
	}
	return -1;
}

/*
* 函数名         : usb_write
* 函数功能		 : USB发送数据
* 输入           : handle:ADB结构体;data:数据指针; len:数据长度
* 输出         	 : 成功:0;失败:-1
*/
int usb_write(adb_handle* handle, const void* data, int len) {
	int ret;
	if (NULL != handle) {
		ret = libusb_bulk_transfer(handle->dev_handle,
			handle->adb_write_pipe,  //写端点地址
			(uint8_t*)data,
			len,
			NULL,
			0);
		if (ret == 0) {
			if (handle->zero_mask && (len & handle->zero_mask) == 0) {  //确保数据发送完
				libusb_bulk_transfer(handle->dev_handle,
					handle->adb_write_pipe,
					(uint8_t*)data,
					0,
					NULL,
					0);
			}
			return 0;
		}
	}
	return -1;
}

(4)通信协议,调用底层通信函数进行读写数据包。

代码如下(示例):

/*
* 函数名         : remote_read
* 函数功能		 : 读取数据包
* 输入           : handle:ADB结构体;p:数据包指针
* 输出         	 : 成功:0;失败:-1
*/
int remote_read(adb_handle* handle, apacket* p)
{
	if (usb_read(handle, &p->msg, sizeof(amessage))) {  //接收数据头
		return -1;
	}
	printf("remote_read p->cmd:x\n", p->msg.command);
	if (check_header(p)) {								//检验数据头
		return -1;
	}
	if (p->msg.data_length) {
		if (usb_read(handle, p->data, p->msg.data_length)) {  //接收数据
			return -1;
		}
		printf("remote_read p->data:%s\n", p->data);
		if (check_data(p)) {							//检验数据
			return -1;
		}
	}
	return 0;
}

/*
* 函数名         : remote_write
* 函数功能		 : 发送数据头和数据
* 输入           : handle:ADB结构体;p:数据包指针
* 输出         	 : 成功:0;失败:-1
*/
int remote_write(adb_handle* handle, apacket* p)
{
	uint16_t len = p->msg.data_length;
	if (usb_write(handle, &p->msg, sizeof(amessage))) {  //发送数据头
		return -1;
	}
	printf("remote_write p->cmd:x\n", p->msg.command);
	if (p->msg.data_length == 0) {
		return 0;
	}
	if (usb_write(handle, p->data, len)) {	//发送数据
		return -1;
	}
	printf("remote_write p->data:%s\n", p->data);
	return 0;
}

(5)USB调试权限申请和ADB命令传输函数。

代码如下(示例):

/*
* 函数名         : send_auth_remote
* 函数功能		 : 发送auth key,向设备申请USB调试权限
* 输入           : handle:ADB结构体;key:auth key(任意字符串即可)
* 输出         	 : 成功:0;失败:-1
*/
int send_auth_remote(adb_handle* handle, const char* key)
{
	int r;
	apacket* p = get_apacket();  //数据包内存分配
	uint16_t len = (uint16_t)strlen(key)   1;
	if (len > (MAX_PAYLOAD - 1)) {
		printf("destination oversized\n");
		return -1;
	}
	do {
		p->msg.command = A_AUTH;
		p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
		p->msg.arg1 = 0;
		p->msg.data_length = len;
		strcpy((char*)p->data, key);
		send_packet_remote(handle, p);  //发送AUTH KEY,向Android设备申请权限
		do {
			memset(p, 0, sizeof(apacket));
			r = remote_read(handle, p);
		} while (!(r == 0));
	} while (!(p->msg.command == A_CNXN));  //阻塞式读取,直到设备应答A_CNXN,表示申请成功
	put_apacket(p); //释放数据包内存
	return 0;
}

/*
* 函数名         : send_cmd_remote
* 函数功能		 : 发送shell命令行
* 输入           : handle:ADB结构体;cmd:命令行字符串
* 输出         	 : 成功:0;失败:-1
*/
int send_cmd_remote(adb_handle* handle, const char* cmd)
{
	apacket* p = get_apacket();
	uint16_t len = (uint16_t)strlen(cmd)   1;
	if (len > (MAX_PAYLOAD - 1)) {
		printf("destination oversized\n");
		return -1;
	}
	p->msg.command = A_OPEN;
	p->msg.arg0 = A_VERSION;
	p->msg.arg1 = 0;
	p->msg.data_length = len;
	strcpy((char*)p->data, cmd);
	send_packet_remote(handle, p);  //发送ADB命令
	do {
		memset(p, 0, sizeof(apacket));
		remote_read(handle, p);
	} while (!(p->msg.command == A_OKAY));  //阻塞式读取,直到设备应答A_OKAY,表示成功执行命令
	put_apacket(p);
	return 0;
}

(6)主函数功能,屏幕输入字符a,发送熄屏命令,手机熄屏。

代码如下(示例):

int main(void)
{
	int r;
	char ch;
	adb_handle* handle = (adb_handle*)malloc(sizeof(adb_handle));
	r = libusb_init(NULL);  //初始化libusb
	if (r < 0) {
		printf("libusb_init faild\r\n");
		return -1;
	}
	match_with_interface(handle);  //匹配ADB接口
	if (handle->state != INIT_DONE) {
		printf("match_with_interface faild\r\n");
		return -1;
	}
	while (1) {
		switch (handle->state) {
			case INIT_DONE:
				printf("start get cmd\n");
				handle->state = SEND_AUTH;
				break;
			case SEND_AUTH: //发送AUTH KEY 申请USB调试权限
				r = send_auth_remote(handle, AUTH_KEY);
				if (r < 0) {
					printf("*** send_auth_remote failed! \n");
					break;
				}
				handle->state = SEND_CMD;
				break;
			case SEND_CMD:
				printf("请输入字符'a'测试:");
				scanf("%c", &ch);
				getchar();
				switch (ch)
				{
					case 'a':
						r = send_cmd_remote(handle, "shell:input keyevent 26"); //熄屏命令
						if (r < 0) {
							printf("get_logcat_remote faild\r\n");
						}
						break;
					default:
						break;
				}
			default:
				break;
		}
	}
	libusb_exit(NULL);
	free(handle);
	return 0;
}

4、功能实现

(1)手机USB连接电脑
(2)电脑上运行程序
(3)手机上确认USB调试授权
(4)在程序屏幕输入字符’a’
(5)手机自动熄屏

注意:手机需要先开启USB调试模式


总结

利用libusb实现ADB的部分功能让我对ADB的工作原理有了更深层次的理解。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcabij
系列文章
更多 icon
同类精品
更多 icon
继续加载