| /* |
| * 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 <cinttypes> |
| |
| #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" |
| |
| 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("Failed to allocate scan monitoring nanoapps list at startup"); |
| } |
| } |
| |
| 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(); |
| if (!mScanMonitorStateTransitions.empty()) { |
| success = addScanMonitorRequestToQueue(nanoapp, enable, cookie); |
| } else if (scanMonitorIsInRequestedState(enable, instanceId)) { |
| // 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, instanceId)) { |
| success = addScanMonitorRequestToQueue(nanoapp, enable, cookie); |
| if (success) { |
| success = mPlatformWifi.configureScanMonitor(enable); |
| if (!success) { |
| // TODO: Add a pop_back method. |
| mScanMonitorStateTransitions.remove( |
| mScanMonitorStateTransitions.size() - 1); |
| 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::requestScan(Nanoapp *nanoapp, |
| const chreWifiScanParams *params, |
| const void *cookie) { |
| CHRE_ASSERT(nanoapp); |
| |
| bool success = false; |
| if (!mScanRequestingNanoappInstanceId.has_value()) { |
| success = mPlatformWifi.requestScan(params); |
| if (success) { |
| mScanRequestingNanoappInstanceId = nanoapp->getInstanceId(); |
| mScanRequestingNanoappCookie = cookie; |
| } |
| } else { |
| LOGE("Active wifi scan request made while a request is in flight"); |
| } |
| |
| return success; |
| } |
| |
| void WifiRequestManager::handleScanMonitorStateChange(bool enabled, |
| uint8_t errorCode) { |
| struct CallbackState { |
| bool enabled; |
| uint8_t errorCode; |
| }; |
| |
| auto *cbState = memoryAlloc<CallbackState>(); |
| if (cbState == nullptr) { |
| LOGE("Failed to allocate callback state for scan monitor state change"); |
| } else { |
| cbState->enabled = enabled; |
| cbState->errorCode = errorCode; |
| |
| auto callback = [](uint16_t /*eventType*/, void *eventData) { |
| auto *state = static_cast<CallbackState *>(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<CallbackState>(); |
| if (cbState == nullptr) { |
| LOGE("Failed to allocate callback state for wifi scan response"); |
| } else { |
| cbState->pending = pending; |
| cbState->errorCode = errorCode; |
| |
| auto callback = [](uint16_t /*eventType*/, void *eventData) { |
| auto *state = static_cast<CallbackState *>(eventData); |
| EventLoopManagerSingleton::get()->getWifiRequestManager() |
| .handleScanResponseSync(state->pending, state->errorCode); |
| memoryFree(state); |
| }; |
| |
| EventLoopManagerSingleton::get()->deferCallback( |
| SystemCallbackType::WifiRequestScanResponse, cbState, callback); |
| } |
| } |
| |
| void WifiRequestManager::handleScanEvent(chreWifiScanEvent *event) { |
| auto callback = [](uint16_t eventType, void *eventData) { |
| chreWifiScanEvent *scanEvent = static_cast<chreWifiScanEvent *>(eventData); |
| EventLoopManagerSingleton::get()->getWifiRequestManager() |
| .handleScanEventSync(scanEvent); |
| }; |
| |
| EventLoopManagerSingleton::get()->deferCallback( |
| SystemCallbackType::WifiHandleScanEvent, event, callback); |
| } |
| |
| 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) { |
| ScanMonitorStateTransition scanMonitorStateTransition; |
| scanMonitorStateTransition.nanoappInstanceId = nanoapp->getInstanceId(); |
| scanMonitorStateTransition.cookie = cookie; |
| scanMonitorStateTransition.enable = enable; |
| |
| bool success = mScanMonitorStateTransitions.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()-> |
| findNanoappByInstanceId(instanceId); |
| if (nanoapp == nullptr) { |
| CHRE_ASSERT_LOG(false, "Failed to update scan monitoring list for " |
| "non-existent nanoapp"); |
| } else { |
| size_t nanoappIndex; |
| bool hasExistingRequest = nanoappHasScanMonitorRequest(instanceId, |
| &nanoappIndex); |
| if (enable) { |
| if (!hasExistingRequest) { |
| success = nanoapp->registerForBroadcastEvent( |
| CHRE_EVENT_WIFI_SCAN_RESULT); |
| if (!success) { |
| LOGE("Failed to register nanoapp for wifi scan events"); |
| } else { |
| // 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) { |
| nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); |
| LOGE("Failed to add nanoapp to the list of scan monitoring " |
| "nanoapps"); |
| } |
| } |
| } |
| } else { |
| if (!hasExistingRequest) { |
| success = false; |
| LOGE("Received a scan monitor state change for a non-existent nanoapp"); |
| } else { |
| // 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); |
| } |
| } |
| } |
| |
| 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<chreAsyncResult>(); |
| if (event == nullptr) { |
| LOGE("Failed to allocate wifi scan monitor async result event"); |
| } 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()->postEvent( |
| CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeWifiAsyncResultCallback, |
| kSystemInstanceId, nanoappInstanceId); |
| } |
| } |
| |
| 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<chreAsyncResult>(); |
| if (event == nullptr) { |
| LOGE("Failed to allocate wifi scan request async result event"); |
| } 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()->postEvent( |
| CHRE_EVENT_WIFI_ASYNC_RESULT, event, freeWifiAsyncResultCallback, |
| 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) { |
| bool eventPosted = EventLoopManagerSingleton::get()->postEvent( |
| CHRE_EVENT_WIFI_SCAN_RESULT, event, freeWifiScanEventCallback, |
| kSystemInstanceId, kBroadcastInstanceId); |
| if (!eventPosted) { |
| FATAL_ERROR("Failed to send WiFi scan event"); |
| } |
| } |
| |
| void WifiRequestManager::handleScanMonitorStateChangeSync(bool enabled, |
| uint8_t errorCode) { |
| // Success is defined as having no errors ... in life ༼ つ ◕_◕ ༽つ |
| bool success = (errorCode == CHRE_ERROR_NONE); |
| |
| // Always check the front of the queue. |
| CHRE_ASSERT_LOG(!mScanMonitorStateTransitions.empty(), |
| "handleScanMonitorStateChangeSync called with no transitions"); |
| if (!mScanMonitorStateTransitions.empty()) { |
| const auto& stateTransition = mScanMonitorStateTransitions.front(); |
| success &= (stateTransition.enable == enabled); |
| postScanMonitorAsyncResultEventFatal(stateTransition.nanoappInstanceId, |
| success, stateTransition.enable, |
| errorCode, stateTransition.cookie); |
| mScanMonitorStateTransitions.pop(); |
| } |
| |
| while (!mScanMonitorStateTransitions.empty()) { |
| const auto& stateTransition = mScanMonitorStateTransitions.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, |
| success, stateTransition.enable, |
| errorCode, 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; |
| } |
| |
| mScanMonitorStateTransitions.pop(); |
| } |
| } |
| |
| void WifiRequestManager::handleScanResponseSync(bool pending, |
| uint8_t errorCode) { |
| CHRE_ASSERT_LOG(mScanRequestingNanoappInstanceId.has_value(), |
| "handleScanResponseSync called with no outstanding request"); |
| if (mScanRequestingNanoappInstanceId.has_value()) { |
| bool success = (pending && errorCode == CHRE_ERROR_NONE); |
| postScanRequestAsyncResultEventFatal(*mScanRequestingNanoappInstanceId, |
| success, errorCode, |
| mScanRequestingNanoappCookie); |
| |
| // Set a flag to indicate that results may be pending. |
| mScanRequestResultsArePending = pending; |
| |
| if (pending) { |
| Nanoapp *nanoapp = EventLoopManagerSingleton::get()-> |
| findNanoappByInstanceId(*mScanRequestingNanoappInstanceId); |
| if (nanoapp == nullptr) { |
| CHRE_ASSERT_LOG(false, "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(); |
| } |
| } |
| } |
| |
| void WifiRequestManager::handleScanEventSync(chreWifiScanEvent *event) { |
| if (mScanRequestResultsArePending) { |
| // Reset the event distribution logic once an entire scan event has been |
| // received. |
| mScanEventResultCountAccumulator += event->resultCount; |
| if (mScanEventResultCountAccumulator >= event->resultTotal) { |
| mScanEventResultCountAccumulator = 0; |
| mScanRequestResultsArePending = false; |
| } |
| } |
| |
| postScanEventFatal(event); |
| } |
| |
| void WifiRequestManager::handleFreeWifiScanEvent(chreWifiScanEvent *scanEvent) { |
| mPlatformWifi.releaseScanEvent(scanEvent); |
| |
| if (!mScanRequestResultsArePending |
| && mScanRequestingNanoappInstanceId.has_value()) { |
| Nanoapp *nanoapp = EventLoopManagerSingleton::get()-> |
| findNanoappByInstanceId(*mScanRequestingNanoappInstanceId); |
| if (nanoapp == nullptr) { |
| CHRE_ASSERT_LOG(false, "Attempted to unsubscribe unknown nanoapp from " |
| "WiFi scan events"); |
| } else if (!nanoappHasScanMonitorRequest(*mScanRequestingNanoappInstanceId)) { |
| nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT); |
| } |
| |
| mScanRequestingNanoappInstanceId.reset(); |
| } |
| } |
| |
| void WifiRequestManager::freeWifiAsyncResultCallback(uint16_t eventType, |
| void *eventData) { |
| memoryFree(eventData); |
| } |
| |
| void WifiRequestManager::freeWifiScanEventCallback(uint16_t eventType, |
| void *eventData) { |
| chreWifiScanEvent *scanEvent = static_cast<chreWifiScanEvent *>(eventData); |
| EventLoopManagerSingleton::get()->getWifiRequestManager() |
| .handleFreeWifiScanEvent(scanEvent); |
| } |
| |
| } // namespace chre |