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 #ifndef CHRE_CORE_WIFI_REQUEST_MANAGER_H_
18 #define CHRE_CORE_WIFI_REQUEST_MANAGER_H_
19 
20 #include "chre/core/nanoapp.h"
21 #include "chre/platform/platform_wifi.h"
22 #include "chre/util/buffer.h"
23 #include "chre/util/non_copyable.h"
24 #include "chre/util/optional.h"
25 #include "chre/util/system/debug_dump.h"
26 #include "chre/util/time.h"
27 #include "chre_api/chre/wifi.h"
28 
29 namespace chre {
30 
31 /**
32  * The WifiRequestManager handles requests from nanoapps for Wifi information.
33  * This includes multiplexing multiple requests into one for the platform to
34  * handle.
35  *
36  * This class is effectively a singleton as there can only be one instance of
37  * the PlatformWifi instance.
38  */
39 class WifiRequestManager : public NonCopyable {
40  public:
41   /**
42    * Initializes the WifiRequestManager with a default state and memory for any
43    * requests.
44    */
45   WifiRequestManager();
46 
47   /**
48    * Initializes the underlying platform-specific WiFi module. Must be called
49    * prior to invoking any other methods in this class.
50    */
51   void init();
52 
53   /**
54    * @return the WiFi capabilities exposed by this platform.
55    */
56   uint32_t getCapabilities();
57 
58   /**
59    * Handles a request from a nanoapp to configure the scan monitor. This
60    * includes merging multiple requests for scan monitoring to the PAL (ie: if
61    * multiple apps enable the scan monitor the PAL is only enabled once).
62    *
63    * @param nanoapp The nanoapp that has requested that the scan monitor be
64    *        configured.
65    * @param enable true to enable scan monitoring, false to disable scan
66    *        monitoring.
67    * @param cookie A cookie that is round-tripped back to the nanoapp to
68    *        provide a context when making the request.
69    *
70    * @return true if the request was accepted. The result is delivered
71    *         asynchronously through a CHRE event.
72    */
73   bool configureScanMonitor(Nanoapp *nanoapp, bool enable, const void *cookie);
74 
75   /**
76    * Handles a nanoapp's request for RTT ranging against a set of devices.
77    *
78    * @param nanoapp Nanoapp issuing the request.
79    * @param params Non-null pointer to parameters, supplied by the nanoapp via
80    *        chreWifiRequestRangingAsync()
81    * @param cookie Opaque pointer supplied by the nanoapp and passed back in the
82    *        async result.
83    *
84    * @return true if the request was accepted. The result is delivered
85    *         asynchronously through a CHRE event.
86    */
87   bool requestRanging(Nanoapp *nanoapp, const chreWifiRangingParams *params,
88                       const void *cookie);
89 
90   /**
91    * Performs an active wifi scan.
92    *
93    * This is currently a 1:1 mapping into the PAL. If more than one nanoapp
94    * requests an active wifi scan, this will be an assertion failure for debug
95    * builds and a no-op in production (ie: subsequent requests are ignored).
96    *
97    * @param nanoapp The nanoapp that has requested an active wifi scan.
98    * @param params Non-null pointer to the scan parameters structure supplied by
99    *        the nanoapp.
100    * @param cookie A cookie that is round-tripped back to the nanoapp to provide
101    *        a context when making the request.
102    *
103    * @return true if the request was accepted. The result is delivered
104    *         asynchronously through a CHRE event.
105    */
106   bool requestScan(Nanoapp *nanoapp, const chreWifiScanParams *params,
107                    const void *cookie);
108 
109   /**
110    * Passes the result of an RTT ranging request on to the requesting nanoapp.
111    *
112    * @param errorCode Value from enum chreError
113    * @param event Event containing ranging results, or null if errorCode is not
114    *        chreError
115    */
116   void handleRangingEvent(uint8_t errorCode,
117                           struct chreWifiRangingEvent *event);
118 
119   /**
120    * Handles the result of a request to PlatformWifi to change the state of the
121    * scan monitor.
122    *
123    * @param enabled true if the result of the operation was an enabled scan
124    *        monitor.
125    * @param errorCode an error code that is provided to indicate success or what
126    *        type of error has occurred. See the chreError enum in the CHRE API
127    *        for additional details.
128    */
129   void handleScanMonitorStateChange(bool enabled, uint8_t errorCode);
130 
131   /**
132    * Handles the result of a request to the PlatformWifi to request an active
133    * Wifi scan.
134    *
135    * @param pending The result of the request was successful and the results
136    *        be sent via the handleScanEvent method.
137    * @param errorCode an error code that is used to indicate success or what
138    *        type of error has occurred. See the chreError enum in the CHRE API
139    *        for additional details.
140    */
141   void handleScanResponse(bool pending, uint8_t errorCode);
142 
143   /**
144    * Handles a CHRE wifi scan event.
145    *
146    * @param event The wifi scan event provided to the wifi request manager. This
147    *        memory is guaranteed not to be modified until it has been explicitly
148    *        released through the PlatformWifi instance.
149    */
150   void handleScanEvent(struct chreWifiScanEvent *event);
151 
152   /**
153    * Prints state in a string buffer. Must only be called from the context of
154    * the main CHRE thread.
155    *
156    * @param debugDump The debug dump wrapper where a string can be printed
157    *     into one of the buffers.
158    */
159   void logStateToBuffer(DebugDumpWrapper &debugDump) const;
160 
161  private:
162   struct PendingRequestBase {
163     uint32_t nanoappInstanceId;  //!< ID of the Nanoapp issuing this request
164     const void *cookie;          //!< User data supplied by the nanoapp
165   };
166 
167   struct PendingRangingRequest : public PendingRequestBase {
168     //! If the request was queued, a variable-length list of devices to
169     //! perform ranging against (used to reconstruct chreWifiRangingParams)
170     Buffer<struct chreWifiRangingTarget> targetList;
171   };
172 
173   struct PendingScanMonitorRequest : public PendingRequestBase {
174     bool enable;  //!< Requested scan monitor state
175   };
176 
177   //! An internal struct to hold scan request data for logging
178   struct WifiScanRequestLog {
WifiScanRequestLogWifiScanRequestLog179     WifiScanRequestLog(Nanoseconds timestampIn, uint32_t instanceIdIn,
180                        chreWifiScanType scanTypeIn, Milliseconds maxScanAgeMsIn)
181         : timestamp(timestampIn),
182           instanceId(instanceIdIn),
183           scanType(scanTypeIn),
184           maxScanAgeMs(maxScanAgeMsIn) {}
185 
186     Nanoseconds timestamp;
187     uint32_t instanceId;
188     enum chreWifiScanType scanType;
189     Milliseconds maxScanAgeMs;
190   };
191 
192   static constexpr size_t kMaxScanMonitorStateTransitions = 8;
193   static constexpr size_t kMaxPendingRangingRequests = 4;
194 
195   PlatformWifi mPlatformWifi;
196 
197   //! The queue of state transition requests for the scan monitor. Only one
198   //! asynchronous scan monitor state transition can be in flight at one time.
199   //! Any further requests are queued here.
200   ArrayQueue<PendingScanMonitorRequest, kMaxScanMonitorStateTransitions>
201       mPendingScanMonitorRequests;
202 
203   //! The list of nanoapps who have enabled scan monitoring. This list is
204   //! maintained to ensure that nanoapps are always subscribed to wifi scan
205   //! results as requested. Note that a request for wifi scan monitoring can
206   //! exceed the duration of a single active wifi scan request. This makes it
207   //! insuitable only subscribe to wifi scan events when an active request is
208   //! made and the scan monitor must remain enabled when an active request has
209   //! completed.
210   DynamicVector<uint32_t> mScanMonitorNanoapps;
211 
212   // TODO: Support multiple requests for active wifi scans.
213   //! The instance ID of the nanoapp that has a pending active scan request. At
214   //! this time, only one nanoapp can have a pending request for an active WiFi
215   //! scan.
216   Optional<uint32_t> mScanRequestingNanoappInstanceId;
217 
218   //! The cookie passed in by a nanoapp making an active request for wifi scans.
219   //! Note that this will only be valid if the mScanRequestingNanoappInstanceId
220   //! is set.
221   const void *mScanRequestingNanoappCookie;
222 
223   //! This is set to true if the results of an active scan request are pending.
224   bool mScanRequestResultsArePending = false;
225 
226   //! Accumulates the number of scan event results to determine when the last
227   //! in a scan event stream has been received.
228   uint8_t mScanEventResultCountAccumulator = 0;
229 
230   //! System time when last scan request was made.
231   Nanoseconds mLastScanRequestTime;
232 
233   //! Tracks the in-flight ranging request and any others queued up behind it
234   ArrayQueue<PendingRangingRequest, kMaxPendingRangingRequests>
235       mPendingRangingRequests;
236 
237   //! List of most recent wifi scan request logs
238   static constexpr size_t kNumWifiRequestLogs = 10;
239   ArrayQueue<WifiScanRequestLog, kNumWifiRequestLogs> mWifiScanRequestLogs;
240 
241   //! Helps ensure we don't get stuck if platform isn't behaving as expected
242   Nanoseconds mRangingResponseTimeout;
243 
244   //! System time when the last WiFi scan event was received.
245   Milliseconds mLastScanEventTime;
246 
247   /**
248    * @return true if the scan monitor is enabled by any nanoapps.
249    */
250   bool scanMonitorIsEnabled() const;
251 
252   /**
253    * @param instanceId the instance ID of the nanoapp.
254    * @param index an optional pointer to a size_t to populate with the index of
255    *        the nanoapp in the list of nanoapps.
256    *
257    * @return true if the nanoapp has an active request for scan monitoring.
258    */
259   bool nanoappHasScanMonitorRequest(uint32_t instanceId,
260                                     size_t *index = nullptr) const;
261 
262   /**
263    * @param requestedState The requested state to compare against.
264    * @param nanoappHasRequest The requesting nanoapp has an existing request.
265    *
266    * @return true if the scan monitor is in the requested state.
267    */
268   bool scanMonitorIsInRequestedState(bool requestedState,
269                                      bool nanoappHasRequest) const;
270 
271   /**
272    * @param requestedState The requested state to compare against.
273    * @param nanoappHasRequest The requesting nanoapp has an existing request.
274    *
275    * @return true if a state transition is required to reach the requested
276    * state.
277    */
278   bool scanMonitorStateTransitionIsRequired(bool requestedState,
279                                             bool nanoappHasRequest) const;
280 
281   /**
282    * Builds a scan monitor state transition and adds it to the queue of incoming
283    * requests.
284    * @param nanoapp A non-null pointer to a nanoapp that is requesting the
285    *        change.
286    * @param enable The target requested scan monitoring state.
287    * @param cookie The pointer cookie passed in by the calling nanoapp to return
288    *        to the nanoapp when the request completes.
289    *
290    * @return true if the request is enqueued or false if the queue is full.
291    */
292   bool addScanMonitorRequestToQueue(Nanoapp *nanoapp, bool enable,
293                                     const void *cookie);
294 
295   /**
296    * Adds a nanoapp to the list of nanoapps that are monitoring for wifi scans.
297    * @param enable true if enabling scan monitoring.
298    * @param instanceId The instance ID of the scan monitoring nanoapp.
299    *
300    * @return true if the nanoapp was added to the list.
301    */
302   bool updateNanoappScanMonitoringList(bool enable, uint32_t instanceId);
303 
304   /**
305    * Posts an event to a nanoapp indicating the result of a wifi scan monitoring
306    * configuration change.
307    *
308    * @param nanoappInstanceId The nanoapp instance ID to direct the event to.
309    * @param success If the request for a wifi resource was successful.
310    * @param enable The target state of the request. If enable is set to false
311    *        and the request was successful, the nanoapp is removed from the
312    *        list of nanoapps requesting scan monitoring.
313    * @param errorCode The error code when success is set to false.
314    * @param cookie The cookie to be provided to the nanoapp. This is
315    *        round-tripped from the nanoapp to provide context.
316    *
317    * @return true if the event was successfully posted to the event loop.
318    */
319   bool postScanMonitorAsyncResultEvent(uint32_t nanoappInstanceId, bool success,
320                                        bool enable, uint8_t errorCode,
321                                        const void *cookie);
322 
323   /**
324    * Calls through to postScanMonitorAsyncResultEvent but invokes the
325    * FATAL_ERROR macro if the event is not posted successfully. This is used in
326    * asynchronous contexts where a nanoapp could be stuck waiting for a response
327    * but CHRE failed to enqueue one. For parameter details,
328    * @see postScanMonitorAsyncResultEvent
329    */
330   void postScanMonitorAsyncResultEventFatal(uint32_t nanoappInstanceId,
331                                             bool success, bool enable,
332                                             uint8_t errorCode,
333                                             const void *cookie);
334 
335   /**
336    * Posts an event to a nanoapp indicating the result of a request for an
337    * active wifi scan.
338    *
339    * @param nanoappInstanceId The nanoapp instance ID to direct the event to.
340    * @param success If the request for a wifi resource was successful.
341    * @param errorCode The error code when success is set to false.
342    * @param cookie The cookie to be provided to the nanoapp. This is
343    *        round-tripped from the nanoapp to provide context.
344    *
345    * @return true if the event was successfully posted to the event loop.
346    */
347   bool postScanRequestAsyncResultEvent(uint32_t nanoappInstanceId, bool success,
348                                        uint8_t errorCode, const void *cookie);
349 
350   /**
351    * Calls through to postScanRequestAsyncResultEvent but invokes the
352    * FATAL_ERROR macro if the event is not posted successfully. This is used in
353    * asynchronous contexts where a nanoapp could be stuck waiting for a response
354    * but CHRE failed to enqueue one. For parameter details,
355    * @see postScanRequestAsyncResultEvent
356    */
357   void postScanRequestAsyncResultEventFatal(uint32_t nanoappInstanceId,
358                                             bool success, uint8_t errorCode,
359                                             const void *cookie);
360 
361   /**
362    * Posts a broadcast event containing the results of a wifi scan. Failure to
363    * post this event is a FATAL_ERROR. This is unrecoverable as the nanoapp will
364    * be stuck waiting for wifi scan results but there may be a gap.
365    *
366    * @param event the wifi scan event.
367    */
368   void postScanEventFatal(chreWifiScanEvent *event);
369 
370   /**
371    * Handles the result of a request to PlatformWifi to change the state of the
372    * scan monitor. See the handleScanMonitorStateChange method which may be
373    * called from any thread. This method is intended to be invoked on the CHRE
374    * event loop thread.
375    *
376    * @param enabled true if the result of the operation was an enabled scan
377    *        monitor.
378    * @param errorCode an error code that is provided to indicate success or what
379    *        type of error has occurred. See the chreError enum in the CHRE API
380    *        for additional details.
381    */
382   void handleScanMonitorStateChangeSync(bool enabled, uint8_t errorCode);
383 
384   /**
385    * Handles the result of a request to PlatformWifi to perform an active WiFi
386    * scan. See the handleScanResponse method which may be called from any
387    * thread. This method is intended to be invoked on the CHRE event loop
388    * thread.
389    *
390    * @param enabled true if the result of the operation was an enabled scan
391    *        monitor.
392    * @param errorCode an error code that is provided to indicate success or what
393    *        type of error has occurred. See the chreError enum in the CHRE API
394    *        for additional details.
395    */
396   void handleScanResponseSync(bool pending, uint8_t errorCode);
397 
398   /**
399    * Sends CHRE_EVENT_WIFI_ASYNC_RESULT for the ranging request at the head of
400    * the pending queue.
401    *
402    * @param errorCode Indicates the overall result of the ranging operation
403    *
404    * @return true on success
405    */
406   bool postRangingAsyncResult(uint8_t errorCode);
407 
408   /**
409    * Issues the next pending ranging request to the platform.
410    *
411    * @return Result of PlatformWifi::requestRanging()
412    */
413   bool dispatchQueuedRangingRequest();
414 
415   /**
416    * Processes the result of a ranging request within the context of the CHRE
417    * thread.
418    *
419    * @param errorCode Result of the ranging operation
420    * @param event On success, pointer to event data provided by platform
421    */
422   void handleRangingEventSync(uint8_t errorCode,
423                               struct chreWifiRangingEvent *event);
424 
425   /**
426    * Handles the releasing of a WiFi scan event and unsubscribes a nanoapp who
427    * has made an active request for a wifi scan from WiFi scan events in the
428    * future (if it has not subscribed to passive events).
429    *
430    * @param scanEvent The scan event to release.
431    */
432   void handleFreeWifiScanEvent(chreWifiScanEvent *scanEvent);
433 
434   /**
435    * Adds a wifi scan request log onto list possibly kicking earliest log out
436    * if full.
437    *
438    * @param nanoappInstanceId The instance Id of the requesting nanoapp
439    * @param params The chre wifi scan params
440    */
441   void addWifiScanRequestLog(uint32_t nanoappInstanceId,
442                              const chreWifiScanParams *params);
443 
444   /**
445    * Releases a wifi scan event after nanoapps have consumed it.
446    *
447    * @param eventType the type of event being freed.
448    * @param eventData a pointer to the scan event to release.
449    */
450   static void freeWifiScanEventCallback(uint16_t eventType, void *eventData);
451   static void freeWifiRangingEventCallback(uint16_t eventType, void *eventData);
452 };
453 
454 }  // namespace chre
455 
456 #endif  // CHRE_CORE_WIFI_REQUEST_MANAGER_H_
457