1 /*
2  * Copyright (C) 2020 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 "chre/pal/util/wifi_scan_cache.h"
18 
19 #include <inttypes.h>
20 
21 #include "chre/util/macros.h"
22 
23 /************************************************
24  *  Prototypes
25  ***********************************************/
26 
27 struct chreWifiScanCacheState {
28   //! true if the scan cache has started, i.e. chreWifiScanCacheScanEventBegin
29   //! was invoked and has not yet ended.
30   bool started;
31 
32   //! true if the current scan cache is a result of a CHRE active scan request.
33   bool activeScanResult;
34 
35   //! The number of chreWifiScanResults dropped due to OOM.
36   uint16_t numWifiScanResultsDropped;
37 
38   //! Stores the WiFi cache elements
39   struct chreWifiScanEvent event;
40   struct chreWifiScanResult resultList[CHRE_PAL_WIFI_SCAN_CACHE_CAPACITY];
41 
42   //! The number of chreWifiScanEvent data pending release via
43   //! chreWifiScanCacheReleaseScanEvent().
44   uint8_t numWifiEventsPendingRelease;
45 
46   bool scanMonitoringEnabled;
47 
48   uint32_t scannedFreqList[CHRE_WIFI_FREQUENCY_LIST_MAX_LEN];
49 };
50 
51 /************************************************
52  *  Global variables
53  ***********************************************/
54 static const struct chrePalSystemApi *gSystemApi = NULL;
55 static const struct chrePalWifiCallbacks *gCallbacks = NULL;
56 
57 static struct chreWifiScanCacheState gWifiCacheState;
58 
59 //! true if scan monitoring is enabled via
60 //! chreWifiScanCacheConfigureScanMonitor().
61 static bool gScanMonitoringEnabled;
62 
63 static const uint64_t kOneMillisecondInNanoseconds = UINT64_C(1000000);
64 
65 /************************************************
66  *  Private functions
67  ***********************************************/
chreWifiScanCacheIsInitialized(void)68 static bool chreWifiScanCacheIsInitialized(void) {
69   return (gSystemApi != NULL && gCallbacks != NULL);
70 }
71 
areAllScanEventsReleased(void)72 static bool areAllScanEventsReleased(void) {
73   return gWifiCacheState.numWifiEventsPendingRelease == 0;
74 }
75 
isFrequencyListValid(const uint32_t * frequencyList,uint16_t frequencyListLen)76 static bool isFrequencyListValid(const uint32_t *frequencyList,
77                                  uint16_t frequencyListLen) {
78   return (frequencyListLen == 0) || (frequencyList != NULL);
79 }
80 
paramsMatchScanCache(const struct chreWifiScanParams * params)81 static bool paramsMatchScanCache(const struct chreWifiScanParams *params) {
82   uint64_t timeNs = gWifiCacheState.event.referenceTime;
83   bool scan_within_age =
84       (timeNs >= gSystemApi->getCurrentTime() -
85                      (params->maxScanAgeMs * kOneMillisecondInNanoseconds));
86 
87   // Perform a conservative check for the params and scan cache.
88   // TODO(b/174510035): Consider optimizing for the case for channelSet ==
89   // CHRE_WIFI_CHANNEL_SET_ALL.
90   bool params_non_dfs =
91       (params->scanType == CHRE_WIFI_SCAN_TYPE_ACTIVE) ||
92       ((params->scanType == CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE) &&
93        (params->channelSet == CHRE_WIFI_CHANNEL_SET_NON_DFS));
94   bool cache_non_dfs =
95       (gWifiCacheState.event.scanType == CHRE_WIFI_SCAN_TYPE_ACTIVE) ||
96       (gWifiCacheState.event.scanType == CHRE_WIFI_SCAN_TYPE_PASSIVE);
97 
98   bool cache_all_freq = (gWifiCacheState.event.scannedFreqListLen == 0);
99   bool cache_all_ssid = (gWifiCacheState.event.ssidSetSize == 0);
100 
101   return scan_within_age && (params_non_dfs || !cache_non_dfs) &&
102          cache_all_freq && cache_all_ssid;
103 }
104 
isWifiScanCacheBusy(bool logOnBusy)105 static bool isWifiScanCacheBusy(bool logOnBusy) {
106   bool busy = true;
107   if (gWifiCacheState.started) {
108     if (logOnBusy) {
109       gSystemApi->log(CHRE_LOG_ERROR, "Scan cache already started");
110     }
111   } else if (!areAllScanEventsReleased()) {
112     if (logOnBusy) {
113       gSystemApi->log(CHRE_LOG_ERROR, "Scan cache events pending release");
114     }
115   } else {
116     busy = false;
117   }
118 
119   return busy;
120 }
121 
chreWifiScanCacheDispatchAll(void)122 static void chreWifiScanCacheDispatchAll(void) {
123   gSystemApi->log(CHRE_LOG_DEBUG, "Dispatching %" PRIu8 " events",
124                   gWifiCacheState.event.resultTotal);
125   if (gWifiCacheState.event.resultTotal == 0) {
126     gWifiCacheState.event.eventIndex = 0;
127     gWifiCacheState.event.resultCount = 0;
128     gWifiCacheState.event.results = NULL;
129     gCallbacks->scanEventCallback(&gWifiCacheState.event);
130   } else {
131     uint8_t eventIndex = 0;
132     for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal;
133          i += CHRE_PAL_WIFI_SCAN_CACHE_MAX_RESULT_COUNT) {
134       gWifiCacheState.event.resultCount =
135           MIN(CHRE_PAL_WIFI_SCAN_CACHE_MAX_RESULT_COUNT,
136               (uint8_t)(gWifiCacheState.event.resultTotal - i));
137       gWifiCacheState.event.eventIndex = eventIndex++;
138       gWifiCacheState.event.results = &gWifiCacheState.resultList[i];
139 
140       // TODO(b/174511061): The current approach only works for situations where
141       // the event is released immediately. Add a way to handle this scenario
142       // (e.g. an array of chreWifiScanEvent's).
143       gWifiCacheState.numWifiEventsPendingRelease++;
144       gCallbacks->scanEventCallback(&gWifiCacheState.event);
145     }
146   }
147 }
148 
isWifiScanResultInCache(const struct chreWifiScanResult * result,size_t * index)149 static bool isWifiScanResultInCache(const struct chreWifiScanResult *result,
150                                     size_t *index) {
151   for (uint8_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
152     const struct chreWifiScanResult *cacheResult =
153         &gWifiCacheState.resultList[i];
154     // Filtering based on BSSID + SSID + frequency based on Linux cfg80211.
155     // https://github.com/torvalds/linux/blob/master/net/wireless/scan.c
156     if ((result->primaryChannel == cacheResult->primaryChannel) &&
157         (memcmp(result->bssid, cacheResult->bssid, CHRE_WIFI_BSSID_LEN) == 0) &&
158         (result->ssidLen == cacheResult->ssidLen) &&
159         (memcmp(result->ssid, cacheResult->ssid, result->ssidLen) == 0)) {
160       *index = i;
161       return true;
162     }
163   }
164 
165   return false;
166 }
167 
168 /************************************************
169  *  Public functions
170  ***********************************************/
chreWifiScanCacheInit(const struct chrePalSystemApi * systemApi,const struct chrePalWifiCallbacks * callbacks)171 bool chreWifiScanCacheInit(const struct chrePalSystemApi *systemApi,
172                            const struct chrePalWifiCallbacks *callbacks) {
173   if (systemApi == NULL || callbacks == NULL) {
174     return false;
175   }
176 
177   gSystemApi = systemApi;
178   gCallbacks = callbacks;
179   memset(&gWifiCacheState, 0, sizeof(gWifiCacheState));
180   gScanMonitoringEnabled = false;
181 
182   return true;
183 }
184 
chreWifiScanCacheDeinit(void)185 void chreWifiScanCacheDeinit(void) {
186   gSystemApi = NULL;
187   gCallbacks = NULL;
188 }
189 
chreWifiScanCacheScanEventBegin(enum chreWifiScanType scanType,uint8_t ssidSetSize,const uint32_t * scannedFreqList,uint16_t scannedFreqListLength,uint8_t radioChainPref,bool activeScanResult)190 bool chreWifiScanCacheScanEventBegin(enum chreWifiScanType scanType,
191                                      uint8_t ssidSetSize,
192                                      const uint32_t *scannedFreqList,
193                                      uint16_t scannedFreqListLength,
194                                      uint8_t radioChainPref,
195                                      bool activeScanResult) {
196   bool success = false;
197   if (chreWifiScanCacheIsInitialized()) {
198     enum chreError error = CHRE_ERROR_NONE;
199     if (!isFrequencyListValid(scannedFreqList, scannedFreqListLength)) {
200       gSystemApi->log(CHRE_LOG_ERROR, "Invalid frequency argument");
201       error = CHRE_ERROR_INVALID_ARGUMENT;
202     } else if (isWifiScanCacheBusy(true /* logOnBusy */)) {
203       error = CHRE_ERROR_BUSY;
204     } else {
205       success = true;
206       memset(&gWifiCacheState, 0, sizeof(gWifiCacheState));
207 
208       gWifiCacheState.event.version = CHRE_WIFI_SCAN_EVENT_VERSION;
209       gWifiCacheState.event.scanType = scanType;
210       gWifiCacheState.event.ssidSetSize = ssidSetSize;
211 
212       scannedFreqListLength =
213           MIN(scannedFreqListLength, CHRE_WIFI_FREQUENCY_LIST_MAX_LEN);
214       if (scannedFreqList != NULL) {
215         memcpy(gWifiCacheState.scannedFreqList, scannedFreqList,
216                scannedFreqListLength * sizeof(uint32_t));
217       }
218       gWifiCacheState.event.scannedFreqListLen = scannedFreqListLength;
219       gWifiCacheState.event.radioChainPref = radioChainPref;
220 
221       gWifiCacheState.activeScanResult = activeScanResult;
222       gWifiCacheState.started = true;
223     }
224 
225     if (activeScanResult && !success) {
226       gCallbacks->scanResponseCallback(false /* pending */, error);
227     }
228   }
229 
230   return success;
231 }
232 
chreWifiScanCacheScanEventAdd(const struct chreWifiScanResult * result)233 void chreWifiScanCacheScanEventAdd(const struct chreWifiScanResult *result) {
234   if (!gWifiCacheState.started) {
235     gSystemApi->log(CHRE_LOG_ERROR, "Cannot add to cache before starting it");
236   } else {
237     size_t index;
238     bool exists = isWifiScanResultInCache(result, &index);
239     if (!exists && gWifiCacheState.event.resultTotal >=
240                        CHRE_PAL_WIFI_SCAN_CACHE_CAPACITY) {
241       // TODO(b/174510884): Filter based on e.g. RSSI if full
242       gWifiCacheState.numWifiScanResultsDropped++;
243     } else {
244       if (!exists) {
245         // Only add a new entry if the result was not already cached.
246         index = gWifiCacheState.event.resultTotal;
247         gWifiCacheState.event.resultTotal++;
248       }
249 
250       memcpy(&gWifiCacheState.resultList[index], result,
251              sizeof(const struct chreWifiScanResult));
252 
253       // ageMs will be properly populated in chreWifiScanCacheScanEventEnd
254       gWifiCacheState.resultList[index].ageMs = (uint32_t)(
255           gSystemApi->getCurrentTime() / kOneMillisecondInNanoseconds);
256     }
257   }
258 }
259 
chreWifiScanCacheScanEventEnd(enum chreError errorCode)260 void chreWifiScanCacheScanEventEnd(enum chreError errorCode) {
261   if (gWifiCacheState.started) {
262     if (gWifiCacheState.numWifiScanResultsDropped > 0) {
263       gSystemApi->log(CHRE_LOG_WARN,
264                       "Dropped total of %" PRIu32 " access points",
265                       gWifiCacheState.numWifiScanResultsDropped);
266     }
267     if (gWifiCacheState.activeScanResult) {
268       gCallbacks->scanResponseCallback(
269           errorCode == CHRE_ERROR_NONE /* pending */, errorCode);
270     }
271 
272     if (errorCode == CHRE_ERROR_NONE &&
273         (gWifiCacheState.activeScanResult || gScanMonitoringEnabled)) {
274       gWifiCacheState.event.referenceTime = gSystemApi->getCurrentTime();
275       gWifiCacheState.event.scannedFreqList = gWifiCacheState.scannedFreqList;
276 
277       uint32_t referenceTimeMs = (uint32_t)(
278           gWifiCacheState.event.referenceTime / kOneMillisecondInNanoseconds);
279       for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
280         gWifiCacheState.resultList[i].ageMs =
281             referenceTimeMs - gWifiCacheState.resultList[i].ageMs;
282       }
283 
284       chreWifiScanCacheDispatchAll();
285     }
286 
287     gWifiCacheState.started = false;
288     gWifiCacheState.activeScanResult = false;
289   }
290 }
291 
chreWifiScanCacheDispatchFromCache(const struct chreWifiScanParams * params)292 bool chreWifiScanCacheDispatchFromCache(
293     const struct chreWifiScanParams *params) {
294   if (!chreWifiScanCacheIsInitialized()) {
295     return false;
296   }
297 
298   if (paramsMatchScanCache(params) &&
299       !isWifiScanCacheBusy(false /* logOnBusy */)) {
300     // TODO(b/174511061): Handle scenario where cache is working on delivering
301     // a scan event. Ideally the library will wait until it is complete to
302     // dispatch from the cache if it meets the criteria, rather than scheduling
303     // a fresh scan.
304     gCallbacks->scanResponseCallback(true /* pending */, CHRE_ERROR_NONE);
305     chreWifiScanCacheDispatchAll();
306     return true;
307   } else {
308     return false;
309   }
310 }
311 
chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent * event)312 void chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent *event) {
313   if (!chreWifiScanCacheIsInitialized()) {
314     return;
315   }
316 
317   if (event != &gWifiCacheState.event) {
318     gSystemApi->log(CHRE_LOG_ERROR, "Invalid event pointer %p", event);
319   } else if (gWifiCacheState.numWifiEventsPendingRelease > 0) {
320     gWifiCacheState.numWifiEventsPendingRelease--;
321   }
322 }
323 
chreWifiScanCacheConfigureScanMonitor(bool enable)324 void chreWifiScanCacheConfigureScanMonitor(bool enable) {
325   if (!chreWifiScanCacheIsInitialized()) {
326     return;
327   }
328 
329   gScanMonitoringEnabled = enable;
330 }
331