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