/* * 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. */ #include #include #include #include "chre/core/event_loop_manager.h" #include "chre/core/wifi_request_manager.h" #include "chre/platform/fatal_error.h" #include "chre/platform/log.h" #include "chre/platform/system_time.h" #include "chre/util/system/debug_dump.h" #include "chre_api/chre/version.h" namespace chre { 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(); } bool WifiRequestManager::configureScanMonitor(Nanoapp *nanoapp, bool enable, const void *cookie) { CHRE_ASSERT(nanoapp); bool success = false; uint32_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); if (!success) { mPendingScanMonitorRequests.pop_back(); LOGE("Failed to enable the scan monitor for nanoapp instance %" PRIu32, instanceId); } } } else { CHRE_ASSERT_LOG(false, "Invalid scan monitor configuration"); } return success; } bool WifiRequestManager::requestRanging( Nanoapp *nanoapp, const struct chreWifiRangingParams *params, const void *cookie) { CHRE_ASSERT(nanoapp); 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 success = mPlatformWifi.requestRanging(params); if (!success) { LOGE("WiFi RTT request failed"); mPendingRangingRequests.pop_back(); } else { mRangingResponseTimeout = SystemTime::getMonotonicTime() + Nanoseconds(CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS); } } else { // Dispatch request later, after prior requests finish // TODO(b/65331248): use a timer to ensure the platform is meeting its // contract CHRE_ASSERT_LOG(SystemTime::getMonotonicTime() <= mRangingResponseTimeout, "WiFi platform didn't give callback in time"); success = req.targetList.copy_array(params->targetList, params->targetListLen); if (!success) { LOG_OOM(); mPendingRangingRequests.pop_back(); } } } return success; } bool WifiRequestManager::requestScan(Nanoapp *nanoapp, const struct chreWifiScanParams *params, const void *cookie) { CHRE_ASSERT(nanoapp); // TODO(b/65331248): replace with a timer to actively check response timeout bool timedOut = (mScanRequestingNanoappInstanceId.has_value() && mLastScanRequestTime + Nanoseconds(CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS) < SystemTime::getMonotonicTime()); if (timedOut) { LOGE("Scan request async response timed out"); mScanRequestingNanoappInstanceId.reset(); } // 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; if (mScanRequestingNanoappInstanceId.has_value()) { LOGE("Active wifi scan request made while a request is in flight"); } else { success = mPlatformWifi.requestScan(params); if (!success) { LOGE("Wifi scan request failed"); } else { mScanRequestingNanoappInstanceId = nanoapp->getInstanceId(); mScanRequestingNanoappCookie = cookie; mLastScanRequestTime = SystemTime::getMonotonicTime(); } } return success; } void WifiRequestManager::handleScanMonitorStateChange(bool enabled, uint8_t errorCode) { struct CallbackState { bool enabled; uint8_t errorCode; }; auto *cbState = memoryAlloc(); if (cbState == nullptr) { LOG_OOM(); } else { cbState->enabled = enabled; cbState->errorCode = errorCode; auto callback = [](uint16_t /* eventType */, void *eventData) { auto *state = static_cast(eventData); EventLoopManagerSingleton::get()->getWifiRequestManager() .handleScanMonitorStateChangeSync(state->enabled, state->errorCode); memoryFree(state); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiScanMonitorStateChange, cbState, callback); } } void WifiRequestManager::handleScanResponse(bool pending, uint8_t errorCode) { struct CallbackState { bool pending; uint8_t errorCode; }; auto *cbState = memoryAlloc(); if (cbState == nullptr) { LOG_OOM(); } else { cbState->pending = pending; cbState->errorCode = errorCode; auto callback = [](uint16_t /* eventType */, void *eventData) { auto *state = static_cast(eventData); EventLoopManagerSingleton::get()->getWifiRequestManager() .handleScanResponseSync(state->pending, state->errorCode); memoryFree(state); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiRequestScanResponse, cbState, callback); } } void WifiRequestManager::handleRangingEvent( uint8_t errorCode, struct chreWifiRangingEvent *event) { // Use two different callbacks to avoid needing a temporary allocation to // carry the error code into the event loop context if (errorCode != CHRE_ERROR_NONE) { // Enables passing the error code through the event data pointer to avoid // allocating memory union NestedErrorCode { void *eventData; uint8_t errorCode; }; auto errorCb = [](uint16_t /* eventType */, void *eventData) { NestedErrorCode cbErrorCode; cbErrorCode.eventData = eventData; EventLoopManagerSingleton::get()->getWifiRequestManager() .handleRangingEventSync(cbErrorCode.errorCode, nullptr); }; NestedErrorCode error = {}; error.errorCode = errorCode; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiHandleFailedRanging, error.eventData, errorCb); } else { auto successCb = [](uint16_t /* eventType */, void *eventData) { auto *rttEvent = static_cast(eventData); EventLoopManagerSingleton::get()->getWifiRequestManager() .handleRangingEventSync(CHRE_ERROR_NONE, rttEvent); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiHandleRangingEvent, event, successCb); } } void WifiRequestManager::handleScanEvent(chreWifiScanEvent *event) { auto callback = [](uint16_t eventType, void *eventData) { chreWifiScanEvent *scanEvent = static_cast(eventData); EventLoopManagerSingleton::get()->getWifiRequestManager() .postScanEventFatal(scanEvent); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::WifiHandleScanEvent, event, callback); } void WifiRequestManager::logStateToBuffer(char *buffer, size_t *bufferPos, size_t bufferSize) const { debugDumpPrint(buffer, bufferPos, bufferSize, "\nWifi: scan monitor %s\n", scanMonitorIsEnabled() ? "enabled" : "disabled"); debugDumpPrint(buffer, bufferPos, bufferSize, " Wifi scan monitor enabled nanoapps:\n"); for (const auto& instanceId : mScanMonitorNanoapps) { debugDumpPrint(buffer, bufferPos, bufferSize, " nanoappId=%" PRIu32 "\n", instanceId); } if (mScanRequestingNanoappInstanceId.has_value()) { debugDumpPrint(buffer, bufferPos, bufferSize, " Wifi request pending nanoappId=%" PRIu32 "\n", mScanRequestingNanoappInstanceId.value()); } debugDumpPrint(buffer, bufferPos, bufferSize, " Wifi transition queue:\n"); for (const auto& transition : mPendingScanMonitorRequests) { debugDumpPrint(buffer, bufferPos, bufferSize, " enable=%s nanoappId=%" PRIu32 "\n", transition.enable ? "true" : "false", transition.nanoappInstanceId); } } bool WifiRequestManager::scanMonitorIsEnabled() const { return !mScanMonitorNanoapps.empty(); } bool WifiRequestManager::nanoappHasScanMonitorRequest( uint32_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::updateNanoappScanMonitoringList(bool enable, uint32_t instanceId) { bool success = true; Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop() .findNanoappByInstanceId(instanceId); if (nanoapp == nullptr) { LOGW("Failed to update scan monitoring list for non-existent nanoapp"); } else { size_t nanoappIndex; bool hasExistingRequest = nanoappHasScanMonitorRequest(instanceId, &nanoappIndex); 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( uint32_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 (!success || 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; // Post the event. eventPosted = EventLoopManagerSingleton::get()->getEventLoop() .postEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, kSystemInstanceId, nanoappInstanceId); if (!eventPosted) { memoryFree(event); } } } return eventPosted; } void WifiRequestManager::postScanMonitorAsyncResultEventFatal( uint32_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( uint32_t nanoappInstanceId, bool success, uint8_t errorCode, const void *cookie) { 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; // Post the event. eventPosted = EventLoopManagerSingleton::get()->getEventLoop() .postEvent(CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, kSystemInstanceId, nanoappInstanceId); } return eventPosted; } void WifiRequestManager::postScanRequestAsyncResultEventFatal( uint32_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() .postEvent(CHRE_EVENT_WIFI_SCAN_RESULT, event, freeWifiScanEventCallback); } void WifiRequestManager::handleScanMonitorStateChangeSync(bool enabled, uint8_t errorCode) { // Success is defined as having no errors ... in life ༼ つ ◕_◕ ༽つ bool success = (errorCode == CHRE_ERROR_NONE); // TODO(b/62904616): re-enable this assertion //CHRE_ASSERT_LOG(!mScanMonitorStateTransitions.empty(), // "handleScanMonitorStateChangeSync called with no transitions"); if (mPendingScanMonitorRequests.empty()) { LOGE("WiFi PAL error: handleScanMonitorStateChangeSync called with no " "transitions (enabled %d errorCode %" PRIu8 ")", enabled, errorCode); } // Always check the front of the queue. if (!mPendingScanMonitorRequests.empty()) { const auto& stateTransition = mPendingScanMonitorRequests.front(); success &= (stateTransition.enable == enabled); postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId, success, stateTransition.enable, errorCode, stateTransition.cookie); mPendingScanMonitorRequests.pop(); } 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)) { if (mPlatformWifi.configureScanMonitor(stateTransition.enable)) { break; } else { postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId, false /* success */, stateTransition.enable, CHRE_ERROR, stateTransition.cookie); } } else { CHRE_ASSERT_LOG(false, "Invalid scan monitor state"); break; } mPendingScanMonitorRequests.pop(); } } void WifiRequestManager::handleScanResponseSync(bool pending, uint8_t errorCode) { // TODO(b/65206783): re-enable this assertion //CHRE_ASSERT_LOG(mScanRequestingNanoappInstanceId.has_value(), // "handleScanResponseSync called with no outstanding request"); if (!mScanRequestingNanoappInstanceId.has_value()) { LOGE("handleScanResponseSync called with no outstanding request"); } // TODO: raise this to CHRE_ASSERT_LOG if (!pending && errorCode == CHRE_ERROR_NONE) { LOGE("Invalid wifi scan response"); errorCode = CHRE_ERROR; } if (mScanRequestingNanoappInstanceId.has_value()) { bool success = (pending && errorCode == CHRE_ERROR_NONE); if (!success) { LOGW("Wifi scan request failed: pending %d, errorCode %" PRIu8, pending, errorCode); } postScanRequestAsyncResultEventFatal(*mScanRequestingNanoappInstanceId, success, errorCode, mScanRequestingNanoappCookie); // Set a flag to indicate that results may be pending. mScanRequestResultsArePending = pending; if (pending) { Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop() .findNanoappByInstanceId(*mScanRequestingNanoappInstanceId); 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, clear the nanoapp instance ID. // Otherwise, wait for the results to be delivered and then clear the // instance ID. mScanRequestingNanoappInstanceId.reset(); } } } 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; eventPosted = EventLoopManagerSingleton::get()->getEventLoop().postEvent( CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeEventDataCallback, kSystemInstanceId, req.nanoappInstanceId); if (!eventPosted) { memoryFree(event); } } } return eventPosted; } bool WifiRequestManager::dispatchQueuedRangingRequest() { const PendingRangingRequest& req = mPendingRangingRequests.front(); struct chreWifiRangingParams params = {}; params.targetListLen = static_cast(req.targetList.size()); params.targetList = req.targetList.data(); bool success = mPlatformWifi.requestRanging(¶ms); if (!success) { LOGE("Failed to issue queued ranging result"); postRangingAsyncResult(CHRE_ERROR); mPendingRangingRequests.pop(); } else { mRangingResponseTimeout = SystemTime::getMonotonicTime() + Nanoseconds(CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS); } return success; } void WifiRequestManager::handleRangingEventSync( uint8_t errorCode, struct chreWifiRangingEvent *event) { if (postRangingAsyncResult(errorCode)) { if (errorCode != CHRE_ERROR_NONE) { LOGW("RTT ranging failed with error %d", errorCode); } else { EventLoopManagerSingleton::get()->getEventLoop().postEvent( CHRE_EVENT_WIFI_RANGING_RESULT, event, freeWifiRangingEventCallback, kSystemInstanceId, 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) { 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) { mScanEventResultCountAccumulator = 0; mScanRequestResultsArePending = false; } if (!mScanRequestResultsArePending && mScanRequestingNanoappInstanceId.has_value()) { Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop() .findNanoappByInstanceId(*mScanRequestingNanoappInstanceId); if (nanoapp == nullptr) { LOGW("Attempted to unsubscribe unknown nanoapp from WiFi scan events"); } else if (!nanoappHasScanMonitorRequest( *mScanRequestingNanoappInstanceId)) { nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); } mScanRequestingNanoappInstanceId.reset(); } } mPlatformWifi.releaseScanEvent(scanEvent); } void WifiRequestManager::freeWifiScanEventCallback(uint16_t eventType, void *eventData) { chreWifiScanEvent *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); } } // namespace chre