Wyświetlanie kodu źródłowego w Android Code Search
Jeśli nie jesteś producentem urządzenia z Androidem ani producentem układu Thread, możesz już przestać czytać.
Ten dokument zawiera instrukcje tworzenia nowego urządzenia z Androidem Thread Border Router z najnowszym kodem źródłowym AOSP. Z tego dokumentu dowiesz się:
- ogólna architektura i stan obsługi Thread na Androidzie
- jak utworzyć własną usługę Thread HAL
- jak sprawić, aby urządzenie było zgodne z Google Home
- jak przetestować router graniczny Thread
Jeśli potrzebujesz pomocy, zgłoś problem na GitHub lub otwórz dyskusję, jeśli masz pytania.
Omówienie
Grupa wątków Androida opiera się na OpenThread i ot-br-posix
, które są dostępne w formie open source na GitHubie. Podobnie jak OpenThread, jest on rozwijany w publicznym repozytorium GitHub, więc stos wątków Androida jest rozwijany w publicznej bazie kodu AOSP. Wszystkie funkcje i poprawki błędów są najpierw przesyłane do AOSP. Dzięki temu dostawcy mogą zacząć stosować najnowsze wersje Thread bez oczekiwania na regularne wersje Androida.
Architektura
Cały stos wątków Androida składa się z 2 głównych komponentów: podstawowego stosu wątków na ogólnym partycji systemu i usługi Thread HAL na partycji dostawcy. Producenci urządzeń zwykle muszą tylko zająć się stworzeniem usługi HAL.
Oto krótkie podsumowanie działania wątku w Androidzie:
– Na serwerze systemowym jest usługa systemu wątku w języku Java, która zarządza całym stosem – udostępnia interfejs API systemu wątku, tworzy interfejs tunelu thread-wpan
, rejestruje sieć wątku w usłudze Connectivity i wdraża funkcje Border Routing i Advertising Proxy.
– Główny pakiet Thread lub OpenThread jest hostowany w nieuprzywilejowanym procesie natywnym o nazwie ot-daemon
. ot-daemon
jest zarządzany bezpośrednio przez usługę systemową Java za pomocą prywatnych interfejsów AIDL API i uzyskuje dostęp do radia Thread za pomocą interfejsu Thread HAL API.
– Usługa Thread HAL dostarczona przez dostawcę MUSI implementować interfejs Thread HAL API. Zazwyczaj działa jako RCP i implementuje protokół spinel.
Gdzie jest kod?
- Interfejs API i usługa Android Thread framework: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/thread/
- Interfejs HAL Thread i domyślna implementacja usługi: https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/threadnetwork/
- Zaimportowane repozytorium OpenThread: https://cs.android.com/android/platform/superproject/main/+/main:external/openthread/
- Zaimportowany repozytorium ot-br-posix: https://cs.android.com/android/platform/superproject/main/+/main:external/ot-br-posix/
Konfigurowanie środowiska programistycznego
Dostawcy urządzeń z Androidem, którzy już utworzyli środowisko programistyczne Androida na urządzeniu, mogą pominąć tę sekcję.
Jeśli dopiero zaczynasz korzystać z ekosystemu Androida lub jesteś dostawcą układów scalonych, który chce zapewnić zgodność swojego modułu Thread z Androidem i obsługę dla dostawców urządzeń, czytaj dalej.
Wykonaj ćwiczenia z programowania dla deweloperów aplikacji na Androida
Aby po raz pierwszy skonfigurować środowisko programistyczne Androida, skorzystaj z tego ćwiczenia z programowania: https://source.android.com/docs/setup/start. Na koniec tego ćwiczenia będziesz mieć możliwość utworzenia i uruchomienia symulowanego urządzenia Cuttlefish na podstawie kodu źródłowego.
Tworzenie usługi Thread HAL
Wypróbuj wątek w Cuttlefish
Cuttlefish to wirtualne urządzenie z Androidem. Zanim zaczniesz tworzyć własną usługę HAL, lepiej najpierw zapoznaj się z działaniem wątków w Cuttlefish.
W Cuttlefish jest dostępna domyślna usługa Thread HAL, która jest implementowana za pomocą symulowanego RCP, który przesyła i odbiera pakiety za pomocą gniazda UDP do i z symulowanego radia Thread (802.15.4).
W przypadku instancji Cuttlefish aplikacja „ThreadNetworkDemoApp” jest wstępnie zainstalowana. Otwórz tę aplikację, aby połączyć urządzenie Cuttlefish z domyślną siecią Thread.
Dostępne są też narzędzia wiersza poleceń ot-ctl
i ot-cli-ftd
, które umożliwiają skonfigurowanie sieci Thread na potrzeby testów. Te narzędzia obsługują wszystkie polecenia OpenThread CLI, które mogą być Ci już znane.
Aby wyszukać dzienniki usługi Cuttlefish Thread HAL, wykonaj te czynności:
adb logcat | egrep -i threadnetwork-service
07-21 10:43:05.048 0 0 I init : Parsing file /apex/com.android.hardware.threadnetwork/etc/threadnetwork-service.rc...
07-21 10:59:27.233 580 580 W android.hardware.threadnetwork-service: ThreadChip binder is unlinked
07-21 10:59:27.233 580 580 I android.hardware.threadnetwork-service: Close IThreadChip successfully
07-21 10:59:27.385 580 580 I android.hardware.threadnetwork-service: Open IThreadChip successfully
Możesz też wyszukać dzienniki demona ot, wykonując jedną z tych czynności:
adb logcat | egrep -i ot-daemon
07-21 10:43:48.741 0 0 I init : starting service 'ot-daemon'...
07-21 10:43:48.742 0 0 I init : Created socket '/dev/socket/ot-daemon/thread-wpan.sock', mode 660, user 1084, group 1084
07-21 10:43:48.762 0 0 I init : ... started service 'ot-daemon' has pid 2473
07-21 10:46:26.320 2473 2473 I ot-daemon: [I] P-Daemon------: Session socket is ready
07-21 10:46:30.290 2473 2473 W ot-daemon: [W] P-Daemon------: Daemon read: Connection reset by peer
07-21 10:48:07.264 2473 2473 I ot-daemon: [INFO]-BINDER--: Start joining...
07-21 10:48:07.267 2473 2473 I ot-daemon: [I] Settings------: Saved ActiveDataset
07-21 10:48:07.267 2473 2473 I ot-daemon: [I] DatasetManager: Active dataset set
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] DnssdServer---: Started
07-21 10:48:07.273 2473 2473 I ot-daemon: [N] Mle-----------: Role disabled -> detached
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] Mle-----------: AttachState Idle -> Start
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] Notifier------: StateChanged (0x111fd11d) [Ip6+ Role LLAddr MLAddr KeySeqCntr Ip6Mult+ Channel PanId NetName ExtPanId ...
07-21 10:48:07.273 2473 2473 I ot-daemon: [I] Notifier------: StateChanged (0x111fd11d) ... NetworkKey PSKc SecPolicy NetifState ActDset]
Usługa Cuttlefish Thread HAL korzysta z domyślnej usługi Thread HAL oraz z binarnego pliku RCP symulowanego przez OpenThread. Więcej informacji znajdziesz w następnej sekcji.
Domyślna usługa HAL
Wraz z interfejsem Thread HAL API jest dołączona domyślna usługa HAL. Domyślna usługa HAL obsługuje zarówno symulowane, jak i rzeczywiste urządzenia RCP. Otrzymuje opcjonalny URL urządzenia RCP. Jeśli nie zostanie podany, domyślnie zostanie użyte symulowane urządzenie RCP.
W pliku hardware/interfaces/threadnetwork/aidl/default/threadnetwork-service.rc
:
service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service class hal user thread_network
Jest to równoważne:
service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service spinel+hdlc+forkpty:///apex/com.android.hardware.threadnetwork/bin/ot-rcp?forkpty-arg=1 class hal user thread_network
W przypadku prawdziwych urządzeń RCP obsługuje interfejsy SPI i UART. Urządzenie można określić za pomocą schematu spinel+spi://
, spinel+hdlc+uart://
lub spinel+socket://
.
Omówienie dostawcy APEX
Podobnie jak w przypadku pakietu Thread w module głównego wiązania, domyślna usługa Thread HAL w Cuttlefish jest również spakowana w moduł APEX. Jest to jednak moduł APEX dostawcy, który zostanie zainstalowany w folderze /vendor/apex/
(elementy w module zostaną rozpakowane do folderu /apex/com.android.hardware.threadnetwork/
).
apex { name: "com.android.hardware.threadnetwork", manifest: "manifest.json", file_contexts: "file_contexts", key: "com.android.hardware.key", certificate: ":com.android.hardware.certificate", updatable: false, vendor: true, binaries: [ "android.hardware.threadnetwork-service", "ot-rcp", ], prebuilts: [ "threadnetwork-default.xml", // vintf_fragment "threadnetwork-service.rc", // init_rc "android.hardware.thread_network.prebuilt.xml", // permission ], }
Podczas tworzenia własnego modułu HAL APEX musisz zwrócić uwagę na kilka ważnych konfiguracji lub wprowadzić w nich zmiany:
file_contexts
: zawiera opis plików binarnych lub danych dostarczanych w tym module APEX lub plików, do których usługa HAL musi mieć dostęp (np. urządzenia RCP). Umożliwia to określenie konkretnych reguł sepolicy dla usługi HAL, aby uzyskać dostęp do sprzętowego urządzenia RCP.binaries
: plik binarny dostarczony w tym module APEXthreadnetwork-service.rc
: sposób uruchamiania usługi HAL. Musisz tutaj określić ścieżkę urządzenia RCP.android.hardware.thread_network.prebuilt.xml
: definiuje funkcję sprzętowąandroid.hardware.thread_network
. Jest to konieczne, aby system Android wiedział, że Twoje urządzenie ma sprzętową obsługę Thread. W przeciwnym razie stos wątków Androida nie zostanie włączony.
Tworzenie usługi HAL
Niezależnie od tego, czy jesteś deweloperem urządzeń z Androidem, czy dostawcą układów scalonych, powinieneś znać proces tworzenia oprogramowania układowego OT RCP dla chipa Thread. W tych instrukcjach przyjęto, że układ scalony jest prawidłowo podłączony i zweryfikowany.
Najprostszym sposobem skompilowania HAL APEX jest utworzenie nowego APEX z binarnymi plikami i kompletnymi pakietami domyślnego HAL APEX. Jeśli na przykład Twoja firma to Banana, a urządzenie RCP na Twoim urządzeniu to /dev/ttyACM0
, Twoja usługa HAL APEX dla Thread będzie wyglądać tak:
Android.bp
:
prebuilt_etc { name: "banana-threadnetwork-service.rc", src: "banana-threadnetwork-service.rc", installable: false, } apex { name: "com.banana.android.hardware.threadnetwork", manifest: "manifest.json", file_contexts: "file_contexts", key: "com.android.hardware.key", certificate: ":com.android.hardware.certificate", updatable: false, vendor: true, binaries: [ "android.hardware.threadnetwork-service", ], prebuilts: [ "banana-threadnetwork-service.rc", "threadnetwork-default.xml", "android.hardware.thread_network.prebuilt.xml", ], }
file_contexts
:
(/.*)? u:object_r:vendor_file:s0 /etc(/.*)? u:object_r:vendor_configs_file:s0 /bin/hw/android\.hardware\.threadnetwork-service u:object_r:hal_threadnetwork_default_exec:s0 /dev/ttyACM0 u:object_r:threadnetwork_rcp_device:s0
Ścieżki do plików w pierwszej kolumnie są powiązane z /apex/com.android.hardware.threadnetwork/
.
threadnetwork-service.rc
:
service vendor.threadnetwork_hal /apex/com.android.hardware.threadnetwork/bin/hw/android.hardware.threadnetwork-service spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200 class hal user root
manifest.json
:
{ "name": "com.android.hardware.threadnetwork", "version": 1 }
Zakładając, że tworzysz nowe urządzenie o nazwie Orange, katalog konfiguracji będzie wyglądał tak:
device/banana/orange/threadnetwork/ sepolicy/ Android.bp file_contexts manifest.json threadnetwork-default.xml threadnetwork-service.rc
W następnej sekcji znajdziesz informacje o tym, jakie reguły sepolicy należy dodać w podkatalogu sepolicy/
.
Reguły Sepolicy dla urządzenia RCP
Domyślnie usługa HAL Thread nie ma dostępu do urządzenia RCP (na przykład /dev/ttyACM0
). Aby to umożliwić, do katalogu sepolicy/
należy dodać niestandardowe reguły sepolicy.
Utwórz nowy plik sepolicy/threadnetwork_hal.te
z podanymi niżej danymi:
type threadnetwork_rcp_device, dev_type; # Allows the Thread HAL service to read / write the Thread RCP device allow hal_threadnetwork_default threadnetwork_rcp_device:chr_file rw_file_perms;
Złożenie
Teraz masz już prawie cały kod potrzebny do dodania Thread. Ostatnim krokiem jest dodanie do obrazu urządzenia pliku APEX Thread HAL i reguł sepolicy.
Aby to zrobić, dodaj podany niżej kod do Makefile
na urządzeniu (na przykład device.mk
):
PRODUCT_PACKAGES += com.banana.hardware.threadnetwork BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy
Jeśli wszystko działa prawidłowo, zobaczysz dziennik usługi Thread HAL podobny do tego:
adb logcat | egrep -i threadnetwork-service
08-13 13:26:41.751 477 477 I android.hardware.threadnetwork-service: ServiceName: android.hardware.threadnetwork.IThreadChip/chip0, Url: spinel+spi
08-13 13:26:41.751 477 477 I android.hardware.threadnetwork-service: Thread Network HAL is running
08-13 13:26:55.165 477 477 I android.hardware.threadnetwork-service: Open IThreadChip successfully
A log ot-daemon
będzie wyglądał tak:
adb logcat -s ot-daemon
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Running OTBR_AGENT/Unknown
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Thread version: 1.3.0
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Thread interface: thread-wpan
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Backbone interface is not specified
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-AGENT---: Radio URL: threadnetwork_hal://binder?none
08-13 13:26:55.157 1019 1019 I ot-daemon: [NOTE]-ILS-----: Infra link selected:
08-13 13:26:55.160 1019 1019 I ot-daemon: [I] Platform------: [HAL] Wait for getting the service android.hardware.threadnetwork.IThreadChip/chip0 ...
08-13 13:26:55.165 1019 1019 I ot-daemon: [I] Platform------: [HAL] Successfully got the service android.hardware.threadnetwork.IThreadChip/chip0
08-13 13:26:55.275 1019 1019 I ot-daemon: [I] P-RadioSpinel-: RCP reset: RESET_UNKNOWN
08-13 13:26:55.276 1019 1019 I ot-daemon: [I] P-RadioSpinel-: Software reset RCP successfully
08-13 13:26:55.277 1019 1019 I ot-daemon: [I] P-RadioSpinel-: RCP reset: RESET_POWER_ON
08-13 13:26:55.322 1019 1019 I ot-daemon: [I] ChildSupervsn-: Timeout: 0 -> 190
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] RoutingManager: Initializing - InfraIfIndex:0
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] InfraIf-------: Init infra netif 0
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] Settings------: Read BrUlaPrefix fd7b:cc45:ff06::/48
08-13 13:26:55.324 1019 1019 I ot-daemon: [N] RoutingManager: BR ULA prefix: fd7b:cc45:ff06::/48 (loaded)
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] RoutingManager: Generated local OMR prefix: fd7b:cc45:ff06:1::/64
08-13 13:26:55.324 1019 1019 I ot-daemon: [N] RoutingManager: Local on-link prefix: fdde:ad00:beef:cafe::/64
08-13 13:26:55.324 1019 1019 I ot-daemon: [I] RoutingManager: Enabling
Dostosowywanie
Główny moduł Thread (jest on częścią modułu „Tethering”) udostępnia kilka konfiguracji nakładających się na siebie, które mogą być określane przez dostawców w celu dostosowania zachowania modułu. Pełną listę znajdziesz w pliku config_thread.xml.
Aby umożliwić korzystanie z urządzenia jako routera granicznego Thread, musisz zazwyczaj ustawić wartość parametru config_thread_border_router_default_enabled
na true
, a także zmienić wartości parametrów config_thread_vendor_name
, config_thread_vendor_oui
i config_thread_model_name
na wartości od dostawcy lub producenta. Te wartości będą uwzględniane w usłudze _meshcop._udp
mDNS, która jest zawsze reklamowana przez router graniczny Thread.
Aby dodać nakładkę, musisz utworzyć nowy cel ConnectivityOverlayOrange
runtime_resource_overlay dla urządzenia Orange. Utwórz nowy katalog ConnectivityOverlay/
w katalogu device/banana/orange/rro_overlays
i dodaj w nim te elementy:
device/banana/orange/rro_overlays/ConnectivityOverlay/ res values config_thread.xml Android.bp AndroidManifest.xml
Android.bp
:
package { default_applicable_licenses: ["Android-Apache-2.0"], } runtime_resource_overlay { name: "ConnectivityOverlayOrange", manifest: "AndroidManifest.xml", resource_dirs: ["res"], certificate: "platform", product_specific: true, sdk_version: "current", }
AndroidManifest.xml
:
<!-- Orange overlays for the Connectivity module --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.banana.android.connectivity.resources.orange" android:versionCode="1" android:versionName="1.0"> <application android:hasCode="false" /> <!-- If your device uses google-signed mainline modules, the targetPackage needs to be "com.google.android.connectivity.resources", otherise, it should be "com.android.connectivity.resources" --> <overlay android:targetPackage="com.google.android.connectivity.resources" android:targetName="ServiceConnectivityResourcesConfig" android:isStatic="true" android:priority="1"/> </manifest>
config_thread.xml
:
<bool name="config_thread_border_router_default_enabled">true</bool> <string translatable="false" name="config_thread_vendor_name">Banana Inc.</string> <string translatable="false" name="config_thread_vendor_oui">AC:DE:48</string> <string translatable="false" name="config_thread_model_name">Orange</string>
Podobnie jak w przypadku HAL APEX, musisz dodać aplikację nakładki do pliku device.mk
:
PRODUCT_PACKAGES += \ ConnectivityOverlayOrange</code>
Jeśli wszystko działa prawidłowo, zobaczysz, że ot-daemon
rejestruje nazwę dostawcy i modela na samym początku dziennika:
adb logcat -s ot-daemon
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] P-Daemon------: Session socket is ready
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] Cli-----------: Input: state
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] Cli-----------: Output: disabled
07-22 15:31:37.693 1472 1472 I ot-daemon: [I] Cli-----------: Output: Done
07-22 15:31:37.693 1472 1472 W ot-daemon: [W] P-Daemon------: Daemon read: Connection reset by peer
07-22 15:31:50.091 1472 1472 I ot-daemon: [I] P-Daemon------: Session socket is ready
07-22 15:31:50.091 1472 1472 I ot-daemon: [I] Cli-----------: Input: factoryreset
07-22 15:31:50.092 1472 1472 I ot-daemon: [I] Settings------: Wiped all info
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-ADPROXY-: Stopped
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-DPROXY--: Stopped
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-BA------: Stop Thread Border Agent
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-BA------: Unpublish meshcop service Banana Inc. Orange #4833._meshcop._udp.local
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-MDNS----: Removing service Banana Inc. Orange #4833._meshcop._udp
07-22 15:31:50.092 1472 1472 I ot-daemon: [INFO]-MDNS----: Unpublishing service Banana Inc. Orange #4833._meshcop._udp listener ID = 0
być zgodne z Google Home.
Jeśli chcesz, aby router graniczny był używany przez ekosystem Google Home, możesz określić tę konfigurację w config_thread.xml
:
<string-array name="config_thread_mdns_vendor_specific_txts"> <item>vgh=1</item> </string-array>
Testowanie
Urządzenie powinno być teraz zgodne ze specyfikacją routera granicznego Thread w wersji 1.3 lub nowszej. Zanim prześlesz urządzenie do programu certyfikacji Thread, musisz przeprowadzić kilka testów Androida xTS, aby sprawdzić jego zgodność.
Test VTS sprawdza, czy usługa HAL Thread działa zgodnie z oczekiwaniami na urządzeniu. Testy możesz uruchomić za pomocą polecenia
atest VtsHalThreadNetworkTargetTest
Test CTS sprawdza, czy interfejsy API Thread działają zgodnie z oczekiwaniami na urządzeniu. Możesz uruchamiać testy za pomocą polecenia
atest CtsThreadNetworkTestCases
Test integracji zapewnia większą gwarancję jakości kodu głównego Thread na Twoim urządzeniu. Testy możesz uruchomić za pomocą polecenia
atest ThreadNetworkIntegrationTests
Więcej instrukcji dotyczących uruchamiania testów VTS, CTS i MTS znajdziesz w tych opublikowanych zestawach testów:
- https://source.android.com/docs/core/tests/vts
- https://source.android.com/docs/compatibility/cts/run
- https://docs.partner.android.com/mainline/test/mts (aby uzyskać dostęp do tego linku, musisz być partnerem)
Testowanie za pomocą aplikacji demonstracyjnej Thread
Podobnie jak w przypadku urządzenia Cuttlefish, możesz dodać aplikację demonstracyjną Thread do obrazu systemu:
# ThreadNetworkDemoApp for testing PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp
Pamiętaj, że należy dodać go tylko do wersji debugowania lub wersji inżynierskiej (np. PRODUCT_PACKAGES_DEBUG
), ponieważ nie powinien on być uwzględniany w wersji użytkownika przeznaczonej dla użytkowników końcowych.