xref: /aosp_15_r20/system/chre/apps/ble_world/ble_world.cc (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <inttypes.h>
17 #include <cstdint>
18 
19 #include "chre/util/nanoapp/ble.h"
20 #include "chre/util/nanoapp/log.h"
21 #include "chre/util/time.h"
22 #include "chre_api/chre.h"
23 
24 #define BLE_FILTER_TYPE_SERVICE_DATA 0
25 #define BLE_FILTER_TYPE_MANUFACTURER_DATA 1
26 #define BLE_FILTER_TYPE_BROADCASTER_ADDRESS 2
27 
28 /**
29  * @file
30  *
31  * This nanoapp is designed to verify the functionality of CHRE's BLE APIs.
32  * After confirming whether the platform has the expected capabilities, It tests
33  * scan functionality by continually starting and stopping scan requests and
34  * decoding scan results to be verified manually via the logs. It tests read
35  * RSSI functionality by continually requesting to read RSSI from a hard coded
36  * connection handle.
37  *
38  * The BLE scanning test can be configured to test batching and flushing by
39  * defining BLE_WORLD_ENABLE_BATCHING. If the platform supports the
40  * CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING capability, this flag will modify
41  * the BLE scan request to use a batch window and periodically make flush
42  * requests to get batched BLE scan result events.
43  *
44  * The BLE scanning test can also be configured by filter type. By default, the
45  * test will filter by service data, but it can be modified to filter by
46  * manufacturer data or broadcaster address by setting the BLE_FILTER_TYPE flag
47  * to either BLE_FILTER_TYPE_MANUFACTURER_DATA or
48  * BLE_FILTER_TYPE_BROADCASTER_ADDRESS. It is recommended to use an app that can
49  * create advertisers corresponding to the filters to do the tests.
50  */
51 
52 #ifdef CHRE_NANOAPP_INTERNAL
53 namespace chre {
54 namespace {
55 #endif  // CHRE_NANOAPP_INTERNAL
56 
57 using chre::ble_constants::kNumBroadcasterFilters;
58 using chre::ble_constants::kNumManufacturerDataFilters;
59 using chre::ble_constants::kNumScanFilters;
60 
61 constexpr uint8_t kDataTypeServiceData = 0x16;
62 constexpr uint8_t kDataTypeManufacturerData = 0xFF;
63 constexpr uint8_t kUuidLengthInBytes = 2;
64 constexpr uint32_t kScanCookie = 10;
65 
66 #ifdef BLE_WORLD_ENABLE_BATCHING
67 //! A timer handle to request the BLE flush.
68 uint32_t gFlushTimerHandle = 0;
69 //! The period to which to make the BLE flush request.
70 uint64_t gFlushPeriodNs = 7 * chre::kOneSecondInNanoseconds;
71 #endif  // BLE_WORLD_ENABLE_BATCHING
72 
73 //! Report delay for BLE scans.
74 uint32_t gBleBatchDurationMs = 0;
75 //! A timer handle to toggle enable/disable BLE scans.
76 uint32_t gEnableDisableTimerHandle = 0;
77 //! The period at which to enable/disable BLE scans.
78 uint64_t gEnableDisablePeriodNs = 10 * chre::kOneSecondInNanoseconds;
79 //! True if BLE scans are currently enabled
80 bool gBleEnabled = false;
81 
82 //! A timer handle to poll for RSSI.
83 uint32_t gReadRssiTimerHandle = CHRE_TIMER_INVALID;
84 //! A hardcoded connection handle on which the RSSI will be read
85 //! On the Broadcom controllers used by Pixel, if a connection is made
86 //! immediately after startup, it will be on this handle.
87 uint16_t gReadRssiConnectionHandle = 0x40;
88 //! The period at which to read RSSI of kConnectionHandle.
89 uint64_t gReadRssiPeriodNs = 3 * chre::kOneSecondInNanoseconds;
90 
isScanningSupported(uint32_t capabilities,uint32_t filterCapabilities)91 bool isScanningSupported(uint32_t capabilities, uint32_t filterCapabilities) {
92   if ((capabilities & CHRE_BLE_CAPABILITIES_SCAN) == 0) {
93     LOGE("BLE scan is not supported");
94     return false;
95   }
96 #if BLE_FILTER_TYPE == BLE_FILTER_TYPE_MANUFACTURER_DATA
97   if ((filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_MANUFACTURER_DATA) ==
98       0) {
99     LOGE("BLE manufacturer data filters are not supported");
100     return false;
101   }
102 #elif BLE_FILTER_TYPE == BLE_FILTER_TYPE_BROADCASTER_ADDRESS
103   if ((filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_BROADCASTER_ADDRESS) ==
104       0) {
105     LOGE("BLE broadcaster address filters are not supported");
106     return false;
107   }
108 #else
109   if ((filterCapabilities & CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA) == 0) {
110     LOGE("BLE service data filters are not supported");
111     return false;
112   }
113 #endif
114   return true;
115 }
116 
enableBleScans()117 bool enableBleScans() {
118   struct chreBleScanFilterV1_9 filter;
119 #if BLE_FILTER_TYPE == BLE_FILTER_TYPE_MANUFACTURER_DATA
120   chreBleGenericFilter genericFilters[kNumManufacturerDataFilters];
121   chre::createBleManufacturerDataFilter(kNumManufacturerDataFilters,
122                                         genericFilters, filter);
123 #elif BLE_FILTER_TYPE == BLE_FILTER_TYPE_BROADCASTER_ADDRESS
124   chreBleBroadcasterAddressFilter broadcasterFilters[kNumBroadcasterFilters];
125   if (!chre::createBleScanFilterForAdvertiser(filter, broadcasterFilters,
126                                               kNumBroadcasterFilters)) {
127     LOGE("Failed to create BLE scan filters for known beacons and advertiser");
128   }
129 #else
130   chreBleGenericFilter genericFilters[kNumScanFilters];
131   if (!chre::createBleScanFilterForKnownBeaconsV1_9(filter, genericFilters,
132                                                     kNumScanFilters)) {
133     LOGE("Failed to create BLE scan filters for known beacons");
134   }
135 #endif
136   return chreBleStartScanAsyncV1_9(CHRE_BLE_SCAN_MODE_BACKGROUND,
137                                    gBleBatchDurationMs, &filter, &kScanCookie);
138 }
139 
disableBleScans()140 bool disableBleScans() {
141   return chreBleStopScanAsync();
142 }
143 
nanoappStart()144 bool nanoappStart() {
145   LOGI("BLE world from version 0x%08" PRIx32, chreGetVersion());
146   uint32_t capabilities = chreBleGetCapabilities();
147   uint32_t filterCapabilities = chreBleGetFilterCapabilities();
148   LOGI("Got BLE capabilities 0x%" PRIx32, capabilities);
149 #ifdef BLE_WORLD_ENABLE_BATCHING
150   bool batchingAvailable =
151       ((capabilities & CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING) != 0);
152   if (!batchingAvailable) {
153     LOGE("BLE scan result batching is unavailable");
154   } else {
155     gBleBatchDurationMs = 5000;
156     LOGI("BLE batching enabled");
157   }
158 #endif  // BLE_WORLD_ENABLE_BATCHING
159   if (!isScanningSupported(capabilities, filterCapabilities)) {
160     LOGE("BLE scanning is not supported");
161   } else if (!enableBleScans()) {
162     LOGE("Failed to send BLE start scan request");
163   } else {
164     gEnableDisableTimerHandle =
165         chreTimerSet(gEnableDisablePeriodNs, &gEnableDisableTimerHandle,
166                      false /* oneShot */);
167     if (gEnableDisableTimerHandle == CHRE_TIMER_INVALID) {
168       LOGE("Could not set enable/disable timer");
169     }
170 
171 #ifdef BLE_WORLD_ENABLE_BATCHING
172     if (batchingAvailable) {
173       gFlushTimerHandle =
174           chreTimerSet(gFlushPeriodNs, &gFlushTimerHandle, false /* oneShot */);
175       if (gFlushTimerHandle == CHRE_TIMER_INVALID) {
176         LOGE("Could not set flush timer");
177       }
178     }
179 #endif  // BLE_WORLD_ENABLE_BATCHING
180   }
181 
182   if (capabilities & CHRE_BLE_CAPABILITIES_READ_RSSI) {
183     gReadRssiPeriodNs = chreTimerSet(gReadRssiPeriodNs, &gReadRssiTimerHandle,
184                                      false /* oneShot */);
185     if (gReadRssiTimerHandle == CHRE_TIMER_INVALID) {
186       LOGE("Could not set RSSI timer");
187     }
188   } else {
189     LOGW(
190         "Skipping RSSI read since CHRE_BLE_CAPABILITIES_READ_RSSI not "
191         "supported");
192   }
193 
194   return true;
195 }
196 
getUuidInLittleEndian(const uint8_t data[kUuidLengthInBytes])197 uint16_t getUuidInLittleEndian(const uint8_t data[kUuidLengthInBytes]) {
198   return static_cast<uint16_t>(data[0] + (data[1] << 8));
199 }
200 
parseReport(const chreBleAdvertisingReport * report)201 void parseReport(const chreBleAdvertisingReport *report) {
202   for (uint16_t i = 0; i < report->dataLength;) {
203     // First byte has the advertisement data length.
204     uint16_t adDataLength = report->data[i];
205     // Early termination with zero length advertisement.
206     if (adDataLength == 0) break;
207 
208     // Log 2 byte UUIDs for service data or manufacturer data AD types.
209     if (adDataLength < kUuidLengthInBytes) {
210       i += adDataLength + 1;
211       continue;
212     }
213     uint8_t adDataType = report->data[++i];
214     switch (adDataType) {
215       case kDataTypeServiceData:
216         LOGD("Service Data UUID: %" PRIx16,
217              getUuidInLittleEndian(&report->data[i + 1]));
218         break;
219       case kDataTypeManufacturerData:
220         LOGD("Manufacturer Data UUID: %" PRIx16,
221              getUuidInLittleEndian(&report->data[i + 1]));
222         break;
223       default:
224         break;
225     }
226     // Moves to next advertisement.
227     i += adDataLength;
228   }
229   LOGD("application address type 0x%" PRIx8, report->addressType);
230   LOGD("address=%02X:%02X:%02X:%02X:%02X:%02X", report->address[0],
231        report->address[1], report->address[2], report->address[3],
232        report->address[4], report->address[5]);
233   LOGD("direct address=%02X:%02X:%02X:%02X:%02X:%02X", report->directAddress[0],
234        report->directAddress[1], report->directAddress[2],
235        report->directAddress[3], report->directAddress[4],
236        report->directAddress[5]);
237 }
238 
handleAsyncResultEvent(const chreAsyncResult * result)239 void handleAsyncResultEvent(const chreAsyncResult *result) {
240   const char *requestType =
241       result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN ? "start"
242                                                               : "stop";
243   if (result->success) {
244     LOGI("BLE %s scan success", requestType);
245     gBleEnabled = (result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN);
246   } else {
247     LOGE("BLE %s scan failure: %" PRIu8, requestType, result->errorCode);
248   }
249 }
250 
handleAdvertismentEvent(const chreBleAdvertisementEvent * event)251 void handleAdvertismentEvent(const chreBleAdvertisementEvent *event) {
252   for (uint8_t i = 0; i < event->numReports; i++) {
253     LOGD("BLE Report %" PRIu32, static_cast<uint32_t>(i + 1));
254     LOGD("Event type and data status: 0x%" PRIx8,
255          event->reports[i].eventTypeAndDataStatus);
256     LOGD("Timestamp: %" PRIu64 " ms",
257          event->reports[i].timestamp / chre::kOneMillisecondInNanoseconds);
258     parseReport(&event->reports[i]);
259   }
260 }
261 
handleTimerEvent(const void * cookie)262 void handleTimerEvent(const void *cookie) {
263   if (cookie == &gEnableDisableTimerHandle) {
264     bool success = false;
265     if (!gBleEnabled) {
266       success = enableBleScans();
267     } else {
268       success = disableBleScans();
269     }
270     if (!success) {
271       LOGE("Failed to send BLE %s scan request",
272            !gBleEnabled ? "start" : "stop");
273     }
274 #ifdef BLE_WORLD_ENABLE_BATCHING
275   } else if (cookie == &gFlushTimerHandle) {
276     if (gBleEnabled) {
277       if (!chreBleFlushAsync(nullptr /* cookie */)) {
278         LOGE("Could not send flush request");
279       } else {
280         LOGI("Successfully sent flush request at time %" PRIu64 " ms",
281              chreGetTime() / chre::kOneMillisecondInNanoseconds);
282       }
283     }
284 #endif  // BLE_WORLD_ENABLE_BATCHING
285   } else if (cookie == &gReadRssiTimerHandle) {
286     bool success = chreBleReadRssiAsync(gReadRssiConnectionHandle, nullptr);
287     LOGI("Reading RSSI for handle 0x%" PRIx16 ", status=%d",
288          gReadRssiConnectionHandle, success);
289   } else {
290     LOGE("Received unknown timer cookie %p", cookie);
291   }
292 }
293 
handleRssiEvent(const chreBleReadRssiEvent * event)294 void handleRssiEvent(const chreBleReadRssiEvent *event) {
295   LOGI("Received RSSI Read with status 0x%" PRIx8 " and rssi %" PRIi8,
296        event->result.errorCode, event->rssi);
297 }
298 
handleBatchCompleteEvent(const chreBatchCompleteEvent * event)299 void handleBatchCompleteEvent(const chreBatchCompleteEvent *event) {
300   LOGI("Received Batch complete event with event type %" PRIu16,
301        event->eventType);
302 }
303 
handleFlushCompleteEvent(const chreAsyncResult * event)304 void handleFlushCompleteEvent(const chreAsyncResult *event) {
305   LOGI("Received flush complete event with status 0x%" PRIx8, event->errorCode);
306 }
307 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)308 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
309                         const void *eventData) {
310   LOGI("Received event 0x%" PRIx16 " from 0x%" PRIx32 " at time %" PRIu64 " ms",
311        eventType, senderInstanceId,
312        chreGetTime() / chre::kOneMillisecondInNanoseconds);
313   switch (eventType) {
314     case CHRE_EVENT_BLE_ADVERTISEMENT:
315       handleAdvertismentEvent(
316           static_cast<const chreBleAdvertisementEvent *>(eventData));
317       break;
318     case CHRE_EVENT_BLE_ASYNC_RESULT:
319       handleAsyncResultEvent(static_cast<const chreAsyncResult *>(eventData));
320       break;
321     case CHRE_EVENT_TIMER:
322       handleTimerEvent(eventData);
323       break;
324     case CHRE_EVENT_BLE_FLUSH_COMPLETE:
325       handleFlushCompleteEvent(static_cast<const chreAsyncResult *>(eventData));
326       break;
327     case CHRE_EVENT_BLE_RSSI_READ:
328       handleRssiEvent(static_cast<const chreBleReadRssiEvent *>(eventData));
329       break;
330     case CHRE_EVENT_BLE_BATCH_COMPLETE:
331       handleBatchCompleteEvent(
332           static_cast<const chreBatchCompleteEvent *>(eventData));
333       break;
334     default:
335       LOGW("Unhandled event type %" PRIu16, eventType);
336       break;
337   }
338 }
339 
nanoappEnd()340 void nanoappEnd() {
341   if (gBleEnabled && !chreBleStopScanAsync()) {
342     LOGE("Error sending BLE stop scan request sent to PAL");
343   }
344   if (!chreTimerCancel(gEnableDisableTimerHandle)) {
345     LOGE("Error canceling BLE scan timer");
346   }
347 #ifdef BLE_WORLD_ENABLE_BATCHING
348   if (!chreTimerCancel(gFlushTimerHandle)) {
349     LOGE("Error canceling BLE flush timer");
350   }
351 #endif
352   if (!chreTimerCancel(gReadRssiTimerHandle)) {
353     LOGE("Error canceling RSSI read timer");
354   }
355   LOGI("nanoapp stopped");
356 }
357 
358 #ifdef CHRE_NANOAPP_INTERNAL
359 }  // anonymous namespace
360 }  // namespace chre
361 
362 #include "chre/platform/static_nanoapp_init.h"
363 #include "chre/util/nanoapp/app_id.h"
364 #include "chre/util/system/napp_permissions.h"
365 
366 CHRE_STATIC_NANOAPP_INIT(BleWorld, kBleWorldAppId, 0,
367                          NanoappPermissions::CHRE_PERMS_BLE);
368 #endif  // CHRE_NANOAPP_INTERNAL
369