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