原文由 自然生長 發(fā)表于TesterHome社區(qū),點擊 原文鏈接 可與作者直接交流芽突。
一纬乍、前言
遠程使用 WeTest 和 Testin 的 ios 設備時笔时,總會覺得操作不流暢粒褒,因為它在松開鼠標時斤寂, 手機才會動耿焊。應該是由于 WDA 中是使用 XCUITest 來實現(xiàn) Tap 操作,而它的一次操作需要完整的動作鏈遍搞,不能像 Android 中一樣分解為 Down罗侯、Move、Up 操作溪猿。
最近發(fā)現(xiàn)了一款名叫蟲洞的軟件钩杰,地址:https://er.run/ ,它可以實現(xiàn)流暢的遠程操作诊县。進過研究讲弄,發(fā)現(xiàn)其原理是通過藍牙向手機發(fā)送藍牙鼠標操作信號,從而實現(xiàn)一個指針設備來操控設備依痊。
目前只實現(xiàn)了一個 DEMO避除,在實用性方面還有待提高。
二胸嘁、基礎知識
1瓶摆、藍牙 HID 設備
The Human Interface Device (HID) 定義了藍牙在人機接口設備中的協(xié)議、特征和使用規(guī)程缴渊。典型的應用包括藍牙鼠標赏壹、藍牙鍵盤、藍牙游戲手柄等衔沼。該協(xié)議改編自 USB HID Protocol蝌借。
HID 建立 Control Channel 和 Interrupt Channel 兩個通道,Control Channel 通道主要用于傳輸控制封包指蚁,在這個通道傳輸?shù)姆獍Q為同步封包(synchronous reports)菩佑,Interrupt Channel 通道傳輸?shù)姆獍恍枰_認,所以稱為異步封包(asynchronous reports)凝化。
不同的設備發(fā)送的包是不同的稍坯,下文會列舉鍵盤的包和鼠標的包。
2搓劫、藍牙 SDP 協(xié)議
SDP 協(xié)議讓客戶機的應用程序發(fā)現(xiàn)存在的服務器應用程序提供的服務以及這些服務的屬性瞧哟。SDP 只提供發(fā)現(xiàn)服務的機制,不提供使用這些服務的方法枪向。每個藍牙設備都需要一個 SDP Service勤揩,只做 Client 的藍牙設備除外。
3秘蛔、藍牙鼠標信號
和使用 XCUITest 最大的不同是藍牙鼠標移動的 xy 位置不是屏幕的位置陨亡,而是鼠標當前點的相對位置傍衡,這點對于實現(xiàn) Tap 操作是不利的,因為需要將相對位置轉換成在屏幕中的絕對位置负蠕。
4蛙埂、IOS 中的輔助控制
與藍牙鍵盤不同,支持藍牙鼠標功能是需要主動開啟的遮糖,位置在 設置>輔助功能>觸控>輔助功能绣的,當藍牙鼠標信號成功發(fā)送后,在手機上會出現(xiàn)一個小圓點止吁,在設置>輔助功能>觸控>輔助功能>設備 中也會出現(xiàn)該藍牙設備被辑。
三、具體實現(xiàn)
1敬惦、基礎源碼
這個項目支持 Swift5盼理,但只實現(xiàn)了模擬藍牙鍵盤的功能,只要跑起來后俄删,將手機通過藍牙連接到你的 Mac宏怔,然后就能讀取你 Mac 的鍵盤輸入轉到手機中。PS:Monterey 的藍牙連接似乎有些問題畴椰,ios 設備根本連不上藍牙臊诊。
2、修改 SDP斜脂,加入鼠標 Report 結構
在項目中有個 HIDReportDescriptor.txt 的文件抓艳,可以看到其中有鍵盤的 Report 結構,這是需要加在 sdp 協(xié)議中的帚戳,但它并不是 sdp 協(xié)議文件玷或,真正的 sdp 文件是 SerialPortDictionary.plist。
0206 - HIDDescriptorList > item 0 > item 1 > DataElementValue 中是 HIDReportDescriptor.txt 的 Hex 格式片任,將鼠標的 Report 結構加入其中即可偏友。
3、定義鼠標 Report
我們先看下它已經(jīng)定義好的鍵盤 report对供,需要結合 HIDReportDescriptor.txt 來看位他。
let bytes: [UInt8] = [
0xA1, // 0 DATA | INPUT (HIDP Bluetooth)
0x01, // 1 Report ID
modifier, // 2 Modifier Keys
0x00, // 3 Reserved
keyCode, // 4 Keys ( 6 keys can be held at the same time )
0x00, // 5
0x00, // 6
0x00, // 7
0x00, // 8
0x00, // 9
0x00 // 10 Reserved
]
其中第一個字節(jié) A1 代表它是 HID 設備,第二個字節(jié) Report ID 01 代表是鍵盤产场,可以對應0x85, 0x01, // Report ID (1)
接下來 modifier 代表的是功能鍵鹅髓,可以表示同時按下多個功能鍵,例如 0b1 代表按下 control 健京景,0b10 代表按下 shift 鍵迈勋,0b11 代表同時按下了 control 和 shift 鍵。
下面第 3 個字節(jié)雖然注釋上標記了保留字段醋粟,但其實它是有意義的靡菇,但只在輸出時有用,它表示了一些鎖米愿,比如大小寫鎖和小鍵盤鎖厦凤。
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (Num Lock)
0x29, 0x05, // Usage Maximum (Kana)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
第 4 到 9 字節(jié)代表了按鍵,可以同時按下 6 個鍵育苟,這個項目中只支持 1 個较鼓。
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0x00, // Usage Minimum (0x00)
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
第 10 個字節(jié)是保留字段。
0x05, 0xFF, // Usage Page (Reserved 0xFF)
0x09, 0x03, // Usage (0x03)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
由此可以通過網(wǎng)上找到的鼠標的 Report 結構來寫成項目中的包格式违柏。
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x03, // Usage Maximum (0x03)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x03, // Report Count (3)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x05, // Report Size (5)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x38, // Usage (Wheel)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x03, // Report Count (3)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
可以看出 Report ID 變成了了 2博烂,接下來的 1 個字節(jié)(3+1*5 bit)代表了鼠標的按鍵,最后三個字節(jié)分別代表 X漱竖、Y 和 Wheel 移動的距離禽篱,同時也是帶符號的。
所以馍惹,鼠標的格式可以定義為下:
let bytes: [Int8] = [
// 0b10100001, // 0 DATA | INPUT (HIDP Bluetooth)
-0b01011111,
0x02, // 0 Report ID
Action, // 0 just move 1 left_button 2 middle_button 3 right_button
Rel_X, // x
Rel_Y, // y
0x00 // scroll
]
為了方便表示 xy 的負數(shù)躺率,所以定義成了 Int8 格式,第一個字節(jié)也從 UInt8 的 0xA1 變成了 Int8 格式万矾。
由于原先的 sendBytes 方法只支持 [UInt8] 參數(shù)悼吱,所以需要改成泛型方法 sendBytes(channel: IOBluetoothL2CAPChannel, _ bytes: [T])
4、操作方法
先定義一個 sendMouse 方法
func sendMouse(Action: Int8, Rel_X: Int8, Rel_Y: Int8){
sendData(bytes: hidMouseReport(Action: Action, Rel_X: Rel_X, Rel_Y: Rel_Y))
}
Action 為 0 時是移動光標良狈,Action 為 1 時是按下后添,在按住的過程中,所有操作的 Action 都是 1薪丁,當發(fā)送 Action 為 0 的包時視為松開操作遇西。
如果想快速體驗,可以將鍵盤觸發(fā)綁定發(fā)送此方法窥突,也可以在程序中循環(huán)一段操作努溃。
四、總結
目前只是初步實現(xiàn)了這個想法阻问,后續(xù)還有很多工作要做梧税,比如:
1、實現(xiàn)對外的訪問接口称近,例如實現(xiàn) socket 服務或者 rpc 來讓外部調用控制方法第队。
2、設備管理刨秆,多個藍牙設備之間的控制不能沖突凳谦,同時能識別指定的藍牙設備。
3衡未、封裝操作尸执,將原子操作 sendMouse 封裝成 Down家凯、Move 和 Up 操作,根據(jù)指針的相對位置換算成屏幕的絕對位置如失,這樣就能方便的接到已有的自動化流程中绊诲。
原文由 自然生長 發(fā)表于TesterHome社區(qū),點擊 原文鏈接 可與作者直接交流褪贵。
以上是今天的分享掂之,你學廢了嗎~
想學習更多干貨知識和前沿技術?
想結識測試行業(yè)大咖和業(yè)界精英脆丁?
歡迎關注2022 MTSC大會(第十屆中國互聯(lián)網(wǎng)測試開發(fā)大會)>>>