1# Avatar with Android 2 3Since Android provides an implementation of the [Pandora APIs]( 4https://github.com/google/bt-test-interfaces), Avatar can run with Android 5devices. 6 7## Setup 8 9The standard Avatar setup on Android is to test a [Cuttlefish]( 10https://source.android.com/docs/setup/create/cuttlefish) virtual Android DUT 11against a [Bumble](https://github.com/google/bumble) virtual Reference device 12(REF). 13 14Pandora APIs are implemented both on Android in a 15[PandoraServer][pandora-server-code] app and on [Bumble]( 16https://github.com/google/bumble/tree/main/bumble/pandora). The communication 17between the virtual Android DUT and the virtual Bumble Reference device is made 18through [Rootcanal][rootcanal-code], a virtual Bluetooth Controller. 19 20 22 23## Usage 24 25There are two different command line interfaces to use Avatar on Android. 26 27### Prerequisites 28 29You must have a running CF instance. If not, you can run the following commands 30from the root of your Android repository: 31 32```shell 33source build/envsetup.sh 34lunch aosp_cf_x86_64_phone-userdebug 35acloud create --local-image --local-instance 36``` 37 38Note: For Googlers, from an internal Android repository, use the 39`cf_x86_64_phone-userdebug` target instead. You can also use a CF remote 40instance by removing `--local-instance`. 41 42### `avatar` CLI (preferred) 43 44You can run all the existing Avatar tests on Android by running the following 45commands from the root of your Android repository: 46 47```shell 48cd packages/modules/Bluetooth 49source android/pandora/test/envsetup.sh 50avatar --help 51avatar format # Format test files 52avatar lint # Lint test files 53avatar run --mobly-std-log # '--mobly-std-log' to print mobly logs, silent otherwise 54``` 55 56Note: If you have errors such as `ModuleNotFoundError: no module named pip`, 57reset your Avatar cache by doing `rm -rf ~/.cache/avatar/venv`. 58 59### `atest` CLI 60 61You can also run all Avatar tests using [`atest`]( 62https://source.android.com/docs/core/tests/development/atest): 63 64```shell 65atest avatar -v # All tests in verbose 66``` 67 68## Build a new Avatar test 69 70Follow the instructions below to create your first Avatar test. 71 72### Create a test class 73 74Create a new Avatar test class file `codelab_test.py` in the Android Avatar 75tests folder, `packages/modules/Bluetooth/android/pandora/test/`: 76 77```python 78from typing import Optional # Avatar is strictly typed. 79 80# Importing Mobly modules required for the test. 81from mobly import base_test # Mobly base test class . 82 83# Importing Avatar classes and functions required for the test. 84from avatar import PandoraDevices 85from avatar.aio import asynchronous # A decorator to run asynchronous functions. 86from avatar.pandora_client import BumblePandoraClient, PandoraClient 87 88# Importing Pandora gRPC message & enum types. 89from pandora.host_pb2 import RANDOM, DataTypes 90 91 92# The test class to test the LE (Bluetooth Low Energy) Connectivity. 93class CodelabTest(base_test.BaseTestClass): 94 devices: Optional[PandoraDevices] = None 95 dut: PandoraClient 96 ref: BumblePandoraClient # `BumblePandoraClient` is a sub-class of `PandoraClient` 97 98 # Method to set up the DUT and REF devices for the test (called once). 99 def setup_class(self) -> None: 100 self.devices = PandoraDevices(self) # Create Pandora devices from the config. 101 self.dut, ref = self.devices 102 assert isinstance(ref, BumblePandoraClient) # REF device is a Bumble device. 103 self.ref = ref 104 105 # Method to tear down the DUT and REF devices after the test (called once). 106 def teardown_class(self) -> None: 107 # Stopping all the devices if any. 108 if self.devices: self.devices.stop_all() 109 110 # Method to set up the test environment (called before each test). 111 @asynchronous 112 async def setup_test(self) -> None: 113 # Reset DUT and REF devices asynchronously. 114 await asyncio.gather(self.dut.reset(), self.ref.reset()) 115 116 # Method to write the actual test. 117 def test_void(self) -> None: 118 assert True # This is a placeholder for the test body. 119``` 120 121For now, your test class contains only a single `test_void`. 122 123### Add a test class to Avatar test suite runner 124 125Add the tests from your test class into 126[Avatar Android test suite runner][avatar-android-suite-runner-code]: 127 128```diff 129diff --git a/android/pandora/test/main.py b/android/pandora/test/main.py 130index a124306e8f..742e087521 100644 131--- a/android/pandora/test/main.py 132+++ b/android/pandora/test/main.py 133@@ -1,11 +1,12 @@ 134 from mobly import suite_runner 135 136+import codelab_test 137 import example 138 139 import logging 140 import sys 141 142-_TEST_CLASSES_LIST = [example.ExampleTest] 143+_TEST_CLASSES_LIST = [example.ExampleTest, codelab_test.CodelabTest] 144``` 145 146You can now try to run your test class using `avatar`: 147 148```shell 149avatar run --mobly-std-log --include-filter 'CodelabTest' # All the CodelabTest tests 150avatar run --mobly-std-log --include-filter 'CodelabTest#test_void' # Run only test_void 151``` 152 153Or using `atest`: 154 155```shell 156atest avatar -v # all tests 157atest avatar:'CodelabTest#test_void' -v # Run only test_void 158``` 159 160### Add a real test 161 162You can add a new test to your test class by creating a new method `test_<>`, 163which is implemented by a series of calls to the Pandora APIs of either the 164Android DUT or the Bumble REF device and assert statement. 165 166A call to a Pandora API is made using `<device>.<api>.<method>(<arguments>)`. 167Pandora APIs and their descriptions are in 168[`external/pandora/bt-test-interfaces`][pandora-api-code] or 169[`package/module/Bluetooth/pandora/interfaces/pandora_experimental`][pandora-experimental-api-code]. 170 171For example, add the following test to your `codelab_test.py` test class: 172 173```python 174# Test the LE connection between the central device (DUT) and peripheral device (REF). 175def test_le_connect_central(self) -> None: 176 # Start advertising on the REF device, this makes it discoverable by the DUT. 177 # The REF advertises as `connectable` and the own address type is set to `random`. 178 advertisement = self.ref.host.Advertise( 179 # Legacy since extended advertising is not yet supported in Bumble. 180 legacy=True, 181 connectable=True, 182 own_address_type=RANDOM, 183 # DUT device matches the REF device using the specific manufacturer data. 184 data=DataTypes(manufacturer_specific_data=b'pause cafe'), 185 ) 186 187 # Start scanning on the DUT device. 188 scan = self.dut.host.Scan(own_address_type=RANDOM) 189 # Find the REF device using the specific manufacturer data. 190 peer = next((peer for peer in scan 191 if b'pause cafe' in peer.data.manufacturer_specific_data)) 192 scan.cancel() # Stop the scan process on the DUT device. 193 194 # Connect the DUT device to the REF device as central device. 195 connect_res = self.dut.host.ConnectLE( 196 own_address_type=RANDOM, 197 random=peer.random, # Random REF address found during scanning. 198 ) 199 advertisement.cancel() 200 201 # Assert that the connection was successful. 202 assert connect_res.connection 203 dut_ref = connect_res.connection 204 205 # Disconnect the DUT device from the REF device. 206 self.dut.host.Disconnect(connection=dut_ref) 207``` 208 209Then, run your new `test_le_connect_central` test: 210 211```shell 212avatar run --mobly-std-log --include-filter 'CodelabTest' 213``` 214 215### Implement your own tests 216 217Before starting, you should make sure you have clearly identified the tests you 218want to build: see [Where should I start to implement Avatar tests?]( 219overview#designing-avatar-tests) 220 221When your test is defined, you can implement it using the available 222[stable Pandora APIs][pandora-api-code]. 223 224Note: You can find many test examples in 225[`packages/modules/Bluetooth/android/pandora/test/`][avatar-android-tests-code]. 226 227**If you need an API which is not part of the finalized Pandora APIs to build 228your test**: 229 2301. If the API you need is **on the Android side**: you can also directly use the 231 [experimental Pandora API][pandora-experimental-api-code], in the same 232 fashion as the stable ones. 233 234 Warning: those APIs are subject to changes. 235 2361. If the API you need is on the Bumble side: you can also use the experimental 237 Pandora APIs by creating custom [Bumble extensions]( 238 android-bumble-extensions). 239 2401. If the API you need is not part of the experimental Pandora APIs: 241 242 * Create an issue. The Avatar team will decide whether to create a new API or 243 not. We notably don't want to create APIs for device specific behaviors. 244 245 * If it is decided not to add a new API, you can instead access the Bumble 246 Bluetooth stack directly within your test. For example: 247 248 ```python 249 @asynchronous 250 async def test_pause_cafe(self) -> None: 251 from bumble.core import BT_LE_TRANSPORT 252 253 # `self.ref.device` an instance of `bumble.device.Device` 254 connection = await self.ref.device.find_peer_by_name( # type: ignore 255 "Pause cafe", 256 transport=BT_LE_TRANSPORT, 257 ) 258 259 assert connection 260 await self.ref.device.encrypt(connection, enable=True) # type: ignore 261 ``` 262 263## Contribution guide 264 265### Modify the Avatar repository 266 267All contributions to Avatar (not tests) must be submitted first to GitHub since 268it is the source of truth for Avatar. To simplify the development process, 269Android developers can make their changes on Android and get reviews on Gerrit 270as usual, but then push it first to GitHub: 271 2721. Create your CL in [`external/pandora/Avatar`][avatar-code]. 2731. Ask for review on Gerrit as usual. 2741. Upon review and approval, the Avatar team creates a Pull Request on GitHub 275 with you as the author. The PR is directly approved. 2761. After it passes GitHub Avatar CI, the PR is merged. 2771. Then, the Avatar team merges the change from GitHub to Android. 278 279### Upstream experimental Pandora APIs 280 281The Pandora team continuously works to stabilize new Pandora APIs. When an 282experimental Pandora API is considered stable, it is moved from 283[`package/module/Bluetooth/pandora/interfaces/pandora_experimental`][pandora-experimental-api-code] 284to the official stable Pandora API repository in 285[`external/pandora/bt-test-interfaces`][pandora-api-code]. 286 287### Upstream Android tests to the Avatar repository 288 289On a regular basis, the Avatar team evaluates Avatar tests which have been 290submitted to Android and upstream them to the Avatar repository in the 291[`cases`](/cases/) folder if they are generic, meaning not related to Android 292specifically. 293 294Such added generic tests are removed from 295[`packages/modules/Bluetooth/android/pandora/test/`][avatar-android-tests-code]. 296 297## Presubmit tests 298 299All Avatar tests submitted in the 300[Android Avatar tests folder][avatar-android-tests-code] and added to 301[Avatar suite runner][avatar-android-suite-runner-code] as well as the tests in 302the [generic Avatar tests folder][avatar-tests-code], are run in Android 303Bluetooth presubmit tests (for every CL). 304 305Note: Avatar tests will soon also be run regularly on physical devices in 306Android postsubmit tests. 307 308[pandora-server-code]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/android/pandora/server/ 309 310[rootcanal-code]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/tools/rootcanal 311 312[pandora-api-code]: https://cs.android.com/android/platform/superproject/+/main:external/pandora/bt-test-interfaces/pandora 313 314[pandora-experimental-api-code]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/pandora/interfaces/pandora_experimental/ 315 316[avatar-tests-code]: https://cs.android.com/android/platform/superproject/+/main:external/pandora/avatar/cases 317 318[avatar-android-tests-code]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/android/pandora/test/ 319 320[avatar-code]: https://cs.android.com/android/platform/superproject/+/main:external/pandora/avatar 321 322[avatar-android-suite-runner-code]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/android/pandora/test/main.py 323