【单片机】Android手机USB外接STM32单片机通过ADB实现投屏反向控制的功能

Android手机USB外接STM32单片机通过ADB实现投屏反向控制的功能

  • 前言
  • 一、功能演示
  • 二、实现步骤
    • 1、开发环境
    • 2、代码分析
      • (1)USBHost处理函数
      • (2)ADB事务处理函数
      • (3)ADB底层通信函数
      • (4)ADB通信相关数据
  • 三、实现原理
    • 1、APP输出控制信息
    • 2、截取命令行字符串
  • 四、项目地址
  • 总结


前言

在上一篇的文章,【Android】局域网屏幕共享与反向控制功能的实现,APP实现了局域网屏幕共享与反向控制的功能,但是发现需要连接电脑才能反向控制,不是很方便,要是连接到单片机的话,也许会方便点,于是,就翻阅了ADB的源码,大概了解了ADB的通信协议,移植到单片机有可行性,于是就开始了无休止的Coding and Debugging,最终,取得了比较理想的成果。


一、功能演示

二、实现步骤

1、开发环境

(1)STM32使用Keil μVision5开发
(2)MCU型号为STM32F429IGT6
(3)STM32外设驱动库使用标准库,USB库使用ST官方的USB OTG Host and device library
(4)STM32单片机为USB Host,Android手机为USB Device
(5)手机Android版本为5.1.1和6.0
(6)STM32的USB OTG FS时钟频率为48MHz,需要设置系统时钟为168MHz,如下图所示,

【单片机】Android手机USB外接STM32单片机通过ADB实现投屏反向控制的功能_第1张图片
使用外部高速时钟为25MHz

【单片机】Android手机USB外接STM32单片机通过ADB实现投屏反向控制的功能_第2张图片
PLL_VCO = (25M / 25) * 336 = 336M
USBCLK = 336M / 7 = 48M
SYSCLK = 336M / 2 = 168M

2、代码分析

(1)USBHost处理函数

USBH_Init 初始化USB驱动后会进入USBH_Process 处理USB Host的各种状态事务

代码如下(示例):

/**
* @brief  USBH_Process
*         USB Host core main state machine process
* @param  None 
* @retval None
*/
void USBH_Process(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost)
{
  volatile USBH_Status status = USBH_FAIL;
  /* check for Host port events */
  if ((HCD_IsDeviceConnected(pdev) == 0)&& (phost->gState != HOST_IDLE))  //检测主机状态
  {
    if(phost->gState != HOST_DEV_DISCONNECTED) 
    {
      phost->gState = HOST_DEV_DISCONNECTED;   //进入连接失败状态
    }
  }
    
  switch (phost->gState)
  {
  case HOST_IDLE : //空闲
    if (HCD_IsDeviceConnected(pdev))  //判断设备是否已连接
    {
      phost->gState = HOST_DEV_ATTACHED;  //进入连接成功状态
      USB_OTG_BSP_mDelay(100);
    }
    break;
   
  case HOST_DEV_ATTACHED :  //设备已连接
    phost->usr_cb->DeviceAttached();  //usr_cb为用户回调句柄,用于打印相关信息
    phost->Control.hc_num_out = USBH_Alloc_Channel(pdev, 0x00);  //分配控制输出端点号
    phost->Control.hc_num_in = USBH_Alloc_Channel(pdev, 0x80);  //分配控制输入端点号
    /* Reset USB Device */
    if ( HCD_ResetPort(pdev) == 0)  //复位USB设备
    {
      phost->usr_cb->ResetDevice();
      /*  Wait for USB USBH_ISR_PrtEnDisableChange()  
      Host is Now ready to start the Enumeration 
      */
      phost->device_prop.speed = HCD_GetCurrentSpeed(pdev);  //获取USB设备速度类型
      phost->gState = HOST_ENUMERATION;  //进入事务处理状态
      phost->usr_cb->DeviceSpeedDetected(phost->device_prop.speed);
      /* Open Control pipes */
      USBH_Open_Channel (pdev, 							//打开控制输入通道
                           phost->Control.hc_num_in,  	//输入端点号
                           phost->device_prop.address,	//设备地址
                           phost->device_prop.speed,	//设备速度
                           EP_TYPE_CTRL,				//控制传输类型
                           phost->Control.ep0size); 	//数据量大小
      /* Open Control pipes */
      USBH_Open_Channel (pdev,							//打开控制输出通道
                           phost->Control.hc_num_out,
                           phost->device_prop.address,
                           phost->device_prop.speed,
                           EP_TYPE_CTRL,
                           phost->Control.ep0size);          
   }
    break;
    
  case HOST_ENUMERATION:     	//事务处理
    /* Check for enumeration status */  
    if ( USBH_HandleEnum(pdev , phost) == USBH_OK)  //事务处理,用于获取USB的设备、配置描述符等数据
    { 
      /* The function shall return USBH_OK when full enumeration is complete */
      /* user callback for end of device basic enumeration */
      phost->usr_cb->EnumerationDone();
      phost->gState  = HOST_USR_INPUT;    //进入用户输入状态
    }
    break;
    
  case HOST_USR_INPUT:    //用户输入
    /*The function should return user response true to move to class state */
    if ( phost->usr_cb->UserInput() == USBH_USR_RESP_OK)  //无需输入,已设置为自动返回OK
    {													   //class_cb是接口类的回调句柄,用于回调ADB接口类的函数
      if((phost->class_cb->Init(pdev, phost)) == USBH_OK)  //此处执行ADB接口初始化操作,USBH_ADB_InterfaceInit  
      {
        phost->gState  = HOST_CLASS_REQUEST;     //进入类请求状态
      }     
    }   
    break;
    
  case HOST_CLASS_REQUEST:  //类请求
    /* process class standard contol requests state machine */ 
    status = phost->class_cb->Requests(pdev, phost);   //此处执行ADB接口类请求(无需请求),USBH_ADB_ClassRequest 
     if(status == USBH_OK)
     {
       phost->gState  = HOST_CLASS;   //进入类事务处理状态
     }  
     else
     {
       USBH_ErrorHandle(phost, status);    //错误状态处理
     }
    break;    
		 
  case HOST_CLASS:   //类事务处理
    /* process class state machine */
    status = phost->class_cb->Machine(pdev, phost);  //此处执行ADB接口类的事务处理,USBH_ADB_Handle
    USBH_ErrorHandle(phost, status); //错误状态处理
    break;       
    
  case HOST_CTRL_XFER:  //控制传输,与HOST_ENUMERATION交替执行获取设备信息
    /* process control transfer state machine */
    USBH_HandleControl(pdev, phost);     //对控制端点的数据进行传输
    break;
    
  case HOST_SUSPENDED:  //挂起
    break;
  
  case HOST_ERROR_STATE:  //错误状态
    /* Re-Initilaize Host for new Enumeration */
    USBH_DeInit(pdev, phost);   //失能USB
    phost->usr_cb->DeInit();  
    phost->class_cb->DeInit(pdev, &phost->device_prop);  //此处执行ADB接口的失能处理,USBH_ADB_InterfaceDeInit  
    break;
    
  case HOST_DEV_DISCONNECTED:   //设备连接失败
    /* Manage User disconnect operations*/
    phost->usr_cb->DeviceDisconnected();
    /* Re-Initilaize Host for new Enumeration */
    USBH_DeInit(pdev, phost);  //失能USB
    phost->usr_cb->DeInit();
    phost->class_cb->DeInit(pdev, &phost->device_prop);   //此处执行ADB接口的失能处理,USBH_ADB_InterfaceDeInit  
    USBH_DeAllocate_AllChannel(pdev);  //清除所有端点
    phost->gState = HOST_IDLE;  //进入空闲状态
    break;
    
  default :
    break;
  }
}

注意:usbh_conf.h中的USBH_MAX_NUM_INTERFACES需要修改,否则无法获取到USB设备信息

【单片机】Android手机USB外接STM32单片机通过ADB实现投屏反向控制的功能_第3张图片

(2)ADB事务处理函数

在USBH_Process 函数中会执行ADB接口的相关事务函数

代码如下(示例):

/**
  * @brief  USBH_ADB_InterfaceInit 
  *         Interface initialization for ADB class.
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval USBH_Status : Status of class request handled.
  */
static USBH_Status USBH_ADB_InterfaceInit  (USB_OTG_CORE_HANDLE *pdev, void *phost)
{
	USBH_HOST *pphost = phost;
	uint8_t i, j;
	for (i = 0; i < pphost->device_prop.Cfg_Desc.bNumInterfaces; i++)  	//遍历设备接口号
	{
		if((pphost->device_prop.Itf_Desc[i].bInterfaceClass == ADB_CLASS) && \
			(pphost->device_prop.Itf_Desc[i].bInterfaceSubClass == ADB_SUBCLASS)  && \
			(pphost->device_prop.Itf_Desc[i].bInterfaceProtocol == ADB_PROTOCOL))   //匹配ADB接口
		{
			for (j = 0; j < pphost->device_prop.Itf_Desc[i].bNumEndpoints; j++)   //遍历端点号
			{
				if (pphost->device_prop.Ep_Desc[i][j].bEndpointAddress & 0x80)    //匹配ADB输入端点,高位为1是输入
				{
					ADB_Machine.BulkInEp = pphost->device_prop.Ep_Desc[i][j].bEndpointAddress;   //获取端点地址
					ADB_Machine.BulkInEpSize  = pphost->device_prop.Ep_Desc[i][j].wMaxPacketSize; //获取端点最大数据容量
				}
				else
				{
					ADB_Machine.BulkOutEp = pphost->device_prop.Ep_Desc[i][j].bEndpointAddress;  //匹配ADB输出端点
					ADB_Machine.BulkOutEpSize  = pphost->device_prop.Ep_Desc[i][j].wMaxPacketSize;
					ADB_Machine.zero_mask = ADB_Machine.BulkOutEpSize - 1;  //零掩码,用于检验数据传输完毕
				}
			}
			ADB_Machine.hc_num_out = USBH_Alloc_Channel(pdev, ADB_Machine.BulkOutEp);   //用端点地址匹配端点号
			ADB_Machine.hc_num_in = USBH_Alloc_Channel(pdev, ADB_Machine.BulkInEp);  
			USBH_Open_Channel(pdev,							//打开ADB输出端点
							  ADB_Machine.hc_num_out,
							  pphost->device_prop.address,
							  pphost->device_prop.speed,
							  EP_TYPE_BULK,					//BULK传输模式
							  ADB_Machine.BulkOutEpSize);  
			USBH_Open_Channel(pdev,							//打开ADB输入端点
							  ADB_Machine.hc_num_in,
							  pphost->device_prop.address,
							  pphost->device_prop.speed,
							  EP_TYPE_BULK,
							  ADB_Machine.BulkInEpSize);   
			ADB_Machine.state = ADB_SEND_AUTH;  //进入发送AUTH KEY状态(向设备申请USB调试权限)
			USB_DEBUG("> USB_ADB_Init\r\n");
			return USBH_OK;
		}
	}
	return USBH_FAIL;
}

/**
  * @brief  USBH_ADB_InterfaceDeInit 
  *         De-Initialize interface by freeing host channels allocated to interface
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval None
  */
static void USBH_ADB_InterfaceDeInit  (USB_OTG_CORE_HANDLE *pdev, void *phost)
{
	if (ADB_Machine.hc_num_out)
  {
    USB_OTG_HC_Halt(pdev, ADB_Machine.hc_num_out);    //停止ADB输出端点传输
    USBH_Free_Channel(pdev, ADB_Machine.hc_num_out);  //释放ADB输出端点传输
    ADB_Machine.hc_num_out = 0;
  }
   
  if (ADB_Machine.hc_num_in)
  {
    USB_OTG_HC_Halt(pdev, ADB_Machine.hc_num_in);		//停止ADB输入端点传输
    USBH_Free_Channel(pdev, ADB_Machine.hc_num_in);	    //释放ADB输出端点传输
    ADB_Machine.hc_num_in = 0;
  } 
	USB_DEBUG("> USB_ADB_Deinit\r\n");
}

/**
  * @brief  USBH_ADB_ClassRequest 
  *         This function will only initialize the ADB state machine
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval USBH_Status : Status of class request handled.
  */
static USBH_Status USBH_ADB_ClassRequest(USB_OTG_CORE_HANDLE *pdev, void *phost)
{
	USBH_Status status = USBH_OK;  //ADB类无需进行请求
	USB_DEBUG("> USB_ADB_ClassRequest\r\n");
	return status;
}

/**
  * @brief  USBH_ADB_Handle 
  *         ADB state machine handler 
  * @param  pdev: Selected device
  * @param  phost: Selected device property
  * @retval USBH_Status
  */
static USBH_Status USBH_ADB_Handle(USB_OTG_CORE_HANDLE *pdev, void *phost)
{
	USBH_Status status = USBH_BUSY;
	
	switch (ADB_Machine.state)
	{
		case ADB_SEND_AUTH:    //发送AUTH KEY
			USB_DEBUG("> USB_ADB_SEND_AUTH\r\n");
			if (USBH_OK == USBH_ADB_SEND_AUTH(pdev))
			{
				ADB_Machine.state = ADB_SEND_CMD;   //进入命令行发送状态
			}
			break;
			
		case ADB_SEND_CMD:   //发送SHELL命令行
			USB_DEBUG("> USB_ADB_SEND_CMD\r\n");
			if (USBH_OK == USBH_ADB_SEND_CMD(pdev))
			{
				ADB_Machine.state = ADB_IDLE;   //进入空闲状态
				USB_DEBUG("> USB_ADB_SEND_CMD_SUCCESS\r\n");
			}
			break;
		
		default:
			break;
	}
	status = USBH_OK;
	return status;
}

/*以上函数通过ADB接口类句柄进行调用*/
USBH_Class_cb_TypeDef USBH_ADB_cb =  //ADB接口句柄
{
  USBH_ADB_InterfaceInit,  //ADB接口初始化
  USBH_ADB_InterfaceDeInit,	//ADB接口失能
  USBH_ADB_ClassRequest,  //ADB类请求
  USBH_ADB_Handle,		 //ADB事务处理
};

(3)ADB底层通信函数

底层调用USBH_BulkReceiveData和USBH_BulkSendData函数收发数据

代码如下(示例):

/**
  * @brief  get_apacket 
  *         ADB通信数据包动态分配内存 
  * @param  None
  * @retval 数据包指针
  */
static apacket* get_apacket(void)
{
	apacket* p = (apacket*)malloc(sizeof(apacket));
	memset(p, 0, sizeof(apacket));
	return p;
}

/**
  * @brief  put_apacket 
  *         ADB通信数据包内存释放
	* @param  p: 数据包指针
  * @retval None
  */
static void put_apacket(apacket* p)
{
	free(p);
}

/**
  * @brief  usb_read 
  *         USB读取数据
	* @param  pdev: USB设备句柄
	* @param  data: 数据指针
	* @param  p: 数据长度
	* @retval 成功: 0 失败: -1
  */
static int usb_read(USB_OTG_CORE_HANDLE *pdev, void* data, int len)
{
	if (pdev != NULL)
	{
		int xfer = (len > MAX_PAYLOAD) ? MAX_PAYLOAD : len;
		do
		{
			USBH_BulkReceiveData(pdev, (uint8_t*)data, xfer, ADB_Machine.hc_num_in);  
			USB_OTG_BSP_mDelay(50);  //加上延时,能读取(还待优化)
		}
		while (!(pdev->host.URB_State[ADB_Machine.hc_num_in] == URB_DONE));  //阻塞式读取
		return 0;
	}
	return -1;
}

/**
  * @brief  check_header 
  *         检验接收到的数据头
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int check_header(apacket* p)
{
	if (p->msg.magic != (p->msg.command ^ 0xffffffff))  //校验是否为4字节的标识符
	{
		return -1;
	}
	if (p->msg.data_length > MAX_PAYLOAD)  //检验数据长度
	{
		return -1;
	}
	return 0;
}

/**
  * @brief  check_data 
  *         检验接收到的数据体
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int check_data(apacket* p)
{
	uint8_t* x;
	uint16_t count, sum;
	count = p->msg.data_length;
	x = p->data;
	for (sum = 0; count > 0; count--)
	{
		sum += *x++;
	}
	if (sum != p->msg.data_check)  //校验数据体
	{
		return -1;
	}
	return 0;
}

/**
  * @brief  remote_read 
  *         读取数据包
	* @param  pdev: USB设备句柄
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int remote_read(USB_OTG_CORE_HANDLE *pdev, apacket* p)
{
	if (usb_read(pdev, &p->msg, sizeof(amessage)))  //读取数据头
	{
		return -1;
	}
	USB_OTG_BSP_mDelay(10);
//	printf("remote_read p->cmd:%04x\r\n", p->msg.command);
	if (check_header(p))  //校验数据头
	{
		return -1;
	}
	if (p->msg.data_length)
	{
		if (usb_read(pdev, p->data, p->msg.data_length))  //读取数据体
		{
			return -1;
		}
		USB_OTG_BSP_mDelay(10);
//		printf("remote_read p->data:%s\r\n", p->data);
		if (check_data(p))   //校验数据体
		{
			return -1;
		}
	}
	return 0;
}

/**
  * @brief  usb_write 
  *         USB写数据
	* @param  pdev: USB设备句柄
	* @param  data: 数据指针
	* @param  len:  数据长度
	* @retval 成功: 0 失败: -1
  */
static int usb_write(USB_OTG_CORE_HANDLE *pdev, const void* data, int len)
{
	if (pdev != NULL)
	{
		USBH_BulkSendData(pdev, (uint8_t*)data, len, ADB_Machine.hc_num_out);  //发送数据
		if (ADB_Machine.zero_mask && (len & ADB_Machine.zero_mask) == 0)  //检验数据是否发送完毕
		{
			USBH_BulkSendData(pdev, (uint8_t*)data, 0, ADB_Machine.hc_num_out);
		}
		return 0;
	}
	return -1;
}


/**
  * @brief  remote_write 
  *         写数据包
	* @param  pdev: USB设备句柄
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int remote_write(USB_OTG_CORE_HANDLE *pdev, apacket* p)
{
	uint16_t len = p->msg.data_length;
	if (usb_write(pdev, &p->msg, sizeof(amessage)))  //写数据头
	{
		return -1;
	}
	USB_OTG_BSP_mDelay(10);  //需要延时,区分数据头和数据体
//	printf("remote_write p->cmd:%04x\r\n", p->msg.command);
	if (p->msg.data_length == 0)
	{
		return 0;
	}
	if (usb_write(pdev, p->data, len))   //写数据体
	{
		return -1;
	}
	USB_OTG_BSP_mDelay(10);
//	printf("remote_write p->data:%s\r\n", p->data);
	return 0;
}


/**
  * @brief  send_packet_remote 
  *         发送数据包
	* @param  pdev: USB设备句柄
	* @param  p: 数据包指针
	* @retval 成功: 0 失败: -1
  */
static int send_packet_remote(USB_OTG_CORE_HANDLE *pdev, apacket* p)
{
	uint8_t* x;
	uint16_t count, sum;
	p->msg.magic = p->msg.command ^ 0xffffffff; //标识符校验
	count = p->msg.data_length;
	x = p->data;
	for (sum = 0; count > 0; count--)  //数据体校验
	{
		sum += *x++;
	}
	p->msg.data_check = sum;
	if (remote_write(pdev, p))
	{
		return -1;
	}
	return 0;
}


/**
  * @brief  send_auth_remote 
  *         发送AUTH KEY,向设备申请USB调试权限
	* @param  pdev: USB设备句柄
	* @param  key: AUTH KEY(任意字符串)
	* @retval 成功: 0 失败: -1
  */
static int send_auth_remote(USB_OTG_CORE_HANDLE *pdev, const char* key)
{
	int r;
	uint16_t len;
	apacket* p = get_apacket();
	len = (uint16_t)strlen(key) + 1;
	if (len > (MAX_PAYLOAD - 1))
	{
		printf("destination oversized\r\n");
		put_apacket(p);
		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(pdev, p);  //发送数据包
		do
		{
			memset(p, 0, sizeof(apacket));
			r = remote_read(pdev, p);   //接收数据包
		}
		while (!(r == 0));
	}
	while (!(p->msg.command == A_CNXN));  //阻塞式发送,直到接收到A_CNXN才结束
	put_apacket(p);
	return 0;
}


/**
  * @brief  send_cmd_remote 
  *         发送SHELL命令行
	* @param  pdev: USB设备句柄
	* @param  cmd: 命令行字符串
	* @retval 成功: 0 失败: -1
  */
static int send_cmd_remote(USB_OTG_CORE_HANDLE *pdev, const char* cmd)
{
	uint16_t len;
	apacket* p = get_apacket();
	len = (uint16_t)strlen(cmd) + 1;
	if (len > (MAX_PAYLOAD - 1))
	{
		printf("destination oversized\r\n");
		put_apacket(p);
		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(pdev, p);  //发送数据包
	do
	{
		memset(p, 0, sizeof(apacket));
		remote_read(pdev, p);   //接收数据包
	}
	while (!(p->msg.command == A_WRTE));  //阻塞式读取,直到读取到A_WRTE才结束
	put_apacket(p);
	return 0;
}

/**
  * @brief  USBH_ADB_SEND_AUTH 
  *         send_auth_remote
	* @param  pdev: USB设备句柄
	* @retval 成功: 0 失败: -1
  */
static USBH_Status USBH_ADB_SEND_AUTH(USB_OTG_CORE_HANDLE *pdev)
{
	if (send_auth_remote(pdev, AUTH_KEY))
	{
		return USBH_FAIL;
	}
	return USBH_OK;
}

/**
  * @brief  USBH_ADB_SEND_CMD 
  *         send_cmd_remote
	* @param  pdev: USB设备句柄
	* @retval 成功: 0 失败: -1
  */
static USBH_Status USBH_ADB_SEND_CMD(USB_OTG_CORE_HANDLE *pdev)
{
	if (send_cmd_remote(pdev, TOUCH_CMD))
	{
		return USBH_FAIL;
	}
	return USBH_OK;
}

(4)ADB通信相关数据

标识符常量和数据结构的定义

代码如下(示例):

/* ADB通信数据量最大负载 */
#define MAX_PAYLOAD 255

/* ADB数据头标识符 */
#define A_SYNC 0x434e5953
#define A_CNXN 0x4e584e43
#define A_OPEN 0x4e45504f
#define A_OKAY 0x59414b4f
#define A_CLSE 0x45534c43
#define A_WRTE 0x45545257
#define A_AUTH 0x48545541

/* ADB版本,任意取值 */
#define A_VERSION 0x01

/* ADB AUTH KEY 参数 */
#define ADB_AUTH_RSAPUBLICKEY  0x03

/* ADB接口类 */
#define ADB_CLASS              0xff
#define ADB_SUBCLASS           0x42
#define ADB_PROTOCOL           0x01

/* AUTH KEY, 任意字符串 */
static const char* AUTH_KEY = "ADB AUTH RSAPUBLICKEY";
/* SHELL CMD */
static const char* TOUCH_CMD = "shell:while (true); do r=`logcat -t 1 | grep input`;  if [ $? -eq 0 ]; then echo $r; logcat -c; ${r#*@}; fi; done &";

typedef struct amessage amessage;
typedef struct apacket apacket;
typedef enum ADB_State ADB_State;
typedef struct _ADB_Process ADB_Machine_TypeDef;

/* ADB数据头结构体 */
struct amessage {
	uint32_t command;   	//数据头标识符
	uint32_t arg0;      	//参数1   
	uint32_t arg1;      	//参数2    
	uint32_t data_length;   //数据体长度
	uint32_t data_check;    //数据体校验
	uint32_t magic;         //数据头校验
};

/* ADB数据包结构体 */
struct apacket {
	amessage msg;  				//数据头
	uint8_t data[MAX_PAYLOAD];  //数据体
};

/* ADB状态枚举 */
enum ADB_State {
  ADB_IDLE = 0, 	//空闲
  ADB_SEND_AUTH, 	//申请权限
  ADB_SEND_CMD,  	//发送命令行
  ADB_ERROR,     	//错误
};

/* ADB接口结构体 */
struct _ADB_Process
{
  uint8_t   hc_num_in;   		//输入端点号
  uint8_t   hc_num_out;  		//输出端点号
  uint8_t   BulkInEp;			//输入端点
  uint8_t   BulkOutEp;  		//输出端点
  uint16_t  BulkInEpSize;	 	//输入端点大小
  uint16_t  BulkOutEpSize;	    //输出端点大小
  uint16_t  zero_mask;			//零掩码
  ADB_State state;      		//ADB状态
}; 

三、实现原理

其实Android的底层系统是Linux,而ADB就是与底层的Linux系统进行通信,发送的命令行也是让Linux去执行。那APP怎样与底层的Linux进行通信呢?这里用到了一个比较巧妙的方法了,没错,就是Log,日志信息,我们在APP的服务端用Log函数输出客户端反向控制的信息,然后用单片机发送捕获这个Log的命令(logcat)给服务端底层的Linux系统去执行,捕获到反向控制的Log,进一步处理成可执行的命令(input),再去执行,这样就实现了反向控制的功能啦,说实话,这样是不是觉得STM32就有点大才小用了呢?但是不要忘记了,Android的底层可是Linux系统,只要我们让它后台执行这个捕获Log的脚本就可以拔掉USB线了,直到下次手机重启前都可以进行无线控制。

1、APP输出控制信息

代码如下(示例):

@Override
public void onServerReceiveTouchPosition(int staX, int staY, int endX, int endY, int keyC) {
 if (keyC == KeyEvent.KEYCODE_BACK) {
      Log.i(TAG, "@" + "input keyevent 4");  //返回键
  } else if ((staX - endX) == 0 && (staY - endY) == 0) {
      Log.i(TAG, "@" + "input tap" + " " + staX + " " + staY);   //点击(X,Y)
  } else if (Math.abs(staX - endX) > 100 || Math.abs(staY - endY) > 100) {
      Log.i(TAG, "@" + "input swipe" + " " + staX + " " + staY + " " + endX + " " + endY);  //滑动(X1,Y1,X2,Y2)
  }
}

在APP服务端上设置有接收客户端控制信息的侦听器,客户端的控制信息为触摸屏幕起始位置的X和Y坐标,通过逻辑判断确定控制的操作是点击还是滑动或返回,进一步打包ADB命令,通过Log函数进行输出。

2、截取命令行字符串

代码如下(示例):

shell:while (true); do r=`logcat -t 1 | grep input`;  if [ $? -eq 0 ]; then echo $r; logcat -c; ${r#*@}; fi; done &

该shell脚本由STM32发送给Android手机底层的Linux系统运行,它会在后台一直捕获APP的Log信息,由于Log输出的信息中字符@后才是要执行的ADB命令,所以我们只需要截取@右边的字符串,截取后直接输出即可执行,即通过ADB命令实现反向控制。

工作原理:
(1)shell:表示该命令是shell脚本。
(2)logcat:ADB打印日志命令,logcat -t 1 | grep input 打印最新一条含input的日志,过滤出需要的日志信息,并将结果赋给变量r。
(3)input:ADB模拟控制命令,可实现模拟按键、点击滑动或输入文本等功能,极其强大。
(4)$? -eq 0:判断上一条命令是否执行成功, $?表示上一条命令执行结果,若过滤到含input的日志,则执行成功,返回0,-eq表示等于。
(5)logcat -c:清空日志缓存,否则会重复捕获到相同的日志信息。
(6) ${r#*@}:字符串截取,截取@右边的全部字符串,不包含@,即ADB模拟控制命令,截取后直接执行。
(7)&:脚本末尾加&表示后台运行。

下面的脚本是进一步改进的,APP退出后脚本失效

代码如下(示例):

while [[ true ]];do 
	r=`logcat -t 1 | grep -E 'input|Killing'`; #过滤input:控制信息;Killing:APP退出
	if [[ $? -eq 0 ]];then if [[ $r == *'screenprojection'* ]];then  #screenprojection为APP名
		exit; #后台清除APP时会有含Killing和screenprojection的日志信息,借此退出脚本,即APP退出后脚本失效
	fi;
	if [[ $r == *'@input'* ]];then 
		${r#*@}; #截取ADB控制命令
	fi;
	logcat -c; #清除日志,避免重复
fi;
done &

四、项目地址

Screen Projection
RemoteAssistance


总结

现在实现的大致功能是,可以通过一台手机投屏给另一台手机,并且被投屏的手机可以控制投屏的手机,但是要实现这个控制的功能,需要至少一次USB连接单片机来获取权限,并且投屏的手机需要开启USB调试模式。这么看来,虽然实现了想要的功能,但是还需要一些先决条件。接下来的工作就是对其进行优化,以及思考如何巧妙地应用到实际生活中。

你可能感兴趣的:(Android,ADB,单片机,android,stm32)