在上一篇的文章,【Android】局域网屏幕共享与反向控制功能的实现,APP实现了局域网屏幕共享与反向控制的功能,但是发现需要连接电脑才能反向控制,不是很方便,要是连接到单片机的话,也许会方便点,于是,就翻阅了ADB的源码,大概了解了ADB的通信协议,移植到单片机有可行性,于是就开始了无休止的Coding and Debugging,最终,取得了比较理想的成果。
(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,如下图所示,
PLL_VCO = (25M / 25) * 336 = 336M
USBCLK = 336M / 7 = 48M
SYSCLK = 336M / 2 = 168M
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设备信息
在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事务处理
};
底层调用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;
}
标识符常量和数据结构的定义
代码如下(示例):
/* 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线了,直到下次手机重启前都可以进行无线控制。
代码如下(示例):
@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函数进行输出。
代码如下(示例):
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调试模式。这么看来,虽然实现了想要的功能,但是还需要一些先决条件。接下来的工作就是对其进行优化,以及思考如何巧妙地应用到实际生活中。