| /* Copyright (c) 2011-2013, 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_NDDEBUG 0 |
| #define LOG_TAG "LocSvc_eng" |
| |
| #include <loc_eng_agps.h> |
| #include <loc_eng_log.h> |
| #include <log_util.h> |
| #include <platform_lib_includes.h> |
| #include <loc_eng_dmn_conn_handler.h> |
| #include <loc_eng_dmn_conn.h> |
| #include <sys/time.h> |
| |
| //====================================================================== |
| // C callbacks |
| //====================================================================== |
| |
| // This is given to linked_list_add as the dealloc callback |
| // data -- an instance of Subscriber |
| static void deleteObj(void* data) |
| { |
| delete (Subscriber*)data; |
| } |
| |
| // This is given to linked_list_search() as the comparison callback |
| // when the state manchine needs to process for particular subscriber |
| // fromCaller -- caller provides this obj |
| // fromList -- linked_list_search() function take this one from list |
| static bool hasSubscriber(void* fromCaller, void* fromList) |
| { |
| Notification* notification = (Notification*)fromCaller; |
| Subscriber* s1 = (Subscriber*)fromList; |
| |
| return s1->forMe(*notification); |
| } |
| |
| // This is gvien to linked_list_search() to notify subscriber objs |
| // when the state machine needs to inform all subscribers of resource |
| // status changes, e.g. when resource is GRANTED. |
| // fromCaller -- caller provides this ptr to a Notification obj. |
| // fromList -- linked_list_search() function take this one from list |
| static bool notifySubscriber(void* fromCaller, void* fromList) |
| { |
| Notification* notification = (Notification*)fromCaller; |
| Subscriber* s1 = (Subscriber*)fromList; |
| |
| // we notify every subscriber indiscriminatively |
| // each subscriber decides if this notification is interesting. |
| return s1->notifyRsrcStatus(*notification) && |
| // if we do not want to delete the subscriber from the |
| // the list, we must set this to false so this function |
| // returns false |
| notification->postNotifyDelete; |
| } |
| |
| //====================================================================== |
| // Notification |
| //====================================================================== |
| const int Notification::BROADCAST_ALL = 0x80000000; |
| const int Notification::BROADCAST_ACTIVE = 0x80000001; |
| const int Notification::BROADCAST_INACTIVE = 0x80000002; |
| const unsigned char DSStateMachine::MAX_START_DATA_CALL_RETRIES = 4; |
| const unsigned int DSStateMachine::DATA_CALL_RETRY_DELAY_MSEC = 500; |
| //====================================================================== |
| // Subscriber: BITSubscriber / ATLSubscriber / WIFISubscriber |
| //====================================================================== |
| bool Subscriber::forMe(Notification ¬ification) |
| { |
| if (NULL != notification.rcver) { |
| return equals(notification.rcver); |
| } else { |
| return Notification::BROADCAST_ALL == notification.groupID || |
| (Notification::BROADCAST_ACTIVE == notification.groupID && |
| !isInactive()) || |
| (Notification::BROADCAST_INACTIVE == notification.groupID && |
| isInactive()); |
| } |
| } |
| bool BITSubscriber::equals(const Subscriber *s) const |
| { |
| BITSubscriber* bitS = (BITSubscriber*)s; |
| |
| return (ID == bitS->ID && |
| (INADDR_NONE != (unsigned int)ID || |
| 0 == strncmp(mIPv6Addr, bitS->mIPv6Addr, sizeof(mIPv6Addr)))); |
| } |
| |
| bool BITSubscriber::notifyRsrcStatus(Notification ¬ification) |
| { |
| bool notify = forMe(notification); |
| |
| if (notify) { |
| switch(notification.rsrcStatus) |
| { |
| case RSRC_UNSUBSCRIBE: |
| case RSRC_RELEASED: |
| loc_eng_dmn_conn_loc_api_server_data_conn( |
| LOC_ENG_IF_REQUEST_SENDER_ID_GPSONE_DAEMON, |
| GPSONE_LOC_API_IF_RELEASE_SUCCESS); |
| break; |
| case RSRC_DENIED: |
| loc_eng_dmn_conn_loc_api_server_data_conn( |
| LOC_ENG_IF_REQUEST_SENDER_ID_GPSONE_DAEMON, |
| GPSONE_LOC_API_IF_FAILURE); |
| break; |
| case RSRC_GRANTED: |
| loc_eng_dmn_conn_loc_api_server_data_conn( |
| LOC_ENG_IF_REQUEST_SENDER_ID_GPSONE_DAEMON, |
| GPSONE_LOC_API_IF_REQUEST_SUCCESS); |
| break; |
| default: |
| notify = false; |
| } |
| } |
| |
| return notify; |
| } |
| |
| bool ATLSubscriber::notifyRsrcStatus(Notification ¬ification) |
| { |
| bool notify = forMe(notification); |
| |
| if (notify) { |
| switch(notification.rsrcStatus) |
| { |
| case RSRC_UNSUBSCRIBE: |
| case RSRC_RELEASED: |
| ((LocEngAdapter*)mLocAdapter)->atlCloseStatus(ID, 1); |
| break; |
| case RSRC_DENIED: |
| { |
| AGpsExtType type = mBackwardCompatibleMode ? |
| AGPS_TYPE_INVALID : mStateMachine->getType(); |
| ((LocEngAdapter*)mLocAdapter)->atlOpenStatus(ID, 0, |
| (char*)mStateMachine->getAPN(), |
| mStateMachine->getBearer(), |
| type); |
| } |
| break; |
| case RSRC_GRANTED: |
| { |
| AGpsExtType type = mBackwardCompatibleMode ? |
| AGPS_TYPE_INVALID : mStateMachine->getType(); |
| ((LocEngAdapter*)mLocAdapter)->atlOpenStatus(ID, 1, |
| (char*)mStateMachine->getAPN(), |
| mStateMachine->getBearer(), |
| type); |
| } |
| break; |
| default: |
| notify = false; |
| } |
| } |
| |
| return notify; |
| } |
| |
| bool WIFISubscriber::notifyRsrcStatus(Notification ¬ification) |
| { |
| bool notify = forMe(notification); |
| |
| if (notify) { |
| switch(notification.rsrcStatus) |
| { |
| case RSRC_UNSUBSCRIBE: |
| break; |
| case RSRC_RELEASED: |
| loc_eng_dmn_conn_loc_api_server_data_conn( |
| senderId, |
| GPSONE_LOC_API_IF_RELEASE_SUCCESS); |
| break; |
| case RSRC_DENIED: |
| loc_eng_dmn_conn_loc_api_server_data_conn( |
| senderId, |
| GPSONE_LOC_API_IF_FAILURE); |
| break; |
| case RSRC_GRANTED: |
| loc_eng_dmn_conn_loc_api_server_data_conn( |
| senderId, |
| GPSONE_LOC_API_IF_REQUEST_SUCCESS); |
| break; |
| default: |
| notify = false; |
| } |
| } |
| |
| return notify; |
| } |
| bool DSSubscriber::notifyRsrcStatus(Notification ¬ification) |
| { |
| bool notify = forMe(notification); |
| LOC_LOGD("DSSubscriber::notifyRsrcStatus. notify:%d \n",(int)(notify)); |
| if(notify) { |
| switch(notification.rsrcStatus) { |
| case RSRC_UNSUBSCRIBE: |
| case RSRC_RELEASED: |
| case RSRC_DENIED: |
| case RSRC_GRANTED: |
| ((DSStateMachine *)mStateMachine)->informStatus(notification.rsrcStatus, ID); |
| break; |
| default: |
| notify = false; |
| } |
| } |
| return notify; |
| } |
| void DSSubscriber :: setInactive() |
| { |
| mIsInactive = true; |
| ((DSStateMachine *)mStateMachine)->informStatus(RSRC_UNSUBSCRIBE, ID); |
| } |
| //====================================================================== |
| // AgpsState: AgpsReleasedState / AgpsPendingState / AgpsAcquiredState |
| //====================================================================== |
| |
| // AgpsReleasedState |
| class AgpsReleasedState : public AgpsState |
| { |
| friend class AgpsStateMachine; |
| |
| inline AgpsReleasedState(AgpsStateMachine* stateMachine) : |
| AgpsState(stateMachine) |
| { mReleasedState = this; } |
| |
| inline ~AgpsReleasedState() {} |
| public: |
| virtual AgpsState* onRsrcEvent(AgpsRsrcStatus event, void* data); |
| inline virtual char* whoami() {return (char*)"AgpsReleasedState";} |
| }; |
| |
| AgpsState* AgpsReleasedState::onRsrcEvent(AgpsRsrcStatus event, void* data) |
| { |
| LOC_LOGD("AgpsReleasedState::onRsrcEvent; event:%d\n", (int)event); |
| if (mStateMachine->hasSubscribers()) { |
| LOC_LOGE("Error: %s subscriber list not empty!!!", whoami()); |
| // I don't know how to recover from it. I am adding this rather |
| // for debugging purpose. |
| } |
| |
| AgpsState* nextState = this; |
| switch (event) |
| { |
| case RSRC_SUBSCRIBE: |
| { |
| // no notification until we get RSRC_GRANTED |
| // but we need to add subscriber to the list |
| mStateMachine->addSubscriber((Subscriber*)data); |
| // request from connecivity service for NIF |
| //The if condition is added so that if the data call setup fails |
| //for DS State Machine, we want to retry in released state. |
| //for AGps State Machine, sendRsrcRequest() will always return success |
| if(!mStateMachine->sendRsrcRequest(GPS_REQUEST_AGPS_DATA_CONN)) { |
| // move the state to PENDING |
| nextState = mPendingState; |
| } |
| } |
| break; |
| |
| case RSRC_UNSUBSCRIBE: |
| { |
| // the list should really be empty, nothing to remove. |
| // but we might as well just tell the client it is |
| // unsubscribed. False tolerance, right? |
| Subscriber* subscriber = (Subscriber*) data; |
| Notification notification(subscriber, event, false); |
| subscriber->notifyRsrcStatus(notification); |
| } |
| // break; |
| case RSRC_GRANTED: |
| case RSRC_RELEASED: |
| case RSRC_DENIED: |
| default: |
| LOC_LOGW("%s: unrecognized event %d", whoami(), event); |
| // no state change. |
| break; |
| } |
| |
| LOC_LOGD("onRsrcEvent, old state %s, new state %s, event %d", |
| whoami(), nextState->whoami(), event); |
| return nextState; |
| } |
| |
| // AgpsPendingState |
| class AgpsPendingState : public AgpsState |
| { |
| friend class AgpsStateMachine; |
| |
| inline AgpsPendingState(AgpsStateMachine* stateMachine) : |
| AgpsState(stateMachine) |
| { mPendingState = this; } |
| |
| inline ~AgpsPendingState() {} |
| public: |
| virtual AgpsState* onRsrcEvent(AgpsRsrcStatus event, void* data); |
| inline virtual char* whoami() {return (char*)"AgpsPendingState";} |
| }; |
| |
| AgpsState* AgpsPendingState::onRsrcEvent(AgpsRsrcStatus event, void* data) |
| { |
| AgpsState* nextState = this;; |
| LOC_LOGD("AgpsPendingState::onRsrcEvent; event:%d\n", (int)event); |
| switch (event) |
| { |
| case RSRC_SUBSCRIBE: |
| { |
| // already requested for NIF resource, |
| // do nothing until we get RSRC_GRANTED indication |
| // but we need to add subscriber to the list |
| mStateMachine->addSubscriber((Subscriber*)data); |
| // no state change. |
| } |
| break; |
| |
| case RSRC_UNSUBSCRIBE: |
| { |
| Subscriber* subscriber = (Subscriber*) data; |
| if (subscriber->waitForCloseComplete()) { |
| subscriber->setInactive(); |
| } else { |
| // auto notify this subscriber of the unsubscribe |
| Notification notification(subscriber, event, true); |
| mStateMachine->notifySubscribers(notification); |
| } |
| |
| // now check if there is any subscribers left |
| if (!mStateMachine->hasSubscribers()) { |
| // no more subscribers, move to RELEASED state |
| nextState = mReleasedState; |
| |
| // tell connecivity service we can release NIF |
| mStateMachine->sendRsrcRequest(GPS_RELEASE_AGPS_DATA_CONN); |
| } else if (!mStateMachine->hasActiveSubscribers()) { |
| // only inactive subscribers, move to RELEASING state |
| nextState = mReleasingState; |
| |
| // tell connecivity service we can release NIF |
| mStateMachine->sendRsrcRequest(GPS_RELEASE_AGPS_DATA_CONN); |
| } |
| } |
| break; |
| |
| case RSRC_GRANTED: |
| { |
| nextState = mAcquiredState; |
| Notification notification(Notification::BROADCAST_ACTIVE, event, false); |
| // notify all subscribers NIF resource GRANTED |
| // by setting false, we keep subscribers on the linked list |
| mStateMachine->notifySubscribers(notification); |
| } |
| break; |
| |
| case RSRC_RELEASED: |
| // no state change. |
| // we are expecting either GRANTED or DENIED. Handling RELEASED |
| // may like break our state machine in race conditions. |
| break; |
| |
| case RSRC_DENIED: |
| { |
| nextState = mReleasedState; |
| Notification notification(Notification::BROADCAST_ALL, event, true); |
| // notify all subscribers NIF resource RELEASED or DENIED |
| // by setting true, we remove subscribers from the linked list |
| mStateMachine->notifySubscribers(notification); |
| } |
| break; |
| |
| default: |
| LOC_LOGE("%s: unrecognized event %d", whoami(), event); |
| // no state change. |
| } |
| |
| LOC_LOGD("onRsrcEvent, old state %s, new state %s, event %d", |
| whoami(), nextState->whoami(), event); |
| return nextState; |
| } |
| |
| |
| class AgpsAcquiredState : public AgpsState |
| { |
| friend class AgpsStateMachine; |
| |
| inline AgpsAcquiredState(AgpsStateMachine* stateMachine) : |
| AgpsState(stateMachine) |
| { mAcquiredState = this; } |
| |
| inline ~AgpsAcquiredState() {} |
| public: |
| virtual AgpsState* onRsrcEvent(AgpsRsrcStatus event, void* data); |
| inline virtual char* whoami() { return (char*)"AgpsAcquiredState"; } |
| }; |
| |
| |
| AgpsState* AgpsAcquiredState::onRsrcEvent(AgpsRsrcStatus event, void* data) |
| { |
| AgpsState* nextState = this; |
| LOC_LOGD("AgpsAcquiredState::onRsrcEvent; event:%d\n", (int)event); |
| switch (event) |
| { |
| case RSRC_SUBSCRIBE: |
| { |
| // we already have the NIF resource, simply notify subscriber |
| Subscriber* subscriber = (Subscriber*) data; |
| // we have rsrc in hand, so grant it right away |
| Notification notification(subscriber, RSRC_GRANTED, false); |
| subscriber->notifyRsrcStatus(notification); |
| // add subscriber to the list |
| mStateMachine->addSubscriber(subscriber); |
| // no state change. |
| } |
| break; |
| |
| case RSRC_UNSUBSCRIBE: |
| { |
| Subscriber* subscriber = (Subscriber*) data; |
| if (subscriber->waitForCloseComplete()) { |
| subscriber->setInactive(); |
| } else { |
| // auto notify this subscriber of the unsubscribe |
| Notification notification(subscriber, event, true); |
| mStateMachine->notifySubscribers(notification); |
| } |
| |
| // now check if there is any subscribers left |
| if (!mStateMachine->hasSubscribers()) { |
| // no more subscribers, move to RELEASED state |
| nextState = mReleasedState; |
| |
| // tell connecivity service we can release NIF |
| mStateMachine->sendRsrcRequest(GPS_RELEASE_AGPS_DATA_CONN); |
| } else if (!mStateMachine->hasActiveSubscribers()) { |
| // only inactive subscribers, move to RELEASING state |
| nextState = mReleasingState; |
| |
| // tell connecivity service we can release NIF |
| mStateMachine->sendRsrcRequest(GPS_RELEASE_AGPS_DATA_CONN); |
| } |
| } |
| break; |
| |
| case RSRC_GRANTED: |
| LOC_LOGW("%s: %d, RSRC_GRANTED already received", whoami(), event); |
| // no state change. |
| break; |
| |
| case RSRC_RELEASED: |
| { |
| LOC_LOGW("%s: %d, a force rsrc release", whoami(), event); |
| nextState = mReleasedState; |
| Notification notification(Notification::BROADCAST_ALL, event, true); |
| // by setting true, we remove subscribers from the linked list |
| mStateMachine->notifySubscribers(notification); |
| } |
| break; |
| |
| case RSRC_DENIED: |
| // no state change. |
| // we are expecting RELEASED. Handling DENIED |
| // may like break our state machine in race conditions. |
| break; |
| |
| default: |
| LOC_LOGE("%s: unrecognized event %d", whoami(), event); |
| // no state change. |
| } |
| |
| LOC_LOGD("onRsrcEvent, old state %s, new state %s, event %d", |
| whoami(), nextState->whoami(), event); |
| return nextState; |
| } |
| |
| // AgpsPendingState |
| class AgpsReleasingState : public AgpsState |
| { |
| friend class AgpsStateMachine; |
| |
| inline AgpsReleasingState(AgpsStateMachine* stateMachine) : |
| AgpsState(stateMachine) |
| { mReleasingState = this; } |
| |
| inline ~AgpsReleasingState() {} |
| public: |
| virtual AgpsState* onRsrcEvent(AgpsRsrcStatus event, void* data); |
| inline virtual char* whoami() {return (char*)"AgpsReleasingState";} |
| }; |
| |
| AgpsState* AgpsReleasingState::onRsrcEvent(AgpsRsrcStatus event, void* data) |
| { |
| AgpsState* nextState = this;; |
| LOC_LOGD("AgpsReleasingState::onRsrcEvent; event:%d\n", (int)event); |
| |
| switch (event) |
| { |
| case RSRC_SUBSCRIBE: |
| { |
| // already requested for NIF resource, |
| // do nothing until we get RSRC_GRANTED indication |
| // but we need to add subscriber to the list |
| mStateMachine->addSubscriber((Subscriber*)data); |
| // no state change. |
| } |
| break; |
| |
| case RSRC_UNSUBSCRIBE: |
| { |
| Subscriber* subscriber = (Subscriber*) data; |
| if (subscriber->waitForCloseComplete()) { |
| subscriber->setInactive(); |
| } else { |
| // auto notify this subscriber of the unsubscribe |
| Notification notification(subscriber, event, true); |
| mStateMachine->notifySubscribers(notification); |
| } |
| |
| // now check if there is any subscribers left |
| if (!mStateMachine->hasSubscribers()) { |
| // no more subscribers, move to RELEASED state |
| nextState = mReleasedState; |
| } |
| } |
| break; |
| |
| case RSRC_DENIED: |
| // A race condition subscriber unsubscribes before AFW denies resource. |
| case RSRC_RELEASED: |
| { |
| nextState = mAcquiredState; |
| Notification notification(Notification::BROADCAST_INACTIVE, event, true); |
| // notify all subscribers that are active NIF resource RELEASE |
| // by setting false, we keep subscribers on the linked list |
| mStateMachine->notifySubscribers(notification); |
| |
| if (mStateMachine->hasActiveSubscribers()) { |
| nextState = mPendingState; |
| // request from connecivity service for NIF |
| mStateMachine->sendRsrcRequest(GPS_REQUEST_AGPS_DATA_CONN); |
| } else { |
| nextState = mReleasedState; |
| } |
| } |
| break; |
| |
| case RSRC_GRANTED: |
| default: |
| LOC_LOGE("%s: unrecognized event %d", whoami(), event); |
| // no state change. |
| } |
| |
| LOC_LOGD("onRsrcEvent, old state %s, new state %s, event %d", |
| whoami(), nextState->whoami(), event); |
| return nextState; |
| } |
| //====================================================================== |
| //Servicer |
| //====================================================================== |
| Servicer* Servicer :: getServicer(servicerType type, void *cb_func) |
| { |
| LOC_LOGD(" Enter getServicer type:%d\n", (int)type); |
| switch(type) { |
| case servicerTypeNoCbParam: |
| return (new Servicer(cb_func)); |
| case servicerTypeExt: |
| return (new ExtServicer(cb_func)); |
| case servicerTypeAgps: |
| return (new AGpsServicer(cb_func)); |
| default: |
| return NULL; |
| } |
| } |
| |
| int Servicer :: requestRsrc(void *cb_data) |
| { |
| callback(); |
| return 0; |
| } |
| |
| int ExtServicer :: requestRsrc(void *cb_data) |
| { |
| int ret=-1; |
| LOC_LOGD("Enter ExtServicer :: requestRsrc\n"); |
| ret = callbackExt(cb_data); |
| LOC_LOGD("Exit ExtServicer :: requestRsrc\n"); |
| return(ret); |
| } |
| |
| int AGpsServicer :: requestRsrc(void *cb_data) |
| { |
| callbackAGps((AGpsStatus *)cb_data); |
| return 0; |
| } |
| |
| //====================================================================== |
| // AgpsStateMachine |
| //====================================================================== |
| |
| AgpsStateMachine::AgpsStateMachine(servicerType servType, |
| void *cb_func, |
| AGpsExtType type, |
| bool enforceSingleSubscriber) : |
| mStatePtr(new AgpsReleasedState(this)),mType(type), |
| mAPN(NULL), |
| mAPNLen(0), |
| mBearer(AGPS_APN_BEARER_INVALID), |
| mEnforceSingleSubscriber(enforceSingleSubscriber), |
| mServicer(Servicer :: getServicer(servType, (void *)cb_func)) |
| { |
| linked_list_init(&mSubscribers); |
| |
| // setting up mReleasedState |
| mStatePtr->mPendingState = new AgpsPendingState(this); |
| mStatePtr->mAcquiredState = new AgpsAcquiredState(this); |
| mStatePtr->mReleasingState = new AgpsReleasingState(this); |
| |
| // setting up mAcquiredState |
| mStatePtr->mAcquiredState->mReleasedState = mStatePtr; |
| mStatePtr->mAcquiredState->mPendingState = mStatePtr->mPendingState; |
| mStatePtr->mAcquiredState->mReleasingState = mStatePtr->mReleasingState; |
| |
| // setting up mPendingState |
| mStatePtr->mPendingState->mAcquiredState = mStatePtr->mAcquiredState; |
| mStatePtr->mPendingState->mReleasedState = mStatePtr; |
| mStatePtr->mPendingState->mReleasingState = mStatePtr->mReleasingState; |
| |
| // setting up mReleasingState |
| mStatePtr->mReleasingState->mReleasedState = mStatePtr; |
| mStatePtr->mReleasingState->mPendingState = mStatePtr->mPendingState; |
| mStatePtr->mReleasingState->mAcquiredState = mStatePtr->mAcquiredState; |
| } |
| |
| AgpsStateMachine::~AgpsStateMachine() |
| { |
| dropAllSubscribers(); |
| |
| // free the 3 states. We must read out all 3 pointers first. |
| // Otherwise we run the risk of getting pointers from already |
| // freed memory. |
| AgpsState* acquiredState = mStatePtr->mAcquiredState; |
| AgpsState* releasedState = mStatePtr->mReleasedState; |
| AgpsState* pendindState = mStatePtr->mPendingState; |
| AgpsState* releasingState = mStatePtr->mReleasingState; |
| |
| delete acquiredState; |
| delete releasedState; |
| delete pendindState; |
| delete releasingState; |
| delete mServicer; |
| linked_list_destroy(&mSubscribers); |
| |
| if (NULL != mAPN) { |
| delete[] mAPN; |
| mAPN = NULL; |
| } |
| } |
| |
| void AgpsStateMachine::setAPN(const char* apn, unsigned int len) |
| { |
| if (NULL != mAPN) { |
| delete mAPN; |
| } |
| |
| if (NULL != apn) { |
| mAPN = new char[len+1]; |
| memcpy(mAPN, apn, len); |
| mAPN[len] = '\0'; |
| |
| mAPNLen = len; |
| } else { |
| mAPN = NULL; |
| mAPNLen = 0; |
| } |
| } |
| |
| void AgpsStateMachine::onRsrcEvent(AgpsRsrcStatus event) |
| { |
| switch (event) |
| { |
| case RSRC_GRANTED: |
| case RSRC_RELEASED: |
| case RSRC_DENIED: |
| mStatePtr = mStatePtr->onRsrcEvent(event, NULL); |
| break; |
| default: |
| LOC_LOGW("AgpsStateMachine: unrecognized event %d", event); |
| break; |
| } |
| } |
| |
| void AgpsStateMachine::notifySubscribers(Notification& notification) const |
| { |
| if (notification.postNotifyDelete) { |
| // just any non NULL value to get started |
| Subscriber* s = (Subscriber*)~0; |
| while (NULL != s) { |
| s = NULL; |
| // if the last param sets to true, _search will delete |
| // the node from the list for us. But the problem is |
| // once that is done, _search returns, leaving the |
| // rest of the list unprocessed. So we need a loop. |
| linked_list_search(mSubscribers, (void**)&s, notifySubscriber, |
| (void*)¬ification, true); |
| delete s; |
| } |
| } else { |
| // no loop needed if it the last param sets to false, which |
| // mean nothing gets deleted from the list. |
| linked_list_search(mSubscribers, NULL, notifySubscriber, |
| (void*)¬ification, false); |
| } |
| } |
| |
| void AgpsStateMachine::addSubscriber(Subscriber* subscriber) const |
| { |
| Subscriber* s = NULL; |
| Notification notification((const Subscriber*)subscriber); |
| linked_list_search(mSubscribers, (void**)&s, |
| hasSubscriber, (void*)¬ification, false); |
| |
| if (NULL == s) { |
| linked_list_add(mSubscribers, subscriber->clone(), deleteObj); |
| } |
| } |
| |
| int AgpsStateMachine::sendRsrcRequest(AGpsStatusValue action) const |
| { |
| Subscriber* s = NULL; |
| Notification notification(Notification::BROADCAST_ACTIVE); |
| linked_list_search(mSubscribers, (void**)&s, hasSubscriber, |
| (void*)¬ification, false); |
| |
| if ((NULL == s) == (GPS_RELEASE_AGPS_DATA_CONN == action)) { |
| AGpsExtStatus nifRequest; |
| nifRequest.size = sizeof(nifRequest); |
| nifRequest.type = mType; |
| nifRequest.status = action; |
| |
| if (s == NULL) { |
| nifRequest.ipv4_addr = INADDR_NONE; |
| memset(&nifRequest.addr, 0, sizeof(nifRequest.addr)); |
| nifRequest.ssid[0] = '\0'; |
| nifRequest.password[0] = '\0'; |
| } else { |
| s->setIPAddresses(nifRequest.addr); |
| s->setWifiInfo(nifRequest.ssid, nifRequest.password); |
| } |
| |
| CALLBACK_LOG_CALLFLOW("agps_cb", %s, loc_get_agps_status_name(action)); |
| mServicer->requestRsrc((void *)&nifRequest); |
| } |
| return 0; |
| } |
| |
| void AgpsStateMachine::subscribeRsrc(Subscriber *subscriber) |
| { |
| if (mEnforceSingleSubscriber && hasSubscribers()) { |
| Notification notification(Notification::BROADCAST_ALL, RSRC_DENIED, true); |
| notifySubscriber(¬ification, subscriber); |
| } else { |
| mStatePtr = mStatePtr->onRsrcEvent(RSRC_SUBSCRIBE, (void*)subscriber); |
| } |
| } |
| |
| bool AgpsStateMachine::unsubscribeRsrc(Subscriber *subscriber) |
| { |
| Subscriber* s = NULL; |
| Notification notification((const Subscriber*)subscriber); |
| linked_list_search(mSubscribers, (void**)&s, |
| hasSubscriber, (void*)¬ification, false); |
| |
| if (NULL != s) { |
| mStatePtr = mStatePtr->onRsrcEvent(RSRC_UNSUBSCRIBE, (void*)s); |
| return true; |
| } |
| return false; |
| } |
| |
| bool AgpsStateMachine::hasActiveSubscribers() const |
| { |
| Subscriber* s = NULL; |
| Notification notification(Notification::BROADCAST_ACTIVE); |
| linked_list_search(mSubscribers, (void**)&s, |
| hasSubscriber, (void*)¬ification, false); |
| return NULL != s; |
| } |
| |
| //====================================================================== |
| // DSStateMachine |
| //====================================================================== |
| void delay_callback(void *callbackData, int result) |
| { |
| if(callbackData) { |
| DSStateMachine *DSSMInstance = (DSStateMachine *)callbackData; |
| DSSMInstance->retryCallback(); |
| } |
| else { |
| LOC_LOGE(" NULL argument received. Failing.\n"); |
| goto err; |
| } |
| err: |
| return; |
| } |
| |
| DSStateMachine :: DSStateMachine(servicerType type, void *cb_func, |
| LocEngAdapter* adapterHandle): |
| AgpsStateMachine(type, cb_func, AGPS_TYPE_INVALID,false), |
| mLocAdapter(adapterHandle) |
| { |
| LOC_LOGD("%s:%d]: New DSStateMachine\n", __func__, __LINE__); |
| mRetries = 0; |
| } |
| |
| void DSStateMachine :: retryCallback(void) |
| { |
| DSSubscriber *subscriber = NULL; |
| Notification notification(Notification::BROADCAST_ACTIVE); |
| linked_list_search(mSubscribers, (void**)&subscriber, hasSubscriber, |
| (void*)¬ification, false); |
| if(subscriber) |
| mLocAdapter->requestSuplES(subscriber->ID); |
| else |
| LOC_LOGE("DSStateMachine :: retryCallback: No subscriber found." \ |
| "Cannot retry data call\n"); |
| return; |
| } |
| |
| int DSStateMachine :: sendRsrcRequest(AGpsStatusValue action) const |
| { |
| DSSubscriber* s = NULL; |
| dsCbData cbData; |
| int ret=-1; |
| int connHandle=-1; |
| LOC_LOGD("Enter DSStateMachine :: sendRsrcRequest\n"); |
| Notification notification(Notification::BROADCAST_ACTIVE); |
| linked_list_search(mSubscribers, (void**)&s, hasSubscriber, |
| (void*)¬ification, false); |
| if(s) { |
| connHandle = s->ID; |
| LOC_LOGD("DSStateMachine :: sendRsrcRequest - subscriber found\n"); |
| } |
| else |
| LOC_LOGD("DSStateMachine :: sendRsrcRequest - No subscriber found\n"); |
| |
| cbData.action = action; |
| cbData.mAdapter = mLocAdapter; |
| ret = mServicer->requestRsrc((void *)&cbData); |
| //Only the request to start data call returns a success/failure |
| //The request to stop data call will always succeed |
| //Hence, the below block will only be executed when the |
| //request to start the data call fails |
| switch(ret) { |
| case LOC_API_ADAPTER_ERR_ENGINE_BUSY: |
| LOC_LOGD("DSStateMachine :: sendRsrcRequest - Failure returned: %d\n",ret); |
| ((DSStateMachine *)this)->incRetries(); |
| if(mRetries > MAX_START_DATA_CALL_RETRIES) { |
| LOC_LOGE(" Failed to start Data call. Fallback to normal ATL SUPL\n"); |
| informStatus(RSRC_DENIED, connHandle); |
| } |
| else { |
| if(loc_timer_start(DATA_CALL_RETRY_DELAY_MSEC, delay_callback, (void *)this)) { |
| LOC_LOGE("Error: Could not start delay thread\n"); |
| ret = -1; |
| goto err; |
| } |
| } |
| break; |
| case LOC_API_ADAPTER_ERR_UNSUPPORTED: |
| LOC_LOGE("No profile found for emergency call. Fallback to normal SUPL ATL\n"); |
| informStatus(RSRC_DENIED, connHandle); |
| break; |
| case LOC_API_ADAPTER_ERR_SUCCESS: |
| LOC_LOGD("%s:%d]: Request to start data call sent\n", __func__, __LINE__); |
| break; |
| case -1: |
| //One of the ways this case can be encountered is if the callback function |
| //receives a null argument, it just exits with -1 error |
| LOC_LOGE("Error: Something went wrong somewhere. Falling back to normal SUPL ATL\n"); |
| informStatus(RSRC_DENIED, connHandle); |
| break; |
| default: |
| LOC_LOGE("%s:%d]: Unrecognized return value\n", __func__, __LINE__); |
| } |
| err: |
| LOC_LOGD("EXIT DSStateMachine :: sendRsrcRequest; ret = %d\n", ret); |
| return ret; |
| } |
| |
| void DSStateMachine :: onRsrcEvent(AgpsRsrcStatus event) |
| { |
| void* currState = (void *)mStatePtr; |
| LOC_LOGD("Enter DSStateMachine :: onRsrcEvent. event = %d\n", (int)event); |
| switch (event) |
| { |
| case RSRC_GRANTED: |
| LOC_LOGD("DSStateMachine :: onRsrcEvent RSRC_GRANTED\n"); |
| mStatePtr = mStatePtr->onRsrcEvent(event, NULL); |
| break; |
| case RSRC_RELEASED: |
| LOC_LOGD("DSStateMachine :: onRsrcEvent RSRC_RELEASED\n"); |
| mStatePtr = mStatePtr->onRsrcEvent(event, NULL); |
| //To handle the case where we get a RSRC_RELEASED in |
| //pending state, we translate that to a RSRC_DENIED state |
| //since the callback from DSI is either RSRC_GRANTED or RSRC_RELEASED |
| //for when the call is connected or disconnected respectively. |
| if((void *)mStatePtr != currState) |
| break; |
| else { |
| event = RSRC_DENIED; |
| LOC_LOGE(" Switching event to RSRC_DENIED\n"); |
| } |
| case RSRC_DENIED: |
| mStatePtr = mStatePtr->onRsrcEvent(event, NULL); |
| break; |
| default: |
| LOC_LOGW("AgpsStateMachine: unrecognized event %d", event); |
| break; |
| } |
| LOC_LOGD("Exit DSStateMachine :: onRsrcEvent. event = %d\n", (int)event); |
| } |
| |
| void DSStateMachine :: informStatus(AgpsRsrcStatus status, int ID) const |
| { |
| LOC_LOGD("DSStateMachine :: informStatus. Status=%d\n",(int)status); |
| switch(status) { |
| case RSRC_UNSUBSCRIBE: |
| mLocAdapter->atlCloseStatus(ID, 1); |
| break; |
| case RSRC_RELEASED: |
| mLocAdapter->closeDataCall(); |
| break; |
| case RSRC_DENIED: |
| ((DSStateMachine *)this)->mRetries = 0; |
| mLocAdapter->requestATL(ID, AGPS_TYPE_SUPL); |
| break; |
| case RSRC_GRANTED: |
| mLocAdapter->atlOpenStatus(ID, 1, |
| NULL, |
| AGPS_APN_BEARER_INVALID, |
| AGPS_TYPE_INVALID); |
| break; |
| default: |
| LOC_LOGW("DSStateMachine :: informStatus - unknown status"); |
| } |
| return; |
| } |