/* 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 #include #include #include #include /* -------------------------------------------------------------------- * 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::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::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::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::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::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::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::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(); } }