1*84e33947SAndroid Build Coastguard Worker /*
2*84e33947SAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*84e33947SAndroid Build Coastguard Worker *
4*84e33947SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*84e33947SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*84e33947SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*84e33947SAndroid Build Coastguard Worker *
8*84e33947SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*84e33947SAndroid Build Coastguard Worker *
10*84e33947SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*84e33947SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*84e33947SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*84e33947SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*84e33947SAndroid Build Coastguard Worker * limitations under the License.
15*84e33947SAndroid Build Coastguard Worker */
16*84e33947SAndroid Build Coastguard Worker
17*84e33947SAndroid Build Coastguard Worker #include "chre/pal/util/wifi_scan_cache.h"
18*84e33947SAndroid Build Coastguard Worker
19*84e33947SAndroid Build Coastguard Worker #include <inttypes.h>
20*84e33947SAndroid Build Coastguard Worker
21*84e33947SAndroid Build Coastguard Worker #include "chre/util/macros.h"
22*84e33947SAndroid Build Coastguard Worker
23*84e33947SAndroid Build Coastguard Worker /************************************************
24*84e33947SAndroid Build Coastguard Worker * Constants
25*84e33947SAndroid Build Coastguard Worker ***********************************************/
26*84e33947SAndroid Build Coastguard Worker
27*84e33947SAndroid Build Coastguard Worker // Constants used in chreWifiScanCacheInitialAgeMsValue() and
28*84e33947SAndroid Build Coastguard Worker // chreWifiScanCacheFinalizeAgeMs()
29*84e33947SAndroid Build Coastguard Worker // These values are selected because msec = nsec / 1000000 and
30*84e33947SAndroid Build Coastguard Worker // 1000000 = 64 * 15625 = (1 << 6) * 15625
31*84e33947SAndroid Build Coastguard Worker #define CHRE_WIFI_SCAN_CACHE_AGE_MS_SHIFT (6)
32*84e33947SAndroid Build Coastguard Worker #define CHRE_WIFI_SCAN_CACHE_AGE_MS_DIVISOR (15625)
33*84e33947SAndroid Build Coastguard Worker
34*84e33947SAndroid Build Coastguard Worker /************************************************
35*84e33947SAndroid Build Coastguard Worker * Prototypes
36*84e33947SAndroid Build Coastguard Worker ***********************************************/
37*84e33947SAndroid Build Coastguard Worker
38*84e33947SAndroid Build Coastguard Worker struct chreWifiScanCacheState {
39*84e33947SAndroid Build Coastguard Worker //! true if the scan cache has started, i.e. chreWifiScanCacheScanEventBegin
40*84e33947SAndroid Build Coastguard Worker //! was invoked and has not yet ended.
41*84e33947SAndroid Build Coastguard Worker bool started;
42*84e33947SAndroid Build Coastguard Worker
43*84e33947SAndroid Build Coastguard Worker //! true if the current scan cache is a result of a CHRE active scan request.
44*84e33947SAndroid Build Coastguard Worker bool scanRequestedByChre;
45*84e33947SAndroid Build Coastguard Worker
46*84e33947SAndroid Build Coastguard Worker //! The number of chreWifiScanResults dropped due to OOM.
47*84e33947SAndroid Build Coastguard Worker uint16_t numWifiScanResultsDropped;
48*84e33947SAndroid Build Coastguard Worker
49*84e33947SAndroid Build Coastguard Worker //! Stores the WiFi cache elements
50*84e33947SAndroid Build Coastguard Worker struct chreWifiScanEvent event;
51*84e33947SAndroid Build Coastguard Worker struct chreWifiScanResult resultList[CHRE_PAL_WIFI_SCAN_CACHE_CAPACITY];
52*84e33947SAndroid Build Coastguard Worker
53*84e33947SAndroid Build Coastguard Worker //! The number of chreWifiScanEvent data pending release via
54*84e33947SAndroid Build Coastguard Worker //! chreWifiScanCacheReleaseScanEvent().
55*84e33947SAndroid Build Coastguard Worker uint8_t numWifiEventsPendingRelease;
56*84e33947SAndroid Build Coastguard Worker
57*84e33947SAndroid Build Coastguard Worker bool scanMonitoringEnabled;
58*84e33947SAndroid Build Coastguard Worker
59*84e33947SAndroid Build Coastguard Worker uint32_t scannedFreqList[CHRE_WIFI_FREQUENCY_LIST_MAX_LEN];
60*84e33947SAndroid Build Coastguard Worker
61*84e33947SAndroid Build Coastguard Worker uint64_t scanStartTimeNs;
62*84e33947SAndroid Build Coastguard Worker };
63*84e33947SAndroid Build Coastguard Worker
64*84e33947SAndroid Build Coastguard Worker /************************************************
65*84e33947SAndroid Build Coastguard Worker * Global variables
66*84e33947SAndroid Build Coastguard Worker ***********************************************/
67*84e33947SAndroid Build Coastguard Worker static const struct chrePalSystemApi *gSystemApi = NULL;
68*84e33947SAndroid Build Coastguard Worker static const struct chrePalWifiCallbacks *gCallbacks = NULL;
69*84e33947SAndroid Build Coastguard Worker
70*84e33947SAndroid Build Coastguard Worker static struct chreWifiScanCacheState gWifiCacheState;
71*84e33947SAndroid Build Coastguard Worker
72*84e33947SAndroid Build Coastguard Worker //! true if scan monitoring is enabled via
73*84e33947SAndroid Build Coastguard Worker //! chreWifiScanCacheConfigureScanMonitor().
74*84e33947SAndroid Build Coastguard Worker static bool gScanMonitoringEnabled;
75*84e33947SAndroid Build Coastguard Worker
76*84e33947SAndroid Build Coastguard Worker static const uint64_t kOneMillisecondInNanoseconds = UINT64_C(1000000);
77*84e33947SAndroid Build Coastguard Worker
78*84e33947SAndroid Build Coastguard Worker /************************************************
79*84e33947SAndroid Build Coastguard Worker * Private functions
80*84e33947SAndroid Build Coastguard Worker ***********************************************/
chreWifiScanCacheIsInitialized(void)81*84e33947SAndroid Build Coastguard Worker static bool chreWifiScanCacheIsInitialized(void) {
82*84e33947SAndroid Build Coastguard Worker return (gSystemApi != NULL && gCallbacks != NULL);
83*84e33947SAndroid Build Coastguard Worker }
84*84e33947SAndroid Build Coastguard Worker
areAllScanEventsReleased(void)85*84e33947SAndroid Build Coastguard Worker static bool areAllScanEventsReleased(void) {
86*84e33947SAndroid Build Coastguard Worker return gWifiCacheState.numWifiEventsPendingRelease == 0;
87*84e33947SAndroid Build Coastguard Worker }
88*84e33947SAndroid Build Coastguard Worker
isFrequencyListValid(const uint32_t * frequencyList,uint16_t frequencyListLen)89*84e33947SAndroid Build Coastguard Worker static bool isFrequencyListValid(const uint32_t *frequencyList,
90*84e33947SAndroid Build Coastguard Worker uint16_t frequencyListLen) {
91*84e33947SAndroid Build Coastguard Worker return (frequencyListLen == 0) || (frequencyList != NULL);
92*84e33947SAndroid Build Coastguard Worker }
93*84e33947SAndroid Build Coastguard Worker
paramsMatchScanCache(const struct chreWifiScanParams * params)94*84e33947SAndroid Build Coastguard Worker static bool paramsMatchScanCache(const struct chreWifiScanParams *params) {
95*84e33947SAndroid Build Coastguard Worker uint64_t timeNs = gWifiCacheState.event.referenceTime;
96*84e33947SAndroid Build Coastguard Worker bool scan_within_age =
97*84e33947SAndroid Build Coastguard Worker gWifiCacheState.started ||
98*84e33947SAndroid Build Coastguard Worker (timeNs >= gSystemApi->getCurrentTime() -
99*84e33947SAndroid Build Coastguard Worker (params->maxScanAgeMs * kOneMillisecondInNanoseconds));
100*84e33947SAndroid Build Coastguard Worker
101*84e33947SAndroid Build Coastguard Worker // Perform a conservative check for the params and scan cache.
102*84e33947SAndroid Build Coastguard Worker // TODO(b/174510035): Consider optimizing for the case for channelSet ==
103*84e33947SAndroid Build Coastguard Worker // CHRE_WIFI_CHANNEL_SET_ALL.
104*84e33947SAndroid Build Coastguard Worker bool params_non_dfs =
105*84e33947SAndroid Build Coastguard Worker (params->scanType == CHRE_WIFI_SCAN_TYPE_ACTIVE) ||
106*84e33947SAndroid Build Coastguard Worker ((params->scanType == CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE) &&
107*84e33947SAndroid Build Coastguard Worker (params->channelSet == CHRE_WIFI_CHANNEL_SET_NON_DFS));
108*84e33947SAndroid Build Coastguard Worker bool cache_non_dfs =
109*84e33947SAndroid Build Coastguard Worker (gWifiCacheState.event.scanType == CHRE_WIFI_SCAN_TYPE_ACTIVE) ||
110*84e33947SAndroid Build Coastguard Worker (gWifiCacheState.event.scanType == CHRE_WIFI_SCAN_TYPE_PASSIVE);
111*84e33947SAndroid Build Coastguard Worker
112*84e33947SAndroid Build Coastguard Worker bool cache_all_freq = (gWifiCacheState.event.scannedFreqListLen == 0);
113*84e33947SAndroid Build Coastguard Worker bool cache_all_ssid = (gWifiCacheState.event.ssidSetSize == 0);
114*84e33947SAndroid Build Coastguard Worker
115*84e33947SAndroid Build Coastguard Worker return scan_within_age && (params_non_dfs || !cache_non_dfs) &&
116*84e33947SAndroid Build Coastguard Worker cache_all_freq && cache_all_ssid;
117*84e33947SAndroid Build Coastguard Worker }
118*84e33947SAndroid Build Coastguard Worker
isWifiScanCacheBusy(bool logOnBusy)119*84e33947SAndroid Build Coastguard Worker static bool isWifiScanCacheBusy(bool logOnBusy) {
120*84e33947SAndroid Build Coastguard Worker bool busy = true;
121*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.started) {
122*84e33947SAndroid Build Coastguard Worker if (logOnBusy) {
123*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Scan cache already started");
124*84e33947SAndroid Build Coastguard Worker }
125*84e33947SAndroid Build Coastguard Worker } else if (!areAllScanEventsReleased()) {
126*84e33947SAndroid Build Coastguard Worker if (logOnBusy) {
127*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Scan cache events pending release");
128*84e33947SAndroid Build Coastguard Worker }
129*84e33947SAndroid Build Coastguard Worker } else {
130*84e33947SAndroid Build Coastguard Worker busy = false;
131*84e33947SAndroid Build Coastguard Worker }
132*84e33947SAndroid Build Coastguard Worker
133*84e33947SAndroid Build Coastguard Worker return busy;
134*84e33947SAndroid Build Coastguard Worker }
135*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheDispatchAll(void)136*84e33947SAndroid Build Coastguard Worker static void chreWifiScanCacheDispatchAll(void) {
137*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_DEBUG, "Dispatching %" PRIu8 " events",
138*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.resultTotal);
139*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.event.resultTotal == 0) {
140*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.eventIndex = 0;
141*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.resultCount = 0;
142*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.results = NULL;
143*84e33947SAndroid Build Coastguard Worker gCallbacks->scanEventCallback(&gWifiCacheState.event);
144*84e33947SAndroid Build Coastguard Worker } else {
145*84e33947SAndroid Build Coastguard Worker uint8_t eventIndex = 0;
146*84e33947SAndroid Build Coastguard Worker for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal;
147*84e33947SAndroid Build Coastguard Worker i += CHRE_PAL_WIFI_SCAN_CACHE_MAX_RESULT_COUNT) {
148*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.resultCount =
149*84e33947SAndroid Build Coastguard Worker MIN(CHRE_PAL_WIFI_SCAN_CACHE_MAX_RESULT_COUNT,
150*84e33947SAndroid Build Coastguard Worker (uint8_t)(gWifiCacheState.event.resultTotal - i));
151*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.eventIndex = eventIndex++;
152*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.results = &gWifiCacheState.resultList[i];
153*84e33947SAndroid Build Coastguard Worker
154*84e33947SAndroid Build Coastguard Worker // TODO(b/174511061): The current approach only works for situations where
155*84e33947SAndroid Build Coastguard Worker // the event is released immediately. Add a way to handle this scenario
156*84e33947SAndroid Build Coastguard Worker // (e.g. an array of chreWifiScanEvent's).
157*84e33947SAndroid Build Coastguard Worker gWifiCacheState.numWifiEventsPendingRelease++;
158*84e33947SAndroid Build Coastguard Worker gCallbacks->scanEventCallback(&gWifiCacheState.event);
159*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.numWifiEventsPendingRelease != 0) {
160*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Scan event not released immediately");
161*84e33947SAndroid Build Coastguard Worker }
162*84e33947SAndroid Build Coastguard Worker }
163*84e33947SAndroid Build Coastguard Worker }
164*84e33947SAndroid Build Coastguard Worker }
165*84e33947SAndroid Build Coastguard Worker
isWifiScanResultInCache(const struct chreWifiScanResult * result,size_t * index)166*84e33947SAndroid Build Coastguard Worker static bool isWifiScanResultInCache(const struct chreWifiScanResult *result,
167*84e33947SAndroid Build Coastguard Worker size_t *index) {
168*84e33947SAndroid Build Coastguard Worker for (uint8_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
169*84e33947SAndroid Build Coastguard Worker const struct chreWifiScanResult *cacheResult =
170*84e33947SAndroid Build Coastguard Worker &gWifiCacheState.resultList[i];
171*84e33947SAndroid Build Coastguard Worker // Filtering based on BSSID + SSID + frequency based on Linux cfg80211.
172*84e33947SAndroid Build Coastguard Worker // https://github.com/torvalds/linux/blob/master/net/wireless/scan.c
173*84e33947SAndroid Build Coastguard Worker if ((result->primaryChannel == cacheResult->primaryChannel) &&
174*84e33947SAndroid Build Coastguard Worker (memcmp(result->bssid, cacheResult->bssid, CHRE_WIFI_BSSID_LEN) == 0) &&
175*84e33947SAndroid Build Coastguard Worker (result->ssidLen == cacheResult->ssidLen) &&
176*84e33947SAndroid Build Coastguard Worker (memcmp(result->ssid, cacheResult->ssid, result->ssidLen) == 0)) {
177*84e33947SAndroid Build Coastguard Worker *index = i;
178*84e33947SAndroid Build Coastguard Worker return true;
179*84e33947SAndroid Build Coastguard Worker }
180*84e33947SAndroid Build Coastguard Worker }
181*84e33947SAndroid Build Coastguard Worker
182*84e33947SAndroid Build Coastguard Worker return false;
183*84e33947SAndroid Build Coastguard Worker }
184*84e33947SAndroid Build Coastguard Worker
isLowerRssiScanResultInCache(const struct chreWifiScanResult * result,size_t * index)185*84e33947SAndroid Build Coastguard Worker static bool isLowerRssiScanResultInCache(
186*84e33947SAndroid Build Coastguard Worker const struct chreWifiScanResult *result, size_t *index) {
187*84e33947SAndroid Build Coastguard Worker int8_t lowestRssi = result->rssi;
188*84e33947SAndroid Build Coastguard Worker bool foundWeakerResult = false;
189*84e33947SAndroid Build Coastguard Worker for (uint8_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
190*84e33947SAndroid Build Coastguard Worker const struct chreWifiScanResult *cacheResult =
191*84e33947SAndroid Build Coastguard Worker &gWifiCacheState.resultList[i];
192*84e33947SAndroid Build Coastguard Worker // Filter based on RSSI to determine weakest result in cache.
193*84e33947SAndroid Build Coastguard Worker if (cacheResult->rssi < lowestRssi) {
194*84e33947SAndroid Build Coastguard Worker lowestRssi = cacheResult->rssi;
195*84e33947SAndroid Build Coastguard Worker *index = i;
196*84e33947SAndroid Build Coastguard Worker foundWeakerResult = true;
197*84e33947SAndroid Build Coastguard Worker }
198*84e33947SAndroid Build Coastguard Worker }
199*84e33947SAndroid Build Coastguard Worker
200*84e33947SAndroid Build Coastguard Worker return foundWeakerResult;
201*84e33947SAndroid Build Coastguard Worker }
202*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheInitialAgeMsValue()203*84e33947SAndroid Build Coastguard Worker static uint32_t chreWifiScanCacheInitialAgeMsValue() {
204*84e33947SAndroid Build Coastguard Worker // ageMs will be finalized via chreWifiScanCacheFinalizeAgeMs() once the scan
205*84e33947SAndroid Build Coastguard Worker // finishes, because it is relative to the scan end time that we can't know
206*84e33947SAndroid Build Coastguard Worker // yet. Before the end of the scan, populate ageMs with the time since the
207*84e33947SAndroid Build Coastguard Worker // start of the scan.
208*84e33947SAndroid Build Coastguard Worker //
209*84e33947SAndroid Build Coastguard Worker // We avoid 64-bit integer division by:
210*84e33947SAndroid Build Coastguard Worker // - Only considering the delta between this result and the start of the
211*84e33947SAndroid Build Coastguard Worker // scan, which constrains the range of expected values to what should be
212*84e33947SAndroid Build Coastguard Worker // only a few seconds
213*84e33947SAndroid Build Coastguard Worker // - Instead of directly dividing by 1000000, we first divide by 64 (right
214*84e33947SAndroid Build Coastguard Worker // shift by 6), then truncate to 32 bits, then later we'll do integer
215*84e33947SAndroid Build Coastguard Worker // division by 15625 to get milliseconds
216*84e33947SAndroid Build Coastguard Worker // - This works because x/1000000 = x/(64 * 15625) = (x/64)/15625
217*84e33947SAndroid Build Coastguard Worker // - The largest delta we can fit here is 2^32/15625 ms = 274877 ms or
218*84e33947SAndroid Build Coastguard Worker // about 4.5 minutes
219*84e33947SAndroid Build Coastguard Worker uint64_t timeSinceScanStartNs =
220*84e33947SAndroid Build Coastguard Worker (gSystemApi->getCurrentTime() - gWifiCacheState.scanStartTimeNs);
221*84e33947SAndroid Build Coastguard Worker return (timeSinceScanStartNs >> CHRE_WIFI_SCAN_CACHE_AGE_MS_SHIFT);
222*84e33947SAndroid Build Coastguard Worker }
223*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheFinalizeAgeMs()224*84e33947SAndroid Build Coastguard Worker static void chreWifiScanCacheFinalizeAgeMs() {
225*84e33947SAndroid Build Coastguard Worker // Convert ageMs from the chreWifiScanCacheInitialAgeMsValue() to its final,
226*84e33947SAndroid Build Coastguard Worker // correct value using the formula derived from these steps:
227*84e33947SAndroid Build Coastguard Worker // ageMs = (referenceTimeNs - absoluteScanResultTimeNs) / 1000000
228*84e33947SAndroid Build Coastguard Worker // = (referenceTimeNs - (scanStartTimeNs + scanOffsetNs)) / 1000000
229*84e33947SAndroid Build Coastguard Worker // = ((referenceTimeNs - scanStartTimeNs) - scanOffsetNs) / 1000000
230*84e33947SAndroid Build Coastguard Worker // = (scanDuration / 64 - scanOffsetNs / 64) / 15625
231*84e33947SAndroid Build Coastguard Worker // ageMs = (scanDurationShifted - currentAgeMsValue) / 15625
232*84e33947SAndroid Build Coastguard Worker uint64_t referenceTimeNs = gWifiCacheState.event.referenceTime;
233*84e33947SAndroid Build Coastguard Worker uint64_t scanStartTimeNs = gWifiCacheState.scanStartTimeNs;
234*84e33947SAndroid Build Coastguard Worker uint32_t scanDurationShifted =
235*84e33947SAndroid Build Coastguard Worker (referenceTimeNs - scanStartTimeNs) >> CHRE_WIFI_SCAN_CACHE_AGE_MS_SHIFT;
236*84e33947SAndroid Build Coastguard Worker if (referenceTimeNs < scanStartTimeNs) {
237*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Invalid scan timestamp, clamping");
238*84e33947SAndroid Build Coastguard Worker // Use a smaller number to avoid very large ageMs values
239*84e33947SAndroid Build Coastguard Worker scanDurationShifted = 78125000; // 5 seconds --> 5*10e9 / 64
240*84e33947SAndroid Build Coastguard Worker }
241*84e33947SAndroid Build Coastguard Worker
242*84e33947SAndroid Build Coastguard Worker for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
243*84e33947SAndroid Build Coastguard Worker if (scanDurationShifted < gWifiCacheState.resultList[i].ageMs) {
244*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR,
245*84e33947SAndroid Build Coastguard Worker "Invalid result timestamp %" PRIu32 " vs. %" PRIu32,
246*84e33947SAndroid Build Coastguard Worker gWifiCacheState.resultList[i].ageMs, scanDurationShifted);
247*84e33947SAndroid Build Coastguard Worker gWifiCacheState.resultList[i].ageMs = 0;
248*84e33947SAndroid Build Coastguard Worker } else {
249*84e33947SAndroid Build Coastguard Worker gWifiCacheState.resultList[i].ageMs =
250*84e33947SAndroid Build Coastguard Worker (scanDurationShifted - gWifiCacheState.resultList[i].ageMs) /
251*84e33947SAndroid Build Coastguard Worker CHRE_WIFI_SCAN_CACHE_AGE_MS_DIVISOR;
252*84e33947SAndroid Build Coastguard Worker }
253*84e33947SAndroid Build Coastguard Worker }
254*84e33947SAndroid Build Coastguard Worker }
255*84e33947SAndroid Build Coastguard Worker
256*84e33947SAndroid Build Coastguard Worker /************************************************
257*84e33947SAndroid Build Coastguard Worker * Public functions
258*84e33947SAndroid Build Coastguard Worker ***********************************************/
chreWifiScanCacheInit(const struct chrePalSystemApi * systemApi,const struct chrePalWifiCallbacks * callbacks)259*84e33947SAndroid Build Coastguard Worker bool chreWifiScanCacheInit(const struct chrePalSystemApi *systemApi,
260*84e33947SAndroid Build Coastguard Worker const struct chrePalWifiCallbacks *callbacks) {
261*84e33947SAndroid Build Coastguard Worker if (systemApi == NULL || callbacks == NULL) {
262*84e33947SAndroid Build Coastguard Worker return false;
263*84e33947SAndroid Build Coastguard Worker }
264*84e33947SAndroid Build Coastguard Worker
265*84e33947SAndroid Build Coastguard Worker gSystemApi = systemApi;
266*84e33947SAndroid Build Coastguard Worker gCallbacks = callbacks;
267*84e33947SAndroid Build Coastguard Worker memset(&gWifiCacheState, 0, sizeof(gWifiCacheState));
268*84e33947SAndroid Build Coastguard Worker gScanMonitoringEnabled = false;
269*84e33947SAndroid Build Coastguard Worker
270*84e33947SAndroid Build Coastguard Worker return true;
271*84e33947SAndroid Build Coastguard Worker }
272*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheDeinit(void)273*84e33947SAndroid Build Coastguard Worker void chreWifiScanCacheDeinit(void) {
274*84e33947SAndroid Build Coastguard Worker gSystemApi = NULL;
275*84e33947SAndroid Build Coastguard Worker gCallbacks = NULL;
276*84e33947SAndroid Build Coastguard Worker }
277*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheScanEventBegin(enum chreWifiScanType scanType,uint8_t ssidSetSize,const uint32_t * scannedFreqList,uint16_t scannedFreqListLength,uint8_t radioChainPref,bool scanRequestedByChre)278*84e33947SAndroid Build Coastguard Worker bool chreWifiScanCacheScanEventBegin(enum chreWifiScanType scanType,
279*84e33947SAndroid Build Coastguard Worker uint8_t ssidSetSize,
280*84e33947SAndroid Build Coastguard Worker const uint32_t *scannedFreqList,
281*84e33947SAndroid Build Coastguard Worker uint16_t scannedFreqListLength,
282*84e33947SAndroid Build Coastguard Worker uint8_t radioChainPref,
283*84e33947SAndroid Build Coastguard Worker bool scanRequestedByChre) {
284*84e33947SAndroid Build Coastguard Worker bool success = false;
285*84e33947SAndroid Build Coastguard Worker if (chreWifiScanCacheIsInitialized()) {
286*84e33947SAndroid Build Coastguard Worker enum chreError error = CHRE_ERROR_NONE;
287*84e33947SAndroid Build Coastguard Worker if (!isFrequencyListValid(scannedFreqList, scannedFreqListLength)) {
288*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Invalid frequency argument");
289*84e33947SAndroid Build Coastguard Worker error = CHRE_ERROR_INVALID_ARGUMENT;
290*84e33947SAndroid Build Coastguard Worker } else if (isWifiScanCacheBusy(true /* logOnBusy */)) {
291*84e33947SAndroid Build Coastguard Worker error = CHRE_ERROR_BUSY;
292*84e33947SAndroid Build Coastguard Worker } else {
293*84e33947SAndroid Build Coastguard Worker success = true;
294*84e33947SAndroid Build Coastguard Worker memset(&gWifiCacheState, 0, sizeof(gWifiCacheState));
295*84e33947SAndroid Build Coastguard Worker
296*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.version = CHRE_WIFI_SCAN_EVENT_VERSION;
297*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.scanType = scanType;
298*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.ssidSetSize = ssidSetSize;
299*84e33947SAndroid Build Coastguard Worker
300*84e33947SAndroid Build Coastguard Worker scannedFreqListLength =
301*84e33947SAndroid Build Coastguard Worker MIN(scannedFreqListLength, CHRE_WIFI_FREQUENCY_LIST_MAX_LEN);
302*84e33947SAndroid Build Coastguard Worker if (scannedFreqList != NULL) {
303*84e33947SAndroid Build Coastguard Worker memcpy(gWifiCacheState.scannedFreqList, scannedFreqList,
304*84e33947SAndroid Build Coastguard Worker scannedFreqListLength * sizeof(uint32_t));
305*84e33947SAndroid Build Coastguard Worker }
306*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.scannedFreqListLen = scannedFreqListLength;
307*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.radioChainPref = radioChainPref;
308*84e33947SAndroid Build Coastguard Worker
309*84e33947SAndroid Build Coastguard Worker gWifiCacheState.scanRequestedByChre = scanRequestedByChre;
310*84e33947SAndroid Build Coastguard Worker gWifiCacheState.started = true;
311*84e33947SAndroid Build Coastguard Worker gWifiCacheState.scanStartTimeNs = gSystemApi->getCurrentTime();
312*84e33947SAndroid Build Coastguard Worker }
313*84e33947SAndroid Build Coastguard Worker
314*84e33947SAndroid Build Coastguard Worker if (scanRequestedByChre && !success) {
315*84e33947SAndroid Build Coastguard Worker gCallbacks->scanResponseCallback(false /* pending */, error);
316*84e33947SAndroid Build Coastguard Worker }
317*84e33947SAndroid Build Coastguard Worker }
318*84e33947SAndroid Build Coastguard Worker
319*84e33947SAndroid Build Coastguard Worker return success;
320*84e33947SAndroid Build Coastguard Worker }
321*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheScanEventAdd(const struct chreWifiScanResult * result)322*84e33947SAndroid Build Coastguard Worker void chreWifiScanCacheScanEventAdd(const struct chreWifiScanResult *result) {
323*84e33947SAndroid Build Coastguard Worker if (!gWifiCacheState.started) {
324*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Cannot add to cache before starting it");
325*84e33947SAndroid Build Coastguard Worker return;
326*84e33947SAndroid Build Coastguard Worker }
327*84e33947SAndroid Build Coastguard Worker
328*84e33947SAndroid Build Coastguard Worker size_t index;
329*84e33947SAndroid Build Coastguard Worker if (!isWifiScanResultInCache(result, &index)) {
330*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.event.resultTotal >=
331*84e33947SAndroid Build Coastguard Worker CHRE_PAL_WIFI_SCAN_CACHE_CAPACITY) {
332*84e33947SAndroid Build Coastguard Worker gWifiCacheState.numWifiScanResultsDropped++;
333*84e33947SAndroid Build Coastguard Worker // Determine weakest result in cache to replace with the new result.
334*84e33947SAndroid Build Coastguard Worker if (!isLowerRssiScanResultInCache(result, &index)) {
335*84e33947SAndroid Build Coastguard Worker return;
336*84e33947SAndroid Build Coastguard Worker }
337*84e33947SAndroid Build Coastguard Worker } else {
338*84e33947SAndroid Build Coastguard Worker // Result was not already cached, add new entry to the end of the cache
339*84e33947SAndroid Build Coastguard Worker index = gWifiCacheState.event.resultTotal;
340*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.resultTotal++;
341*84e33947SAndroid Build Coastguard Worker }
342*84e33947SAndroid Build Coastguard Worker }
343*84e33947SAndroid Build Coastguard Worker
344*84e33947SAndroid Build Coastguard Worker memcpy(&gWifiCacheState.resultList[index], result,
345*84e33947SAndroid Build Coastguard Worker sizeof(const struct chreWifiScanResult));
346*84e33947SAndroid Build Coastguard Worker
347*84e33947SAndroid Build Coastguard Worker gWifiCacheState.resultList[index].ageMs =
348*84e33947SAndroid Build Coastguard Worker chreWifiScanCacheInitialAgeMsValue();
349*84e33947SAndroid Build Coastguard Worker }
350*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheScanEventEnd(enum chreError errorCode)351*84e33947SAndroid Build Coastguard Worker void chreWifiScanCacheScanEventEnd(enum chreError errorCode) {
352*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.started) {
353*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.numWifiScanResultsDropped > 0) {
354*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_WARN,
355*84e33947SAndroid Build Coastguard Worker "Dropped total of %" PRIu32 " access points",
356*84e33947SAndroid Build Coastguard Worker gWifiCacheState.numWifiScanResultsDropped);
357*84e33947SAndroid Build Coastguard Worker }
358*84e33947SAndroid Build Coastguard Worker if (gWifiCacheState.scanRequestedByChre) {
359*84e33947SAndroid Build Coastguard Worker gCallbacks->scanResponseCallback(
360*84e33947SAndroid Build Coastguard Worker errorCode == CHRE_ERROR_NONE /* pending */, errorCode);
361*84e33947SAndroid Build Coastguard Worker }
362*84e33947SAndroid Build Coastguard Worker
363*84e33947SAndroid Build Coastguard Worker if (errorCode == CHRE_ERROR_NONE &&
364*84e33947SAndroid Build Coastguard Worker (gWifiCacheState.scanRequestedByChre || gScanMonitoringEnabled)) {
365*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.referenceTime = gSystemApi->getCurrentTime();
366*84e33947SAndroid Build Coastguard Worker gWifiCacheState.event.scannedFreqList = gWifiCacheState.scannedFreqList;
367*84e33947SAndroid Build Coastguard Worker chreWifiScanCacheFinalizeAgeMs();
368*84e33947SAndroid Build Coastguard Worker chreWifiScanCacheDispatchAll();
369*84e33947SAndroid Build Coastguard Worker }
370*84e33947SAndroid Build Coastguard Worker
371*84e33947SAndroid Build Coastguard Worker gWifiCacheState.started = false;
372*84e33947SAndroid Build Coastguard Worker gWifiCacheState.scanRequestedByChre = false;
373*84e33947SAndroid Build Coastguard Worker }
374*84e33947SAndroid Build Coastguard Worker }
375*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheDispatchFromCache(const struct chreWifiScanParams * params)376*84e33947SAndroid Build Coastguard Worker bool chreWifiScanCacheDispatchFromCache(
377*84e33947SAndroid Build Coastguard Worker const struct chreWifiScanParams *params) {
378*84e33947SAndroid Build Coastguard Worker if (!chreWifiScanCacheIsInitialized()) {
379*84e33947SAndroid Build Coastguard Worker return false;
380*84e33947SAndroid Build Coastguard Worker }
381*84e33947SAndroid Build Coastguard Worker
382*84e33947SAndroid Build Coastguard Worker if (paramsMatchScanCache(params)) {
383*84e33947SAndroid Build Coastguard Worker if (!isWifiScanCacheBusy(false /* logOnBusy */)) {
384*84e33947SAndroid Build Coastguard Worker // Satisfied by cache
385*84e33947SAndroid Build Coastguard Worker gCallbacks->scanResponseCallback(true /* pending */, CHRE_ERROR_NONE);
386*84e33947SAndroid Build Coastguard Worker chreWifiScanCacheDispatchAll();
387*84e33947SAndroid Build Coastguard Worker return true;
388*84e33947SAndroid Build Coastguard Worker } else if (gWifiCacheState.started) {
389*84e33947SAndroid Build Coastguard Worker // Will be satisfied by cache once the scan completes
390*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_INFO, "Using in-progress scan for CHRE request");
391*84e33947SAndroid Build Coastguard Worker gWifiCacheState.scanRequestedByChre = true;
392*84e33947SAndroid Build Coastguard Worker return true;
393*84e33947SAndroid Build Coastguard Worker } else {
394*84e33947SAndroid Build Coastguard Worker // Assumed: busy because !areAllScanEventsReleased()
395*84e33947SAndroid Build Coastguard Worker // TODO(b/174511061): the current code assumes scan events are released
396*84e33947SAndroid Build Coastguard Worker // synchronously, so this should never happen
397*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR,
398*84e33947SAndroid Build Coastguard Worker "Unexpected scan request while delivering results");
399*84e33947SAndroid Build Coastguard Worker return false;
400*84e33947SAndroid Build Coastguard Worker }
401*84e33947SAndroid Build Coastguard Worker } else {
402*84e33947SAndroid Build Coastguard Worker // Cache contains results from incompatible scan parameters (either too old
403*84e33947SAndroid Build Coastguard Worker // or different scan type), so a new scan is needed
404*84e33947SAndroid Build Coastguard Worker return false;
405*84e33947SAndroid Build Coastguard Worker }
406*84e33947SAndroid Build Coastguard Worker }
407*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent * event)408*84e33947SAndroid Build Coastguard Worker void chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent *event) {
409*84e33947SAndroid Build Coastguard Worker if (!chreWifiScanCacheIsInitialized()) {
410*84e33947SAndroid Build Coastguard Worker return;
411*84e33947SAndroid Build Coastguard Worker }
412*84e33947SAndroid Build Coastguard Worker
413*84e33947SAndroid Build Coastguard Worker if (event != &gWifiCacheState.event) {
414*84e33947SAndroid Build Coastguard Worker gSystemApi->log(CHRE_LOG_ERROR, "Invalid event pointer %p", event);
415*84e33947SAndroid Build Coastguard Worker } else if (gWifiCacheState.numWifiEventsPendingRelease > 0) {
416*84e33947SAndroid Build Coastguard Worker gWifiCacheState.numWifiEventsPendingRelease--;
417*84e33947SAndroid Build Coastguard Worker }
418*84e33947SAndroid Build Coastguard Worker }
419*84e33947SAndroid Build Coastguard Worker
chreWifiScanCacheConfigureScanMonitor(bool enable)420*84e33947SAndroid Build Coastguard Worker void chreWifiScanCacheConfigureScanMonitor(bool enable) {
421*84e33947SAndroid Build Coastguard Worker if (!chreWifiScanCacheIsInitialized()) {
422*84e33947SAndroid Build Coastguard Worker return;
423*84e33947SAndroid Build Coastguard Worker }
424*84e33947SAndroid Build Coastguard Worker
425*84e33947SAndroid Build Coastguard Worker gScanMonitoringEnabled = enable;
426*84e33947SAndroid Build Coastguard Worker }
427