You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
12 KiB
331 lines
12 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "chre/pal/util/wifi_scan_cache.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include "chre/util/macros.h"
|
|
|
|
/************************************************
|
|
* Prototypes
|
|
***********************************************/
|
|
|
|
struct chreWifiScanCacheState {
|
|
//! true if the scan cache has started, i.e. chreWifiScanCacheScanEventBegin
|
|
//! was invoked and has not yet ended.
|
|
bool started;
|
|
|
|
//! true if the current scan cache is a result of a CHRE active scan request.
|
|
bool activeScanResult;
|
|
|
|
//! The number of chreWifiScanResults dropped due to OOM.
|
|
uint16_t numWifiScanResultsDropped;
|
|
|
|
//! Stores the WiFi cache elements
|
|
struct chreWifiScanEvent event;
|
|
struct chreWifiScanResult resultList[CHRE_PAL_WIFI_SCAN_CACHE_CAPACITY];
|
|
|
|
//! The number of chreWifiScanEvent data pending release via
|
|
//! chreWifiScanCacheReleaseScanEvent().
|
|
uint8_t numWifiEventsPendingRelease;
|
|
|
|
bool scanMonitoringEnabled;
|
|
|
|
uint32_t scannedFreqList[CHRE_WIFI_FREQUENCY_LIST_MAX_LEN];
|
|
};
|
|
|
|
/************************************************
|
|
* Global variables
|
|
***********************************************/
|
|
static const struct chrePalSystemApi *gSystemApi = NULL;
|
|
static const struct chrePalWifiCallbacks *gCallbacks = NULL;
|
|
|
|
static struct chreWifiScanCacheState gWifiCacheState;
|
|
|
|
//! true if scan monitoring is enabled via
|
|
//! chreWifiScanCacheConfigureScanMonitor().
|
|
static bool gScanMonitoringEnabled;
|
|
|
|
static const uint64_t kOneMillisecondInNanoseconds = UINT64_C(1000000);
|
|
|
|
/************************************************
|
|
* Private functions
|
|
***********************************************/
|
|
static bool chreWifiScanCacheIsInitialized(void) {
|
|
return (gSystemApi != NULL && gCallbacks != NULL);
|
|
}
|
|
|
|
static bool areAllScanEventsReleased(void) {
|
|
return gWifiCacheState.numWifiEventsPendingRelease == 0;
|
|
}
|
|
|
|
static bool isFrequencyListValid(const uint32_t *frequencyList,
|
|
uint16_t frequencyListLen) {
|
|
return (frequencyListLen == 0) || (frequencyList != NULL);
|
|
}
|
|
|
|
static bool paramsMatchScanCache(const struct chreWifiScanParams *params) {
|
|
uint64_t timeNs = gWifiCacheState.event.referenceTime;
|
|
bool scan_within_age =
|
|
(timeNs >= gSystemApi->getCurrentTime() -
|
|
(params->maxScanAgeMs * kOneMillisecondInNanoseconds));
|
|
|
|
// Perform a conservative check for the params and scan cache.
|
|
// TODO(b/174510035): Consider optimizing for the case for channelSet ==
|
|
// CHRE_WIFI_CHANNEL_SET_ALL.
|
|
bool params_non_dfs =
|
|
(params->scanType == CHRE_WIFI_SCAN_TYPE_ACTIVE) ||
|
|
((params->scanType == CHRE_WIFI_SCAN_TYPE_NO_PREFERENCE) &&
|
|
(params->channelSet == CHRE_WIFI_CHANNEL_SET_NON_DFS));
|
|
bool cache_non_dfs =
|
|
(gWifiCacheState.event.scanType == CHRE_WIFI_SCAN_TYPE_ACTIVE) ||
|
|
(gWifiCacheState.event.scanType == CHRE_WIFI_SCAN_TYPE_PASSIVE);
|
|
|
|
bool cache_all_freq = (gWifiCacheState.event.scannedFreqListLen == 0);
|
|
bool cache_all_ssid = (gWifiCacheState.event.ssidSetSize == 0);
|
|
|
|
return scan_within_age && (params_non_dfs || !cache_non_dfs) &&
|
|
cache_all_freq && cache_all_ssid;
|
|
}
|
|
|
|
static bool isWifiScanCacheBusy(bool logOnBusy) {
|
|
bool busy = true;
|
|
if (gWifiCacheState.started) {
|
|
if (logOnBusy) {
|
|
gSystemApi->log(CHRE_LOG_ERROR, "Scan cache already started");
|
|
}
|
|
} else if (!areAllScanEventsReleased()) {
|
|
if (logOnBusy) {
|
|
gSystemApi->log(CHRE_LOG_ERROR, "Scan cache events pending release");
|
|
}
|
|
} else {
|
|
busy = false;
|
|
}
|
|
|
|
return busy;
|
|
}
|
|
|
|
static void chreWifiScanCacheDispatchAll(void) {
|
|
gSystemApi->log(CHRE_LOG_DEBUG, "Dispatching %" PRIu8 " events",
|
|
gWifiCacheState.event.resultTotal);
|
|
if (gWifiCacheState.event.resultTotal == 0) {
|
|
gWifiCacheState.event.eventIndex = 0;
|
|
gWifiCacheState.event.resultCount = 0;
|
|
gWifiCacheState.event.results = NULL;
|
|
gCallbacks->scanEventCallback(&gWifiCacheState.event);
|
|
} else {
|
|
uint8_t eventIndex = 0;
|
|
for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal;
|
|
i += CHRE_PAL_WIFI_SCAN_CACHE_MAX_RESULT_COUNT) {
|
|
gWifiCacheState.event.resultCount =
|
|
MIN(CHRE_PAL_WIFI_SCAN_CACHE_MAX_RESULT_COUNT,
|
|
(uint8_t)(gWifiCacheState.event.resultTotal - i));
|
|
gWifiCacheState.event.eventIndex = eventIndex++;
|
|
gWifiCacheState.event.results = &gWifiCacheState.resultList[i];
|
|
|
|
// TODO(b/174511061): The current approach only works for situations where
|
|
// the event is released immediately. Add a way to handle this scenario
|
|
// (e.g. an array of chreWifiScanEvent's).
|
|
gWifiCacheState.numWifiEventsPendingRelease++;
|
|
gCallbacks->scanEventCallback(&gWifiCacheState.event);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool isWifiScanResultInCache(const struct chreWifiScanResult *result,
|
|
size_t *index) {
|
|
for (uint8_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
|
|
const struct chreWifiScanResult *cacheResult =
|
|
&gWifiCacheState.resultList[i];
|
|
// Filtering based on BSSID + SSID + frequency based on Linux cfg80211.
|
|
// https://github.com/torvalds/linux/blob/master/net/wireless/scan.c
|
|
if ((result->primaryChannel == cacheResult->primaryChannel) &&
|
|
(memcmp(result->bssid, cacheResult->bssid, CHRE_WIFI_BSSID_LEN) == 0) &&
|
|
(result->ssidLen == cacheResult->ssidLen) &&
|
|
(memcmp(result->ssid, cacheResult->ssid, result->ssidLen) == 0)) {
|
|
*index = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/************************************************
|
|
* Public functions
|
|
***********************************************/
|
|
bool chreWifiScanCacheInit(const struct chrePalSystemApi *systemApi,
|
|
const struct chrePalWifiCallbacks *callbacks) {
|
|
if (systemApi == NULL || callbacks == NULL) {
|
|
return false;
|
|
}
|
|
|
|
gSystemApi = systemApi;
|
|
gCallbacks = callbacks;
|
|
memset(&gWifiCacheState, 0, sizeof(gWifiCacheState));
|
|
gScanMonitoringEnabled = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void chreWifiScanCacheDeinit(void) {
|
|
gSystemApi = NULL;
|
|
gCallbacks = NULL;
|
|
}
|
|
|
|
bool chreWifiScanCacheScanEventBegin(enum chreWifiScanType scanType,
|
|
uint8_t ssidSetSize,
|
|
const uint32_t *scannedFreqList,
|
|
uint16_t scannedFreqListLength,
|
|
uint8_t radioChainPref,
|
|
bool activeScanResult) {
|
|
bool success = false;
|
|
if (chreWifiScanCacheIsInitialized()) {
|
|
enum chreError error = CHRE_ERROR_NONE;
|
|
if (!isFrequencyListValid(scannedFreqList, scannedFreqListLength)) {
|
|
gSystemApi->log(CHRE_LOG_ERROR, "Invalid frequency argument");
|
|
error = CHRE_ERROR_INVALID_ARGUMENT;
|
|
} else if (isWifiScanCacheBusy(true /* logOnBusy */)) {
|
|
error = CHRE_ERROR_BUSY;
|
|
} else {
|
|
success = true;
|
|
memset(&gWifiCacheState, 0, sizeof(gWifiCacheState));
|
|
|
|
gWifiCacheState.event.version = CHRE_WIFI_SCAN_EVENT_VERSION;
|
|
gWifiCacheState.event.scanType = scanType;
|
|
gWifiCacheState.event.ssidSetSize = ssidSetSize;
|
|
|
|
scannedFreqListLength =
|
|
MIN(scannedFreqListLength, CHRE_WIFI_FREQUENCY_LIST_MAX_LEN);
|
|
if (scannedFreqList != NULL) {
|
|
memcpy(gWifiCacheState.scannedFreqList, scannedFreqList,
|
|
scannedFreqListLength * sizeof(uint32_t));
|
|
}
|
|
gWifiCacheState.event.scannedFreqListLen = scannedFreqListLength;
|
|
gWifiCacheState.event.radioChainPref = radioChainPref;
|
|
|
|
gWifiCacheState.activeScanResult = activeScanResult;
|
|
gWifiCacheState.started = true;
|
|
}
|
|
|
|
if (activeScanResult && !success) {
|
|
gCallbacks->scanResponseCallback(false /* pending */, error);
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void chreWifiScanCacheScanEventAdd(const struct chreWifiScanResult *result) {
|
|
if (!gWifiCacheState.started) {
|
|
gSystemApi->log(CHRE_LOG_ERROR, "Cannot add to cache before starting it");
|
|
} else {
|
|
size_t index;
|
|
bool exists = isWifiScanResultInCache(result, &index);
|
|
if (!exists && gWifiCacheState.event.resultTotal >=
|
|
CHRE_PAL_WIFI_SCAN_CACHE_CAPACITY) {
|
|
// TODO(b/174510884): Filter based on e.g. RSSI if full
|
|
gWifiCacheState.numWifiScanResultsDropped++;
|
|
} else {
|
|
if (!exists) {
|
|
// Only add a new entry if the result was not already cached.
|
|
index = gWifiCacheState.event.resultTotal;
|
|
gWifiCacheState.event.resultTotal++;
|
|
}
|
|
|
|
memcpy(&gWifiCacheState.resultList[index], result,
|
|
sizeof(const struct chreWifiScanResult));
|
|
|
|
// ageMs will be properly populated in chreWifiScanCacheScanEventEnd
|
|
gWifiCacheState.resultList[index].ageMs = (uint32_t)(
|
|
gSystemApi->getCurrentTime() / kOneMillisecondInNanoseconds);
|
|
}
|
|
}
|
|
}
|
|
|
|
void chreWifiScanCacheScanEventEnd(enum chreError errorCode) {
|
|
if (gWifiCacheState.started) {
|
|
if (gWifiCacheState.numWifiScanResultsDropped > 0) {
|
|
gSystemApi->log(CHRE_LOG_WARN,
|
|
"Dropped total of %" PRIu32 " access points",
|
|
gWifiCacheState.numWifiScanResultsDropped);
|
|
}
|
|
if (gWifiCacheState.activeScanResult) {
|
|
gCallbacks->scanResponseCallback(
|
|
errorCode == CHRE_ERROR_NONE /* pending */, errorCode);
|
|
}
|
|
|
|
if (errorCode == CHRE_ERROR_NONE &&
|
|
(gWifiCacheState.activeScanResult || gScanMonitoringEnabled)) {
|
|
gWifiCacheState.event.referenceTime = gSystemApi->getCurrentTime();
|
|
gWifiCacheState.event.scannedFreqList = gWifiCacheState.scannedFreqList;
|
|
|
|
uint32_t referenceTimeMs = (uint32_t)(
|
|
gWifiCacheState.event.referenceTime / kOneMillisecondInNanoseconds);
|
|
for (uint16_t i = 0; i < gWifiCacheState.event.resultTotal; i++) {
|
|
gWifiCacheState.resultList[i].ageMs =
|
|
referenceTimeMs - gWifiCacheState.resultList[i].ageMs;
|
|
}
|
|
|
|
chreWifiScanCacheDispatchAll();
|
|
}
|
|
|
|
gWifiCacheState.started = false;
|
|
gWifiCacheState.activeScanResult = false;
|
|
}
|
|
}
|
|
|
|
bool chreWifiScanCacheDispatchFromCache(
|
|
const struct chreWifiScanParams *params) {
|
|
if (!chreWifiScanCacheIsInitialized()) {
|
|
return false;
|
|
}
|
|
|
|
if (paramsMatchScanCache(params) &&
|
|
!isWifiScanCacheBusy(false /* logOnBusy */)) {
|
|
// TODO(b/174511061): Handle scenario where cache is working on delivering
|
|
// a scan event. Ideally the library will wait until it is complete to
|
|
// dispatch from the cache if it meets the criteria, rather than scheduling
|
|
// a fresh scan.
|
|
gCallbacks->scanResponseCallback(true /* pending */, CHRE_ERROR_NONE);
|
|
chreWifiScanCacheDispatchAll();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void chreWifiScanCacheReleaseScanEvent(struct chreWifiScanEvent *event) {
|
|
if (!chreWifiScanCacheIsInitialized()) {
|
|
return;
|
|
}
|
|
|
|
if (event != &gWifiCacheState.event) {
|
|
gSystemApi->log(CHRE_LOG_ERROR, "Invalid event pointer %p", event);
|
|
} else if (gWifiCacheState.numWifiEventsPendingRelease > 0) {
|
|
gWifiCacheState.numWifiEventsPendingRelease--;
|
|
}
|
|
}
|
|
|
|
void chreWifiScanCacheConfigureScanMonitor(bool enable) {
|
|
if (!chreWifiScanCacheIsInitialized()) {
|
|
return;
|
|
}
|
|
|
|
gScanMonitoringEnabled = enable;
|
|
}
|