IOS 自動化探索——使用藍牙實現(xiàn) XCUITest 的 Tap 操作

原文由 自然生長 發(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敬惦、基礎源碼

地址:https://github.com/bonyadmitr/XcodeProjects/tree/de6f1b11b2c847b30b900c8a2539e0a30a06c7ba/_mac/Bluetooth-Keyboard-Emulator

這個項目支持 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ā)大會)>>>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末世舰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子槽卫,更是在濱河造成了極大的恐慌跟压,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晒夹,死亡現(xiàn)場離奇詭異裆馒,居然都是意外死亡,警方通過查閱死者的電腦和手機丐怯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門喷好,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人读跷,你說我怎么就攤上這事梗搅。” “怎么了效览?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵无切,是天一觀的道長。 經(jīng)常有香客問我丐枉,道長哆键,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任瘦锹,我火速辦了婚禮籍嘹,結果婚禮上,老公的妹妹穿的比我還像新娘弯院。我一直安慰自己辱士,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布听绳。 她就那樣靜靜地躺著颂碘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椅挣。 梳的紋絲不亂的頭發(fā)上头岔,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天塔拳,我揣著相機與錄音,去河邊找鬼切油。 笑死蝙斜,一個胖子當著我的面吹牛,可吹牛的內容都是我干的澎胡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼娩鹉,長吁一口氣:“原來是場噩夢啊……” “哼攻谁!你這毒婦竟也來了?” 一聲冷哼從身側響起弯予,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤戚宦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锈嫩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體受楼,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年呼寸,在試婚紗的時候發(fā)現(xiàn)自己被綠了艳汽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡对雪,死狀恐怖河狐,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情瑟捣,我是刑警寧澤馋艺,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站迈套,受9級特大地震影響捐祠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜桑李,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一踱蛀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芙扎,春花似錦星岗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圈浇,卻和暖如春寥掐,著一層夾襖步出監(jiān)牢的瞬間靴寂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工召耘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留百炬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓污它,卻偏偏與公主長得像剖踊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衫贬,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內容