1# Android Bumble extensions
2
3While [experimental Pandora API][pandora-experimental-api-code] are implemented
4for Android, they may not be for Bumble. When writing Android Avatar tests, if
5you need one of these APIs on Bumble, you can implement it by adding a custom
6service.
7
8Note: Before going further, make sure you read the
9[Implementing your own tests](android-guide#implementing-your-own-tests)
10section of the Avatar Android guide.
11
12In the following example, we will add stub files required to write HID tests for
13the [`hid.proto`][hid-proto] interface.
14
15Note: The code for this example is available in a [WIP CL][hid-example].
16
17## Create a new test class
18
19Follow [Create a test class](android-guide#create-a-test-class) to create a
20`HidTest` test class in `hid_test.py`.
21
22## Create a Bumble HID extension
23
24Create an HID extension file:
25
26```shell
27cd packages/modules/Bluetooth/
28touch pandora/server/bumble_experimental/hid.py
29```
30
31Add the following code to it:
32
33```python
34import grpc
35import logging
36
37from bumble.device import Device
38from pandora_experimental.hid_grpc import (
39    SendHostReportRequest,
40    SendHostReportResponse,
41)
42from pandora_experimental.hid_grpc_aio import HIDServicer
43
44# This class implements the HID experimental Pandora interface.
45class HIDService(HIDServicer):
46    device: Device
47
48    def __init__(self, device: Device) -> None:
49        self.device = device
50
51    async def SendHostReport(
52        self,
53        request: SendHostReportRequest,
54        context: grpc.ServicerContext
55    ) -> SendHostReportResponse:
56        logging.info(
57            f'SendHostReport(address={request.address}, '
58            f'type={request.report_type}, report="{request.report}")'
59        )
60        # You should implement SendHostReport here by doing direct call to the
61        # Bumble instance (i.e. `self.device`).
62        return SendHostReportResponse()
63```
64
65## Add an HID test to your test class
66
67In `hid_test.py`:
68
69```python
70def test_report(self) -> None:
71    from pandora_experimental.hid_grpc import HID, HidReportType
72    HID(self.ref.channel).SendHostReport(
73        address=self.dut.address,
74        report_type=HidReportType.HID_REPORT_TYPE_INPUT,
75        report="pause cafe"
76    )
77```
78
79### Add your HID test class to Avatar test suite runner
80
81```diff
82diff --git a/android/pandora/test/main.py b/android/pandora/test/main.py
83--- a/android/pandora/test/main.py
84+++ b/android/pandora/test/main.py
85@@ -3,18 +3,22 @@ from avatar import bumble_server
86
87 import example
88 import gatt_test
89+import hid_test
90
91 import logging
92 import sys
93
94 from bumble_experimental.gatt import GATTService
95 from pandora_experimental.gatt_grpc_aio import add_GATTServicer_to_server
96+from bumble_experimental.hid import HIDService
97+from pandora_experimental.hid_grpc_aio import add_HIDServicer_to_server
98
99-_TEST_CLASSES_LIST = [example.ExampleTest, gatt_test.GattTest]
100+_TEST_CLASSES_LIST = [example.ExampleTest, gatt_test.GattTest, hid_test.HidTest]
101
102
103 def _bumble_servicer_hook(server: bumble_server.Server) -> None:
104   add_GATTServicer_to_server(GATTService(server.bumble.device), server.server)
105+  add_HIDServicer_to_server(HIDService(server.bumble.device), server.server)
106
107
108 if __name__ == "__main__":
109```
110
111You can now run your new HID test:
112
113```shell
114avatar run --mobly-std-log --include-filter 'HidTest'
115```
116
117[pandora-experimental-api-code]: https://cs.android.com/android/platform/superproject/+/main:packages/modules/Bluetooth/pandora/interfaces/pandora_experimental/
118
119[hid-proto]: https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/pandora/interfaces/pandora_experimental/hid.proto
120
121[hid-example]: https://android-review.git.corp.google.com/c/platform/packages/modules/Bluetooth/+/2454811
122