diff options
Diffstat (limited to 'gps/gnss/Agps.cpp')
-rw-r--r-- | gps/gnss/Agps.cpp | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/gps/gnss/Agps.cpp b/gps/gnss/Agps.cpp new file mode 100644 index 0000000..9255f88 --- /dev/null +++ b/gps/gnss/Agps.cpp @@ -0,0 +1,675 @@ +/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation, nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#define LOG_NDEBUG 0 +#define LOG_TAG "LocSvc_Agps" + +#include <Agps.h> +#include <loc_pla.h> +#include <ContextBase.h> +#include <loc_timer.h> +#include <inttypes.h> + +/* -------------------------------------------------------------------- + * AGPS State Machine Methods + * -------------------------------------------------------------------*/ +void AgpsStateMachine::processAgpsEvent(AgpsEvent event){ + + LOC_LOGD("processAgpsEvent(): SM %p, Event %d, State %d", + this, event, mState); + + switch (event) { + + case AGPS_EVENT_SUBSCRIBE: + processAgpsEventSubscribe(); + break; + + case AGPS_EVENT_UNSUBSCRIBE: + processAgpsEventUnsubscribe(); + break; + + case AGPS_EVENT_GRANTED: + processAgpsEventGranted(); + break; + + case AGPS_EVENT_RELEASED: + processAgpsEventReleased(); + break; + + case AGPS_EVENT_DENIED: + processAgpsEventDenied(); + break; + + default: + LOC_LOGE("Invalid Loc Agps Event"); + } +} + +void AgpsStateMachine::processAgpsEventSubscribe(){ + + switch (mState) { + + case AGPS_STATE_RELEASED: + /* Add subscriber to list + * No notifications until we get RSRC_GRANTED */ + addSubscriber(mCurrentSubscriber); + requestOrReleaseDataConn(true); + transitionState(AGPS_STATE_PENDING); + break; + + case AGPS_STATE_PENDING: + /* Already requested for data connection, + * do nothing until we get RSRC_GRANTED event; + * Just add this subscriber to the list, for notifications */ + addSubscriber(mCurrentSubscriber); + break; + + case AGPS_STATE_ACQUIRED: + /* We already have the data connection setup, + * Notify current subscriber with GRANTED event, + * And add it to the subscriber list for further notifications. */ + notifyEventToSubscriber(AGPS_EVENT_GRANTED, mCurrentSubscriber, false); + addSubscriber(mCurrentSubscriber); + break; + + case AGPS_STATE_RELEASING: + addSubscriber(mCurrentSubscriber); + break; + + default: + LOC_LOGE("Invalid state: %d", mState); + } +} + +void AgpsStateMachine::processAgpsEventUnsubscribe(){ + + switch (mState) { + + case AGPS_STATE_RELEASED: + notifyEventToSubscriber( + AGPS_EVENT_UNSUBSCRIBE, mCurrentSubscriber, false); + break; + + case AGPS_STATE_PENDING: + case AGPS_STATE_ACQUIRED: + /* If the subscriber wishes to wait for connection close, + * before being removed from list, move to inactive state + * and notify */ + if (mCurrentSubscriber->mWaitForCloseComplete) { + mCurrentSubscriber->mIsInactive = true; + } + else { + /* Notify only current subscriber and then delete it from + * subscriberList */ + notifyEventToSubscriber( + AGPS_EVENT_UNSUBSCRIBE, mCurrentSubscriber, true); + } + + /* If no subscribers in list, release data connection */ + if (mSubscriberList.empty()) { + transitionState(AGPS_STATE_RELEASED); + requestOrReleaseDataConn(false); + } + /* Some subscribers in list, but all inactive; + * Release data connection */ + else if(!anyActiveSubscribers()) { + transitionState(AGPS_STATE_RELEASING); + requestOrReleaseDataConn(false); + } + break; + + case AGPS_STATE_RELEASING: + /* If the subscriber wishes to wait for connection close, + * before being removed from list, move to inactive state + * and notify */ + if (mCurrentSubscriber->mWaitForCloseComplete) { + mCurrentSubscriber->mIsInactive = true; + } + else { + /* Notify only current subscriber and then delete it from + * subscriberList */ + notifyEventToSubscriber( + AGPS_EVENT_UNSUBSCRIBE, mCurrentSubscriber, true); + } + + /* If no subscribers in list, just move the state. + * Request for releasing data connection should already have been + * sent */ + if (mSubscriberList.empty()) { + transitionState(AGPS_STATE_RELEASED); + } + break; + + default: + LOC_LOGE("Invalid state: %d", mState); + } +} + +void AgpsStateMachine::processAgpsEventGranted(){ + + switch (mState) { + + case AGPS_STATE_RELEASED: + case AGPS_STATE_ACQUIRED: + case AGPS_STATE_RELEASING: + LOC_LOGE("Unexpected event GRANTED in state %d", mState); + break; + + case AGPS_STATE_PENDING: + // Move to acquired state + transitionState(AGPS_STATE_ACQUIRED); + notifyAllSubscribers( + AGPS_EVENT_GRANTED, false, + AGPS_NOTIFICATION_TYPE_FOR_ACTIVE_SUBSCRIBERS); + break; + + default: + LOC_LOGE("Invalid state: %d", mState); + } +} + +void AgpsStateMachine::processAgpsEventReleased(){ + + switch (mState) { + + case AGPS_STATE_RELEASED: + /* Subscriber list should be empty if we are in released state */ + if (!mSubscriberList.empty()) { + LOC_LOGE("Unexpected event RELEASED in RELEASED state"); + } + break; + + case AGPS_STATE_ACQUIRED: + /* Force release received */ + LOC_LOGW("Force RELEASED event in ACQUIRED state"); + transitionState(AGPS_STATE_RELEASED); + notifyAllSubscribers( + AGPS_EVENT_RELEASED, true, + AGPS_NOTIFICATION_TYPE_FOR_ALL_SUBSCRIBERS); + break; + + case AGPS_STATE_RELEASING: + /* Notify all inactive subscribers about the event */ + notifyAllSubscribers( + AGPS_EVENT_RELEASED, true, + AGPS_NOTIFICATION_TYPE_FOR_INACTIVE_SUBSCRIBERS); + + /* If we have active subscribers now, they must be waiting for + * data conn setup */ + if (anyActiveSubscribers()) { + transitionState(AGPS_STATE_PENDING); + requestOrReleaseDataConn(true); + } + /* No active subscribers, move to released state */ + else { + transitionState(AGPS_STATE_RELEASED); + } + break; + + case AGPS_STATE_PENDING: + /* NOOP */ + break; + + default: + LOC_LOGE("Invalid state: %d", mState); + } +} + +void AgpsStateMachine::processAgpsEventDenied(){ + + switch (mState) { + + case AGPS_STATE_RELEASED: + LOC_LOGE("Unexpected event DENIED in state %d", mState); + break; + + case AGPS_STATE_ACQUIRED: + /* NOOP */ + break; + + case AGPS_STATE_RELEASING: + /* Notify all inactive subscribers about the event */ + notifyAllSubscribers( + AGPS_EVENT_RELEASED, true, + AGPS_NOTIFICATION_TYPE_FOR_INACTIVE_SUBSCRIBERS); + + /* If we have active subscribers now, they must be waiting for + * data conn setup */ + if (anyActiveSubscribers()) { + transitionState(AGPS_STATE_PENDING); + requestOrReleaseDataConn(true); + } + /* No active subscribers, move to released state */ + else { + transitionState(AGPS_STATE_RELEASED); + } + break; + + case AGPS_STATE_PENDING: + transitionState(AGPS_STATE_RELEASED); + notifyAllSubscribers( + AGPS_EVENT_DENIED, true, + AGPS_NOTIFICATION_TYPE_FOR_ALL_SUBSCRIBERS); + break; + + default: + LOC_LOGE("Invalid state: %d", mState); + } +} + +/* Request or Release data connection + * bool request : + * true = Request data connection + * false = Release data connection */ +void AgpsStateMachine::requestOrReleaseDataConn(bool request){ + + AGnssExtStatusIpV4 nifRequest; + memset(&nifRequest, 0, sizeof(nifRequest)); + + nifRequest.type = mAgpsType; + nifRequest.apnTypeMask = mApnTypeMask; + if (request) { + LOC_LOGD("AGPS Data Conn Request mAgpsType=%d mApnTypeMask=0x%X", + mAgpsType, mApnTypeMask); + nifRequest.status = LOC_GPS_REQUEST_AGPS_DATA_CONN; + } + else{ + LOC_LOGD("AGPS Data Conn Release mAgpsType=%d mApnTypeMask=0x%X", + mAgpsType, mApnTypeMask); + nifRequest.status = LOC_GPS_RELEASE_AGPS_DATA_CONN; + } + + mFrameworkStatusV4Cb(nifRequest); +} + +void AgpsStateMachine::notifyAllSubscribers( + AgpsEvent event, bool deleteSubscriberPostNotify, + AgpsNotificationType notificationType){ + + LOC_LOGD("notifyAllSubscribers(): " + "SM %p, Event %d Delete %d Notification Type %d", + this, event, deleteSubscriberPostNotify, notificationType); + + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + while ( it != mSubscriberList.end() ) { + + AgpsSubscriber* subscriber = *it; + + if (notificationType == AGPS_NOTIFICATION_TYPE_FOR_ALL_SUBSCRIBERS || + (notificationType == AGPS_NOTIFICATION_TYPE_FOR_INACTIVE_SUBSCRIBERS && + subscriber->mIsInactive) || + (notificationType == AGPS_NOTIFICATION_TYPE_FOR_ACTIVE_SUBSCRIBERS && + !subscriber->mIsInactive)) { + + /* Deleting via this call would require another traversal + * through subscriber list, inefficient; hence pass in false*/ + notifyEventToSubscriber(event, subscriber, false); + + if (deleteSubscriberPostNotify) { + it = mSubscriberList.erase(it); + delete subscriber; + } else { + it++; + } + } else { + it++; + } + } +} + +void AgpsStateMachine::notifyEventToSubscriber( + AgpsEvent event, AgpsSubscriber* subscriberToNotify, + bool deleteSubscriberPostNotify) { + + LOC_LOGD("notifyEventToSubscriber(): " + "SM %p, Event %d Subscriber %p Delete %d", + this, event, subscriberToNotify, deleteSubscriberPostNotify); + + switch (event) { + + case AGPS_EVENT_GRANTED: + mAgpsManager->mAtlOpenStatusCb( + subscriberToNotify->mConnHandle, 1, getAPN(), getAPNLen(), + getBearer(), mAgpsType, mApnTypeMask); + break; + + case AGPS_EVENT_DENIED: + mAgpsManager->mAtlOpenStatusCb( + subscriberToNotify->mConnHandle, 0, getAPN(), getAPNLen(), + getBearer(), mAgpsType, mApnTypeMask); + break; + + case AGPS_EVENT_UNSUBSCRIBE: + case AGPS_EVENT_RELEASED: + mAgpsManager->mAtlCloseStatusCb(subscriberToNotify->mConnHandle, 1); + break; + + default: + LOC_LOGE("Invalid event %d", event); + } + + /* Search this subscriber in list and delete */ + if (deleteSubscriberPostNotify) { + deleteSubscriber(subscriberToNotify); + } +} + +void AgpsStateMachine::transitionState(AgpsState newState){ + + LOC_LOGD("transitionState(): SM %p, old %d, new %d", + this, mState, newState); + + mState = newState; + + // notify state transitions to all subscribers ? +} + +void AgpsStateMachine::addSubscriber(AgpsSubscriber* subscriberToAdd){ + + LOC_LOGD("addSubscriber(): SM %p, Subscriber %p", + this, subscriberToAdd); + + // Check if subscriber is already present in the current list + // If not, then add + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + for (; it != mSubscriberList.end(); it++) { + AgpsSubscriber* subscriber = *it; + if (subscriber->equals(subscriberToAdd)) { + LOC_LOGE("Subscriber already in list"); + return; + } + } + + AgpsSubscriber* cloned = subscriberToAdd->clone(); + LOC_LOGD("addSubscriber(): cloned subscriber: %p", cloned); + mSubscriberList.push_back(cloned); +} + +void AgpsStateMachine::deleteSubscriber(AgpsSubscriber* subscriberToDelete){ + + LOC_LOGD("deleteSubscriber(): SM %p, Subscriber %p", + this, subscriberToDelete); + + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + while ( it != mSubscriberList.end() ) { + + AgpsSubscriber* subscriber = *it; + if (subscriber && subscriber->equals(subscriberToDelete)) { + + it = mSubscriberList.erase(it); + delete subscriber; + } else { + it++; + } + } +} + +bool AgpsStateMachine::anyActiveSubscribers(){ + + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + for (; it != mSubscriberList.end(); it++) { + AgpsSubscriber* subscriber = *it; + if (!subscriber->mIsInactive) { + return true; + } + } + return false; +} + +void AgpsStateMachine::setAPN(char* apn, unsigned int len){ + + if (NULL != mAPN) { + delete mAPN; + mAPN = NULL; + } + + if (NULL == apn || len <= 0 || len > MAX_APN_LEN || strlen(apn) != len) { + LOC_LOGD("Invalid apn len (%d) or null apn", len); + mAPN = NULL; + mAPNLen = 0; + } else { + mAPN = new char[len+1]; + if (NULL != mAPN) { + memcpy(mAPN, apn, len); + mAPN[len] = '\0'; + mAPNLen = len; + } + } +} + +AgpsSubscriber* AgpsStateMachine::getSubscriber(int connHandle){ + + /* Go over the subscriber list */ + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + for (; it != mSubscriberList.end(); it++) { + AgpsSubscriber* subscriber = *it; + if (subscriber->mConnHandle == connHandle) { + return subscriber; + } + } + + /* Not found, return NULL */ + return NULL; +} + +AgpsSubscriber* AgpsStateMachine::getFirstSubscriber(bool isInactive){ + + /* Go over the subscriber list */ + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + for (; it != mSubscriberList.end(); it++) { + AgpsSubscriber* subscriber = *it; + if(subscriber->mIsInactive == isInactive) { + return subscriber; + } + } + + /* Not found, return NULL */ + return NULL; +} + +void AgpsStateMachine::dropAllSubscribers(){ + + LOC_LOGD("dropAllSubscribers(): SM %p", this); + + /* Go over the subscriber list */ + std::list<AgpsSubscriber*>::const_iterator it = mSubscriberList.begin(); + while ( it != mSubscriberList.end() ) { + AgpsSubscriber* subscriber = *it; + it = mSubscriberList.erase(it); + delete subscriber; + } +} + +/* -------------------------------------------------------------------- + * Loc AGPS Manager Methods + * -------------------------------------------------------------------*/ + +/* CREATE AGPS STATE MACHINES + * Must be invoked in Msg Handler context */ +void AgpsManager::createAgpsStateMachines(const AgpsCbInfo& cbInfo) { + + LOC_LOGD("AgpsManager::createAgpsStateMachines"); + + bool agpsCapable = + ((loc_core::ContextBase::mGps_conf.CAPABILITIES & LOC_GPS_CAPABILITY_MSA) || + (loc_core::ContextBase::mGps_conf.CAPABILITIES & LOC_GPS_CAPABILITY_MSB)); + + if (NULL == mInternetNif && (cbInfo.atlType & AGPS_ATL_TYPE_WWAN)) { + mInternetNif = new AgpsStateMachine(this, LOC_AGPS_TYPE_WWAN_ANY); + mInternetNif->registerFrameworkStatusCallback((AgnssStatusIpV4Cb)cbInfo.statusV4Cb); + LOC_LOGD("Internet NIF: %p", mInternetNif); + } + if (agpsCapable) { + if (NULL == mAgnssNif && (cbInfo.atlType & AGPS_ATL_TYPE_SUPL) && + (cbInfo.atlType & AGPS_ATL_TYPE_SUPL_ES)) { + mAgnssNif = new AgpsStateMachine(this, LOC_AGPS_TYPE_SUPL); + mAgnssNif->registerFrameworkStatusCallback((AgnssStatusIpV4Cb)cbInfo.statusV4Cb); + LOC_LOGD("AGNSS NIF: %p", mAgnssNif); + } + } +} + +AgpsStateMachine* AgpsManager::getAgpsStateMachine(AGpsExtType agpsType) { + + LOC_LOGD("AgpsManager::getAgpsStateMachine(): agpsType %d", agpsType); + + switch (agpsType) { + + case LOC_AGPS_TYPE_INVALID: + case LOC_AGPS_TYPE_SUPL: + case LOC_AGPS_TYPE_SUPL_ES: + if (mAgnssNif == NULL) { + LOC_LOGE("NULL AGNSS NIF !"); + } + return mAgnssNif; + case LOC_AGPS_TYPE_WWAN_ANY: + if (mInternetNif == NULL) { + LOC_LOGE("NULL Internet NIF !"); + } + return mInternetNif; + default: + return mInternetNif; + } + + LOC_LOGE("No SM found !"); + return NULL; +} + +void AgpsManager::requestATL(int connHandle, AGpsExtType agpsType, + LocApnTypeMask apnTypeMask){ + + LOC_LOGD("AgpsManager::requestATL(): connHandle %d, agpsType 0x%X apnTypeMask: 0x%X", + connHandle, agpsType, apnTypeMask); + + if (0 == loc_core::ContextBase::mGps_conf.USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL && + LOC_AGPS_TYPE_SUPL_ES == agpsType) { + agpsType = LOC_AGPS_TYPE_SUPL; + apnTypeMask &= ~LOC_APN_TYPE_MASK_EMERGENCY; + apnTypeMask |= LOC_APN_TYPE_MASK_SUPL; + LOC_LOGD("Changed agpsType to non-emergency when USE_EMERGENCY... is 0" + "and removed LOC_APN_TYPE_MASK_EMERGENCY from apnTypeMask" + "agpsType 0x%X apnTypeMask : 0x%X", + agpsType, apnTypeMask); + } + AgpsStateMachine* sm = getAgpsStateMachine(agpsType); + + if (sm == NULL) { + + LOC_LOGE("No AGPS State Machine for agpsType: %d apnTypeMask: 0x%X", + agpsType, apnTypeMask); + mAtlOpenStatusCb( + connHandle, 0, NULL, 0, AGPS_APN_BEARER_INVALID, agpsType, apnTypeMask); + return; + } + sm->setType(agpsType); + sm->setApnTypeMask(apnTypeMask); + + /* Invoke AGPS SM processing */ + AgpsSubscriber subscriber(connHandle, false, false, apnTypeMask); + sm->setCurrentSubscriber(&subscriber); + /* Send subscriber event */ + sm->processAgpsEvent(AGPS_EVENT_SUBSCRIBE); +} + +void AgpsManager::releaseATL(int connHandle){ + + LOC_LOGD("AgpsManager::releaseATL(): connHandle %d", connHandle); + + /* First find the subscriber with specified handle. + * We need to search in all state machines. */ + AgpsStateMachine* sm = NULL; + AgpsSubscriber* subscriber = NULL; + + if (mAgnssNif && + (subscriber = mAgnssNif->getSubscriber(connHandle)) != NULL) { + sm = mAgnssNif; + } + else if (mInternetNif && + (subscriber = mInternetNif->getSubscriber(connHandle)) != NULL) { + sm = mInternetNif; + } + if (sm == NULL) { + LOC_LOGE("Subscriber with connHandle %d not found in any SM", + connHandle); + return; + } + + /* Now send unsubscribe event */ + sm->setCurrentSubscriber(subscriber); + sm->processAgpsEvent(AGPS_EVENT_UNSUBSCRIBE); +} + +void AgpsManager::reportAtlOpenSuccess( + AGpsExtType agpsType, char* apnName, int apnLen, + AGpsBearerType bearerType){ + + LOC_LOGD("AgpsManager::reportAtlOpenSuccess(): " + "AgpsType %d, APN [%s], Len %d, BearerType %d", + agpsType, apnName, apnLen, bearerType); + + /* Find the state machine instance */ + AgpsStateMachine* sm = getAgpsStateMachine(agpsType); + + /* Set bearer and apn info in state machine instance */ + sm->setBearer(bearerType); + sm->setAPN(apnName, apnLen); + + /* Send GRANTED event to state machine */ + sm->processAgpsEvent(AGPS_EVENT_GRANTED); +} + +void AgpsManager::reportAtlOpenFailed(AGpsExtType agpsType){ + + LOC_LOGD("AgpsManager::reportAtlOpenFailed(): AgpsType %d", agpsType); + + /* Fetch SM and send DENIED event */ + AgpsStateMachine* sm = getAgpsStateMachine(agpsType); + sm->processAgpsEvent(AGPS_EVENT_DENIED); +} + +void AgpsManager::reportAtlClosed(AGpsExtType agpsType){ + + LOC_LOGD("AgpsManager::reportAtlClosed(): AgpsType %d", agpsType); + + /* Fetch SM and send RELEASED event */ + AgpsStateMachine* sm = getAgpsStateMachine(agpsType); + sm->processAgpsEvent(AGPS_EVENT_RELEASED); +} + +void AgpsManager::handleModemSSR(){ + + LOC_LOGD("AgpsManager::handleModemSSR"); + + /* Drop subscribers from all state machines */ + if (mAgnssNif) { + mAgnssNif->dropAllSubscribers(); + } + if (mInternetNif) { + mInternetNif->dropAllSubscribers(); + } +} |