• 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 
18 #include "chre/util/nanoapp/ble.h"
19 #include "chre/util/nanoapp/log.h"
20 #include "chre/util/time.h"
21 #include "chre_api/chre.h"
22 
23 /**
24  * @file
25  *
26  * This nanoapp is designed to continually start and stop BLE scans and verify
27  * that the expected data is delivered. BLE_WORLD_ENABLE_BATCHING can be defined
28  * to test batching and flushing if the nanoapp has the
29  * CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING capability. This will configure
30  * the BLE scans with a batch window and periodically make flush requests to get
31  * batched BLE scan result events.
32  */
33 
34 #ifdef CHRE_NANOAPP_INTERNAL
35 namespace chre {
36 namespace {
37 #endif  // CHRE_NANOAPP_INTERNAL
38 
39 using chre::ble_constants::kNumScanFilters;
40 
41 constexpr int8_t kDataTypeServiceData = 0x16;
42 
43 #ifdef BLE_WORLD_ENABLE_BATCHING
44 //! A timer handle to request the BLE flush.
45 uint32_t gFlushTimerHandle = 0;
46 //! The period to which to make the BLE flush request.
47 uint64_t gFlushPeriodNs = 7 * chre::kOneSecondInNanoseconds;
48 #endif  // BLE_WORLD_ENABLE_BATCHING
49 
50 //! Report delay for BLE scans.
51 uint32_t gBleBatchDurationMs = 0;
52 //! A timer handle to toggle enable/disable BLE scans.
53 uint32_t gEnableDisableTimerHandle = 0;
54 //! The period at which to enable/disable BLE scans.
55 uint64_t gEnableDisablePeriodNs = 10 * chre::kOneSecondInNanoseconds;
56 //! True if BLE scans are currently enabled
57 bool gBleEnabled = false;
58 
59 //! A timer handle to poll for RSSI.
60 uint32_t gReadRssiTimerHandle = CHRE_TIMER_INVALID;
61 //! A hardcoded connection handle on which the RSSI will be read
62 //! On the Broadcom controllers used by Pixel, if a connection is made
63 //! immediately after startup, it will be on this handle.
64 uint16_t gReadRssiConnectionHandle = 0x40;
65 //! The period at which to read RSSI of kConnectionHandle.
66 uint64_t gReadRssiPeriodNs = 3 * chre::kOneSecondInNanoseconds;
67 
enableBleScans()68 bool enableBleScans() {
69   struct chreBleScanFilter filter;
70   chreBleGenericFilter genericFilters[kNumScanFilters];
71   chre::createBleScanFilterForKnownBeacons(filter, genericFilters,
72                                            kNumScanFilters);
73   return chreBleStartScanAsync(CHRE_BLE_SCAN_MODE_BACKGROUND,
74                                gBleBatchDurationMs, &filter);
75 }
76 
disableBleScans()77 bool disableBleScans() {
78   return chreBleStopScanAsync();
79 }
80 
nanoappStart()81 bool nanoappStart() {
82   LOGI("BLE world from version 0x%08" PRIx32, chreGetVersion());
83   uint32_t capabilities = chreBleGetCapabilities();
84   LOGI("Got BLE capabilities 0x%" PRIx32, capabilities);
85 #ifdef BLE_WORLD_ENABLE_BATCHING
86   bool batchingAvailable =
87       ((capabilities & CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING) != 0);
88   if (!batchingAvailable) {
89     LOGE("BLE scan result batching is unavailable");
90   } else {
91     gBleBatchDurationMs = 5000;
92     LOGI("BLE batching enabled");
93   }
94 #endif  // BLE_WORLD_ENABLE_BATCHING
95   bool success = enableBleScans();
96   if (!success) {
97     LOGE("Failed to send BLE start scan request");
98   } else {
99     gEnableDisableTimerHandle =
100         chreTimerSet(gEnableDisablePeriodNs, &gEnableDisableTimerHandle,
101                      false /* oneShot */);
102     if (gEnableDisableTimerHandle == CHRE_TIMER_INVALID) {
103       LOGE("Could not set enable/disable timer");
104     }
105 
106 #ifdef BLE_WORLD_ENABLE_BATCHING
107     if (batchingAvailable) {
108       gFlushTimerHandle =
109           chreTimerSet(gFlushPeriodNs, &gFlushTimerHandle, false /* oneShot */);
110       if (gFlushTimerHandle == CHRE_TIMER_INVALID) {
111         LOGE("Could not set flush timer");
112       }
113     }
114 #endif  // BLE_WORLD_ENABLE_BATCHING
115   }
116 
117   if (capabilities & CHRE_BLE_CAPABILITIES_READ_RSSI) {
118     gReadRssiPeriodNs = chreTimerSet(gReadRssiPeriodNs, &gReadRssiTimerHandle,
119                                      false /* oneShot */);
120     if (gReadRssiTimerHandle == CHRE_TIMER_INVALID) {
121       LOGE("Could not set RSSI timer");
122     }
123   } else {
124     LOGW(
125         "Skipping RSSI read since CHRE_BLE_CAPABILITIES_READ_RSSI not "
126         "supported");
127   }
128 
129   return true;
130 }
131 
parseAdData(const uint8_t * data,uint16_t size)132 void parseAdData(const uint8_t *data, uint16_t size) {
133   for (uint16_t i = 0; i < size;) {
134     // First byte has the dvertisement data length.
135     uint16_t ad_data_length = data[i];
136     // Early termination with zero length advertisement.
137     if (ad_data_length == 0) break;
138     // Second byte has advertisement data type.
139     // Only retrieves service data for Nearby.
140     if (data[++i] == kDataTypeServiceData) {
141       // First two bytes of the service data are service data UUID in little
142       // endian.
143       uint16_t uuid = static_cast<uint16_t>(data[i + 1] + (data[i + 2] << 8));
144       LOGD("Service Data UUID: %" PRIx16, uuid);
145     }
146     // Moves to next advertisement.
147     i += ad_data_length;
148   }
149 }
150 
handleAsyncResultEvent(const chreAsyncResult * result)151 void handleAsyncResultEvent(const chreAsyncResult *result) {
152   const char *requestType =
153       result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN ? "start"
154                                                               : "stop";
155   if (result->success) {
156     LOGI("BLE %s scan success", requestType);
157     gBleEnabled = (result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN);
158   } else {
159     LOGE("BLE %s scan failure: %" PRIu8, requestType, result->errorCode);
160   }
161 }
162 
handleAdvertismentEvent(const chreBleAdvertisementEvent * event)163 void handleAdvertismentEvent(const chreBleAdvertisementEvent *event) {
164   for (uint8_t i = 0; i < event->numReports; i++) {
165     LOGD("BLE Report %" PRIu32, static_cast<uint32_t>(i + 1));
166     LOGD("Event type and data status: 0x%" PRIx8,
167          event->reports[i].eventTypeAndDataStatus);
168     LOGD("Timestamp: %" PRIu64 " ms",
169          event->reports[i].timestamp / chre::kOneMillisecondInNanoseconds);
170     parseAdData(event->reports[i].data, event->reports[i].dataLength);
171   }
172 }
173 
handleTimerEvent(const void * cookie)174 void handleTimerEvent(const void *cookie) {
175   if (cookie == &gEnableDisableTimerHandle) {
176     bool success = false;
177     if (!gBleEnabled) {
178       success = enableBleScans();
179     } else {
180       success = disableBleScans();
181     }
182     if (!success) {
183       LOGE("Failed to send BLE %s scan request",
184            !gBleEnabled ? "start" : "stop");
185     }
186 #ifdef BLE_WORLD_ENABLE_BATCHING
187   } else if (cookie == &gFlushTimerHandle) {
188     if (gBleEnabled) {
189       if (!chreBleFlushAsync(nullptr /* cookie */)) {
190         LOGE("Could not send flush request");
191       } else {
192         LOGI("Successfully sent flush request at time %" PRIu64 " ms",
193              chreGetTime() / chre::kOneMillisecondInNanoseconds);
194       }
195     }
196 #endif  // BLE_WORLD_ENABLE_BATCHING
197   } else if (cookie == &gReadRssiTimerHandle) {
198     bool success = chreBleReadRssiAsync(gReadRssiConnectionHandle, nullptr);
199     LOGI("Reading RSSI for handle 0x%" PRIx16 ", status=%d",
200          gReadRssiConnectionHandle, success);
201   } else {
202     LOGE("Received unknown timer cookie %p", cookie);
203   }
204 }
205 
handleRssiEvent(const chreBleReadRssiEvent * event)206 void handleRssiEvent(const chreBleReadRssiEvent *event) {
207   LOGI("Received RSSI Read with status 0x%" PRIx8 " and rssi %" PRIi8,
208        event->result.errorCode, event->rssi);
209 }
210 
handleBatchCompleteEvent(const chreBatchCompleteEvent * event)211 void handleBatchCompleteEvent(const chreBatchCompleteEvent *event) {
212   LOGI("Received Batch complete event with event type %" PRIu16,
213        event->eventType);
214 }
215 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)216 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
217                         const void *eventData) {
218   LOGI("Received event 0x%" PRIx16 " from 0x%" PRIx32 " at time %" PRIu64 " ms",
219        eventType, senderInstanceId,
220        chreGetTime() / chre::kOneMillisecondInNanoseconds);
221   switch (eventType) {
222     case CHRE_EVENT_BLE_ADVERTISEMENT:
223       handleAdvertismentEvent(
224           static_cast<const chreBleAdvertisementEvent *>(eventData));
225       break;
226     case CHRE_EVENT_BLE_ASYNC_RESULT:
227       handleAsyncResultEvent(static_cast<const chreAsyncResult *>(eventData));
228       break;
229     case CHRE_EVENT_TIMER:
230       handleTimerEvent(eventData);
231       break;
232     case CHRE_EVENT_BLE_FLUSH_COMPLETE:
233       LOGI("Received flush complete");
234       break;
235     case CHRE_EVENT_BLE_RSSI_READ:
236       handleRssiEvent(static_cast<const chreBleReadRssiEvent *>(eventData));
237       break;
238     case CHRE_EVENT_BLE_BATCH_COMPLETE:
239       handleBatchCompleteEvent(
240           static_cast<const chreBatchCompleteEvent *>(eventData));
241       break;
242     default:
243       LOGW("Unhandled event type %" PRIu16, eventType);
244       break;
245   }
246 }
247 
nanoappEnd()248 void nanoappEnd() {
249   if (gBleEnabled && !chreBleStopScanAsync()) {
250     LOGE("Error sending BLE stop scan request sent to PAL");
251   }
252   if (!chreTimerCancel(gEnableDisableTimerHandle)) {
253     LOGE("Error canceling BLE scan timer");
254   }
255 #ifdef BLE_WORLD_ENABLE_BATCHING
256   if (!chreTimerCancel(gFlushTimerHandle)) {
257     LOGE("Error canceling BLE flush timer");
258   }
259 #endif
260   if (!chreTimerCancel(gReadRssiTimerHandle)) {
261     LOGE("Error canceling RSSI read timer");
262   }
263   LOGI("nanoapp stopped");
264 }
265 
266 #ifdef CHRE_NANOAPP_INTERNAL
267 }  // anonymous namespace
268 }  // namespace chre
269 
270 #include "chre/platform/static_nanoapp_init.h"
271 #include "chre/util/nanoapp/app_id.h"
272 #include "chre/util/system/napp_permissions.h"
273 
274 CHRE_STATIC_NANOAPP_INIT(BleWorld, kBleWorldAppId, 0,
275                          NanoappPermissions::CHRE_PERMS_BLE);
276 #endif  // CHRE_NANOAPP_INTERNAL
277