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