1 /*
2 * Copyright (C) 2023 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
17 #include "location/lbs/contexthub/nanoapps/nearby/ble_scanner.h"
18
19 #include <chre.h>
20
21 #include <utility>
22
23 #include "third_party/contexthub/chre/util/include/chre/util/macros.h"
24 #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h"
25
26 #ifdef MOCK_BLE
27 #include "chre/util/time.h"
28 #include "location/lbs/contexthub/nanoapps/nearby/mock_ble.h"
29
30 uint32_t mock_ble_timer_id = CHRE_TIMER_INVALID;
31 uint32_t mock_ble_flush_complete_timer_id = CHRE_TIMER_INVALID;
32 #endif
33
34 #define LOG_TAG "[NEARBY][BLE_SCANNER]"
35
36 namespace nearby {
37 #ifdef MOCK_BLE
BleScanner()38 BleScanner::BleScanner() {
39 is_ble_scan_supported_ = true;
40 is_batch_supported_ = nearby::MockBle::kBleBatchScanSupported;
41 report_delay_ms_ = kBatchScanReportDelayLowPowerMilliSec;
42 }
43
Start()44 void BleScanner::Start() {
45 if (is_started_) {
46 LOGD("Mock BLE scan already started.");
47 return;
48 }
49 Restart();
50 }
51
Restart()52 void BleScanner::Restart() {
53 LOGD("Start mock BLE events in scan mode %d.", scan_mode_);
54 if (is_started_) {
55 chreTimerCancel(mock_ble_timer_id);
56 }
57 mock_ble_timer_id =
58 chreTimerSet(chre::Milliseconds(report_delay_ms_).toRawNanoseconds(),
59 &mock_ble_timer_id, false);
60 is_started_ = true;
61 }
62
Stop()63 void BleScanner::Stop() {
64 if (!is_started_) {
65 LOGD("Mock BLE scan already stopped.");
66 return;
67 }
68 LOGD("Stop mock BLE events.");
69 chreTimerCancel(mock_ble_timer_id);
70 if (mock_ble_flush_complete_timer_id != CHRE_TIMER_INVALID) {
71 chreTimerCancel(mock_ble_flush_complete_timer_id);
72 mock_ble_flush_complete_timer_id = CHRE_TIMER_INVALID;
73 }
74 is_started_ = false;
75 }
76
UpdateBatchDelay(uint32_t delay_ms)77 void BleScanner::UpdateBatchDelay(uint32_t delay_ms) {
78 bool is_updated = false;
79 if (!is_batch_supported_) {
80 LOGD("Batch scan is not supported");
81 return;
82 }
83 // avoids the report delay from being set too small for simulation
84 if (delay_ms < nearby::MockBle::kBleReportDelayMinMs) {
85 LOGE("Requested report delay is too small");
86 return;
87 }
88 if (report_delay_ms_ != delay_ms) {
89 report_delay_ms_ = delay_ms;
90 is_updated = true;
91 }
92 // restart scan with new parameter if scan is already started
93 if (is_updated && is_started_) {
94 Restart();
95 }
96 }
97
Flush()98 bool BleScanner::Flush() {
99 if (!is_batch_supported_) {
100 LOGD("Batch scan is not supported");
101 return false;
102 }
103 if (!is_started_) {
104 LOGD("Mock BLE scan was not started.");
105 return false;
106 }
107 if (IsFlushing()) {
108 LOGD("Flushing BLE scan is already in progress.");
109 return true;
110 }
111 // stops normal BLE scan result timer internally
112 chreTimerCancel(mock_ble_timer_id);
113 // simulates the flushed scan results
114 mock_ble_flush_complete_timer_id = chreTimerSet(
115 chre::Milliseconds(nearby::MockBle::kBleFlushCompleteTimeoutMs)
116 .toRawNanoseconds(),
117 &mock_ble_flush_complete_timer_id, true);
118 mock_ble_timer_id = chreTimerSet(
119 chre::Milliseconds(nearby::MockBle::kBleFlushScanResultIntervalMs)
120 .toRawNanoseconds(),
121 &mock_ble_timer_id, false);
122 is_batch_flushing_ = true;
123 return true;
124 }
125
HandleEvent(uint16_t event_type,const void * event_data)126 void BleScanner::HandleEvent(uint16_t event_type, const void *event_data) {
127 const chreAsyncResult *async_result;
128 switch (event_type) {
129 case CHRE_EVENT_BLE_FLUSH_COMPLETE:
130 async_result = static_cast<const chreAsyncResult *>(event_data);
131 LOGD("Received mock flush complete event: return_code(%u) cookie(%p)",
132 async_result->errorCode, async_result->cookie);
133 // stops the flushed scan results timer internally
134 chreTimerCancel(mock_ble_timer_id);
135 mock_ble_flush_complete_timer_id = CHRE_TIMER_INVALID;
136 is_batch_flushing_ = false;
137 if (is_started_) {
138 Restart();
139 }
140 break;
141 default:
142 LOGD("Unknown mock scan control event_type: %d", event_type);
143 }
144 }
145 #else
146 constexpr chreBleGenericFilter kDefaultGenericFilters[] = {
147 {
148 .type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE,
149 .len = 2,
150 // Fast Pair Service UUID in OTA format.
151 .data = {0x2c, 0xfe},
152 .dataMask = {0xff, 0xff},
153 },
154 {
155 .type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE,
156 .len = 2,
157 // Presence Service UUID in OTA format.
158 .data = {0xf1, 0xfc},
159 .dataMask = {0xff, 0xff},
160 }};
161
162 BleScanner::BleScanner() {
163 if (!(chreBleGetCapabilities() & CHRE_BLE_CAPABILITIES_SCAN)) {
164 LOGE("BLE scan not supported.");
165 is_ble_scan_supported_ = false;
166 }
167 if (!(chreBleGetFilterCapabilities() &
168 CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA)) {
169 LOGI("BLE filter by service UUID not supported.");
170 }
171 if (chreBleGetCapabilities() & CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING) {
172 is_batch_supported_ = true;
173 report_delay_ms_ = kBatchScanReportDelayLowPowerMilliSec;
174 }
175 }
176
177 void BleScanner::Start() {
178 if (is_started_) {
179 LOGD("BLE scan already started.");
180 return;
181 }
182 Restart();
183 }
184
185 static bool ContainsFilter(
186 const chre::DynamicVector<chreBleGenericFilter> &filters,
187 const chreBleGenericFilter &src) {
188 bool contained = false;
189 for (const auto &dst : filters) {
190 if (src.type == dst.type && src.len == dst.len) {
191 for (int i = 0; i < src.len; i++) {
192 if (src.data[i] != dst.data[i]) {
193 continue;
194 }
195 if (src.dataMask[i] != dst.dataMask[i]) {
196 continue;
197 }
198 }
199 contained = true;
200 break;
201 }
202 }
203 return contained;
204 }
205
206 void BleScanner::Restart() {
207 if (!is_ble_scan_supported_) {
208 LOGE("Failed to start BLE scan on an unsupported device");
209 return;
210 }
211 chre::DynamicVector<chreBleGenericFilter> generic_filters;
212 if (is_default_generic_filter_enabled_) {
213 for (size_t i = 0; i < ARRAY_SIZE(kDefaultGenericFilters); i++) {
214 generic_filters.push_back(kDefaultGenericFilters[i]);
215 }
216 }
217 for (auto &oem_generic_filters : generic_filters_list_) {
218 for (auto &generic_filter : oem_generic_filters.filters) {
219 if (!ContainsFilter(generic_filters, generic_filter)) {
220 generic_filters.push_back(generic_filter);
221 }
222 }
223 }
224 chreBleScanFilter scan_filter;
225 scan_filter.rssiThreshold = CHRE_BLE_RSSI_THRESHOLD_NONE;
226 scan_filter.scanFilters = generic_filters.data();
227 scan_filter.scanFilterCount = static_cast<uint8_t>(generic_filters.size());
228 if (chreBleStartScanAsync(scan_mode_, report_delay_ms_, &scan_filter)) {
229 LOGD("Succeeded to start BLE scan");
230 // is_started_ is set to true here, but it can be set back to false
231 // if CHRE_BLE_REQUEST_TYPE_START_SCAN request is failed in
232 // CHRE_EVENT_BLE_ASYNC_RESULT event.
233 is_started_ = true;
234 } else {
235 LOGE("Failed to start BLE scan");
236 }
237 }
238
239 void BleScanner::Stop() {
240 if (!is_started_) {
241 LOGD("BLE scan already stopped.");
242 return;
243 }
244 if (chreBleStopScanAsync()) {
245 LOGD("Succeeded Stop BLE scan.");
246 is_started_ = false;
247 } else {
248 LOGE("Failed to stop BLE scan");
249 }
250 }
251
252 bool BleScanner::UpdateFilters(
253 uint16_t host_end_point,
254 chre::DynamicVector<chreBleGenericFilter> *generic_filters) {
255 size_t index = 0;
256 while (index < generic_filters_list_.size()) {
257 if (generic_filters_list_[index].end_point == host_end_point) {
258 if (generic_filters->empty()) {
259 generic_filters_list_.erase(index);
260 } else {
261 generic_filters_list_[index].filters = std::move(*generic_filters);
262 }
263 return true;
264 }
265 ++index;
266 }
267 if (generic_filters_list_.push_back(GenericFilters(host_end_point))) {
268 generic_filters_list_.back().filters = std::move(*generic_filters);
269 } else {
270 LOGE("Failed to add new hardware filter.");
271 return false;
272 }
273 return true;
274 }
275
276 void BleScanner::UpdateBatchDelay(uint32_t delay_ms) {
277 bool is_updated = false;
278 if (!is_batch_supported_) {
279 LOGD("Batch scan is not supported");
280 return;
281 }
282 if (report_delay_ms_ != delay_ms) {
283 report_delay_ms_ = delay_ms;
284 is_updated = true;
285 }
286 // restart scan with new parameter if scan is already started
287 if (is_updated && is_started_) {
288 Restart();
289 }
290 }
291
292 bool BleScanner::Flush() {
293 if (!is_batch_supported_) {
294 LOGD("Batch scan is not supported");
295 return false;
296 }
297 if (!is_started_) {
298 LOGE("BLE scan was not started.");
299 return false;
300 }
301 if (IsFlushing()) {
302 LOGD("Flushing BLE scan is already in progress.");
303 return true;
304 }
305 LOGD("Flush batch scan results");
306 if (!chreBleFlushAsync(nullptr)) {
307 LOGE("Failed to call chreBleFlushAsync()");
308 return false;
309 }
310 is_batch_flushing_ = true;
311 return true;
312 }
313
314 void BleScanner::HandleEvent(uint16_t event_type, const void *event_data) {
315 const chreAsyncResult *async_result =
316 static_cast<const chreAsyncResult *>(event_data);
317 switch (event_type) {
318 case CHRE_EVENT_BLE_FLUSH_COMPLETE:
319 LOGD("Received flush complete event: return_code(%u) cookie(%p)",
320 async_result->errorCode, async_result->cookie);
321 if (async_result->errorCode != CHRE_ERROR_NONE) {
322 LOGE("Flush failed: %u", async_result->errorCode);
323 }
324 is_batch_flushing_ = false;
325 break;
326 case CHRE_EVENT_BLE_ASYNC_RESULT:
327 if (async_result->errorCode != CHRE_ERROR_NONE) {
328 LOGE(
329 "Failed to complete the async request: "
330 "request type (%u) error code(%u)",
331 async_result->requestType, async_result->errorCode);
332 if (async_result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN) {
333 LOGD("Failed in CHRE_BLE_REQUEST_TYPE_START_SCAN");
334 if (is_started_) {
335 is_started_ = false;
336 }
337 } else if (async_result->requestType ==
338 CHRE_BLE_REQUEST_TYPE_STOP_SCAN) {
339 LOGD("Failed in CHRE_BLE_REQUEST_TYPE_STOP_SCAN");
340 }
341 }
342 break;
343 default:
344 LOGD("Unknown scan control event_type: %d", event_type);
345 }
346 }
347 #endif /* end ifdef MOCK_BLE */
348
349 } // namespace nearby
350