淺談「Companion Device Pairing」

Android:Android APPs

RICK
11 min readJul 27, 2020

目標

學習如何使用「Companion Device Pairing」。

概要

Companion Device Pairing,是一組關於「藍牙」、「BLE」、「WLAN」…等設備的「新配對方式」,它僅能在執行於「Android 8.0,API 26」以後的 Android 設備,詳細介紹可以參考「Android Dev Summit ‘19」。

在官方文件的說明中,它不僅是擁有更佳的隱私權與安全性,它亦解決困擾多數開發者許久的「位置權限問題」以及在「Android 8.0」以後,背景執行受到限制的問題。

正文

權限問題

在過去,若我們要「發現藍牙設備」,我們需要獲得除了「BLUETOOTH」以外的兩個權限,分別是「BLUETOOTH_ADMIN」,如下:

以及「LOCATION」,如下:

備註:「ACCESS_COARSE_LOCATION」與「ACCESS_FINE_LOCATION」皆屬於「LOCATION」權限組;而藍牙功能所需求的「LOCATION」權限亦會隨著版本不同而有所差異。

BLUETOOTH_ADMIN」的存取,不僅賦予應用程式掃描的功能,同時它亦提供了應用程式更多的權限,包含讀、寫…等。

而「LOCATION」權限的存取就更令人匪夷所思了,筆者認為若是「WiFi」相關能夠理解,但為什麼僅是「發現藍牙設備」,卻需要擁有位置的權限?

事實上,該議題曾被議論過,例如「Why Location permission are required for Ble scan in android Marshmallow onwards」、「Location needs to be enabled for Bluetooth Low Energy Scanning on Android 6.0」…等;甚至有不少開發人員認為這並不合理,覺得這是「Google」的錯誤。

而「Google」的回覆始終都堅持他們並沒有錯,「LOCATION」權限的存取是必要的,呵呵。

不論你的立場如何?至少「Google」推出「Companion Device Pairing」來替眾開發者解套。

在「Companion Device Pairing」的說明文件上,它明確的指出這件事情,如下圖:

藍牙概覽中,也有這樣的描述:

背景執行受到限制的問題

曾經,「Android」四大元件之一的「Service」是至高天,曾經的它,強大無比。

但是由於「安全性」與「隱私權」的抬頭,「Service」逐漸跌落神壇,一路從「Android 6.0,API 23」開始,到「Android 8.0, API 26」的致命一擊,如下圖:

詳細可以參考官方文件:「Background Execution Limits」和「Background Location Limits」。

然而,已經殘廢的「Service」並有沒有就此雨過天晴,「Android 10,API 29」,又是一刀。

連曾經紅極一時的「Service」子類「IntentService」在即將到來的「API 30」也要被廢棄了,如下:

事實上,關於「Service」的改動並不僅於上述,但這並非本文的主要,筆者就不再過多描述。

然而,由於「後台背景程序的限制」改動,導致現在「無法從『Receiver』喚起『Service』」,但這對許多藍牙裝置並不友善,例如藍牙音響、藍牙耳機、藍牙鍵盤與滑鼠,甚至是一些藍牙類型的穿戴式裝置。

同樣地,「Companion Device Pairing」也為其解套。

開發者們可藉由宣告「REQUEST_COMPANION_RUN_IN_BACKGROUND」和「REQUEST_COMPANION_USE_DATA_IN_BACKGROUND」的權限,來執行部份關於背景相關的操作。

容易地與設備關聯與與解除關聯

在過去,若我們要與藍牙設備關聯或解除關聯,常可能因為思慮不周導致非預期的情況。

而在「Companion Device Pairing」的官方文件中就提到,開發者們可以簡單的藉由呼叫「CompanionDeviceManager」類別中的「associate()」方法以及「disassociate()」方法來與「藍牙設備」關聯與解除關聯。

實作「Companion Device Pairing」

宣告「Permission」

雖然「CDP」不需要「BLUETOOTH_ADMIN」和「LOCATION」權限,但它能需要其它基本權限。

例如實作「WLAN」就需要網路及其相關的權限,而本文範為藍牙設備,因此,我們就需要在「Manifest」清單中聲明「BLUETOOTH」權限,如下:

宣告「feature」

要實作「Companion Device Pairing」的功能,首先要在「Manifest」清單中聲明「android.software.companion_device_setup」特性,如下:

取得「CompanionDeviceManager

完成了上述的聲明後,我們就可以在開始撰寫程式碼了。

首先,要實作「CDP」,就必須先取得「CompanionDeviceManager」,取得方式如下:

關聯設備

「CDP」中,用來關聯「設備」的方法是「associate()」,如下:

CompanionDeviceManager#associate()」總共需要傳入三個參數,其分別是「AssociationRequest」、「CompanionDeviceManager.Callback」以及「Handler」,如下:

首先是「AssociationRequest」,該參數是設置藍牙設備的配對詳細資訊,若要取得該類別,需要透過其「Builder」,如下:

而在「Builder」中,有兩個重要的設定方法,分別為「addDeviceFilter()」以及「setSingleDevice()」。

而「addDeviceFilter()」中,需要傳入一個參數「DeviceFilter」,該參數適用於特定配對,換句話說,我們利用該參數來過濾出我們所需要的設備。

若要取得「DeviceFilter」,我們必須藉由「BluetoothDeviceFilter.Builder」來建立,其可設定過濾的條件,包含設備名稱、設備位址或 UUID,如下:

備註:建立「DeviceFilter」時,開發者可以依照自己的需求設定過濾條件,並不需要將所有條件都進行設定。

該參數可以為「null」,其代表著「不進行任何過濾」,如下:

設定「DeviceFilter」後,「BluetoothDeviceFilter.Builder」還有另外一個方法需要設定,即「setSingleDevice()」。

「setSingleDevice()」需要傳入的參數為布林值,該設定關係到「CDP」彈出時,設備清單上的設備數量,「true」為僅顯示一台設備,程式碼如下:

畫面顯示效果如下:

而「false」則是多台設備,畫面顯示效果如下:

接著是「CompanionDeviceManager#associate()」的第二個參數,與上述的第一個參數「AssociationRequest」一樣,其值不能為「null」。

其第二個參數為「Callback」,「CompanionDeviceManager.Callback」,由此可知,該方法是一個異步調用。

在該「Callback」中,有兩個方法必須去實作,分別為「onDeviceFound()」以及「onFailure()」,前者是當設備被查找到時的處理,後者是查找失敗的處理,實作如下:

「onDeviceFound()」傳入的是「IntentSender」,因此,我們通常會再藉由「StartIntentSenderForResult」方法將它傳至「onActivityResult」,如下:

事實上,這裡的處理方式跟傳統藍牙設備的「BroadcastReceiver」很類似,透過「CompanionDeviceManager.EXTRA_DEVICE」來取得我們選擇的設備。

至於該設備會是怎樣的型態,則是取決於我們所選擇的設備項目,如下:

備註:「createBond()」需要「BLUETOOTH_ADMIN」權限。

最後是「CompanionDeviceManager#associate()」的第三個參數。

事實上,該參數只是個「Handler」,如敘述所說,我們不應該在「Main Thread」上執行任何非「UI」相關的任務,尤其是耗時任務。

由於只是範例程式碼,因此筆者沒有實作這部份,而是放入「null」,但在專案上,請盡量避免這樣的程式撰寫。

總結

相較於藍牙舊有的撰寫方式,「Companion Device Pairing」是屬於比較新的機制,它不僅提供了一項設備配對的新選擇。

更重要的是,在「Companion Device Pairing」的出現後,以往困擾開發者許久的「權限問題」及「後台作業限制」終於有了解套的作法。

無法否認的是它仍有著不少限制,但在「注重隱私權及安全性」的現今,這幾乎是必然的結果。

其它

或許該機制比較新,可能並非所有設備都可以使用。

事實上,本來筆者是要將此應用於「Android TV APPs」的開發上,但是卻發現筆者所使用的設備並不支援該特性。

其打印的結果為「False」。

順帶一提,筆者所使用的設備為「ADT-3」。

起初,筆者懷疑是因為「Android TV」的原因;廢話不多說,規格的事情,就找「Android Compatibility Definition Document」。

ADT-3」是「Android 10」的設備,參考文件「連結」,如下圖:

根據文件項目「3.16. Companion Device Pairing」:

其「Device Type Id」為「C」,「C」是「core」,如下:

因此,根據其規格文件的描述,SDK 版本為10 的所有「Android」設備都應該要具備此功能。

此外,筆者在搜尋時,也搜尋到其「CTS」相關的「源碼」,如下:

目前它似乎是被歸類在「CTS Verifier」。

至於,為什麼「ADT-3」不具備該功能?這筆者無法解釋,就目前所知,筆者認為其應該具備「CDP」功能。

完整程式碼連結:My GitHub

--

--

RICK
RICK

Written by RICK

當遇到重開機無法解決的 BUG 時,那就試試關機吧。

No responses yet