普通文本  |  442行  |  16.45 KB

/*
 * 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