• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   LOGD("rssi value: %" PRIi8, report->rssi);
238 }
239 
handleAsyncResultEvent(const chreAsyncResult * result)240 void handleAsyncResultEvent(const chreAsyncResult *result) {
241   const char *requestType =
242       result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN ? "start"
243                                                               : "stop";
244   if (result->success) {
245     LOGI("BLE %s scan success", requestType);
246     gBleEnabled = (result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN);
247   } else {
248     LOGE("BLE %s scan failure: %" PRIu8, requestType, result->errorCode);
249   }
250 }
251 
handleAdvertismentEvent(const chreBleAdvertisementEvent * event)252 void handleAdvertismentEvent(const chreBleAdvertisementEvent *event) {
253   for (uint8_t i = 0; i < event->numReports; i++) {
254     LOGD("BLE Report %" PRIu32, static_cast<uint32_t>(i + 1));
255     LOGD("Event type and data status: 0x%" PRIx8,
256          event->reports[i].eventTypeAndDataStatus);
257     LOGD("Timestamp: %" PRIu64 " ms",
258          event->reports[i].timestamp / chre::kOneMillisecondInNanoseconds);
259     parseReport(&event->reports[i]);
260   }
261 }
262 
handleTimerEvent(const void * cookie)263 void handleTimerEvent(const void *cookie) {
264   if (cookie == &gEnableDisableTimerHandle) {
265     bool success = false;
266     if (!gBleEnabled) {
267       success = enableBleScans();
268     } else {
269       success = disableBleScans();
270     }
271     if (!success) {
272       LOGE("Failed to send BLE %s scan request",
273            !gBleEnabled ? "start" : "stop");
274     }
275 #ifdef BLE_WORLD_ENABLE_BATCHING
276   } else if (cookie == &gFlushTimerHandle) {
277     if (gBleEnabled) {
278       if (!chreBleFlushAsync(nullptr /* cookie */)) {
279         LOGE("Could not send flush request");
280       } else {
281         LOGI("Successfully sent flush request at time %" PRIu64 " ms",
282              chreGetTime() / chre::kOneMillisecondInNanoseconds);
283       }
284     }
285 #endif  // BLE_WORLD_ENABLE_BATCHING
286   } else if (cookie == &gReadRssiTimerHandle) {
287     bool success = chreBleReadRssiAsync(gReadRssiConnectionHandle, nullptr);
288     LOGI("Reading RSSI for handle 0x%" PRIx16 ", status=%d",
289          gReadRssiConnectionHandle, success);
290   } else {
291     LOGE("Received unknown timer cookie %p", cookie);
292   }
293 }
294 
handleRssiEvent(const chreBleReadRssiEvent * event)295 void handleRssiEvent(const chreBleReadRssiEvent *event) {
296   LOGI("Received RSSI Read with status 0x%" PRIx8 " and rssi %" PRIi8,
297        event->result.errorCode, event->rssi);
298 }
299 
handleBatchCompleteEvent(const chreBatchCompleteEvent * event)300 void handleBatchCompleteEvent(const chreBatchCompleteEvent *event) {
301   LOGI("Received Batch complete event with event type %" PRIu16,
302        event->eventType);
303 }
304 
handleFlushCompleteEvent(const chreAsyncResult * event)305 void handleFlushCompleteEvent(const chreAsyncResult *event) {
306   LOGI("Received flush complete event with status 0x%" PRIx8, event->errorCode);
307 }
308 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)309 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
310                         const void *eventData) {
311   LOGI("Received event 0x%" PRIx16 " from 0x%" PRIx32 " at time %" PRIu64 " ms",
312        eventType, senderInstanceId,
313        chreGetTime() / chre::kOneMillisecondInNanoseconds);
314   switch (eventType) {
315     case CHRE_EVENT_BLE_ADVERTISEMENT:
316       handleAdvertismentEvent(
317           static_cast<const chreBleAdvertisementEvent *>(eventData));
318       break;
319     case CHRE_EVENT_BLE_ASYNC_RESULT:
320       handleAsyncResultEvent(static_cast<const chreAsyncResult *>(eventData));
321       break;
322     case CHRE_EVENT_TIMER:
323       handleTimerEvent(eventData);
324       break;
325     case CHRE_EVENT_BLE_FLUSH_COMPLETE:
326       handleFlushCompleteEvent(static_cast<const chreAsyncResult *>(eventData));
327       break;
328     case CHRE_EVENT_BLE_RSSI_READ:
329       handleRssiEvent(static_cast<const chreBleReadRssiEvent *>(eventData));
330       break;
331     case CHRE_EVENT_BLE_BATCH_COMPLETE:
332       handleBatchCompleteEvent(
333           static_cast<const chreBatchCompleteEvent *>(eventData));
334       break;
335     default:
336       LOGW("Unhandled event type %" PRIu16, eventType);
337       break;
338   }
339 }
340 
nanoappEnd()341 void nanoappEnd() {
342   if (gBleEnabled && !chreBleStopScanAsync()) {
343     LOGE("Error sending BLE stop scan request sent to PAL");
344   }
345   if (!chreTimerCancel(gEnableDisableTimerHandle)) {
346     LOGE("Error canceling BLE scan timer");
347   }
348 #ifdef BLE_WORLD_ENABLE_BATCHING
349   if (!chreTimerCancel(gFlushTimerHandle)) {
350     LOGE("Error canceling BLE flush timer");
351   }
352 #endif
353   if (!chreTimerCancel(gReadRssiTimerHandle)) {
354     LOGE("Error canceling RSSI read timer");
355   }
356   LOGI("nanoapp stopped");
357 }
358 
359 #ifdef CHRE_NANOAPP_INTERNAL
360 }  // anonymous namespace
361 }  // namespace chre
362 
363 #include "chre/platform/static_nanoapp_init.h"
364 #include "chre/util/nanoapp/app_id.h"
365 #include "chre/util/system/napp_permissions.h"
366 
367 CHRE_STATIC_NANOAPP_INIT(BleWorld, kBleWorldAppId, 0,
368                          NanoappPermissions::CHRE_PERMS_BLE);
369 #endif  // CHRE_NANOAPP_INTERNAL
370