/* * Copyright (C) 2016 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. */ #ifdef CHRE_WIFI_SUPPORT_ENABLED #include "chre/core/wifi_request_manager.h" #include #include #include #include #include "chre/core/event_loop_manager.h" #include "chre/core/settings.h" #include "chre/core/system_health_monitor.h" #include "chre/platform/fatal_error.h" #include "chre/platform/log.h" #include "chre/platform/system_time.h" #include "chre/util/enum.h" #include "chre/util/nested_data_ptr.h" #include "chre/util/system/debug_dump.h" #include "chre/util/system/event_callbacks.h" #include "chre_api/chre/version.h" // The default timeout values can be overwritten to lower the runtime // for tests. Timeout values cannot be overwritten with a bigger value. #ifdef CHRE_TEST_ASYNC_RESULT_TIMEOUT_NS static_assert(CHRE_TEST_ASYNC_RESULT_TIMEOUT_NS <= CHRE_ASYNC_RESULT_TIMEOUT_NS); #undef CHRE_ASYNC_RESULT_TIMEOUT_NS #define CHRE_ASYNC_RESULT_TIMEOUT_NS CHRE_TEST_ASYNC_RESULT_TIMEOUT_NS #endif #ifdef CHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS static_assert(CHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS <= CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS); #undef CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS #define CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS \ CHRE_TEST_WIFI_RANGING_RESULT_TIMEOUT_NS #endif #ifdef CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS static_assert(CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS <= CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS); #undef CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS #define CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS CHRE_TEST_WIFI_SCAN_RESULT_TIMEOUT_NS #endif namespace chre { WifiRequestManager::DebugLogEntry WifiRequestManager::DebugLogEntry::forScanRequest( uint16_t nanoappInstanceId, const chreWifiScanParams &scanParams, bool syncResult) { DebugLogEntry entry; entry.timestamp = SystemTime::getMonotonicTime(); entry.logType = WifiScanLogType::SCAN_REQUEST; entry.scanRequest.nanoappInstanceId = nanoappInstanceId; entry.scanRequest.maxScanAgeMs = (scanParams.maxScanAgeMs > UINT16_MAX) ? UINT16_MAX : static_cast(scanParams.maxScanAgeMs); entry.scanRequest.scanType = scanParams.scanType; entry.scanRequest.radioChainPref = scanParams.radioChainPref; entry.scanRequest.channelSet = scanParams.channelSet; entry.scanRequest.syncResult = syncResult; return entry; } WifiRequestManager::DebugLogEntry WifiRequestManager::DebugLogEntry::forScanResponse(uint16_t nanoappInstanceId, bool pending, uint8_t errorCode) { DebugLogEntry entry; entry.timestamp = SystemTime::getMonotonicTime(); entry.logType = WifiScanLogType::SCAN_RESPONSE; entry.scanResponse.nanoappInstanceId = nanoappInstanceId; entry.scanResponse.pending = pending; entry.scanResponse.errorCode = errorCode; return entry; } WifiRequestManager::DebugLogEntry WifiRequestManager::DebugLogEntry::forScanEvent( const chreWifiScanEvent &scanEvent) { DebugLogEntry entry; entry.timestamp = SystemTime::getMonotonicTime(); entry.logType = WifiScanLogType::SCAN_EVENT; entry.scanEvent.resultCount = scanEvent.resultCount; entry.scanEvent.resultTotal = scanEvent.resultTotal; entry.scanEvent.eventIndex = scanEvent.eventIndex; entry.scanEvent.scanType = scanEvent.scanType; return entry; } WifiRequestManager::DebugLogEntry WifiRequestManager::DebugLogEntry::forScanMonitorRequest( uint16_t nanoappInstanceId, bool enable, bool syncResult) { DebugLogEntry entry; entry.timestamp = SystemTime::getMonotonicTime(); entry.logType = WifiScanLogType::SCAN_MONITOR_REQUEST; entry.scanMonitorRequest.nanoappInstanceId = nanoappInstanceId; entry.scanMonitorRequest.enable = enable; entry.scanMonitorRequest.syncResult = syncResult; return entry; } WifiRequestManager::DebugLogEntry WifiRequestManager::DebugLogEntry::forScanMonitorResult( uint16_t nanoappInstanceId, bool enabled, uint8_t errorCode) { DebugLogEntry entry; entry.timestamp = SystemTime::getMonotonicTime(); entry.logType = WifiScanLogType::SCAN_MONITOR_RESULT; entry.scanMonitorResult.nanoappInstanceId = nanoappInstanceId; entry.scanMonitorResult.enabled = enabled; entry.scanMonitorResult.errorCode = errorCode; return entry; } WifiRequestManager::WifiRequestManager() { // Reserve space for at least one scan monitoring nanoapp. This ensures that // the first asynchronous push_back will succeed. Future push_backs will be // synchronous and failures will be returned to the client. if (!mScanMonitorNanoapps.reserve(1)) { FATAL_ERROR_OOM(); } } void WifiRequestManager::init() { mPlatformWifi.init(); } uint32_t WifiRequestManager::getCapabilities() { return mPlatformWifi.getCapabilities(); } void WifiRequestManager::dispatchQueuedConfigureScanMonitorRequests() { while (!mPendingScanMonitorRequests.empty()) { const auto &stateTransition = mPendingScanMonitorRequests.front(); bool hasScanMonitorRequest = nanoappHasScanMonitorRequest(stateTransition.nanoappInstanceId); if (scanMonitorIsInRequestedState(stateTransition.enable, hasScanMonitorRequest)) { // We are already in the target state so just post an event indicating // success postScanMonitorAsyncResultEventFatal( stateTransition.nanoappInstanceId, true /* success */, stateTransition.enable, CHRE_ERROR_NONE, stateTransition.cookie); } else if (scanMonitorStateTransitionIsRequired(stateTransition.enable, hasScanMonitorRequest)) { bool syncResult = mPlatformWifi.configureScanMonitor(stateTransition.enable); addDebugLog(DebugLogEntry::forScanMonitorRequest( stateTransition.nanoappInstanceId, stateTransition.enable, syncResult)); if (!syncResult) { postScanMonitorAsyncResultEventFatal( stateTransition.nanoappInstanceId, false /* success */, stateTransition.enable, CHRE_ERROR, stateTransition.cookie); } else { mConfigureScanMonitorTimeoutHandle = setConfigureScanMonitorTimer(); break; } } else { CHRE_ASSERT_LOG(false, "Invalid scan monitor state"); } mPendingScanMonitorRequests.pop(); } } void WifiRequestManager::handleConfigureScanMonitorTimeout() { if (mPendingScanMonitorRequests.empty()) { LOGE("Configure Scan Monitor timer timedout with no pending request."); } else { EventLoopManagerSingleton::get()->getSystemHealthMonitor().onFailure( HealthCheckId::WifiConfigureScanMonitorTimeout); mPendingScanMonitorRequests.pop(); dispatchQueuedConfigureScanMonitorRequests(); } } TimerHandle WifiRequestManager::setConfigureScanMonitorTimer() { auto callback = [](uint16_t /*type*/, void * /*data*/, void * /*extraData*/) { EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleConfigureScanMonitorTimeout(); }; return EventLoopManagerSingleton::get()->setDelayedCallback( SystemCallbackType::RequestTimeoutEvent, nullptr, callback, Nanoseconds(CHRE_ASYNC_RESULT_TIMEOUT_NS)); } bool WifiRequestManager::configureScanMonitor(Nanoapp *nanoapp, bool enable, const void *cookie) { CHRE_ASSERT(nanoapp); bool success = false; uint16_t instanceId = nanoapp->getInstanceId(); bool hasScanMonitorRequest = nanoappHasScanMonitorRequest(instanceId); if (!mPendingScanMonitorRequests.empty()) { success = addScanMonitorRequestToQueue(nanoapp, enable, cookie); } else if (scanMonitorIsInRequestedState(enable, hasScanMonitorRequest)) { // The scan monitor is already in the requested state. A success event can // be posted immediately. success = postScanMonitorAsyncResultEvent(instanceId, true /* success */, enable, CHRE_ERROR_NONE, cookie); } else if (scanMonitorStateTransitionIsRequired(enable, hasScanMonitorRequest)) { success = addScanMonitorRequestToQueue(nanoapp, enable, cookie); if (success) { success = mPlatformWifi.configureScanMonitor(enable); addDebugLog( DebugLogEntry::forScanMonitorRequest(instanceId, enable, success)); if (!success) { mPendingScanMonitorRequests.pop_back(); LOGE("Failed to enable the scan monitor for nanoapp instance %" PRIu16, instanceId); } else { mConfigureScanMonitorTimeoutHandle = setConfigureScanMonitorTimer(); } } } else { CHRE_ASSERT_LOG(false, "Invalid scan monitor configuration"); } return success; } uint32_t WifiRequestManager::disableAllSubscriptions(Nanoapp *nanoapp) { uint32_t numSubscriptionsDisabled = 0; // Disable active scan monitoring. if (nanoappHasScanMonitorRequest(nanoapp->getInstanceId()) || nanoappHasPendingScanMonitorRequest(nanoapp->getInstanceId())) { numSubscriptionsDisabled++; configureScanMonitor(nanoapp, false /*enabled*/, nullptr /*cookie*/); } // Disable active NAN subscriptions. for (size_t i = 0; i < mNanoappSubscriptions.size(); ++i) { if (mNanoappSubscriptions[i].nanoappInstanceId == nanoapp->getInstanceId()) { numSubscriptionsDisabled++; nanSubscribeCancel(nanoapp, mNanoappSubscriptions[i].subscriptionId); } } return numSubscriptionsDisabled; } bool WifiRequestManager::requestRangingByType(RangingType type, const void *rangingParams) { bool success = false; if (type == RangingType::WIFI_AP) { auto *params = static_cast(rangingParams); success = mPlatformWifi.requestRanging(params); } else { auto *params = static_cast(rangingParams); success = mPlatformWifi.requestNanRanging(params); } if (success) { mRequestRangingTimeoutHandle = setRangingRequestTimer(); } return success; } bool WifiRequestManager::updateRangingRequest(RangingType type, PendingRangingRequest &request, const void *rangingParams) { bool success = false; if (type == RangingType::WIFI_AP) { auto *params = static_cast(rangingParams); success = request.targetList.copy_array(params->targetList, params->targetListLen); } else { auto *params = static_cast(rangingParams); std::memcpy(request.nanRangingParams.macAddress, params->macAddress, CHRE_WIFI_BSSID_LEN); success = true; } return success; } bool WifiRequestManager::sendRangingRequest(PendingRangingRequest &request) { bool success = false; if (request.type == RangingType::WIFI_AP) { struct chreWifiRangingParams params = {}; params.targetListLen = static_cast(request.targetList.size()); params.targetList = request.targetList.data(); success = mPlatformWifi.requestRanging(¶ms); } else { struct chreWifiNanRangingParams params; std::memcpy(params.macAddress, request.nanRangingParams.macAddress, CHRE_WIFI_BSSID_LEN); success = mPlatformWifi.requestNanRanging(¶ms); } if (success) { mRequestRangingTimeoutHandle = setRangingRequestTimer(); } return success; } void WifiRequestManager::handleRangingRequestTimeout() { if (mPendingRangingRequests.empty()) { LOGE("Request ranging timer timedout with no pending request."); } else { EventLoopManagerSingleton::get()->getSystemHealthMonitor().onFailure( HealthCheckId::WifiRequestRangingTimeout); mPendingRangingRequests.pop(); while (!mPendingRangingRequests.empty() && !dispatchQueuedRangingRequest()) ; } } TimerHandle WifiRequestManager::setRangingRequestTimer() { auto callback = [](uint16_t /*type*/, void * /*data*/, void * /*extraData*/) { EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleRangingRequestTimeout(); }; return EventLoopManagerSingleton::get()->setDelayedCallback( SystemCallbackType::RequestTimeoutEvent, nullptr, callback, Nanoseconds(CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS)); } bool WifiRequestManager::requestRanging(RangingType rangingType, Nanoapp *nanoapp, const void *rangingParams, const void *cookie) { CHRE_ASSERT(nanoapp); CHRE_ASSERT(rangingParams); bool success = false; if (!mPendingRangingRequests.emplace()) { LOGE("Can't issue new RTT request; pending queue full"); } else { PendingRangingRequest &req = mPendingRangingRequests.back(); req.nanoappInstanceId = nanoapp->getInstanceId(); req.cookie = cookie; if (mPendingRangingRequests.size() == 1) { // First in line; dispatch request immediately if (!areRequiredSettingsEnabled()) { // Treat as success but post async failure per API. success = true; postRangingAsyncResult(CHRE_ERROR_FUNCTION_DISABLED); mPendingRangingRequests.pop_back(); } else if (!requestRangingByType(rangingType, rangingParams)) { LOGE("WiFi ranging request of type %d failed", static_cast(rangingType)); mPendingRangingRequests.pop_back(); } else { success = true; } } else { success = updateRangingRequest(rangingType, req, rangingParams); if (!success) { LOG_OOM(); mPendingRangingRequests.pop_back(); } } } return success; } void WifiRequestManager::handleScanRequestTimeout() { mScanRequestTimeoutHandle = CHRE_TIMER_INVALID; if (mPendingScanRequests.empty()) { LOGE("Scan Request timer timedout with no pending request."); } else { EventLoopManagerSingleton::get()->getSystemHealthMonitor().onFailure( HealthCheckId::WifiScanResponseTimeout); // Reset the scan accumulator logic to prevent interference with the next // scan request. resetScanEventResultCountAccumulator(); mPendingScanRequests.pop(); dispatchQueuedScanRequests(true /* postAsyncResult */); } } TimerHandle WifiRequestManager::setScanRequestTimer() { CHRE_ASSERT(mScanRequestTimeoutHandle == CHRE_TIMER_INVALID); auto callback = [](uint16_t /*type*/, void * /*data*/, void * /*extraData*/) { EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleScanRequestTimeout(); }; return EventLoopManagerSingleton::get()->setDelayedCallback( SystemCallbackType::RequestTimeoutEvent, nullptr, callback, Nanoseconds(CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS)); } void WifiRequestManager::cancelScanRequestTimer() { if (mScanRequestTimeoutHandle != CHRE_TIMER_INVALID) { EventLoopManagerSingleton::get()->cancelDelayedCallback( mScanRequestTimeoutHandle); mScanRequestTimeoutHandle = CHRE_TIMER_INVALID; } } bool WifiRequestManager::nanoappHasPendingScanRequest( uint16_t instanceId) const { for (const auto &scanRequest : mPendingScanRequests) { if (scanRequest.nanoappInstanceId == instanceId) { return true; } } return false; } bool WifiRequestManager::requestScan(Nanoapp *nanoapp, const struct chreWifiScanParams *params, const void *cookie) { CHRE_ASSERT(nanoapp); // Handle compatibility with nanoapps compiled against API v1.1, which doesn't // include the radioChainPref parameter in chreWifiScanParams struct chreWifiScanParams paramsCompat; if (nanoapp->getTargetApiVersion() < CHRE_API_VERSION_1_2) { memcpy(¶msCompat, params, offsetof(chreWifiScanParams, radioChainPref)); paramsCompat.radioChainPref = CHRE_WIFI_RADIO_CHAIN_PREF_DEFAULT; params = ¶msCompat; } bool success = false; uint16_t nanoappInstanceId = nanoapp->getInstanceId(); if (nanoappHasPendingScanRequest(nanoappInstanceId)) { LOGE("Can't issue new scan request: nanoapp: %" PRIx64 " already has a pending request", nanoapp->getAppId()); } else if (!mPendingScanRequests.emplace(nanoappInstanceId, cookie, params)) { LOG_OOM(); } else if (!EventLoopManagerSingleton::get() ->getSettingManager() .getSettingEnabled(Setting::WIFI_AVAILABLE)) { // Treat as success, but send an async failure per API contract. success = true; handleScanResponse(false /* pending */, CHRE_ERROR_FUNCTION_DISABLED); } else { if (mPendingScanRequests.size() == 1) { success = dispatchQueuedScanRequests(false /* postAsyncResult */); } else { success = true; } } return success; } void WifiRequestManager::handleScanMonitorStateChange(bool enabled, uint8_t errorCode) { EventLoopManagerSingleton::get()->cancelDelayedCallback( mConfigureScanMonitorTimeoutHandle); struct CallbackState { bool enabled; uint8_t errorCode; }; auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { CallbackState cbState = NestedDataPtr(data); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleScanMonitorStateChangeSync(cbState.enabled, cbState.errorCode); }; CallbackState cbState = {}; cbState.enabled = enabled; cbState.errorCode = errorCode; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiScanMonitorStateChange, NestedDataPtr(cbState), callback); } void WifiRequestManager::handleScanResponse(bool pending, uint8_t errorCode) { struct CallbackState { bool pending; uint8_t errorCode; }; auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { CallbackState cbState = NestedDataPtr(data); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleScanResponseSync(cbState.pending, cbState.errorCode); }; CallbackState cbState = {}; cbState.pending = pending; cbState.errorCode = errorCode; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiRequestScanResponse, NestedDataPtr(cbState), callback); } void WifiRequestManager::handleRangingEvent( uint8_t errorCode, struct chreWifiRangingEvent *event) { EventLoopManagerSingleton::get()->cancelDelayedCallback( mRequestRangingTimeoutHandle); auto callback = [](uint16_t /*type*/, void *data, void *extraData) { uint8_t cbErrorCode = NestedDataPtr(extraData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleRangingEventSync( cbErrorCode, static_cast(data)); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiHandleRangingEvent, event, callback, NestedDataPtr(errorCode)); } void WifiRequestManager::handleScanEvent(struct chreWifiScanEvent *event) { auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { auto *scanEvent = static_cast(data); EventLoopManagerSingleton::get() ->getWifiRequestManager() .postScanEventFatal(scanEvent); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiHandleScanEvent, event, callback); } void WifiRequestManager::handleNanServiceIdentifierEventSync( uint8_t errorCode, uint32_t subscriptionId) { if (!mPendingNanSubscribeRequests.empty()) { auto &req = mPendingNanSubscribeRequests.front(); chreWifiNanIdentifierEvent *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->id = subscriptionId; event->result.requestType = CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE; event->result.success = (errorCode == CHRE_ERROR_NONE); event->result.errorCode = errorCode; event->result.cookie = req.cookie; if (errorCode == CHRE_ERROR_NONE) { // It is assumed that the NAN discovery engine guarantees a unique ID // for each subscription - avoid redundant checks on uniqueness here. if (!mNanoappSubscriptions.push_back(NanoappNanSubscriptions( req.nanoappInstanceId, subscriptionId))) { LOG_OOM(); // Even though the subscription request was able to successfully // obtain an ID, CHRE ran out of memory and couldn't store the // instance ID - subscription ID pair. Indicate this in the event // result. // TODO(b/204226580): Cancel the subscription if we run out of // memory. event->result.errorCode = CHRE_ERROR_NO_MEMORY; } } EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_NAN_IDENTIFIER_RESULT, event, freeEventDataCallback, req.nanoappInstanceId); } mPendingNanSubscribeRequests.pop(); dispatchQueuedNanSubscribeRequestWithRetry(); } else { LOGE("Received a NAN identifier event with no pending request!"); } } void WifiRequestManager::handleNanServiceIdentifierEvent( uint8_t errorCode, uint32_t subscriptionId) { auto callback = [](uint16_t /*type*/, void *data, void *extraData) { uint8_t errorCode = NestedDataPtr(data); uint32_t subscriptionId = NestedDataPtr(extraData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleNanServiceIdentifierEventSync(errorCode, subscriptionId); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiNanServiceIdEvent, NestedDataPtr(errorCode), callback, NestedDataPtr(subscriptionId)); } bool WifiRequestManager::getNappIdFromSubscriptionId( uint32_t subscriptionId, uint16_t *nanoappInstanceId) { bool success = false; for (auto &sub : mNanoappSubscriptions) { if (sub.subscriptionId == subscriptionId) { *nanoappInstanceId = sub.nanoappInstanceId; success = true; break; } } return success; } void WifiRequestManager::handleNanServiceDiscoveryEventSync( struct chreWifiNanDiscoveryEvent *event) { CHRE_ASSERT(event != nullptr); uint16_t nanoappInstanceId; if (getNappIdFromSubscriptionId(event->subscribeId, &nanoappInstanceId)) { EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_NAN_DISCOVERY_RESULT, event, freeNanDiscoveryEventCallback, nanoappInstanceId); } else { LOGE("Failed to find a nanoapp owning subscription ID %" PRIu32, event->subscribeId); } } void WifiRequestManager::handleNanServiceDiscoveryEvent( struct chreWifiNanDiscoveryEvent *event) { auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { auto *event = static_cast(data); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleNanServiceDiscoveryEventSync(event); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiNanServiceDiscoveryEvent, event, callback); } void WifiRequestManager::handleNanServiceLostEventSync(uint32_t subscriptionId, uint32_t publisherId) { uint16_t nanoappInstanceId; if (getNappIdFromSubscriptionId(subscriptionId, &nanoappInstanceId)) { chreWifiNanSessionLostEvent *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->id = subscriptionId; event->peerId = publisherId; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_NAN_SESSION_LOST, event, freeEventDataCallback, nanoappInstanceId); } } else { LOGE("Failed to find a nanoapp owning subscription ID %" PRIu32, subscriptionId); } } void WifiRequestManager::handleNanServiceLostEvent(uint32_t subscriptionId, uint32_t publisherId) { auto callback = [](uint16_t /*type*/, void *data, void *extraData) { auto subscriptionId = NestedDataPtr(data); auto publisherId = NestedDataPtr(extraData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleNanServiceLostEventSync(subscriptionId, publisherId); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiNanServiceSessionLostEvent, NestedDataPtr(subscriptionId), callback, NestedDataPtr(publisherId)); } void WifiRequestManager::handleNanServiceTerminatedEventSync( uint8_t errorCode, uint32_t subscriptionId) { uint16_t nanoappInstanceId; if (getNappIdFromSubscriptionId(subscriptionId, &nanoappInstanceId)) { chreWifiNanSessionTerminatedEvent *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->id = subscriptionId; event->reason = errorCode; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_NAN_SESSION_TERMINATED, event, freeEventDataCallback, nanoappInstanceId); } } else { LOGE("Failed to find a nanoapp owning subscription ID %" PRIu32, subscriptionId); } } void WifiRequestManager::handleNanServiceSubscriptionCanceledEventSync( uint8_t errorCode, uint32_t subscriptionId) { for (size_t i = 0; i < mNanoappSubscriptions.size(); ++i) { if (mNanoappSubscriptions[i].subscriptionId == subscriptionId) { if (errorCode != CHRE_ERROR_NONE) { LOGE("Subscription %" PRIu32 " cancelation error: %" PRIu8, subscriptionId, errorCode); } mNanoappSubscriptions.erase(i); break; } } } void WifiRequestManager::handleNanServiceTerminatedEvent( uint8_t errorCode, uint32_t subscriptionId) { auto callback = [](uint16_t /*type*/, void *data, void *extraData) { auto errorCode = NestedDataPtr(data); auto subscriptionId = NestedDataPtr(extraData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleNanServiceTerminatedEventSync(errorCode, subscriptionId); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiNanServiceTerminatedEvent, NestedDataPtr(errorCode), callback, NestedDataPtr(subscriptionId)); } void WifiRequestManager::handleNanServiceSubscriptionCanceledEvent( uint8_t errorCode, uint32_t subscriptionId) { auto callback = [](uint16_t /*type*/, void *data, void *extraData) { auto errorCode = NestedDataPtr(data); auto subscriptionId = NestedDataPtr(extraData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleNanServiceSubscriptionCanceledEventSync(errorCode, subscriptionId); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiNanServiceTerminatedEvent, NestedDataPtr(errorCode), callback, NestedDataPtr(subscriptionId)); } void WifiRequestManager::dumpDebugLog(const DebugLogEntry &log, DebugDumpWrapper &debugDump) const { debugDump.print(" ts=%" PRIu64 " ", log.timestamp.toRawNanoseconds()); switch (log.logType) { case WifiScanLogType::SCAN_REQUEST: debugDump.print("scanReq: nappId=%" PRIu16 " scanType=%" PRIu8 " maxScanAge(ms)=%" PRIu16 " radioChainPref=%" PRIu8 " channelSet=%" PRIu8 " syncResult=%d\n", log.scanRequest.nanoappInstanceId, log.scanRequest.scanType, log.scanRequest.maxScanAgeMs, log.scanRequest.radioChainPref, log.scanRequest.channelSet, log.scanRequest.syncResult); break; case WifiScanLogType::SCAN_RESPONSE: debugDump.print("scanRsp: nappId=%" PRIu16 " pending=%" PRIu8 " errorCode=%" PRIu8 "\n", log.scanResponse.nanoappInstanceId, log.scanResponse.pending, log.scanResponse.errorCode); break; case WifiScanLogType::SCAN_EVENT: debugDump.print("scanEvt: resultCount=%" PRIu8 " resultTotal=%" PRIu8 " eventIndex=%" PRIu8 " scanType=%" PRIu8 "\n", log.scanEvent.resultCount, log.scanEvent.resultTotal, log.scanEvent.eventIndex, log.scanEvent.scanType); break; case WifiScanLogType::SCAN_MONITOR_REQUEST: debugDump.print("scanMonReq: nappId=%" PRIu16 " enable=%" PRIu8 " syncResult=%" PRIu8 "\n", log.scanMonitorRequest.nanoappInstanceId, log.scanMonitorRequest.enable, log.scanMonitorRequest.syncResult); break; case WifiScanLogType::SCAN_MONITOR_RESULT: debugDump.print("scanMonRes: nappId=%" PRIu16 " enabled=%" PRIu8 " errorCode=%" PRIu8 "\n", log.scanMonitorResult.nanoappInstanceId, log.scanMonitorResult.enabled, log.scanMonitorResult.errorCode); break; default: debugDump.print("unknown log type %" PRIu8 "\n", asBaseType(log.logType)); } } void WifiRequestManager::logStateToBuffer(DebugDumpWrapper &debugDump) const { debugDump.print("\nWIFI:\n"); debugDump.print(" Scan monitor: %s\n", scanMonitorIsEnabled() ? "enabled" : "disabled"); if (scanMonitorIsEnabled()) { debugDump.print(" Scan monitor nanoapps:\n"); for (uint16_t instanceId : mScanMonitorNanoapps) { debugDump.print(" nappId=%" PRIu16 "\n", instanceId); } } if (!mPendingScanRequests.empty()) { debugDump.print(" Pending scan requests:\n"); for (const auto &request : mPendingScanRequests) { debugDump.print(" nappId=%" PRIu16 "\n", request.nanoappInstanceId); } } if (!mPendingScanMonitorRequests.empty()) { debugDump.print(" Pending scan monitor requests:\n"); for (const auto &transition : mPendingScanMonitorRequests) { debugDump.print(" enable=%s nappId=%" PRIu16 "\n", transition.enable ? "true" : "false", transition.nanoappInstanceId); } } size_t i = mDebugLogs.size(); debugDump.print(" Last %zu debug entries:\n", i); while (i-- > 0) { dumpDebugLog(mDebugLogs[i], debugDump); } debugDump.print(" API error distribution (error-code indexed):\n"); debugDump.print(" Scan monitor:\n"); debugDump.logErrorHistogram(mScanMonitorErrorHistogram, ARRAY_SIZE(mScanMonitorErrorHistogram)); debugDump.print(" Active Scan:\n"); debugDump.logErrorHistogram(mActiveScanErrorHistogram, ARRAY_SIZE(mActiveScanErrorHistogram)); if (!mNanoappSubscriptions.empty()) { debugDump.print(" Active NAN service subscriptions:\n"); for (const auto &sub : mNanoappSubscriptions) { debugDump.print(" nappID=%" PRIu16 " sub ID=%" PRIu32 "\n", sub.nanoappInstanceId, sub.subscriptionId); } } if (!mPendingNanSubscribeRequests.empty()) { debugDump.print(" Pending NAN service subscriptions:\n"); for (const auto &req : mPendingNanSubscribeRequests) { debugDump.print(" nappID=%" PRIu16 " (type %" PRIu8 ") to svc: %s\n", req.nanoappInstanceId, req.type, req.service.data()); } } } bool WifiRequestManager::scanMonitorIsEnabled() const { return !mScanMonitorNanoapps.empty(); } bool WifiRequestManager::nanoappHasScanMonitorRequest( uint16_t instanceId, size_t *nanoappIndex) const { size_t index = mScanMonitorNanoapps.find(instanceId); bool hasScanMonitorRequest = (index != mScanMonitorNanoapps.size()); if (hasScanMonitorRequest && nanoappIndex != nullptr) { *nanoappIndex = index; } return hasScanMonitorRequest; } bool WifiRequestManager::scanMonitorIsInRequestedState( bool requestedState, bool nanoappHasRequest) const { return (requestedState == scanMonitorIsEnabled() || (!requestedState && (!nanoappHasRequest || mScanMonitorNanoapps.size() > 1))); } bool WifiRequestManager::scanMonitorStateTransitionIsRequired( bool requestedState, bool nanoappHasRequest) const { return ((requestedState && mScanMonitorNanoapps.empty()) || (!requestedState && nanoappHasRequest && mScanMonitorNanoapps.size() == 1)); } bool WifiRequestManager::addScanMonitorRequestToQueue(Nanoapp *nanoapp, bool enable, const void *cookie) { PendingScanMonitorRequest scanMonitorStateTransition; scanMonitorStateTransition.nanoappInstanceId = nanoapp->getInstanceId(); scanMonitorStateTransition.cookie = cookie; scanMonitorStateTransition.enable = enable; bool success = mPendingScanMonitorRequests.push(scanMonitorStateTransition); if (!success) { LOGW("Too many scan monitor state transitions"); } return success; } bool WifiRequestManager::nanoappHasPendingScanMonitorRequest( uint16_t instanceId) const { const int numRequests = static_cast(mPendingScanMonitorRequests.size()); for (int i = numRequests - 1; i >= 0; i--) { const PendingScanMonitorRequest &request = mPendingScanMonitorRequests[static_cast(i)]; // The last pending request determines the state of the scan monitoring. if (request.nanoappInstanceId == instanceId) { return request.enable; } } return false; } bool WifiRequestManager::updateNanoappScanMonitoringList(bool enable, uint16_t instanceId) { bool success = true; Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId( instanceId); size_t nanoappIndex; bool hasExistingRequest = nanoappHasScanMonitorRequest(instanceId, &nanoappIndex); if (nanoapp == nullptr) { // When the scan monitoring is disabled from inside nanoappEnd() or when // CHRE cleanup the subscription automatically it is possible that the // current method is called after the nanoapp is unloaded. In such a case // we still want to remove the nanoapp from mScanMonitorNanoapps. if (!enable && hasExistingRequest) { mScanMonitorNanoapps.erase(nanoappIndex); } else { LOGW("Failed to update scan monitoring list for non-existent nanoapp"); } } else { if (enable) { if (!hasExistingRequest) { // The scan monitor was successfully enabled for this nanoapp and // there is no existing request. Add it to the list of scan monitoring // nanoapps. success = mScanMonitorNanoapps.push_back(instanceId); if (!success) { LOG_OOM(); } else { nanoapp->registerForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); } } } else if (hasExistingRequest) { // The scan monitor was successfully disabled for a previously enabled // nanoapp. Remove it from the list of scan monitoring nanoapps. mScanMonitorNanoapps.erase(nanoappIndex); nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); } // else disabling an inactive request, treat as success per the CHRE API. } return success; } bool WifiRequestManager::postScanMonitorAsyncResultEvent( uint16_t nanoappInstanceId, bool success, bool enable, uint8_t errorCode, const void *cookie) { // Allocate and post an event to the nanoapp requesting wifi. bool eventPosted = false; // If we failed to enable, don't add the nanoapp to the list, but always // remove it if it was trying to disable. This keeps us from getting stuck in // a state where we think the scan monitor is enabled (because the list is // non-empty) when we actually aren't sure (e.g. the scan monitor disablement // may have been handled but delivering the result ran into an error). if ((!success && enable) || updateNanoappScanMonitoringList(enable, nanoappInstanceId)) { chreAsyncResult *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->requestType = CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR; event->success = success; event->errorCode = errorCode; event->reserved = 0; event->cookie = cookie; if (errorCode < CHRE_ERROR_SIZE) { mScanMonitorErrorHistogram[errorCode]++; } else { LOGE("Undefined error in ScanMonitorAsyncResult: %" PRIu8, errorCode); } EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, nanoappInstanceId); eventPosted = true; } } return eventPosted; } void WifiRequestManager::postScanMonitorAsyncResultEventFatal( uint16_t nanoappInstanceId, bool success, bool enable, uint8_t errorCode, const void *cookie) { if (!postScanMonitorAsyncResultEvent(nanoappInstanceId, success, enable, errorCode, cookie)) { FATAL_ERROR("Failed to send WiFi scan monitor async result event"); } } bool WifiRequestManager::postScanRequestAsyncResultEvent( uint16_t nanoappInstanceId, bool success, uint8_t errorCode, const void *cookie) { // TODO: the body of this function can be extracted to a common helper for use // across this function, postScanMonitorAsyncResultEvent, // postRangingAsyncResult, and GnssSession::postAsyncResultEvent bool eventPosted = false; chreAsyncResult *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->requestType = CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN; event->success = success; event->errorCode = errorCode; event->reserved = 0; event->cookie = cookie; if (errorCode < CHRE_ERROR_SIZE) { mActiveScanErrorHistogram[errorCode]++; } else { LOGE("Undefined error in ScanRequestAsyncResult: %" PRIu8, errorCode); } EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, nanoappInstanceId); eventPosted = true; } return eventPosted; } void WifiRequestManager::postScanRequestAsyncResultEventFatal( uint16_t nanoappInstanceId, bool success, uint8_t errorCode, const void *cookie) { if (!postScanRequestAsyncResultEvent(nanoappInstanceId, success, errorCode, cookie)) { FATAL_ERROR("Failed to send WiFi scan request async result event"); } } void WifiRequestManager::postScanEventFatal(chreWifiScanEvent *event) { EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_SCAN_RESULT, event, freeWifiScanEventCallback); } void WifiRequestManager::handleScanMonitorStateChangeSync(bool enabled, uint8_t errorCode) { addDebugLog(DebugLogEntry::forScanMonitorResult( mPendingScanMonitorRequests.empty() ? kSystemInstanceId : mPendingScanMonitorRequests.front().nanoappInstanceId, enabled, errorCode)); if (mPendingScanMonitorRequests.empty()) { LOGE("Scan monitor change with no pending requests (enabled %d " "errorCode %" PRIu8 ")", enabled, errorCode); EventLoopManagerSingleton::get()->getSystemHealthMonitor().onFailure( HealthCheckId::UnexpectedWifiScanMonitorStateChange); } // Success is defined as having no errors ... in life ༼ つ ◕_◕ ༽つ bool success = (errorCode == CHRE_ERROR_NONE); if (!mPendingScanMonitorRequests.empty()) { const auto &stateTransition = mPendingScanMonitorRequests.front(); success &= (stateTransition.enable == enabled); postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId, success, stateTransition.enable, errorCode, stateTransition.cookie); mPendingScanMonitorRequests.pop(); } dispatchQueuedConfigureScanMonitorRequests(); } void WifiRequestManager::postNanAsyncResultEvent(uint16_t nanoappInstanceId, uint8_t requestType, bool success, uint8_t errorCode, const void *cookie) { chreAsyncResult *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->requestType = requestType; event->cookie = cookie; event->errorCode = errorCode; event->success = success; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, nanoappInstanceId); } } void WifiRequestManager::handleScanResponseSync(bool pending, uint8_t errorCode) { addDebugLog(DebugLogEntry::forScanResponse( mPendingScanRequests.empty() ? kSystemInstanceId : mPendingScanRequests.front().nanoappInstanceId, pending, errorCode)); if (mPendingScanRequests.empty()) { EventLoopManagerSingleton::get()->getSystemHealthMonitor().onFailure( HealthCheckId::UnexpectedWifiScanResponse); } if (!pending && errorCode == CHRE_ERROR_NONE) { LOGE("Invalid wifi scan response"); errorCode = CHRE_ERROR; } if (!mPendingScanRequests.empty()) { bool success = (pending && errorCode == CHRE_ERROR_NONE); if (!success) { LOGW("Wifi scan request failed: pending %d, errorCode %" PRIu8, pending, errorCode); } PendingScanRequest ¤tScanRequest = mPendingScanRequests.front(); postScanRequestAsyncResultEventFatal(currentScanRequest.nanoappInstanceId, success, errorCode, currentScanRequest.cookie); // Set a flag to indicate that results may be pending. mScanRequestResultsArePending = pending; if (pending) { Nanoapp *nanoapp = EventLoopManagerSingleton::get() ->getEventLoop() .findNanoappByInstanceId(currentScanRequest.nanoappInstanceId); if (nanoapp == nullptr) { LOGW("Received WiFi scan response for unknown nanoapp"); } else { nanoapp->registerForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); } } else { // If the scan results are not pending, pop the first event since it's no // longer waiting for anything. Otherwise, wait for the results to be // delivered and then pop the first request. cancelScanRequestTimer(); mPendingScanRequests.pop(); dispatchQueuedScanRequests(true /* postAsyncResult */); } } } bool WifiRequestManager::postRangingAsyncResult(uint8_t errorCode) { bool eventPosted = false; if (mPendingRangingRequests.empty()) { LOGE("Unexpected ranging event callback"); } else { auto *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { const PendingRangingRequest &req = mPendingRangingRequests.front(); event->requestType = CHRE_WIFI_REQUEST_TYPE_RANGING; event->success = (errorCode == CHRE_ERROR_NONE); event->errorCode = errorCode; event->reserved = 0; event->cookie = req.cookie; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, req.nanoappInstanceId); eventPosted = true; } } return eventPosted; } bool WifiRequestManager::dispatchQueuedRangingRequest() { bool success = false; uint8_t asyncError = CHRE_ERROR_NONE; PendingRangingRequest &req = mPendingRangingRequests.front(); if (!areRequiredSettingsEnabled()) { asyncError = CHRE_ERROR_FUNCTION_DISABLED; } else if (!sendRangingRequest(req)) { asyncError = CHRE_ERROR; } else { success = true; } if (asyncError != CHRE_ERROR_NONE) { postRangingAsyncResult(asyncError); mPendingRangingRequests.pop(); } return success; } bool WifiRequestManager::dispatchQueuedNanSubscribeRequest() { bool success = false; if (!mPendingNanSubscribeRequests.empty()) { uint8_t asyncError = CHRE_ERROR_NONE; const auto &req = mPendingNanSubscribeRequests.front(); struct chreWifiNanSubscribeConfig config = {}; buildNanSubscribeConfigFromRequest(req, &config); if (!areRequiredSettingsEnabled()) { asyncError = CHRE_ERROR_FUNCTION_DISABLED; } else if (!mPlatformWifi.nanSubscribe(&config)) { asyncError = CHRE_ERROR; } if (asyncError != CHRE_ERROR_NONE) { postNanAsyncResultEvent(req.nanoappInstanceId, CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE, false /*success*/, asyncError, req.cookie); mPendingNanSubscribeRequests.pop(); } else { success = true; } } return success; } void WifiRequestManager::dispatchQueuedNanSubscribeRequestWithRetry() { while (!mPendingNanSubscribeRequests.empty() && !dispatchQueuedNanSubscribeRequest()) ; } bool WifiRequestManager::dispatchQueuedScanRequests(bool postAsyncResult) { while (!mPendingScanRequests.empty()) { uint8_t asyncError = CHRE_ERROR_NONE; const PendingScanRequest ¤tScanRequest = mPendingScanRequests.front(); if (!EventLoopManagerSingleton::get() ->getSettingManager() .getSettingEnabled(Setting::WIFI_AVAILABLE)) { asyncError = CHRE_ERROR_FUNCTION_DISABLED; } else { bool syncResult = mPlatformWifi.requestScan(¤tScanRequest.scanParams); addDebugLog(DebugLogEntry::forScanRequest( currentScanRequest.nanoappInstanceId, currentScanRequest.scanParams, syncResult)); if (!syncResult) { asyncError = CHRE_ERROR; } else { mScanRequestTimeoutHandle = setScanRequestTimer(); return true; } } if (postAsyncResult) { postScanRequestAsyncResultEvent(currentScanRequest.nanoappInstanceId, false /*success*/, asyncError, currentScanRequest.cookie); } else { LOGE("Wifi scan request failed"); } mPendingScanRequests.pop(); } return false; } void WifiRequestManager::handleRangingEventSync( uint8_t errorCode, struct chreWifiRangingEvent *event) { if (!areRequiredSettingsEnabled()) { errorCode = CHRE_ERROR_FUNCTION_DISABLED; } if (postRangingAsyncResult(errorCode)) { if (errorCode != CHRE_ERROR_NONE) { LOGW("RTT ranging failed with error %d", errorCode); if (event != nullptr) { freeWifiRangingEventCallback(CHRE_EVENT_WIFI_RANGING_RESULT, event); } } else { EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_RANGING_RESULT, event, freeWifiRangingEventCallback, mPendingRangingRequests.front().nanoappInstanceId); } mPendingRangingRequests.pop(); } // If we have any pending requests, try issuing them to the platform until the // first one succeeds. while (!mPendingRangingRequests.empty() && !dispatchQueuedRangingRequest()) ; } void WifiRequestManager::handleFreeWifiScanEvent(chreWifiScanEvent *scanEvent) { addDebugLog(DebugLogEntry::forScanEvent(*scanEvent)); if (mScanRequestResultsArePending) { // Reset the event distribution logic once an entire scan event has been // received and processed by the nanoapp requesting the scan event. mScanEventResultCountAccumulator += scanEvent->resultCount; if (mScanEventResultCountAccumulator >= scanEvent->resultTotal) { resetScanEventResultCountAccumulator(); cancelScanRequestTimer(); } if (!mScanRequestResultsArePending && !mPendingScanRequests.empty()) { uint16_t pendingNanoappInstanceId = mPendingScanRequests.front().nanoappInstanceId; Nanoapp *nanoapp = EventLoopManagerSingleton::get() ->getEventLoop() .findNanoappByInstanceId(pendingNanoappInstanceId); if (nanoapp == nullptr) { LOGW("Attempted to unsubscribe unknown nanoapp from WiFi scan events"); } else if (!nanoappHasScanMonitorRequest(pendingNanoappInstanceId)) { nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); } mPendingScanRequests.pop(); dispatchQueuedScanRequests(true /* postAsyncResult */); } } mPlatformWifi.releaseScanEvent(scanEvent); } void WifiRequestManager::freeWifiScanEventCallback(uint16_t /* eventType */, void *eventData) { auto *scanEvent = static_cast(eventData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleFreeWifiScanEvent(scanEvent); } void WifiRequestManager::freeWifiRangingEventCallback(uint16_t /* eventType */, void *eventData) { auto *event = static_cast(eventData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .mPlatformWifi.releaseRangingEvent(event); } void WifiRequestManager::freeNanDiscoveryEventCallback(uint16_t /* eventType */, void *eventData) { auto *event = static_cast(eventData); EventLoopManagerSingleton::get() ->getWifiRequestManager() .mPlatformWifi.releaseNanDiscoveryEvent(event); } bool WifiRequestManager::nanSubscribe( Nanoapp *nanoapp, const struct chreWifiNanSubscribeConfig *config, const void *cookie) { CHRE_ASSERT(nanoapp); bool success = false; if (!areRequiredSettingsEnabled()) { success = true; postNanAsyncResultEvent( nanoapp->getInstanceId(), CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE, false /*success*/, CHRE_ERROR_FUNCTION_DISABLED, cookie); } else { if (!mPendingNanSubscribeRequests.emplace()) { LOG_OOM(); } else { auto &req = mPendingNanSubscribeRequests.back(); req.nanoappInstanceId = nanoapp->getInstanceId(); req.cookie = cookie; if (!copyNanSubscribeConfigToRequest(req, config)) { LOG_OOM(); } if (mNanIsAvailable) { if (mPendingNanSubscribeRequests.size() == 1) { // First in line; dispatch request immediately. success = mPlatformWifi.nanSubscribe(config); if (!success) { mPendingNanSubscribeRequests.pop_back(); } } else { success = true; } } else { success = true; sendNanConfiguration(true /*enable*/); } } } return success; } bool WifiRequestManager::nanSubscribeCancel(Nanoapp *nanoapp, uint32_t subscriptionId) { bool success = false; for (size_t i = 0; i < mNanoappSubscriptions.size(); ++i) { if (mNanoappSubscriptions[i].subscriptionId == subscriptionId && mNanoappSubscriptions[i].nanoappInstanceId == nanoapp->getInstanceId()) { success = mPlatformWifi.nanSubscribeCancel(subscriptionId); break; } } if (!success) { LOGE("Failed to cancel subscription %" PRIu32 " for napp %" PRIu16, subscriptionId, nanoapp->getInstanceId()); } return success; } bool WifiRequestManager::copyNanSubscribeConfigToRequest( PendingNanSubscribeRequest &req, const struct chreWifiNanSubscribeConfig *config) { bool success = false; req.type = config->subscribeType; if (req.service.copy_array(config->service, std::strlen(config->service) + 1) && req.serviceSpecificInfo.copy_array(config->serviceSpecificInfo, config->serviceSpecificInfoSize) && req.matchFilter.copy_array(config->matchFilter, config->matchFilterLength)) { success = true; } else { LOG_OOM(); } return success; } void WifiRequestManager::buildNanSubscribeConfigFromRequest( const PendingNanSubscribeRequest &req, struct chreWifiNanSubscribeConfig *config) { config->subscribeType = req.type; config->service = req.service.data(); config->serviceSpecificInfo = req.serviceSpecificInfo.data(); config->serviceSpecificInfoSize = static_cast(req.serviceSpecificInfo.size()); config->matchFilter = req.matchFilter.data(); config->matchFilterLength = static_cast(req.matchFilter.size()); } inline bool WifiRequestManager::areRequiredSettingsEnabled() { SettingManager &settingManager = EventLoopManagerSingleton::get()->getSettingManager(); return settingManager.getSettingEnabled(Setting::LOCATION) && settingManager.getSettingEnabled(Setting::WIFI_AVAILABLE); } void WifiRequestManager::cancelNanSubscriptionsAndInformNanoapps() { for (size_t i = 0; i < mNanoappSubscriptions.size(); ++i) { chreWifiNanSessionTerminatedEvent *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); } else { event->id = mNanoappSubscriptions[i].subscriptionId; event->reason = CHRE_ERROR_FUNCTION_DISABLED; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_NAN_SESSION_TERMINATED, event, freeEventDataCallback, mNanoappSubscriptions[i].nanoappInstanceId); } } mNanoappSubscriptions.clear(); } void WifiRequestManager::cancelNanPendingRequestsAndInformNanoapps() { for (size_t i = 0; i < mPendingNanSubscribeRequests.size(); ++i) { auto &req = mPendingNanSubscribeRequests[i]; chreAsyncResult *event = memoryAlloc(); if (event == nullptr) { LOG_OOM(); break; } else { event->requestType = CHRE_WIFI_REQUEST_TYPE_NAN_SUBSCRIBE; event->success = false; event->errorCode = CHRE_ERROR_FUNCTION_DISABLED; event->cookie = req.cookie; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, req.nanoappInstanceId); } } mPendingNanSubscribeRequests.clear(); } void WifiRequestManager::handleNanAvailabilitySync(bool available) { PendingNanConfigType nanState = available ? PendingNanConfigType::ENABLE : PendingNanConfigType::DISABLE; mNanIsAvailable = available; if (nanState == mNanConfigRequestToHostPendingType) { mNanConfigRequestToHostPending = false; mNanConfigRequestToHostPendingType = PendingNanConfigType::UNKNOWN; } if (available) { dispatchQueuedNanSubscribeRequestWithRetry(); } else { cancelNanPendingRequestsAndInformNanoapps(); cancelNanSubscriptionsAndInformNanoapps(); } } void WifiRequestManager::updateNanAvailability(bool available) { auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { bool cbAvail = NestedDataPtr(data); EventLoopManagerSingleton::get() ->getWifiRequestManager() .handleNanAvailabilitySync(cbAvail); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiNanAvailabilityEvent, NestedDataPtr(available), callback); } void WifiRequestManager::sendNanConfiguration(bool enable) { PendingNanConfigType requiredState = enable ? PendingNanConfigType::ENABLE : PendingNanConfigType::DISABLE; if (!mNanConfigRequestToHostPending || (mNanConfigRequestToHostPendingType != requiredState)) { mNanConfigRequestToHostPending = true; mNanConfigRequestToHostPendingType = requiredState; EventLoopManagerSingleton::get() ->getHostCommsManager() .sendNanConfiguration(enable); } } void WifiRequestManager::onSettingChanged(Setting setting, bool enabled) { if ((setting == Setting::WIFI_AVAILABLE) && !enabled) { cancelNanPendingRequestsAndInformNanoapps(); cancelNanSubscriptionsAndInformNanoapps(); } } } // namespace chre #endif // CHRE_WIFI_SUPPORT_ENABLED