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 =
255 (uint32_t)gSystemApi->getCurrentTime() /
256 (uint32_t)kOneMillisecondInNanoseconds;
257 }
258 }
259 }
260
chreWifiScanCacheScanEventEnd(enum chreError errorCode)261 void chreWifiScanCacheScanEventEnd(enum chreError errorCode) {
262 if (gWifiCacheState.started) {
263 if (gWifiCacheState.numWifiScanResultsDropped > 0) {
264 gSystemApi->log(CHRE_LOG_WARN,
265 "Dropped total of %" PRIu32 " access points",
266 gWifiCacheState.numWifiScanResultsDropped);
267 }
268 if (gWifiCacheState.activeScanResult) {
269 gCallbacks->scanResponseCallback(
270 errorCode == CHRE_ERROR_NONE /* pending */, errorCode);
271 }
272
273 if (errorCode == CHRE_ERROR_NONE &&
274 (gWifiCacheState.activeScanResult || gScanMonitoringEnabled)) {
275 gWifiCacheState.event.referenceTime = gSystemApi->getCurrentTime();
276 gWifiCacheState.event.scannedFreqList = gWifiCacheState.scannedFreqList;
277
278 uint32_t referenceTimeMs = (uint32_t)gWifiCacheState.event.referenceTime /
279 (uint32_t)kOneMillisecondInNanoseconds;
280 for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
281 gWifiCacheState.resultList[i].ageMs =
282 referenceTimeMs - gWifiCacheState.resultList[i].ageMs;
283 }
284
285 chreWifiScanCacheDispatchAll();
286 }
287
288 gWifiCacheState.started = false;
289 gWifiCacheState.activeScanResult = false;
290 }
291 }
292
chreWifiScanCacheDispatchFromCache(const struct chreWifiScanParams * params)293 bool chreWifiScanCacheDispatchFromCache(
294 const struct chreWifiScanParams *params) {
295 if (!chreWifiScanCacheIsInitialized()) {
296 return false;
297 }
298
299 if (paramsMatchScanCache(params) &&
300 !isWifiScanCacheBusy(false /* logOnBusy */)) {
301 // TODO(b/174511061): Handle scenario where cache is working on delivering
302 // a scan event. Ideally the library will wait until it is complete to
303 // dispatch from the cache if it meets the criteria, rather than scheduling
304 // a fresh scan.
305 gCallbacks->scanResponseCallback(true /* pending */, CHRE_ERROR_NONE);
306 chreWifiScanCacheDispatchAll();
307 return true;
308 } else {
309 return false;
310 }
311 }
312
chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent * event)313 void chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent *event) {
314 if (!chreWifiScanCacheIsInitialized()) {
315 return;
316 }
317
318 if (event != &gWifiCacheState.event) {
319 gSystemApi->log(CHRE_LOG_ERROR, "Invalid event pointer %p", event);
320 } else if (gWifiCacheState.numWifiEventsPendingRelease > 0) {
321 gWifiCacheState.numWifiEventsPendingRelease--;
322 }
323 }
324
chreWifiScanCacheConfigureScanMonitor(bool enable)325 void chreWifiScanCacheConfigureScanMonitor(bool enable) {
326 if (!chreWifiScanCacheIsInitialized()) {
327 return;
328 }
329
330 gScanMonitoringEnabled = enable;
331 }
332