471 lines
17 KiB
C++
471 lines
17 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "chre/core/ble_request_manager.h"
|
|
|
|
#include "chre/core/event_loop_manager.h"
|
|
#include "chre/platform/fatal_error.h"
|
|
#include "chre/platform/log.h"
|
|
#include "chre/util/nested_data_ptr.h"
|
|
|
|
namespace chre {
|
|
|
|
void BleRequestManager::init() {
|
|
mPlatformBle.init();
|
|
}
|
|
|
|
uint32_t BleRequestManager::getCapabilities() {
|
|
return mPlatformBle.getCapabilities();
|
|
}
|
|
|
|
uint32_t BleRequestManager::getFilterCapabilities() {
|
|
return mPlatformBle.getFilterCapabilities();
|
|
}
|
|
|
|
void BleRequestManager::handleExistingRequest(uint16_t instanceId,
|
|
bool *hasExistingRequest,
|
|
size_t *requestIndex) {
|
|
const BleRequest *foundRequest =
|
|
mRequests.findRequest(instanceId, requestIndex);
|
|
*hasExistingRequest = (foundRequest != nullptr);
|
|
if (foundRequest != nullptr &&
|
|
foundRequest->getRequestStatus() != RequestStatus::APPLIED) {
|
|
handleAsyncResult(instanceId, foundRequest->isEnabled(),
|
|
false /* success */, CHRE_ERROR_OBSOLETE_REQUEST,
|
|
true /* forceUnregister */);
|
|
}
|
|
}
|
|
|
|
bool BleRequestManager::compliesWithBleSetting(uint16_t instanceId,
|
|
bool enabled,
|
|
bool hasExistingRequest,
|
|
size_t requestIndex) {
|
|
bool success = true;
|
|
if (enabled && !bleSettingEnabled()) {
|
|
success = false;
|
|
handleAsyncResult(instanceId, enabled, false /* success */,
|
|
CHRE_ERROR_FUNCTION_DISABLED);
|
|
if (hasExistingRequest) {
|
|
bool requestChanged = false;
|
|
mRequests.removeRequest(requestIndex, &requestChanged);
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool BleRequestManager::updateRequests(BleRequest &&request,
|
|
bool hasExistingRequest,
|
|
bool *requestChanged,
|
|
size_t *requestIndex) {
|
|
bool success = true;
|
|
if (hasExistingRequest) {
|
|
mRequests.updateRequest(*requestIndex, std::move(request), requestChanged);
|
|
} else if (request.isEnabled()) {
|
|
success =
|
|
mRequests.addRequest(std::move(request), requestIndex, requestChanged);
|
|
} else {
|
|
// Already disabled requests shouldn't result in work for the PAL.
|
|
*requestChanged = false;
|
|
*requestIndex = mRequests.getRequests().size();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool BleRequestManager::startScanAsync(Nanoapp *nanoapp, chreBleScanMode mode,
|
|
uint32_t reportDelayMs,
|
|
const struct chreBleScanFilter *filter) {
|
|
CHRE_ASSERT(nanoapp);
|
|
BleRequest request(nanoapp->getInstanceId(), true, mode, reportDelayMs,
|
|
filter);
|
|
return configure(std::move(request));
|
|
}
|
|
|
|
bool BleRequestManager::stopScanAsync(Nanoapp *nanoapp) {
|
|
CHRE_ASSERT(nanoapp);
|
|
BleRequest request(nanoapp->getInstanceId(), false /* enable */);
|
|
return configure(std::move(request));
|
|
}
|
|
|
|
uint32_t BleRequestManager::disableActiveScan(const Nanoapp *nanoapp) {
|
|
CHRE_ASSERT(nanoapp);
|
|
|
|
size_t requestIndex;
|
|
const BleRequest *foundRequest =
|
|
mRequests.findRequest(nanoapp->getInstanceId(), &requestIndex);
|
|
|
|
if (foundRequest == nullptr || !foundRequest->isEnabled()) {
|
|
// No active request found.
|
|
return 0;
|
|
}
|
|
|
|
BleRequest request(nanoapp->getInstanceId(), false /* enable */);
|
|
configure(std::move(request));
|
|
return 1;
|
|
}
|
|
|
|
void BleRequestManager::addBleRequestLog(uint32_t instanceId, bool enabled,
|
|
size_t requestIndex,
|
|
bool compliesWithBleSetting) {
|
|
BleRequestLog log(SystemTime::getMonotonicTime(), instanceId, enabled,
|
|
compliesWithBleSetting);
|
|
if (enabled) {
|
|
if (instanceId == CHRE_INSTANCE_ID) {
|
|
log.populateRequestData(mRequests.getCurrentMaximalRequest());
|
|
} else if (compliesWithBleSetting) {
|
|
log.populateRequestData(mRequests.getRequests()[requestIndex]);
|
|
}
|
|
}
|
|
mBleRequestLogs.kick_push(log);
|
|
}
|
|
|
|
bool BleRequestManager::configure(BleRequest &&request) {
|
|
bool success = validateParams(request);
|
|
if (success) {
|
|
bool requestChanged = false;
|
|
size_t requestIndex = 0;
|
|
bool hasExistingRequest = false;
|
|
uint16_t instanceId = request.getInstanceId();
|
|
uint8_t enabled = request.isEnabled();
|
|
handleExistingRequest(instanceId, &hasExistingRequest, &requestIndex);
|
|
bool compliant = compliesWithBleSetting(instanceId, enabled,
|
|
hasExistingRequest, requestIndex);
|
|
if (compliant) {
|
|
success = updateRequests(std::move(request), hasExistingRequest,
|
|
&requestChanged, &requestIndex);
|
|
if (success) {
|
|
if (!asyncResponsePending()) {
|
|
if (!requestChanged) {
|
|
handleAsyncResult(instanceId, enabled, true /* success */,
|
|
CHRE_ERROR_NONE);
|
|
if (requestIndex < mRequests.getRequests().size()) {
|
|
mRequests.getMutableRequests()[requestIndex].setRequestStatus(
|
|
RequestStatus::APPLIED);
|
|
}
|
|
} else {
|
|
success = controlPlatform();
|
|
if (!success) {
|
|
handleNanoappEventRegistration(instanceId, enabled,
|
|
false /* success */,
|
|
true /* forceUnregister */);
|
|
mRequests.removeRequest(requestIndex, &requestChanged);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (success) {
|
|
addBleRequestLog(instanceId, enabled, requestIndex, compliant);
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool BleRequestManager::controlPlatform() {
|
|
bool success = false;
|
|
const BleRequest &maxRequest = mRequests.getCurrentMaximalRequest();
|
|
bool enable = bleSettingEnabled() && maxRequest.isEnabled();
|
|
if (enable) {
|
|
chreBleScanFilter filter = maxRequest.getScanFilter();
|
|
success = mPlatformBle.startScanAsync(
|
|
maxRequest.getMode(), maxRequest.getReportDelayMs(), &filter);
|
|
mPendingPlatformRequest =
|
|
BleRequest(0, enable, maxRequest.getMode(),
|
|
maxRequest.getReportDelayMs(), &filter);
|
|
} else {
|
|
success = mPlatformBle.stopScanAsync();
|
|
mPendingPlatformRequest = BleRequest(0, enable);
|
|
}
|
|
if (success) {
|
|
for (BleRequest &req : mRequests.getMutableRequests()) {
|
|
if (req.getRequestStatus() == RequestStatus::PENDING_REQ) {
|
|
req.setRequestStatus(RequestStatus::PENDING_RESP);
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void BleRequestManager::handleFreeAdvertisingEvent(
|
|
struct chreBleAdvertisementEvent *event) {
|
|
mPlatformBle.releaseAdvertisingEvent(event);
|
|
}
|
|
|
|
void BleRequestManager::freeAdvertisingEventCallback(uint16_t /* eventType */,
|
|
void *eventData) {
|
|
auto event = static_cast<chreBleAdvertisementEvent *>(eventData);
|
|
EventLoopManagerSingleton::get()
|
|
->getBleRequestManager()
|
|
.handleFreeAdvertisingEvent(event);
|
|
}
|
|
|
|
void BleRequestManager::handleAdvertisementEvent(
|
|
struct chreBleAdvertisementEvent *event) {
|
|
EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
|
|
CHRE_EVENT_BLE_ADVERTISEMENT, event, freeAdvertisingEventCallback);
|
|
}
|
|
|
|
void BleRequestManager::handlePlatformChange(bool enable, uint8_t errorCode) {
|
|
auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
|
|
bool enable = NestedDataPtr<bool>(data);
|
|
uint8_t errorCode = NestedDataPtr<uint8_t>(extraData);
|
|
EventLoopManagerSingleton::get()
|
|
->getBleRequestManager()
|
|
.handlePlatformChangeSync(enable, errorCode);
|
|
};
|
|
|
|
EventLoopManagerSingleton::get()->deferCallback(
|
|
SystemCallbackType::BleScanResponse, NestedDataPtr<bool>(enable),
|
|
callback, NestedDataPtr<uint8_t>(errorCode));
|
|
}
|
|
|
|
void BleRequestManager::handlePlatformChangeSync(bool enable,
|
|
uint8_t errorCode) {
|
|
bool success = (errorCode == CHRE_ERROR_NONE);
|
|
if (mPendingPlatformRequest.isEnabled() != enable) {
|
|
errorCode = CHRE_ERROR;
|
|
success = false;
|
|
CHRE_ASSERT_LOG(false, "BLE PAL did not transition to expected state");
|
|
}
|
|
if (mInternalRequestPending) {
|
|
mInternalRequestPending = false;
|
|
if (!success) {
|
|
FATAL_ERROR("Failed to resync BLE platform");
|
|
}
|
|
} else {
|
|
for (BleRequest &req : mRequests.getMutableRequests()) {
|
|
if (req.getRequestStatus() == RequestStatus::PENDING_RESP) {
|
|
handleAsyncResult(req.getInstanceId(), req.isEnabled(), success,
|
|
errorCode);
|
|
if (success) {
|
|
req.setRequestStatus(RequestStatus::APPLIED);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!success) {
|
|
mRequests.removeRequests(RequestStatus::PENDING_RESP);
|
|
}
|
|
}
|
|
if (success) {
|
|
// No need to waste memory for requests that have no effect on the overall
|
|
// maximal request.
|
|
mRequests.removeDisabledRequests();
|
|
mActivePlatformRequest = std::move(mPendingPlatformRequest);
|
|
}
|
|
|
|
dispatchPendingRequests();
|
|
|
|
// Only clear mResyncPending if the request succeeded or after all pending
|
|
// requests are dispatched and a resync request can be issued with only the
|
|
// requests that were previously applied.
|
|
if (mResyncPending) {
|
|
if (success) {
|
|
mResyncPending = false;
|
|
} else if (!success && !asyncResponsePending()) {
|
|
mResyncPending = false;
|
|
updatePlatformRequest(true /* forceUpdate */);
|
|
}
|
|
}
|
|
// Finish dispatching pending requests before processing the setting change
|
|
// request to ensure nanoapps receive CHRE_ERROR_FUNCTION_DISABLED responses.
|
|
// If both a resync and a setting change are pending, prioritize the resync.
|
|
// If the resync successfully completes, the PAL will be in the correct state
|
|
// and updatePlatformRequest will not begin a new request.
|
|
if (mSettingChangePending && !asyncResponsePending()) {
|
|
updatePlatformRequest();
|
|
mSettingChangePending = false;
|
|
}
|
|
}
|
|
|
|
void BleRequestManager::dispatchPendingRequests() {
|
|
if (mRequests.hasRequests(RequestStatus::PENDING_REQ)) {
|
|
uint8_t errorCode = CHRE_ERROR_NONE;
|
|
if (!bleSettingEnabled() && mRequests.isMaximalRequestEnabled()) {
|
|
errorCode = CHRE_ERROR_FUNCTION_DISABLED;
|
|
} else if (!controlPlatform()) {
|
|
errorCode = CHRE_ERROR;
|
|
}
|
|
if (errorCode != CHRE_ERROR_NONE) {
|
|
for (const BleRequest &req : mRequests.getRequests()) {
|
|
if (req.getRequestStatus() == RequestStatus::PENDING_REQ) {
|
|
handleAsyncResult(req.getInstanceId(), req.isEnabled(),
|
|
false /* success */, errorCode);
|
|
}
|
|
}
|
|
mRequests.removeRequests(RequestStatus::PENDING_REQ);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BleRequestManager::handleAsyncResult(uint16_t instanceId, bool enabled,
|
|
bool success, uint8_t errorCode,
|
|
bool forceUnregister) {
|
|
uint8_t requestType = enabled ? CHRE_BLE_REQUEST_TYPE_START_SCAN
|
|
: CHRE_BLE_REQUEST_TYPE_STOP_SCAN;
|
|
postAsyncResultEventFatal(instanceId, requestType, success, errorCode);
|
|
handleNanoappEventRegistration(instanceId, enabled, success, forceUnregister);
|
|
}
|
|
|
|
void BleRequestManager::handleNanoappEventRegistration(uint16_t instanceId,
|
|
bool enabled,
|
|
bool success,
|
|
bool forceUnregister) {
|
|
Nanoapp *nanoapp =
|
|
EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId(
|
|
instanceId);
|
|
if (nanoapp != nullptr) {
|
|
if (success && enabled) {
|
|
nanoapp->registerForBroadcastEvent(CHRE_EVENT_BLE_ADVERTISEMENT);
|
|
} else if (!enabled || forceUnregister) {
|
|
nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_BLE_ADVERTISEMENT);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BleRequestManager::handleRequestStateResyncCallback() {
|
|
auto callback = [](uint16_t /* eventType */, void * /* eventData */,
|
|
void * /* extraData */) {
|
|
EventLoopManagerSingleton::get()
|
|
->getBleRequestManager()
|
|
.handleRequestStateResyncCallbackSync();
|
|
};
|
|
EventLoopManagerSingleton::get()->deferCallback(
|
|
SystemCallbackType::BleRequestResyncEvent, nullptr /* data */, callback);
|
|
}
|
|
|
|
void BleRequestManager::handleRequestStateResyncCallbackSync() {
|
|
if (asyncResponsePending()) {
|
|
mResyncPending = true;
|
|
} else {
|
|
updatePlatformRequest(true /* forceUpdate */);
|
|
}
|
|
}
|
|
|
|
void BleRequestManager::onSettingChanged(Setting setting, bool /* state */) {
|
|
if (setting == Setting::BLE_AVAILABLE) {
|
|
if (asyncResponsePending()) {
|
|
mSettingChangePending = true;
|
|
} else {
|
|
updatePlatformRequest();
|
|
}
|
|
}
|
|
}
|
|
|
|
void BleRequestManager::updatePlatformRequest(bool forceUpdate) {
|
|
bool desiredPlatformState =
|
|
bleSettingEnabled() && mRequests.isMaximalRequestEnabled();
|
|
bool updatePlatform = (forceUpdate || (desiredPlatformState !=
|
|
mActivePlatformRequest.isEnabled()));
|
|
|
|
if (updatePlatform) {
|
|
if (controlPlatform()) {
|
|
mInternalRequestPending = true;
|
|
addBleRequestLog(CHRE_INSTANCE_ID, desiredPlatformState,
|
|
mRequests.getRequests().size(),
|
|
true /* compliesWithBleSetting */);
|
|
} else {
|
|
FATAL_ERROR("Failed to send update BLE platform request");
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BleRequestManager::asyncResponsePending() const {
|
|
return (mInternalRequestPending ||
|
|
mRequests.hasRequests(RequestStatus::PENDING_RESP));
|
|
}
|
|
|
|
bool BleRequestManager::validateParams(const BleRequest &request) {
|
|
bool valid = true;
|
|
if (request.isEnabled()) {
|
|
for (const chreBleGenericFilter &filter : request.getGenericFilters()) {
|
|
if (!isValidAdType(filter.type)) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
if (filter.len == 0 || filter.len > CHRE_BLE_DATA_LEN_MAX) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
void BleRequestManager::postAsyncResultEventFatal(uint16_t instanceId,
|
|
uint8_t requestType,
|
|
bool success,
|
|
uint8_t errorCode) {
|
|
chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
|
|
if (event == nullptr) {
|
|
FATAL_ERROR("Failed to alloc BLE async result");
|
|
} else {
|
|
event->requestType = requestType;
|
|
event->success = success;
|
|
event->errorCode = errorCode;
|
|
event->reserved = 0;
|
|
|
|
EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
|
|
CHRE_EVENT_BLE_ASYNC_RESULT, event, freeEventDataCallback, instanceId);
|
|
}
|
|
}
|
|
|
|
bool BleRequestManager::isValidAdType(uint8_t adType) {
|
|
return adType == CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16;
|
|
}
|
|
|
|
bool BleRequestManager::bleSettingEnabled() {
|
|
return EventLoopManagerSingleton::get()
|
|
->getSettingManager()
|
|
.getSettingEnabled(Setting::BLE_AVAILABLE);
|
|
}
|
|
|
|
void BleRequestManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
|
|
debugDump.print("\nBLE:\n");
|
|
debugDump.print(" Active Platform Request:\n");
|
|
mActivePlatformRequest.logStateToBuffer(debugDump,
|
|
true /* isPlatformRequest */);
|
|
if (asyncResponsePending()) {
|
|
debugDump.print(" Pending Platform Request:\n");
|
|
mPendingPlatformRequest.logStateToBuffer(debugDump,
|
|
true /* isPlatformRequest */);
|
|
}
|
|
debugDump.print(" Request Multiplexer:\n");
|
|
for (const BleRequest &req : mRequests.getRequests()) {
|
|
req.logStateToBuffer(debugDump);
|
|
}
|
|
debugDump.print(" Last %zu valid BLE requests:\n", mBleRequestLogs.size());
|
|
static_assert(kNumBleRequestLogs <= INT8_MAX,
|
|
"kNumBleRequestLogs must be less than INT8_MAX.");
|
|
for (int8_t i = static_cast<int8_t>(mBleRequestLogs.size()) - 1; i >= 0;
|
|
i--) {
|
|
const auto &log = mBleRequestLogs[static_cast<size_t>(i)];
|
|
debugDump.print(" ts=%" PRIu64 " instanceId=%" PRIu32 " %s",
|
|
log.timestamp.toRawNanoseconds(), log.instanceId,
|
|
log.enable ? "enable" : "disable\n");
|
|
if (log.enable && log.compliesWithBleSetting) {
|
|
debugDump.print(" mode=%" PRIu8 " reportDelayMs=%" PRIu32
|
|
" rssiThreshold=%" PRId8 " scanCount=%" PRIu8 "\n",
|
|
log.mode, log.reportDelayMs, log.rssiThreshold,
|
|
log.scanFilterCount);
|
|
} else if (log.enable) {
|
|
debugDump.print(" request did not comply with BLE setting\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace chre
|