Tạo Bộ định tuyến đường viền Android

Xem nguồn trên Android Code Search

Nếu không phải là nhà cung cấp thiết bị Android hoặc chip Thread, bạn có thể dừng đọc ngay.

Tài liệu này hướng dẫn bạn từng bước xây dựng một thiết bị Bộ định tuyến biên Thread mới dựa trên Android bằng mã nguồn AOSP mới nhất. Bằng cách làm theo tài liệu này, bạn sẽ tìm hiểu:

  1. cấu trúc tổng thể và trạng thái hỗ trợ Luồng trong Android
  2. cách tạo dịch vụ Thread HAL của riêng bạn
  3. cách giúp thiết bị tương thích với Google Home
  4. cách kiểm thử Bộ định tuyến biên Thread

Nếu bạn cần được hỗ trợ, hãy gửi vấn đề trong GitHub hoặc mở một Cuộc thảo luận nếu bạn có câu hỏi.

Tổng quan

Ngăn xếp Luồng Android dựa trên OpenThread và ot-br-posix do Google phát hành nguồn mở trên GitHub. Tương tự như cách OpenThread được phát triển trong một kho lưu trữ GitHub công khai, ngăn xếp Luồng Android được phát triển trong cơ sở mã AOSP công khai. Tất cả tính năng và bản sửa lỗi đều được gửi trước trong AOSP. Điều này cho phép các nhà cung cấp bắt đầu sử dụng các phiên bản Thread mới nhất mà không cần chờ các bản phát hành Android thông thường.

Kiến trúc

Toàn bộ ngăn xếp Luồng Android bao gồm hai thành phần chính: ngăn xếp Luồng cốt lõi trong một phân vùng hệ thống chung và dịch vụ Luồng HAL trong một phân vùng nhà cung cấp. Nhà cung cấp thiết bị thường chỉ cần chăm sóc và xây dựng dịch vụ HAL.

android-thread-arch

Dưới đây là thông tin tóm tắt ngắn gọn về cách hoạt động của ngăn xếp Luồng Android: – Có một dịch vụ hệ thống Luồng Java trong máy chủ hệ thống quản lý toàn bộ ngăn xếp – cung cấp API hệ thống Luồng, tạo giao diện đường hầm thread-wpan, đăng ký mạng Luồng vào Dịch vụ kết nối và triển khai các chức năng Chuyển phát biên giới và Proxy quảng cáo. – Ngăn xếp Luồng / OpenThread cốt lõi được lưu trữ trong một quy trình gốc độc lập không đặc quyền có tên là ot-daemon. ot-daemon được dịch vụ hệ thống Java quản lý trực tiếp qua các API AIDL riêng tư và truy cập vào đài phát thanh phần cứng Luồng thông qua API Luồng HAL. – Dịch vụ Thread HAL do nhà cung cấp cung cấp PHẢI triển khai Thread HAL API. Thông thường, công cụ này hoạt động như một RCP và triển khai giao thức spinel.

Mã ở đâu?

Thiết lập môi trường phát triển

Các nhà cung cấp thiết bị Android đã thiết lập môi trường phát triển Android cho thiết bị có thể bỏ qua phần này.

Nếu bạn mới sử dụng hệ sinh thái Android hoặc là nhà cung cấp silicon muốn chip Thread tương thích với Android và hỗ trợ nhà cung cấp thiết bị, hãy tiếp tục đọc.

Tham gia lớp học lập trình dành cho nhà phát triển Android

Để thiết lập môi trường phát triển Android lần đầu tiên, hãy sử dụng lớp học lập trình sau: https://source.android.com/docs/setup/start. Khi kết thúc lớp học lập trình này, bạn sẽ có thể tạo và chạy một thiết bị Cuttlefish được mô phỏng từ mã nguồn.

Xây dựng dịch vụ Thread HAL

Thử dùng Thread trong Cuttlefish

Cuttlefish là thiết bị Android ảo. Trước khi bắt đầu xây dựng dịch vụ HAL của riêng mình, bạn nên thử Luồng trong Cuttlefish để hiểu cách hoạt động của HAL.

Dịch vụ Thread HAL mặc định được cung cấp trong Cuttlefish và được triển khai bằng RCP được mô phỏng. Dịch vụ này sẽ truyền và nhận các gói qua ổ cắm UDP đến và từ đài Thread (802.15.4) được mô phỏng.

Trong thực thể Cuttlefish, "ThreadNetworkDemoApp" được cài đặt sẵn. Mở ứng dụng đó để kết nối thiết bị Cuttlefish với mạng Thread mặc định.

demoapp-screenshot

Ngoài ra, bạn cũng có thể sử dụng các công cụ dòng lệnh ot-ctlot-cli-ftd để định cấu hình mạng Thread cho mục đích kiểm thử. Các công cụ đó hỗ trợ tất cả lệnh CLI OpenThread mà bạn có thể đã quen thuộc.

Bạn có thể tìm kiếm nhật ký của dịch vụ HAL luồng Cuttlefish bằng cách:

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

Hoặc tìm kiếm nhật ký của trình nền ot bằng cách:

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]

Dịch vụ HAL luồng Cuttlefish sử dụng dịch vụ HAL luồng mặc định cùng với tệp nhị phân RCP được mô phỏng OpenThread, hãy xem phần tiếp theo để biết cách hoạt động của dịch vụ này.

Dịch vụ HAL mặc định

Dịch vụ HAL mặc định đi kèm với API Thread HAL. Dịch vụ HAL mặc định hỗ trợ cả thiết bị RCP thực và mô phỏng. Phương thức này nhận một URL thiết bị RCP không bắt buộc và nếu không cung cấp URL, thì URL này sẽ mặc định là thiết bị RCP được mô phỏng.

Trong tệp 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

Giá trị này tương đương với:

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

Đối với các thiết bị RCP thực, thiết bị này hỗ trợ cả giao diện SPI và UART, bạn có thể chỉ định thiết bị bằng giản đồ spinel+spi://, spinel+hdlc+uart://spinel+socket:// tương ứng.

Tìm hiểu về APEX của nhà cung cấp

Tương tự như ngăn xếp Luồng trong mô-đun chính của tính năng Cắm mạng, dịch vụ Thread HAL mặc định trong Cuttlefish cũng được đóng gói trong mô-đun APEX. Tuy nhiên, đây là mô-đun APEX của nhà cung cấp sẽ được cài đặt vào /vendor/apex/ (Các cấu phần phần mềm trong mô-đun sẽ được giải nén vào /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
    ],
}

Có một vài cấu hình quan trọng mà bạn cần chú ý hoặc thực hiện thay đổi khi tạo mô-đun HAL APEX của riêng mình:

  • file_contexts: Tệp này mô tả các tệp nhị phân / dữ liệu được phân phối trong mô-đun APEX này hoặc các tệp mà dịch vụ HAL cần truy cập (ví dụ: thiết bị RCP). Điều này cho phép bạn chỉ định các quy tắc chính sách bảo mật cụ thể cho dịch vụ HAL để truy cập vào thiết bị RCP phần cứng.

  • binaries: Tệp nhị phân được phân phối trong mô-đun APEX này

  • threadnetwork-service.rc: Cách bắt đầu dịch vụ HAL. Bạn cần chỉ định đường dẫn thiết bị RCP tại đây.

  • android.hardware.thread_network.prebuilt.xml: Xác định tính năng phần cứng android.hardware.thread_network. Điều này là bắt buộc để hệ thống Android biết rằng thiết bị của bạn có hỗ trợ phần cứng Thread. Nếu không, ngăn xếp Luồng Android sẽ không được bật.

Tạo dịch vụ HAL

Cho dù bạn là nhà phát triển thiết bị Android hay nhà cung cấp silicon, bạn đều phải quen thuộc với việc tạo firmware OT RCP cho chip Thread. Các hướng dẫn sau đây giả định rằng chip phần cứng được kết nối và xác thực chính xác.

Cách đơn giản nhất để tạo HAL APEX là tạo một APEX mới bằng các tệp nhị phân và tệp tạo sẵn của HAL APEX mặc định. Ví dụ: nếu công ty của bạn là Banana và thiết bị RCP trên thiết bị của bạn là /dev/ttyACM0, thì Thread HAL APEX sẽ có dạng như sau:

  • 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

Đường dẫn tệp trong cột đầu tiên có liên quan đến /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
  }

Giả sử bạn đang tạo một thiết bị mới có tên là Orange, thư mục cấu hình dành riêng cho thiết bị của bạn sẽ như sau:

device/banana/orange/threadnetwork/
    sepolicy/
    Android.bp
    file_contexts
    manifest.json
    threadnetwork-default.xml
    threadnetwork-service.rc

Hãy xem phần tiếp theo để biết những quy tắc sepolicy cần thêm vào thư mục con sepolicy/.

Quy tắc sepolicy cho thiết bị RCP

Theo mặc định, dịch vụ Thread HAL không có quyền truy cập vào thiết bị RCP (ví dụ: /dev/ttyACM0), bạn cần thêm các quy tắc chính sách bảo mật tuỳ chỉnh vào thư mục sepolicy/.

Tạo tệp sepolicy/threadnetwork_hal.te mới có nội dung bên dưới:

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;

Kết hợp

Giờ đây, bạn đã hoàn tất gần như tất cả các mã cần thiết để thêm Luồng, bước cuối cùng là thêm Thread HAL APEX và các quy tắc sepolicy vào hình ảnh của thiết bị.

Bạn có thể thực hiện việc này bằng cách thêm mã bên dưới vào Makefile của thiết bị (ví dụ: device.mk):

PRODUCT_PACKAGES += com.banana.hardware.threadnetwork
BOARD_SEPOLICY_DIRS += device/banana/orange/threadnetwork/sepolicy

Nếu mọi thứ hoạt động, giờ đây, bạn sẽ có thể thấy nhật ký dịch vụ Thread HAL tương tự như:

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

Và nhật ký ot-daemon sẽ có dạng như sau:

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

Tuỳ chỉnh

Mô-đun luồng chính (thực sự là một phần của mô-đun "Tethering") cung cấp một số cấu hình có thể phủ lên nhau mà nhà cung cấp có thể chỉ định để tuỳ chỉnh hành vi của ngăn xếp. Hãy xem config_thread.xml để biết danh sách đầy đủ.

Thông thường, bạn phải đặt config_thread_border_router_default_enabled thành true để bật thiết bị làm Bộ định tuyến biên Thread và thay đổi config_thread_vendor_name, config_thread_vendor_ouiconfig_thread_model_name thành giá trị nhà cung cấp hoặc sản phẩm. Các giá trị đó sẽ được đưa vào dịch vụ mDNS _meshcop._udp, dịch vụ này luôn được Trình định tuyến biên của luồng quảng cáo.

Để thêm lớp phủ, bạn cần tạo một mục tiêu ConnectivityOverlayOrange mới cho runtime_resource_overlay cho thiết bị Orange. Tạo thư mục ConnectivityOverlay/ mới trong device/banana/orange/rro_overlays và tạo nội dung bên dưới trong thư mục đó:

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>
  

Tương tự như HAL APEX, bạn cần thêm ứng dụng lớp phủ vào tệp device.mk:

PRODUCT_PACKAGES += \
    ConnectivityOverlayOrange</code>

Nếu mọi thứ hoạt động, bạn sẽ thấy ot-daemon ghi lại tên nhà cung cấp và tên mô hình ngay từ đầu nhật ký:

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

Tương thích với Google Home

Ngoài ra, nếu muốn hệ sinh thái Google Home sử dụng Bộ định tuyến biên, bạn có thể chỉ định cấu hình này trong config_thread.xml:

<string-array name="config_thread_mdns_vendor_specific_txts">
  <item>vgh=1</item>
</string-array>

Thử nghiệm

Thiết bị của bạn hiện phải tương thích với thông số kỹ thuật của Bộ định tuyến biên Thread 1.3 trở lên. Trước khi gửi đến chương trình chứng nhận Thread, bạn nên thực hiện một số kiểm thử Android xTS để đảm bảo khả năng tương thích.

  • Quy trình kiểm thử VTS đảm bảo dịch vụ Thread HAL hoạt động như mong đợi trên thiết bị của bạn. Bạn có thể chạy các chương trình kiểm thử bằng lệnh

    atest VtsHalThreadNetworkTargetTest

  • Quy trình kiểm thử CTS đảm bảo rằng các API Luồng hoạt động như mong đợi trên thiết bị của bạn. Bạn có thể chạy các bài kiểm thử bằng lệnh

    atest CtsThreadNetworkTestCases

  • Kiểm thử tích hợp cung cấp thêm đảm bảo về chất lượng về cách mã luồng chính hoạt động trên thiết bị của bạn. Bạn có thể chạy các chương trình kiểm thử bằng lệnh

    atest ThreadNetworkIntegrationTests

Bạn cũng có thể tìm thêm hướng dẫn về cách chạy các bài kiểm thử VTS/CTS/MTS bằng các bộ kiểm thử đã phát hành đó:

Kiểm thử bằng ứng dụng minh hoạ Thread

Tương tự như thiết bị Cuttlefish, bạn có thể thêm ứng dụng minh hoạ Thread vào hình ảnh hệ thống:

# ThreadNetworkDemoApp for testing
PRODUCT_PACKAGES_DEBUG += ThreadNetworkDemoApp

Xin lưu ý rằng bạn chỉ nên thêm biến thể này vào biến thể gỡ lỗi / eng (ví dụ: PRODUCT_PACKAGES_DEBUG) vì biến thể này không được đưa vào bản dựng người dùng dành cho người tiêu dùng cuối.