1 /*
2 * Copyright (C) 2016 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 <cinttypes>
18
19 #include "chre/core/event_loop_manager.h"
20 #include "chre/core/wifi_request_manager.h"
21 #include "chre/platform/fatal_error.h"
22 #include "chre/platform/log.h"
23
24 namespace chre {
25
WifiRequestManager()26 WifiRequestManager::WifiRequestManager() {
27 // Reserve space for at least one scan monitoring nanoapp. This ensures that
28 // the first asynchronous push_back will succeed. Future push_backs will be
29 // synchronous and failures will be returned to the client.
30 if (!mScanMonitorNanoapps.reserve(1)) {
31 FATAL_ERROR("Failed to allocate scan monitoring nanoapps list at startup");
32 }
33 }
34
getCapabilities()35 uint32_t WifiRequestManager::getCapabilities() {
36 return mPlatformWifi.getCapabilities();
37 }
38
configureScanMonitor(Nanoapp * nanoapp,bool enable,const void * cookie)39 bool WifiRequestManager::configureScanMonitor(Nanoapp *nanoapp, bool enable,
40 const void *cookie) {
41 CHRE_ASSERT(nanoapp);
42
43 bool success = false;
44 uint32_t instanceId = nanoapp->getInstanceId();
45 if (!mScanMonitorStateTransitions.empty()) {
46 success = addScanMonitorRequestToQueue(nanoapp, enable, cookie);
47 } else if (scanMonitorIsInRequestedState(enable, instanceId)) {
48 // The scan monitor is already in the requested state. A success event can
49 // be posted immediately.
50 success = postScanMonitorAsyncResultEvent(instanceId, true /* success */,
51 enable, CHRE_ERROR_NONE, cookie);
52 } else if (scanMonitorStateTransitionIsRequired(enable, instanceId)) {
53 success = addScanMonitorRequestToQueue(nanoapp, enable, cookie);
54 if (success) {
55 success = mPlatformWifi.configureScanMonitor(enable);
56 if (!success) {
57 // TODO: Add a pop_back method.
58 mScanMonitorStateTransitions.remove(
59 mScanMonitorStateTransitions.size() - 1);
60 LOGE("Failed to enable the scan monitor for nanoapp instance %" PRIu32,
61 instanceId);
62 }
63 }
64 } else {
65 CHRE_ASSERT_LOG(false, "Invalid scan monitor configuration");
66 }
67
68 return success;
69 }
70
requestScan(Nanoapp * nanoapp,const chreWifiScanParams * params,const void * cookie)71 bool WifiRequestManager::requestScan(Nanoapp *nanoapp,
72 const chreWifiScanParams *params,
73 const void *cookie) {
74 CHRE_ASSERT(nanoapp);
75
76 bool success = false;
77 if (!mScanRequestingNanoappInstanceId.has_value()) {
78 success = mPlatformWifi.requestScan(params);
79 if (success) {
80 mScanRequestingNanoappInstanceId = nanoapp->getInstanceId();
81 mScanRequestingNanoappCookie = cookie;
82 }
83 } else {
84 LOGE("Active wifi scan request made while a request is in flight");
85 }
86
87 return success;
88 }
89
handleScanMonitorStateChange(bool enabled,uint8_t errorCode)90 void WifiRequestManager::handleScanMonitorStateChange(bool enabled,
91 uint8_t errorCode) {
92 struct CallbackState {
93 bool enabled;
94 uint8_t errorCode;
95 };
96
97 auto *cbState = memoryAlloc<CallbackState>();
98 if (cbState == nullptr) {
99 LOGE("Failed to allocate callback state for scan monitor state change");
100 } else {
101 cbState->enabled = enabled;
102 cbState->errorCode = errorCode;
103
104 auto callback = [](uint16_t /*eventType*/, void *eventData) {
105 auto *state = static_cast<CallbackState *>(eventData);
106 EventLoopManagerSingleton::get()->getWifiRequestManager()
107 .handleScanMonitorStateChangeSync(state->enabled, state->errorCode);
108 memoryFree(state);
109 };
110
111 EventLoopManagerSingleton::get()->deferCallback(
112 SystemCallbackType::WifiScanMonitorStateChange, cbState, callback);
113 }
114 }
115
handleScanResponse(bool pending,uint8_t errorCode)116 void WifiRequestManager::handleScanResponse(bool pending,
117 uint8_t errorCode) {
118 struct CallbackState {
119 bool pending;
120 uint8_t errorCode;
121 };
122
123 auto *cbState = memoryAlloc<CallbackState>();
124 if (cbState == nullptr) {
125 LOGE("Failed to allocate callback state for wifi scan response");
126 } else {
127 cbState->pending = pending;
128 cbState->errorCode = errorCode;
129
130 auto callback = [](uint16_t /*eventType*/, void *eventData) {
131 auto *state = static_cast<CallbackState *>(eventData);
132 EventLoopManagerSingleton::get()->getWifiRequestManager()
133 .handleScanResponseSync(state->pending, state->errorCode);
134 memoryFree(state);
135 };
136
137 EventLoopManagerSingleton::get()->deferCallback(
138 SystemCallbackType::WifiRequestScanResponse, cbState, callback);
139 }
140 }
141
handleScanEvent(chreWifiScanEvent * event)142 void WifiRequestManager::handleScanEvent(chreWifiScanEvent *event) {
143 auto callback = [](uint16_t eventType, void *eventData) {
144 chreWifiScanEvent *scanEvent = static_cast<chreWifiScanEvent *>(eventData);
145 EventLoopManagerSingleton::get()->getWifiRequestManager()
146 .handleScanEventSync(scanEvent);
147 };
148
149 EventLoopManagerSingleton::get()->deferCallback(
150 SystemCallbackType::WifiHandleScanEvent, event, callback);
151 }
152
scanMonitorIsEnabled() const153 bool WifiRequestManager::scanMonitorIsEnabled() const {
154 return !mScanMonitorNanoapps.empty();
155 }
156
nanoappHasScanMonitorRequest(uint32_t instanceId,size_t * nanoappIndex) const157 bool WifiRequestManager::nanoappHasScanMonitorRequest(
158 uint32_t instanceId, size_t *nanoappIndex) const {
159 size_t index = mScanMonitorNanoapps.find(instanceId);
160 bool hasScanMonitorRequest = (index != mScanMonitorNanoapps.size());
161 if (hasScanMonitorRequest && nanoappIndex != nullptr) {
162 *nanoappIndex = index;
163 }
164
165 return hasScanMonitorRequest;
166 }
167
scanMonitorIsInRequestedState(bool requestedState,bool nanoappHasRequest) const168 bool WifiRequestManager::scanMonitorIsInRequestedState(
169 bool requestedState, bool nanoappHasRequest) const {
170 return (requestedState == scanMonitorIsEnabled() || (!requestedState
171 && (!nanoappHasRequest || mScanMonitorNanoapps.size() > 1)));
172 }
173
scanMonitorStateTransitionIsRequired(bool requestedState,bool nanoappHasRequest) const174 bool WifiRequestManager::scanMonitorStateTransitionIsRequired(
175 bool requestedState, bool nanoappHasRequest) const {
176 return ((requestedState && mScanMonitorNanoapps.empty())
177 || (!requestedState && nanoappHasRequest
178 && mScanMonitorNanoapps.size() == 1));
179 }
180
addScanMonitorRequestToQueue(Nanoapp * nanoapp,bool enable,const void * cookie)181 bool WifiRequestManager::addScanMonitorRequestToQueue(Nanoapp *nanoapp,
182 bool enable,
183 const void *cookie) {
184 ScanMonitorStateTransition scanMonitorStateTransition;
185 scanMonitorStateTransition.nanoappInstanceId = nanoapp->getInstanceId();
186 scanMonitorStateTransition.cookie = cookie;
187 scanMonitorStateTransition.enable = enable;
188
189 bool success = mScanMonitorStateTransitions.push(scanMonitorStateTransition);
190 if (!success) {
191 LOGW("Too many scan monitor state transitions");
192 }
193
194 return success;
195 }
196
updateNanoappScanMonitoringList(bool enable,uint32_t instanceId)197 bool WifiRequestManager::updateNanoappScanMonitoringList(bool enable,
198 uint32_t instanceId) {
199 bool success = true;
200 Nanoapp *nanoapp = EventLoopManagerSingleton::get()->
201 findNanoappByInstanceId(instanceId);
202 if (nanoapp == nullptr) {
203 CHRE_ASSERT_LOG(false, "Failed to update scan monitoring list for "
204 "non-existent nanoapp");
205 } else {
206 size_t nanoappIndex;
207 bool hasExistingRequest = nanoappHasScanMonitorRequest(instanceId,
208 &nanoappIndex);
209 if (enable) {
210 if (!hasExistingRequest) {
211 success = nanoapp->registerForBroadcastEvent(
212 CHRE_EVENT_WIFI_SCAN_RESULT);
213 if (!success) {
214 LOGE("Failed to register nanoapp for wifi scan events");
215 } else {
216 // The scan monitor was successfully enabled for this nanoapp and
217 // there is no existing request. Add it to the list of scan monitoring
218 // nanoapps.
219 success = mScanMonitorNanoapps.push_back(instanceId);
220 if (!success) {
221 nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
222 LOGE("Failed to add nanoapp to the list of scan monitoring "
223 "nanoapps");
224 }
225 }
226 }
227 } else {
228 if (!hasExistingRequest) {
229 success = false;
230 LOGE("Received a scan monitor state change for a non-existent nanoapp");
231 } else {
232 // The scan monitor was successfully disabled for a previously enabled
233 // nanoapp. Remove it from the list of scan monitoring nanoapps.
234 mScanMonitorNanoapps.erase(nanoappIndex);
235 nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
236 }
237 }
238 }
239
240 return success;
241 }
242
postScanMonitorAsyncResultEvent(uint32_t nanoappInstanceId,bool success,bool enable,uint8_t errorCode,const void * cookie)243 bool WifiRequestManager::postScanMonitorAsyncResultEvent(
244 uint32_t nanoappInstanceId, bool success, bool enable, uint8_t errorCode,
245 const void *cookie) {
246 // Allocate and post an event to the nanoapp requesting wifi.
247 bool eventPosted = false;
248 if (!success || updateNanoappScanMonitoringList(enable, nanoappInstanceId)) {
249 chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
250 if (event == nullptr) {
251 LOGE("Failed to allocate wifi scan monitor async result event");
252 } else {
253 event->requestType = CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR;
254 event->success = success;
255 event->errorCode = errorCode;
256 event->reserved = 0;
257 event->cookie = cookie;
258
259 // Post the event.
260 eventPosted = EventLoopManagerSingleton::get()->postEvent(
261 CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeWifiAsyncResultCallback,
262 kSystemInstanceId, nanoappInstanceId);
263 }
264 }
265
266 return eventPosted;
267 }
268
postScanMonitorAsyncResultEventFatal(uint32_t nanoappInstanceId,bool success,bool enable,uint8_t errorCode,const void * cookie)269 void WifiRequestManager::postScanMonitorAsyncResultEventFatal(
270 uint32_t nanoappInstanceId, bool success, bool enable, uint8_t errorCode,
271 const void *cookie) {
272 if (!postScanMonitorAsyncResultEvent(nanoappInstanceId, success, enable,
273 errorCode, cookie)) {
274 FATAL_ERROR("Failed to send WiFi scan monitor async result event");
275 }
276 }
277
postScanRequestAsyncResultEvent(uint32_t nanoappInstanceId,bool success,uint8_t errorCode,const void * cookie)278 bool WifiRequestManager::postScanRequestAsyncResultEvent(
279 uint32_t nanoappInstanceId, bool success, uint8_t errorCode,
280 const void *cookie) {
281 bool eventPosted = false;
282 chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
283 if (event == nullptr) {
284 LOGE("Failed to allocate wifi scan request async result event");
285 } else {
286 event->requestType = CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN;
287 event->success = success;
288 event->errorCode = errorCode;
289 event->reserved = 0;
290 event->cookie = cookie;
291
292 // Post the event.
293 eventPosted = EventLoopManagerSingleton::get()->postEvent(
294 CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeWifiAsyncResultCallback,
295 kSystemInstanceId, nanoappInstanceId);
296 }
297
298 return eventPosted;
299 }
300
postScanRequestAsyncResultEventFatal(uint32_t nanoappInstanceId,bool success,uint8_t errorCode,const void * cookie)301 void WifiRequestManager::postScanRequestAsyncResultEventFatal(
302 uint32_t nanoappInstanceId, bool success, uint8_t errorCode,
303 const void *cookie) {
304 if (!postScanRequestAsyncResultEvent(nanoappInstanceId, success, errorCode,
305 cookie)) {
306 FATAL_ERROR("Failed to send WiFi scan request async result event");
307 }
308 }
309
postScanEventFatal(chreWifiScanEvent * event)310 void WifiRequestManager::postScanEventFatal(chreWifiScanEvent *event) {
311 bool eventPosted = EventLoopManagerSingleton::get()->postEvent(
312 CHRE_EVENT_WIFI_SCAN_RESULT, event, freeWifiScanEventCallback,
313 kSystemInstanceId, kBroadcastInstanceId);
314 if (!eventPosted) {
315 FATAL_ERROR("Failed to send WiFi scan event");
316 }
317 }
318
handleScanMonitorStateChangeSync(bool enabled,uint8_t errorCode)319 void WifiRequestManager::handleScanMonitorStateChangeSync(bool enabled,
320 uint8_t errorCode) {
321 // Success is defined as having no errors ... in life ༼ つ ◕_◕ ༽つ
322 bool success = (errorCode == CHRE_ERROR_NONE);
323
324 // Always check the front of the queue.
325 CHRE_ASSERT_LOG(!mScanMonitorStateTransitions.empty(),
326 "handleScanMonitorStateChangeSync called with no transitions");
327 if (!mScanMonitorStateTransitions.empty()) {
328 const auto& stateTransition = mScanMonitorStateTransitions.front();
329 success &= (stateTransition.enable == enabled);
330 postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId,
331 success, stateTransition.enable,
332 errorCode, stateTransition.cookie);
333 mScanMonitorStateTransitions.pop();
334 }
335
336 while (!mScanMonitorStateTransitions.empty()) {
337 const auto& stateTransition = mScanMonitorStateTransitions.front();
338 bool hasScanMonitorRequest = nanoappHasScanMonitorRequest(
339 stateTransition.nanoappInstanceId);
340 if (scanMonitorIsInRequestedState(
341 stateTransition.enable, hasScanMonitorRequest)) {
342 // We are already in the target state so just post an event indicating
343 // success
344 postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId,
345 success, stateTransition.enable,
346 errorCode, stateTransition.cookie);
347 } else if (scanMonitorStateTransitionIsRequired(
348 stateTransition.enable, hasScanMonitorRequest)) {
349 if (mPlatformWifi.configureScanMonitor(stateTransition.enable)) {
350 break;
351 } else {
352 postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId,
353 false /* success */,
354 stateTransition.enable, CHRE_ERROR,
355 stateTransition.cookie);
356 }
357 } else {
358 CHRE_ASSERT_LOG(false, "Invalid scan monitor state");
359 break;
360 }
361
362 mScanMonitorStateTransitions.pop();
363 }
364 }
365
handleScanResponseSync(bool pending,uint8_t errorCode)366 void WifiRequestManager::handleScanResponseSync(bool pending,
367 uint8_t errorCode) {
368 CHRE_ASSERT_LOG(mScanRequestingNanoappInstanceId.has_value(),
369 "handleScanResponseSync called with no outstanding request");
370 if (mScanRequestingNanoappInstanceId.has_value()) {
371 bool success = (pending && errorCode == CHRE_ERROR_NONE);
372 postScanRequestAsyncResultEventFatal(*mScanRequestingNanoappInstanceId,
373 success, errorCode,
374 mScanRequestingNanoappCookie);
375
376 // Set a flag to indicate that results may be pending.
377 mScanRequestResultsArePending = pending;
378
379 if (pending) {
380 Nanoapp *nanoapp = EventLoopManagerSingleton::get()->
381 findNanoappByInstanceId(*mScanRequestingNanoappInstanceId);
382 if (nanoapp == nullptr) {
383 CHRE_ASSERT_LOG(false, "Received WiFi scan response for unknown "
384 "nanoapp");
385 } else {
386 nanoapp->registerForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
387 }
388 } else {
389 // If the scan results are not pending, clear the nanoapp instance ID.
390 // Otherwise, wait for the results to be delivered and then clear the
391 // instance ID.
392 mScanRequestingNanoappInstanceId.reset();
393 }
394 }
395 }
396
handleScanEventSync(chreWifiScanEvent * event)397 void WifiRequestManager::handleScanEventSync(chreWifiScanEvent *event) {
398 if (mScanRequestResultsArePending) {
399 // Reset the event distribution logic once an entire scan event has been
400 // received.
401 mScanEventResultCountAccumulator += event->resultCount;
402 if (mScanEventResultCountAccumulator >= event->resultTotal) {
403 mScanEventResultCountAccumulator = 0;
404 mScanRequestResultsArePending = false;
405 }
406 }
407
408 postScanEventFatal(event);
409 }
410
handleFreeWifiScanEvent(chreWifiScanEvent * scanEvent)411 void WifiRequestManager::handleFreeWifiScanEvent(chreWifiScanEvent *scanEvent) {
412 mPlatformWifi.releaseScanEvent(scanEvent);
413
414 if (!mScanRequestResultsArePending
415 && mScanRequestingNanoappInstanceId.has_value()) {
416 Nanoapp *nanoapp = EventLoopManagerSingleton::get()->
417 findNanoappByInstanceId(*mScanRequestingNanoappInstanceId);
418 if (nanoapp == nullptr) {
419 CHRE_ASSERT_LOG(false, "Attempted to unsubscribe unknown nanoapp from "
420 "WiFi scan events");
421 } else if (!nanoappHasScanMonitorRequest(*mScanRequestingNanoappInstanceId)) {
422 nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
423 }
424
425 mScanRequestingNanoappInstanceId.reset();
426 }
427 }
428
freeWifiAsyncResultCallback(uint16_t eventType,void * eventData)429 void WifiRequestManager::freeWifiAsyncResultCallback(uint16_t eventType,
430 void *eventData) {
431 memoryFree(eventData);
432 }
433
freeWifiScanEventCallback(uint16_t eventType,void * eventData)434 void WifiRequestManager::freeWifiScanEventCallback(uint16_t eventType,
435 void *eventData) {
436 chreWifiScanEvent *scanEvent = static_cast<chreWifiScanEvent *>(eventData);
437 EventLoopManagerSingleton::get()->getWifiRequestManager()
438 .handleFreeWifiScanEvent(scanEvent);
439 }
440
441 } // namespace chre
442