/* 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 &notification)
{
    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 &notification)
{
    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 &notification)
{
    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 &notification)
{
    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 &notification)
{
    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] = NULL;

        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*)&notification, 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*)&notification, false);
    }
}

void AgpsStateMachine::addSubscriber(Subscriber* subscriber) const
{
    Subscriber* s = NULL;
    Notification notification((const Subscriber*)subscriber);
    linked_list_search(mSubscribers, (void**)&s,
                       hasSubscriber, (void*)&notification, 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*)&notification, 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(&notification, 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*)&notification, 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*)&notification, 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*)&notification, 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*)&notification, 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;
}
