You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
25 KiB
713 lines
25 KiB
/*
|
|
* Copyright (C) 2018 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/gnss_manager.h"
|
|
|
|
#include "chre/core/event_loop_manager.h"
|
|
#include "chre/core/settings.h"
|
|
#include "chre/platform/assert.h"
|
|
#include "chre/platform/fatal_error.h"
|
|
#include "chre/util/nested_data_ptr.h"
|
|
#include "chre/util/system/debug_dump.h"
|
|
|
|
namespace chre {
|
|
|
|
namespace {
|
|
|
|
bool getCallbackType(uint16_t eventType, SystemCallbackType *callbackType) {
|
|
bool success = true;
|
|
switch (eventType) {
|
|
case CHRE_EVENT_GNSS_LOCATION: {
|
|
*callbackType = SystemCallbackType::GnssLocationReportEvent;
|
|
break;
|
|
}
|
|
case CHRE_EVENT_GNSS_DATA: {
|
|
*callbackType = SystemCallbackType::GnssMeasurementReportEvent;
|
|
break;
|
|
}
|
|
default: {
|
|
LOGE("Unknown event type %" PRIu16, eventType);
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool getReportEventType(SystemCallbackType callbackType, uint16_t *eventType) {
|
|
bool success = true;
|
|
switch (callbackType) {
|
|
case SystemCallbackType::GnssLocationReportEvent: {
|
|
*eventType = CHRE_EVENT_GNSS_LOCATION;
|
|
break;
|
|
}
|
|
case SystemCallbackType::GnssMeasurementReportEvent: {
|
|
*eventType = CHRE_EVENT_GNSS_DATA;
|
|
break;
|
|
}
|
|
default: {
|
|
LOGE("Unknown callback type %" PRIu16,
|
|
static_cast<uint16_t>(callbackType));
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
GnssManager::GnssManager()
|
|
: mLocationSession(CHRE_EVENT_GNSS_LOCATION),
|
|
mMeasurementSession(CHRE_EVENT_GNSS_DATA) {}
|
|
|
|
void GnssManager::init() {
|
|
mPlatformGnss.init();
|
|
}
|
|
|
|
uint32_t GnssManager::getCapabilities() {
|
|
return mPlatformGnss.getCapabilities();
|
|
}
|
|
|
|
void GnssManager::onSettingChanged(Setting setting, SettingState state) {
|
|
mLocationSession.onSettingChanged(setting, state);
|
|
mMeasurementSession.onSettingChanged(setting, state);
|
|
}
|
|
|
|
void GnssManager::handleRequestStateResyncCallback() {
|
|
auto callback = [](uint16_t /* eventType */, void * /* eventData */,
|
|
void * /* extraData */) {
|
|
EventLoopManagerSingleton::get()
|
|
->getGnssManager()
|
|
.handleRequestStateResyncCallbackSync();
|
|
};
|
|
EventLoopManagerSingleton::get()->deferCallback(
|
|
SystemCallbackType::GnssRequestResyncEvent, nullptr /* data */, callback);
|
|
}
|
|
|
|
bool GnssManager::configurePassiveLocationListener(Nanoapp *nanoapp,
|
|
bool enable) {
|
|
bool success = false;
|
|
uint32_t instanceId = nanoapp->getInstanceId();
|
|
|
|
size_t index;
|
|
if (nanoappHasPassiveLocationListener(instanceId, &index) != enable) {
|
|
uint32_t capabilities = getCapabilities();
|
|
bool locationSupported =
|
|
(capabilities & CHRE_GNSS_CAPABILITIES_LOCATION) != 0;
|
|
bool passiveLocationListenerSupported =
|
|
(capabilities &
|
|
CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER) != 0;
|
|
|
|
if (!locationSupported) {
|
|
LOGE("Platform does not have the location capability");
|
|
} else if (enable && !mPassiveLocationListenerNanoapps.prepareForPush()) {
|
|
LOG_OOM();
|
|
} else {
|
|
bool platformEnable = enable && mPassiveLocationListenerNanoapps.empty();
|
|
bool platformDisable =
|
|
!enable && (mPassiveLocationListenerNanoapps.size() == 1);
|
|
|
|
if (!passiveLocationListenerSupported) {
|
|
// Silently succeed per API, since listener capability will occur within
|
|
// CHRE (nanoapp requests).
|
|
success = true;
|
|
} else if (platformEnable || platformDisable) {
|
|
success = platformConfigurePassiveLocationListener(enable);
|
|
} else {
|
|
// Platform was already in the configured state.
|
|
success = true;
|
|
}
|
|
|
|
if (success) {
|
|
if (enable) {
|
|
mPassiveLocationListenerNanoapps.push_back(instanceId);
|
|
nanoapp->registerForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION);
|
|
} else {
|
|
mPassiveLocationListenerNanoapps.erase(index);
|
|
if (!mLocationSession.nanoappHasRequest(instanceId)) {
|
|
nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else { // else nanoapp request is already at the desired state.
|
|
success = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GnssManager::nanoappHasPassiveLocationListener(uint32_t nanoappInstanceId,
|
|
size_t *index) {
|
|
size_t foundIndex = mPassiveLocationListenerNanoapps.find(nanoappInstanceId);
|
|
bool found = (foundIndex != mPassiveLocationListenerNanoapps.size());
|
|
if (found && index != nullptr) {
|
|
*index = foundIndex;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool GnssManager::platformConfigurePassiveLocationListener(bool enable) {
|
|
bool success = mPlatformGnss.configurePassiveLocationListener(enable);
|
|
if (!success) {
|
|
LOGE("Platform failed to %s passive location listener",
|
|
enable ? "enable" : "disable");
|
|
} else {
|
|
mPlatformPassiveLocationListenerEnabled = enable;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void GnssManager::handleRequestStateResyncCallbackSync() {
|
|
mLocationSession.handleRequestStateResyncCallbackSync();
|
|
mMeasurementSession.handleRequestStateResyncCallbackSync();
|
|
|
|
mPlatformPassiveLocationListenerEnabled = false;
|
|
if (!mPassiveLocationListenerNanoapps.empty()) {
|
|
if (!platformConfigurePassiveLocationListener(true /* enable */)) {
|
|
FATAL_ERROR("Failed to resync passive location listener");
|
|
}
|
|
}
|
|
}
|
|
|
|
void GnssManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
|
|
debugDump.print("\nGNSS:");
|
|
mLocationSession.logStateToBuffer(debugDump);
|
|
mMeasurementSession.logStateToBuffer(debugDump);
|
|
|
|
debugDump.print(
|
|
"\n Passive location listener %s\n",
|
|
mPlatformPassiveLocationListenerEnabled ? "enabled" : "disabled");
|
|
for (uint32_t instanceId : mPassiveLocationListenerNanoapps) {
|
|
debugDump.print(" nappId=%" PRIu32 "\n", instanceId);
|
|
}
|
|
}
|
|
|
|
GnssSession::GnssSession(uint16_t reportEventType)
|
|
: kReportEventType(reportEventType) {
|
|
switch (kReportEventType) {
|
|
case CHRE_EVENT_GNSS_LOCATION:
|
|
mStartRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START;
|
|
mStopRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP;
|
|
mName = "Location";
|
|
break;
|
|
|
|
case CHRE_EVENT_GNSS_DATA:
|
|
mStartRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START;
|
|
mStopRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP;
|
|
mName = "Measurement";
|
|
break;
|
|
|
|
default:
|
|
CHRE_ASSERT_LOG(false, "Unsupported eventType %" PRIu16, reportEventType);
|
|
}
|
|
|
|
if (!mRequests.reserve(1)) {
|
|
FATAL_ERROR_OOM();
|
|
}
|
|
}
|
|
|
|
bool GnssSession::addRequest(Nanoapp *nanoapp, Milliseconds minInterval,
|
|
Milliseconds minTimeToNext, const void *cookie) {
|
|
CHRE_ASSERT(nanoapp);
|
|
return configure(nanoapp, true /* enable */, minInterval, minTimeToNext,
|
|
cookie);
|
|
}
|
|
|
|
bool GnssSession::removeRequest(Nanoapp *nanoapp, const void *cookie) {
|
|
CHRE_ASSERT(nanoapp);
|
|
return configure(nanoapp, false /* enable */, Milliseconds(UINT64_MAX),
|
|
Milliseconds(UINT64_MAX), cookie);
|
|
}
|
|
|
|
void GnssSession::handleStatusChange(bool enabled, uint8_t errorCode) {
|
|
struct CallbackState {
|
|
bool enabled;
|
|
uint8_t errorCode;
|
|
};
|
|
|
|
auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
|
|
auto *session = static_cast<GnssSession *>(data);
|
|
CallbackState cbState = NestedDataPtr<CallbackState>(extraData);
|
|
session->handleStatusChangeSync(cbState.enabled, cbState.errorCode);
|
|
};
|
|
|
|
CallbackState cbState = {};
|
|
cbState.enabled = enabled;
|
|
cbState.errorCode = errorCode;
|
|
EventLoopManagerSingleton::get()->deferCallback(
|
|
SystemCallbackType::GnssSessionStatusChange, /*data=*/this, callback,
|
|
NestedDataPtr<CallbackState>(cbState));
|
|
}
|
|
|
|
void GnssSession::handleReportEvent(void *event) {
|
|
if (mRequests.empty()) {
|
|
LOGW("Unexpected %s event", mName);
|
|
}
|
|
|
|
auto callback = [](uint16_t type, void *data, void * /*extraData*/) {
|
|
uint16_t reportEventType;
|
|
if (!getReportEventType(static_cast<SystemCallbackType>(type),
|
|
&reportEventType) ||
|
|
(getSettingState(Setting::LOCATION) == SettingState::DISABLED)) {
|
|
freeReportEventCallback(reportEventType, data);
|
|
} else {
|
|
EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
|
|
reportEventType, data, freeReportEventCallback);
|
|
}
|
|
};
|
|
|
|
SystemCallbackType type;
|
|
if (!getCallbackType(kReportEventType, &type)) {
|
|
freeReportEventCallback(kReportEventType, event);
|
|
} else {
|
|
EventLoopManagerSingleton::get()->deferCallback(type, event, callback);
|
|
}
|
|
}
|
|
|
|
void GnssSession::onSettingChanged(Setting setting, SettingState state) {
|
|
if (setting == Setting::LOCATION) {
|
|
if (asyncResponsePending()) {
|
|
// A request is in progress, so we wait until the async response arrives
|
|
// to handle the state change.
|
|
mSettingChangePending = true;
|
|
} else {
|
|
mInternalRequestPending = updatePlatformRequest();
|
|
mSettingChangePending = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GnssSession::updatePlatformRequest(bool forceUpdate) {
|
|
SettingState locationSetting = getSettingState(Setting::LOCATION);
|
|
|
|
bool desiredPlatformState =
|
|
(locationSetting == SettingState::ENABLED) && !mRequests.empty();
|
|
bool shouldUpdatePlatform =
|
|
forceUpdate ||
|
|
(desiredPlatformState != mPlatformEnabled) /* (enable/disable) */;
|
|
|
|
bool requestPending = false;
|
|
if (shouldUpdatePlatform) {
|
|
if (controlPlatform(desiredPlatformState, mCurrentInterval,
|
|
Milliseconds(0) /* minTimeToNext */)) {
|
|
LOGD("Configured GNSS %s: enable %d", mName, desiredPlatformState);
|
|
addSessionRequestLog(CHRE_INSTANCE_ID, mCurrentInterval,
|
|
desiredPlatformState);
|
|
requestPending = true;
|
|
} else {
|
|
LOGE("Failed to configure GNSS %s: enable %d", mName,
|
|
desiredPlatformState);
|
|
}
|
|
}
|
|
|
|
return requestPending;
|
|
}
|
|
|
|
void GnssSession::handleRequestStateResyncCallbackSync() {
|
|
if (asyncResponsePending()) {
|
|
// A request is in progress, so we wait until the async response arrives
|
|
// to handle the resync callback.
|
|
mResyncPending = true;
|
|
} else {
|
|
mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
|
|
}
|
|
}
|
|
|
|
void GnssSession::logStateToBuffer(DebugDumpWrapper &debugDump) const {
|
|
// TODO: have all interval values print as INVALID if they are the max
|
|
// unsigned value
|
|
debugDump.print("\n %s: Curr int(ms)=%" PRIu64 "\n", mName,
|
|
mCurrentInterval.getMilliseconds());
|
|
debugDump.print(" Requests:\n");
|
|
for (const auto &request : mRequests) {
|
|
debugDump.print(" minInt(ms)=%" PRIu64 " nappId=%" PRIu32 "\n",
|
|
request.minInterval.getMilliseconds(),
|
|
request.nanoappInstanceId);
|
|
}
|
|
|
|
if (!mStateTransitions.empty()) {
|
|
debugDump.print(" Transition queue:\n");
|
|
for (const auto &transition : mStateTransitions) {
|
|
debugDump.print(" minInt(ms)=%" PRIu64 " enable=%d nappId=%" PRIu32
|
|
"\n",
|
|
transition.minInterval.getMilliseconds(),
|
|
transition.enable, transition.nanoappInstanceId);
|
|
}
|
|
}
|
|
|
|
debugDump.print(" Last %zu session requests:\n", mSessionRequestLogs.size());
|
|
static_assert(kNumSessionRequestLogs <= INT8_MAX,
|
|
"kNumSessionRequestLogs must be less than INT8_MAX.");
|
|
for (int8_t i = static_cast<int8_t>(mSessionRequestLogs.size()) - 1; i >= 0;
|
|
i--) {
|
|
const auto &log = mSessionRequestLogs[static_cast<size_t>(i)];
|
|
debugDump.print(" ts=%" PRIu64 " nappId=%" PRIu32 " %s",
|
|
log.timestamp.toRawNanoseconds(), log.instanceId,
|
|
log.start ? "start" : "stop\n");
|
|
if (log.start) {
|
|
debugDump.print(" int(ms)=%" PRIu64 "\n", log.interval.getMilliseconds());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GnssSession::configure(Nanoapp *nanoapp, bool enable,
|
|
Milliseconds minInterval,
|
|
Milliseconds minTimeToNext, const void *cookie) {
|
|
bool success = false;
|
|
uint32_t instanceId = nanoapp->getInstanceId();
|
|
size_t requestIndex = 0;
|
|
bool hasRequest = nanoappHasRequest(instanceId, &requestIndex);
|
|
if (!mStateTransitions.empty()) {
|
|
success = addRequestToQueue(instanceId, enable, minInterval, cookie);
|
|
} else if (stateTransitionIsRequired(enable, minInterval, hasRequest,
|
|
requestIndex)) {
|
|
if (enable &&
|
|
getSettingState(Setting::LOCATION) == SettingState::DISABLED) {
|
|
// Treat as success but post async failure per API.
|
|
success = postAsyncResultEvent(instanceId, false /* success */, enable,
|
|
minInterval, CHRE_ERROR_FUNCTION_DISABLED,
|
|
cookie);
|
|
} else if (addRequestToQueue(instanceId, enable, minInterval, cookie)) {
|
|
success = controlPlatform(enable, minInterval, minTimeToNext);
|
|
if (!success) {
|
|
mStateTransitions.pop_back();
|
|
LOGE("Failed to request a GNSS session for nanoapp instance %" PRIu32
|
|
" enable %d",
|
|
instanceId, enable);
|
|
}
|
|
}
|
|
} else {
|
|
success = postAsyncResultEvent(instanceId, true /* success */, enable,
|
|
minInterval, CHRE_ERROR_NONE, cookie);
|
|
}
|
|
|
|
if (success) {
|
|
addSessionRequestLog(nanoapp->getInstanceId(), minInterval, enable);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GnssSession::nanoappHasRequest(uint32_t instanceId,
|
|
size_t *requestIndex) const {
|
|
bool hasRequest = false;
|
|
for (size_t i = 0; i < mRequests.size(); i++) {
|
|
if (mRequests[i].nanoappInstanceId == instanceId) {
|
|
hasRequest = true;
|
|
if (requestIndex != nullptr) {
|
|
*requestIndex = i;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hasRequest;
|
|
}
|
|
|
|
bool GnssSession::addRequestToQueue(uint32_t instanceId, bool enable,
|
|
Milliseconds minInterval,
|
|
const void *cookie) {
|
|
StateTransition stateTransition;
|
|
stateTransition.nanoappInstanceId = instanceId;
|
|
stateTransition.enable = enable;
|
|
stateTransition.minInterval = minInterval;
|
|
stateTransition.cookie = cookie;
|
|
|
|
bool success = mStateTransitions.push(stateTransition);
|
|
if (!success) {
|
|
LOGW("Too many session state transitions");
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GnssSession::isEnabled() const {
|
|
return !mRequests.empty();
|
|
}
|
|
|
|
bool GnssSession::stateTransitionIsRequired(bool requestedState,
|
|
Milliseconds minInterval,
|
|
bool nanoappHasRequest,
|
|
size_t requestIndex) const {
|
|
bool requestToEnable = (requestedState && !isEnabled());
|
|
bool requestToIncreaseRate =
|
|
(requestedState && isEnabled() && minInterval < mCurrentInterval);
|
|
bool requestToDisable =
|
|
(!requestedState && nanoappHasRequest && mRequests.size() == 1);
|
|
|
|
// An effective rate decrease for the session can only occur if the nanoapp
|
|
// has an existing request.
|
|
bool requestToDecreaseRate = false;
|
|
if (nanoappHasRequest) {
|
|
// The nanoapp has an existing request. Check that the request does not
|
|
// result in a rate decrease by checking if no other nanoapps have the
|
|
// same request, the nanoapp's existing request is not equal to the current
|
|
// requested interval and the new request is slower than the current
|
|
// requested rate.
|
|
size_t requestCount = 0;
|
|
const auto ¤tRequest = mRequests[requestIndex];
|
|
for (size_t i = 0; i < mRequests.size(); i++) {
|
|
const Request &request = mRequests[i];
|
|
if (i != requestIndex &&
|
|
request.minInterval == currentRequest.minInterval) {
|
|
requestCount++;
|
|
}
|
|
}
|
|
|
|
requestToDecreaseRate =
|
|
(minInterval > mCurrentInterval &&
|
|
currentRequest.minInterval == mCurrentInterval && requestCount == 0);
|
|
}
|
|
|
|
return (requestToEnable || requestToDisable || requestToIncreaseRate ||
|
|
requestToDecreaseRate);
|
|
}
|
|
|
|
bool GnssSession::updateRequests(bool enable, Milliseconds minInterval,
|
|
uint32_t instanceId) {
|
|
bool success = true;
|
|
Nanoapp *nanoapp =
|
|
EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId(
|
|
instanceId);
|
|
if (nanoapp == nullptr) {
|
|
LOGW("Failed to update GNSS session request list for non-existent nanoapp");
|
|
} else {
|
|
size_t requestIndex;
|
|
bool hasExistingRequest = nanoappHasRequest(instanceId, &requestIndex);
|
|
if (enable) {
|
|
if (hasExistingRequest) {
|
|
// If the nanoapp has an open request ensure that the minInterval is
|
|
// kept up to date.
|
|
mRequests[requestIndex].minInterval = minInterval;
|
|
} else {
|
|
// The GNSS session was successfully enabled for this nanoapp and
|
|
// there is no existing request. Add it to the list of GNSS session
|
|
// nanoapps.
|
|
Request request;
|
|
request.nanoappInstanceId = instanceId;
|
|
request.minInterval = minInterval;
|
|
success = mRequests.push_back(request);
|
|
if (!success) {
|
|
LOG_OOM();
|
|
} else {
|
|
nanoapp->registerForBroadcastEvent(kReportEventType);
|
|
}
|
|
}
|
|
} else if (hasExistingRequest) {
|
|
// The session was successfully disabled for a previously enabled
|
|
// nanoapp. Remove it from the list of requests.
|
|
mRequests.erase(requestIndex);
|
|
|
|
// We can only unregister the location events from nanoapps if it has no
|
|
// request and has not configured the passive listener.
|
|
if ((kReportEventType != CHRE_EVENT_GNSS_LOCATION) ||
|
|
!EventLoopManagerSingleton::get()
|
|
->getGnssManager()
|
|
.nanoappHasPassiveLocationListener(instanceId)) {
|
|
nanoapp->unregisterForBroadcastEvent(kReportEventType);
|
|
}
|
|
} // else disabling an inactive request, treat as success per CHRE API
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool GnssSession::postAsyncResultEvent(uint32_t instanceId, bool success,
|
|
bool enable, Milliseconds minInterval,
|
|
uint8_t errorCode, const void *cookie) {
|
|
bool eventPosted = false;
|
|
if (!success || updateRequests(enable, minInterval, instanceId)) {
|
|
chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
|
|
if (event == nullptr) {
|
|
LOG_OOM();
|
|
} else {
|
|
event->requestType = enable ? mStartRequestType : mStopRequestType;
|
|
event->success = success;
|
|
event->errorCode = errorCode;
|
|
event->reserved = 0;
|
|
event->cookie = cookie;
|
|
|
|
EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
|
|
CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback,
|
|
instanceId);
|
|
eventPosted = true;
|
|
}
|
|
}
|
|
|
|
return eventPosted;
|
|
}
|
|
|
|
void GnssSession::postAsyncResultEventFatal(uint32_t instanceId, bool success,
|
|
bool enable,
|
|
Milliseconds minInterval,
|
|
uint8_t errorCode,
|
|
const void *cookie) {
|
|
if (!postAsyncResultEvent(instanceId, success, enable, minInterval, errorCode,
|
|
cookie)) {
|
|
FATAL_ERROR("Failed to send GNSS session request async result event");
|
|
}
|
|
}
|
|
|
|
void GnssSession::handleStatusChangeSync(bool enabled, uint8_t errorCode) {
|
|
bool success = (errorCode == CHRE_ERROR_NONE);
|
|
|
|
CHRE_ASSERT_LOG(asyncResponsePending(),
|
|
"handleStatusChangeSync called with no transitions");
|
|
if (mInternalRequestPending) {
|
|
// Silently handle internal requests from CHRE, since they are not pushed
|
|
// to the mStateTransitions queue.
|
|
mInternalRequestPending = false;
|
|
} else if (!mStateTransitions.empty()) {
|
|
const auto &stateTransition = mStateTransitions.front();
|
|
|
|
if (success) {
|
|
mCurrentInterval = stateTransition.minInterval;
|
|
}
|
|
|
|
success &= (stateTransition.enable == enabled);
|
|
postAsyncResultEventFatal(
|
|
stateTransition.nanoappInstanceId, success, stateTransition.enable,
|
|
stateTransition.minInterval, errorCode, stateTransition.cookie);
|
|
mStateTransitions.pop();
|
|
}
|
|
|
|
// If a previous setting change or resync event is pending process, do that
|
|
// first.
|
|
if (mResyncPending && !success) {
|
|
// We only send a platform request on resync if a pending request failed,
|
|
// because we still need to restore the previous request state.
|
|
mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
|
|
} else if (mSettingChangePending) {
|
|
mInternalRequestPending = updatePlatformRequest();
|
|
}
|
|
|
|
mResyncPending = false;
|
|
mSettingChangePending = false;
|
|
|
|
// If we didn't issue an internally-generated update via
|
|
// updatePlatformRequest(), process pending nanoapp requests (otherwise,
|
|
// wait for it to finish, then process any pending requests)
|
|
if (!mInternalRequestPending) {
|
|
dispatchQueuedStateTransitions();
|
|
}
|
|
}
|
|
|
|
void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) {
|
|
switch (eventType) {
|
|
case CHRE_EVENT_GNSS_LOCATION:
|
|
EventLoopManagerSingleton::get()
|
|
->getGnssManager()
|
|
.mPlatformGnss.releaseLocationEvent(
|
|
static_cast<chreGnssLocationEvent *>(eventData));
|
|
break;
|
|
|
|
case CHRE_EVENT_GNSS_DATA:
|
|
EventLoopManagerSingleton::get()
|
|
->getGnssManager()
|
|
.mPlatformGnss.releaseMeasurementDataEvent(
|
|
static_cast<chreGnssDataEvent *>(eventData));
|
|
break;
|
|
|
|
default:
|
|
CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType);
|
|
}
|
|
}
|
|
|
|
bool GnssSession::controlPlatform(bool enable, Milliseconds minInterval,
|
|
Milliseconds /* minTimeToNext */) {
|
|
bool success = false;
|
|
|
|
switch (kReportEventType) {
|
|
case CHRE_EVENT_GNSS_LOCATION:
|
|
// TODO: Provide support for min time to next report. It is currently sent
|
|
// to the platform as zero.
|
|
success = EventLoopManagerSingleton::get()
|
|
->getGnssManager()
|
|
.mPlatformGnss.controlLocationSession(enable, minInterval,
|
|
Milliseconds(0));
|
|
break;
|
|
|
|
case CHRE_EVENT_GNSS_DATA:
|
|
success =
|
|
EventLoopManagerSingleton::get()
|
|
->getGnssManager()
|
|
.mPlatformGnss.controlMeasurementSession(enable, minInterval);
|
|
break;
|
|
|
|
default:
|
|
CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, kReportEventType);
|
|
}
|
|
|
|
if (success) {
|
|
mPlatformEnabled = enable;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void GnssSession::addSessionRequestLog(uint32_t nanoappInstanceId,
|
|
Milliseconds interval, bool start) {
|
|
mSessionRequestLogs.kick_push(SessionRequestLog(
|
|
SystemTime::getMonotonicTime(), nanoappInstanceId, interval, start));
|
|
}
|
|
|
|
void GnssSession::dispatchQueuedStateTransitions() {
|
|
while (!mStateTransitions.empty()) {
|
|
const auto &stateTransition = mStateTransitions.front();
|
|
|
|
size_t requestIndex;
|
|
bool hasRequest =
|
|
nanoappHasRequest(stateTransition.nanoappInstanceId, &requestIndex);
|
|
|
|
if (stateTransitionIsRequired(stateTransition.enable,
|
|
stateTransition.minInterval, hasRequest,
|
|
requestIndex)) {
|
|
if (getSettingState(Setting::LOCATION) == SettingState::DISABLED) {
|
|
postAsyncResultEventFatal(
|
|
stateTransition.nanoappInstanceId, false /* success */,
|
|
stateTransition.enable, stateTransition.minInterval,
|
|
CHRE_ERROR_FUNCTION_DISABLED, stateTransition.cookie);
|
|
mStateTransitions.pop();
|
|
} else if (controlPlatform(stateTransition.enable,
|
|
stateTransition.minInterval,
|
|
Milliseconds(0))) {
|
|
break;
|
|
} else {
|
|
LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu32,
|
|
stateTransition.nanoappInstanceId);
|
|
postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
|
|
false /* success */, stateTransition.enable,
|
|
stateTransition.minInterval, CHRE_ERROR,
|
|
stateTransition.cookie);
|
|
mStateTransitions.pop();
|
|
}
|
|
} else {
|
|
postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
|
|
true /* success */, stateTransition.enable,
|
|
stateTransition.minInterval, CHRE_ERROR_NONE,
|
|
stateTransition.cookie);
|
|
mStateTransitions.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace chre
|