/* Copyright (c) 2011-2014, 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_api_rpc"

#include <unistd.h>
#include <math.h>
#ifndef USE_GLIB
#include <utils/SystemClock.h>
#endif /* USE_GLIB */
#include <LocApiRpc.h>
#include <LocAdapterBase.h>
#include <loc_api_fixup.h>
#include <loc_api_rpc_glue.h>
#include <loc_log.h>
#include <loc_api_log.h>
#ifdef USE_GLIB
#include <glib.h>
#endif
#include <librpc.h>
#include <platform_lib_includes.h>

using namespace loc_core;

#define LOC_XTRA_INJECT_DEFAULT_TIMEOUT (3100)
#define XTRA_BLOCK_SIZE                 (3072)
#define LOC_IOCTL_DEFAULT_TIMEOUT 1000 // 1000 milli-seconds
#define LOC_NI_NOTIF_KEY_ADDRESS           "Address"

/*===========================================================================
FUNCTION    loc_event_cb

DESCRIPTION
   This is the callback function registered by loc_open.

DEPENDENCIES
   N/A

RETURN VALUE
   RPC_LOC_API_SUCCESS

SIDE EFFECTS
   N/A

===========================================================================*/
static int32 loc_event_cb
(
    void*                                user,
    rpc_loc_client_handle_type           client_handle,
    rpc_loc_event_mask_type              loc_event,
    const rpc_loc_event_payload_u_type*  loc_event_payload
)
{
    MODEM_LOG_CALLFLOW(%s, loc_get_event_name(loc_event));
    loc_callback_log(loc_event, loc_event_payload);
    int32 ret_val = ((LocApiRpc*)user)->locEventCB(client_handle, loc_event, loc_event_payload);
    EXIT_LOG(%d, ret_val);
    return ret_val;
}

/*===========================================================================
FUNCTION    loc_eng_rpc_global_cb

DESCRIPTION
   This is the callback function registered by loc_open for RPC global events

DEPENDENCIES
   N/A

RETURN VALUE
   RPC_LOC_API_SUCCESS

SIDE EFFECTS
   N/A

===========================================================================*/
static void loc_rpc_global_cb(void* user, CLIENT* clnt, enum rpc_reset_event event)
{
    MODEM_LOG_CALLFLOW(%s, loc_get_rpc_reset_event_name(event));
    ((LocApiRpc*)user)->locRpcGlobalCB(clnt, event);
    EXIT_LOG(%p, VOID_RET);
}

const LOC_API_ADAPTER_EVENT_MASK_T LocApiRpc::maskAll =
    LOC_API_ADAPTER_BIT_PARSED_POSITION_REPORT |
    LOC_API_ADAPTER_BIT_SATELLITE_REPORT |
    LOC_API_ADAPTER_BIT_LOCATION_SERVER_REQUEST |
    LOC_API_ADAPTER_BIT_ASSISTANCE_DATA_REQUEST |
    LOC_API_ADAPTER_BIT_IOCTL_REPORT |
    LOC_API_ADAPTER_BIT_STATUS_REPORT |
    LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT |
    LOC_API_ADAPTER_BIT_NI_NOTIFY_VERIFY_REQUEST;

const rpc_loc_event_mask_type LocApiRpc::locBits[] =
{
    RPC_LOC_EVENT_PARSED_POSITION_REPORT,
    RPC_LOC_EVENT_SATELLITE_REPORT,
    RPC_LOC_EVENT_NMEA_1HZ_REPORT,
    RPC_LOC_EVENT_NMEA_POSITION_REPORT,
    RPC_LOC_EVENT_NI_NOTIFY_VERIFY_REQUEST,
    RPC_LOC_EVENT_ASSISTANCE_DATA_REQUEST,
    RPC_LOC_EVENT_LOCATION_SERVER_REQUEST,
    RPC_LOC_EVENT_IOCTL_REPORT,
    RPC_LOC_EVENT_STATUS_REPORT,
    RPC_LOC_EVENT_WPS_NEEDED_REQUEST
};

LocApiRpc*
LocApiRpc::createLocApiRpc(const MsgTask* msgTask,
                     LOC_API_ADAPTER_EVENT_MASK_T exMask,
                     ContextBase* context)
{
    if (NULL == msgTask) {
        return NULL;
    }
    return new LocApiRpc(msgTask, exMask, context);
}

// constructor
LocApiRpc::LocApiRpc(const MsgTask* msgTask,
                     LOC_API_ADAPTER_EVENT_MASK_T exMask,
                     ContextBase* context) :
    LocApiBase(msgTask, exMask, context),
    client_handle(RPC_LOC_CLIENT_HANDLE_INVALID),
    dataEnableLastSet(-1)
{
    memset(apnLastSet, 0, sizeof(apnLastSet));
    loc_api_glue_init();
}

LocApiRpc::~LocApiRpc()
{
    close();
}

rpc_loc_event_mask_type
LocApiRpc::convertMask(LOC_API_ADAPTER_EVENT_MASK_T mask)
{
    rpc_loc_event_mask_type newMask = 0;

    for (unsigned int i = 0, bit=1; 0 != mask; i++, bit<<=1) {
        if (mask & bit) {
            newMask |= locBits[i];
            mask ^= bit;
        }
    }

    return newMask;
}

rpc_loc_lock_e_type
LocApiRpc::convertGpsLockMask(LOC_GPS_LOCK_MASK lockMask)
{
    if (isGpsLockAll(lockMask))
        return RPC_LOC_LOCK_ALL;
    if (isGpsLockMO(lockMask))
        return RPC_LOC_LOCK_MI;
    if (isGpsLockMT(lockMask))
        return RPC_LOC_LOCK_MT;
    if (isGpsLockNone(lockMask))
        return RPC_LOC_LOCK_NONE;
    return (rpc_loc_lock_e_type)lockMask;
}

enum loc_api_adapter_err
LocApiRpc::convertErr(int rpcErr)
{
    switch(rpcErr)
    {
    case RPC_LOC_API_SUCCESS:
        return LOC_API_ADAPTER_ERR_SUCCESS;
    case RPC_LOC_API_GENERAL_FAILURE:
        return LOC_API_ADAPTER_ERR_GENERAL_FAILURE;
    case RPC_LOC_API_UNSUPPORTED:
        return LOC_API_ADAPTER_ERR_UNSUPPORTED;
    case RPC_LOC_API_INVALID_HANDLE:
        return LOC_API_ADAPTER_ERR_INVALID_HANDLE;
    case RPC_LOC_API_INVALID_PARAMETER:
        return LOC_API_ADAPTER_ERR_INVALID_PARAMETER;
    case RPC_LOC_API_ENGINE_BUSY:
        return LOC_API_ADAPTER_ERR_ENGINE_BUSY;
    case RPC_LOC_API_PHONE_OFFLINE:
        return LOC_API_ADAPTER_ERR_PHONE_OFFLINE;
    case RPC_LOC_API_TIMEOUT:
        return LOC_API_ADAPTER_ERR_TIMEOUT;
    case RPC_LOC_API_RPC_MODEM_RESTART:
        return LOC_API_ADAPTER_ERR_ENGINE_DOWN;
    case RPC_LOC_API_RPC_FAILURE:
        return LOC_API_ADAPTER_ERR_FAILURE;
    default:
        return LOC_API_ADAPTER_ERR_UNKNOWN;
    }
}

void LocApiRpc::locRpcGlobalCB(CLIENT* clnt, enum rpc_reset_event event)
{
    static rpc_loc_engine_state_e_type last_state = RPC_LOC_ENGINE_STATE_MAX;

    switch (event) {
    case RPC_SUBSYSTEM_RESTART_BEGIN:
        if (RPC_LOC_ENGINE_STATE_OFF != last_state) {
            last_state = RPC_LOC_ENGINE_STATE_OFF;
            handleEngineDownEvent();
        }
        break;
    case RPC_SUBSYSTEM_RESTART_END:
        if (RPC_LOC_ENGINE_STATE_ON != last_state) {
            last_state = RPC_LOC_ENGINE_STATE_ON;
            handleEngineUpEvent();
        }
        break;
    }
}

int32 LocApiRpc::locEventCB(rpc_loc_client_handle_type client_handle,
                     rpc_loc_event_mask_type loc_event,
                     const rpc_loc_event_payload_u_type* loc_event_payload)
{
    // Parsed report
    if (loc_event & RPC_LOC_EVENT_PARSED_POSITION_REPORT)
    {
        reportPosition(&loc_event_payload->rpc_loc_event_payload_u_type_u.
                       parsed_location_report);
    }

    // Satellite report
    if (loc_event & RPC_LOC_EVENT_SATELLITE_REPORT)
    {
        reportSv(&loc_event_payload->rpc_loc_event_payload_u_type_u.gnss_report);
    }

    // Status report
    if (loc_event & RPC_LOC_EVENT_STATUS_REPORT)
    {
        reportStatus(&loc_event_payload->rpc_loc_event_payload_u_type_u.status_report);
    }

    // NMEA
    if (loc_event & RPC_LOC_EVENT_NMEA_1HZ_REPORT)
    {
        reportNmea(&(loc_event_payload->rpc_loc_event_payload_u_type_u.nmea_report));
    }
    // XTRA support: supports only XTRA download
    if (loc_event & RPC_LOC_EVENT_ASSISTANCE_DATA_REQUEST)
    {
        if (loc_event_payload->rpc_loc_event_payload_u_type_u.assist_data_request.event ==
            RPC_LOC_ASSIST_DATA_PREDICTED_ORBITS_REQ)
        {
            requestXtraData();
        } else if (loc_event_payload->rpc_loc_event_payload_u_type_u.assist_data_request.event ==
                   RPC_LOC_ASSIST_DATA_TIME_REQ)
        {
            requestTime();
        } else if (loc_event_payload->rpc_loc_event_payload_u_type_u.assist_data_request.event ==
                   RPC_LOC_ASSIST_DATA_POSITION_INJECTION_REQ)
        {
            requestLocation();
        }
    }

    // AGPS data request
    if (loc_event & RPC_LOC_EVENT_LOCATION_SERVER_REQUEST)
    {
        ATLEvent(&loc_event_payload->rpc_loc_event_payload_u_type_u.
                 loc_server_request);
    }

    // NI notify request
    if (loc_event & RPC_LOC_EVENT_NI_NOTIFY_VERIFY_REQUEST)
    {
        NIEvent(&loc_event_payload->rpc_loc_event_payload_u_type_u.ni_request);
    }

    return RPC_LOC_API_SUCCESS;//We simply want to return sucess here as we do not want to
    // cause any issues in RPC thread context
}

enum loc_api_adapter_err
LocApiRpc::open(LOC_API_ADAPTER_EVENT_MASK_T mask)
{
    enum loc_api_adapter_err ret_val = LOC_API_ADAPTER_ERR_SUCCESS;

    // RPC does not dynamically update the event mask. And in the
    // case of RPC, all we support are positioning (gps + agps)
    // masks anyways, so we simply mask all of them on always.
    // After doing so the first time in a power cycle, we know there
    // will the following if condition will never be true any more.
    mask = maskAll;

    if (mask != mMask) {
        if (RPC_LOC_CLIENT_HANDLE_INVALID != client_handle) {
            close();
        }

        mMask = mask;
        // it is important to cap the mask here, because not all LocApi's
        // can enable the same bits, e.g. foreground and bckground.
        client_handle = loc_open(convertMask(mask),
                                 loc_event_cb,
                                 loc_rpc_global_cb, this);

        if (client_handle < 0) {
            mMask = 0;
            client_handle = RPC_LOC_CLIENT_HANDLE_INVALID;
            ret_val = LOC_API_ADAPTER_ERR_INVALID_HANDLE;
        }
    }

    return ret_val;
}

enum loc_api_adapter_err
LocApiRpc::close()
{
    if (RPC_LOC_CLIENT_HANDLE_INVALID != client_handle) {
        loc_clear(client_handle);
    }

    loc_close(client_handle);
    mMask = 0;
    client_handle = RPC_LOC_CLIENT_HANDLE_INVALID;

    return LOC_API_ADAPTER_ERR_SUCCESS;
}

enum loc_api_adapter_err
LocApiRpc::startFix(const LocPosMode& posMode) {
   LOC_LOGD("LocApiRpc::startFix() called");
   return convertErr(
       loc_start_fix(client_handle)
       );
}

enum loc_api_adapter_err
LocApiRpc::stopFix() {
   LOC_LOGD("LocApiRpc::stopFix() called");
   return convertErr(
       loc_stop_fix(client_handle)
       );
}

enum loc_api_adapter_err
LocApiRpc::setPositionMode(const LocPosMode& posMode)
{
    rpc_loc_ioctl_data_u_type    ioctl_data;
    rpc_loc_fix_criteria_s_type *fix_criteria_ptr =
        &ioctl_data.rpc_loc_ioctl_data_u_type_u.fix_criteria;
    rpc_loc_ioctl_e_type         ioctl_type = RPC_LOC_IOCTL_SET_FIX_CRITERIA;
    rpc_loc_operation_mode_e_type op_mode;
    int                          ret_val;
    const LocPosMode* fixCriteria = &posMode;

    ALOGD ("loc_eng_set_position mode, client = %d, interval = %d, mode = %d\n",
          (int32) client_handle, fixCriteria->min_interval, fixCriteria->mode);

    switch (fixCriteria->mode)
    {
    case LOC_POSITION_MODE_MS_BASED:
        op_mode = RPC_LOC_OPER_MODE_MSB;
        break;
    case LOC_POSITION_MODE_MS_ASSISTED:
        op_mode = RPC_LOC_OPER_MODE_MSA;
        break;
    case LOC_POSITION_MODE_RESERVED_1:
        op_mode = RPC_LOC_OPER_MODE_SPEED_OPTIMAL;
        break;
    case LOC_POSITION_MODE_RESERVED_2:
        op_mode = RPC_LOC_OPER_MODE_ACCURACY_OPTIMAL;
        break;
    case LOC_POSITION_MODE_RESERVED_3:
        op_mode = RPC_LOC_OPER_MODE_DATA_OPTIMAL;
        break;
    case LOC_POSITION_MODE_RESERVED_4:
    case LOC_POSITION_MODE_RESERVED_5:
        op_mode = RPC_LOC_OPER_MODE_MSA;
        fix_criteria_ptr->preferred_response_time  = 0;
        break;
    default:
        op_mode = RPC_LOC_OPER_MODE_STANDALONE;
    }

    fix_criteria_ptr->valid_mask = RPC_LOC_FIX_CRIT_VALID_PREFERRED_OPERATION_MODE |
                                   RPC_LOC_FIX_CRIT_VALID_RECURRENCE_TYPE;
    fix_criteria_ptr->min_interval = fixCriteria->min_interval;
    fix_criteria_ptr->preferred_operation_mode = op_mode;

    fix_criteria_ptr->min_interval = fixCriteria->min_interval;
    fix_criteria_ptr->valid_mask |= RPC_LOC_FIX_CRIT_VALID_MIN_INTERVAL;

    if (fixCriteria->preferred_accuracy > 0) {
        fix_criteria_ptr->preferred_accuracy = fixCriteria->preferred_accuracy;
        fix_criteria_ptr->valid_mask |= RPC_LOC_FIX_CRIT_VALID_PREFERRED_ACCURACY;
    }
    if (fixCriteria->preferred_time > 0) {
        fix_criteria_ptr->preferred_response_time = fixCriteria->preferred_time;
        fix_criteria_ptr->valid_mask |= RPC_LOC_FIX_CRIT_VALID_PREFERRED_RESPONSE_TIME;
    }

    switch (fixCriteria->recurrence) {
    case LOC_GPS_POSITION_RECURRENCE_SINGLE:
        fix_criteria_ptr->recurrence_type = RPC_LOC_SINGLE_FIX;
        break;
    case LOC_GPS_POSITION_RECURRENCE_PERIODIC:
    default:
        fix_criteria_ptr->recurrence_type = RPC_LOC_PERIODIC_FIX;
        break;
    }
    ioctl_data.disc = ioctl_type;

    ret_val = loc_eng_ioctl (client_handle,
                             ioctl_type,
                             &ioctl_data,
                             LOC_IOCTL_DEFAULT_TIMEOUT,
                             NULL /* No output information is expected*/);

    return convertErr(ret_val);
}

enum loc_api_adapter_err
LocApiRpc::setTime(LocGpsUtcTime time, int64_t timeReference, int uncertainty)
{
    rpc_loc_ioctl_data_u_type        ioctl_data;
    rpc_loc_assist_data_time_s_type *time_info_ptr;
    rpc_loc_ioctl_e_type             ioctl_type = RPC_LOC_IOCTL_INJECT_UTC_TIME;
    int                              ret_val;

    LOC_LOGD ("loc_eng_inject_time, uncertainty = %d\n", uncertainty);

    time_info_ptr = &ioctl_data.rpc_loc_ioctl_data_u_type_u.assistance_data_time;
    time_info_ptr->time_utc = time;
    time_info_ptr->time_utc += (int64_t)(ELAPSED_MILLIS_SINCE_BOOT_PLATFORM_LIB_ABSTRACTION - timeReference);
    time_info_ptr->uncertainty = uncertainty; // Uncertainty in ms

    ioctl_data.disc = ioctl_type;

    ret_val = loc_eng_ioctl (client_handle,
                             ioctl_type,
                             &ioctl_data,
                             LOC_IOCTL_DEFAULT_TIMEOUT,
                             NULL /* No output information is expected*/);

    return convertErr(ret_val);
}

enum loc_api_adapter_err
LocApiRpc::injectPosition(double latitude, double longitude, float accuracy)
{
    /* IOCTL data */
    rpc_loc_ioctl_data_u_type ioctl_data;
    rpc_loc_assist_data_pos_s_type *assistance_data_position =
        &ioctl_data.rpc_loc_ioctl_data_u_type_u.assistance_data_position;
    int                          ret_val;

    /************************************************
     * Fill in latitude, longitude & accuracy
     ************************************************/

    /* This combo is required */
    assistance_data_position->valid_mask =
        RPC_LOC_ASSIST_POS_VALID_LATITUDE |
        RPC_LOC_ASSIST_POS_VALID_LONGITUDE |
        RPC_LOC_ASSIST_POS_VALID_HOR_UNC_CIRCULAR |
        RPC_LOC_ASSIST_POS_VALID_CONFIDENCE_HORIZONTAL;

    assistance_data_position->latitude = latitude;
    assistance_data_position->longitude = longitude;
    assistance_data_position->hor_unc_circular = accuracy; /* Meters assumed */
    assistance_data_position->confidence_horizontal = 63;  /* 63% (1 std dev) assumed */

    /* Log */
    LOC_LOGD("Inject coarse position Lat=%lf, Lon=%lf, Acc=%.2lf\n",
             (double) assistance_data_position->latitude,
             (double) assistance_data_position->longitude,
             (double) assistance_data_position->hor_unc_circular);

    ret_val = loc_eng_ioctl( client_handle,
                             RPC_LOC_IOCTL_INJECT_POSITION,
                             &ioctl_data,
                             LOC_IOCTL_DEFAULT_TIMEOUT,
                             NULL /* No output information is expected*/);
    return convertErr(ret_val);
}

enum loc_api_adapter_err
LocApiRpc::informNiResponse(LocGpsUserResponseType userResponse,
                                   const void* passThroughData)
{
    rpc_loc_ioctl_data_u_type data;
    rpc_loc_ioctl_callback_s_type callback_payload;

    memcpy(&data.rpc_loc_ioctl_data_u_type_u.user_verify_resp.ni_event_pass_back,
           passThroughData, sizeof (rpc_loc_ni_event_s_type));

    rpc_loc_ni_user_resp_e_type resp;
    switch (userResponse)
    {
    case LOC_GPS_NI_RESPONSE_ACCEPT:
        data.rpc_loc_ioctl_data_u_type_u.user_verify_resp.user_resp =
            RPC_LOC_NI_LCS_NOTIFY_VERIFY_ACCEPT;
        break;
    case LOC_GPS_NI_RESPONSE_DENY:
        data.rpc_loc_ioctl_data_u_type_u.user_verify_resp.user_resp =
            RPC_LOC_NI_LCS_NOTIFY_VERIFY_DENY;
        break;
    case LOC_GPS_NI_RESPONSE_NORESP:
    default:
        data.rpc_loc_ioctl_data_u_type_u.user_verify_resp.user_resp =
            RPC_LOC_NI_LCS_NOTIFY_VERIFY_NORESP;
        break;
    }

    return convertErr(
        loc_eng_ioctl(client_handle,
                      RPC_LOC_IOCTL_INFORM_NI_USER_RESPONSE,
                      &data,
                      LOC_IOCTL_DEFAULT_TIMEOUT,
                      &callback_payload)
        );
}

enum loc_api_adapter_err
LocApiRpc::setAPN(char* apn, int len, boolean force)
{
    enum loc_api_adapter_err rtv = LOC_API_ADAPTER_ERR_SUCCESS;
    int size = sizeof(apnLastSet);
    if (force || memcmp(apnLastSet, apn, size)) {
        if (len < size) {
            // size will be not larger than its original value
            size = len + 1;
        }
        memcpy(apnLastSet, apn, size);

        if (!isInSession()) {
            rpc_loc_ioctl_data_u_type ioctl_data = {RPC_LOC_IOCTL_SET_LBS_APN_PROFILE, {0}};
            ioctl_data.rpc_loc_ioctl_data_u_type_u.apn_profiles[0].srv_system_type = LOC_APN_PROFILE_SRV_SYS_MAX;
            ioctl_data.rpc_loc_ioctl_data_u_type_u.apn_profiles[0].pdp_type = LOC_APN_PROFILE_PDN_TYPE_IPV4;
            memcpy(&(ioctl_data.rpc_loc_ioctl_data_u_type_u.apn_profiles[0].apn_name), apn, size);

            rtv = convertErr(
                loc_eng_ioctl (client_handle,
                               RPC_LOC_IOCTL_SET_LBS_APN_PROFILE,
                               &ioctl_data,
                               LOC_IOCTL_DEFAULT_TIMEOUT,
                               NULL)
                );
        }
    }
    return rtv;
}

void LocApiRpc::setInSession(bool inSession)
{
    if (!inSession) {
        enableData(dataEnableLastSet, true);
        setAPN(apnLastSet, sizeof(apnLastSet)-1, true);
    }
}

enum loc_api_adapter_err
LocApiRpc::setServer(const char* url, int len)
{
    rpc_loc_ioctl_data_u_type         ioctl_data;
    rpc_loc_server_info_s_type       *server_info_ptr;
    rpc_loc_ioctl_e_type              ioctl_cmd;

    ioctl_cmd = RPC_LOC_IOCTL_SET_UMTS_SLP_SERVER_ADDR;
    ioctl_data.disc = ioctl_cmd;
    server_info_ptr = &ioctl_data.rpc_loc_ioctl_data_u_type_u.server_addr;
    server_info_ptr->addr_type = RPC_LOC_SERVER_ADDR_URL;
    server_info_ptr->addr_info.disc = server_info_ptr->addr_type;
    server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.url.length = len;
#if (AMSS_VERSION==3200)
    server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.url.addr.addr_val = (char*) url;
    server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.url.addr.addr_len= len;
#else
    strlcpy(server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.url.addr, url,
            sizeof server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.url.addr);
#endif /* #if (AMSS_VERSION==3200) */
    LOC_LOGD ("loc_eng_set_server, addr = %s\n", url);

    return convertErr(
        loc_eng_ioctl (client_handle,
                       ioctl_cmd,
                       &ioctl_data,
                       LOC_IOCTL_DEFAULT_TIMEOUT,
                       NULL /* No output information is expected*/)
        );
}

enum loc_api_adapter_err
LocApiRpc::setServer(unsigned int ip, int port, LocServerType type)
{
    rpc_loc_ioctl_data_u_type         ioctl_data;
    rpc_loc_server_info_s_type       *server_info_ptr;
    rpc_loc_ioctl_e_type              ioctl_cmd;

    switch (type) {
    case LOC_AGPS_MPC_SERVER:
        ioctl_cmd = RPC_LOC_IOCTL_SET_CDMA_MPC_SERVER_ADDR;
        break;
    case LOC_AGPS_CUSTOM_PDE_SERVER:
        ioctl_cmd = RPC_LOC_IOCTL_SET_CUSTOM_PDE_SERVER_ADDR;
        break;
    default:
        ioctl_cmd = RPC_LOC_IOCTL_SET_CDMA_PDE_SERVER_ADDR;
        break;
    }
    ioctl_data.disc = ioctl_cmd;
    server_info_ptr = &ioctl_data.rpc_loc_ioctl_data_u_type_u.server_addr;
    server_info_ptr->addr_type = RPC_LOC_SERVER_ADDR_IPV4;
    server_info_ptr->addr_info.disc = server_info_ptr->addr_type;
    server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.ipv4.addr = ip;
    server_info_ptr->addr_info.rpc_loc_server_addr_u_type_u.ipv4.port = port;
    LOC_LOGD ("setServer, addr = %X:%d\n", (unsigned int) ip, (unsigned int) port);

    return convertErr(
        loc_eng_ioctl (client_handle,
                       ioctl_cmd,
                       &ioctl_data,
                       LOC_IOCTL_DEFAULT_TIMEOUT,
                       NULL /* No output information is expected*/)
        );
}

enum loc_api_adapter_err
LocApiRpc::enableData(int enable, boolean force)
{
    enum loc_api_adapter_err rtv = LOC_API_ADAPTER_ERR_SUCCESS;
    if (force || dataEnableLastSet != enable) {
        dataEnableLastSet = enable;

        if (!isInSession()) {
            rpc_loc_ioctl_data_u_type ioctl_data = {RPC_LOC_IOCTL_SET_DATA_ENABLE, {0}};

            ioctl_data.rpc_loc_ioctl_data_u_type_u.data_enable = enable;
            rtv =  convertErr(
                loc_eng_ioctl (client_handle,
                               RPC_LOC_IOCTL_SET_DATA_ENABLE,
                               &ioctl_data,
                               LOC_IOCTL_DEFAULT_TIMEOUT,
                               NULL)
                );
        }
    }
    return rtv;
}

enum loc_api_adapter_err
LocApiRpc::deleteAidingData(LocGpsAidingData bits)
{
    rpc_loc_ioctl_data_u_type ioctl_data = {RPC_LOC_IOCTL_DELETE_ASSIST_DATA, {0}};
    ioctl_data.rpc_loc_ioctl_data_u_type_u.assist_data_delete.type = bits;

    return convertErr(
        loc_eng_ioctl (client_handle,
                       RPC_LOC_IOCTL_DELETE_ASSIST_DATA,
                       &ioctl_data,
                       LOC_IOCTL_DEFAULT_TIMEOUT,
                       NULL)
        );
}

void LocApiRpc::reportPosition(const rpc_loc_parsed_position_s_type *location_report_ptr)
{
    LocPosTechMask tech_Mask = LOC_POS_TECH_MASK_DEFAULT;

    UlpLocation location = {0};
    GpsLocationExtended locationExtended = {0};

    location.size = sizeof(location);
    locationExtended.size = sizeof(locationExtended);
    if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_SESSION_STATUS)
    {
        // Process the position from final and intermediate reports
        if (location_report_ptr->session_status == RPC_LOC_SESS_STATUS_SUCCESS ||
            location_report_ptr->session_status == RPC_LOC_SESS_STATUS_IN_PROGESS)
        {
            // Latitude & Longitude
            if ((location_report_ptr->valid_mask & RPC_LOC_POS_VALID_LATITUDE) &&
                (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_LONGITUDE) &&
                (location_report_ptr->latitude != 0 ||
                 location_report_ptr->longitude != 0))
            {
                location.gpsLocation.flags    |= LOC_GPS_LOCATION_HAS_LAT_LONG;
                location.gpsLocation.latitude  = location_report_ptr->latitude;
                location.gpsLocation.longitude = location_report_ptr->longitude;

                // Time stamp (UTC)
                if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_TIMESTAMP_UTC)
                {
                    location.gpsLocation.timestamp = location_report_ptr->timestamp_utc;
                }

                // Altitude
                if (location_report_ptr->valid_mask &  RPC_LOC_POS_VALID_ALTITUDE_WRT_ELLIPSOID )
                {
                    location.gpsLocation.flags    |= LOC_GPS_LOCATION_HAS_ALTITUDE;
                    location.gpsLocation.altitude = location_report_ptr->altitude_wrt_ellipsoid;
                }

                // Speed
                if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_SPEED_HORIZONTAL)
                {
                    location.gpsLocation.flags    |= LOC_GPS_LOCATION_HAS_SPEED;
                    location.gpsLocation.speed = location_report_ptr->speed_horizontal;
                }

                // Heading
                if (location_report_ptr->valid_mask &  RPC_LOC_POS_VALID_HEADING)
                {
                    location.gpsLocation.flags    |= LOC_GPS_LOCATION_HAS_BEARING;
                    location.gpsLocation.bearing = location_report_ptr->heading;
                }

                // Uncertainty (circular)
                if ( (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_HOR_UNC_CIRCULAR) )
                {
                    location.gpsLocation.flags    |= LOC_GPS_LOCATION_HAS_ACCURACY;
                    location.gpsLocation.accuracy = location_report_ptr->hor_unc_circular;
                }

                // Technology Mask

                tech_Mask  |= location_report_ptr->technology_mask;
                //Mark the location source as from GNSS
                location.gpsLocation.flags |= LOCATION_HAS_SOURCE_INFO;
                location.position_source = ULP_LOCATION_IS_FROM_GNSS;
                if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_ALTITUDE_WRT_MEAN_SEA_LEVEL)
                {
                    locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL;
                    locationExtended.altitudeMeanSeaLevel = location_report_ptr->altitude_wrt_mean_sea_level;
                }

                if (location_report_ptr->valid_mask &  RPC_LOC_POS_VALID_MAGNETIC_VARIATION )
                {
                    locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_MAG_DEV;
                    locationExtended.magneticDeviation = location_report_ptr->magnetic_deviation;
                }

                if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_VERTICAL_UNC)
                {
                   locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_VERT_UNC;
                   locationExtended.vert_unc = location_report_ptr->vert_unc;
                }

                if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_SPEED_UNC)
                {
                   locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_SPEED_UNC;
                   locationExtended.speed_unc = location_report_ptr->speed_unc;
                }

                LOC_LOGV("reportPosition: fire callback\n");
                enum loc_sess_status fixStatus =
                    (location_report_ptr->session_status
                     == RPC_LOC_SESS_STATUS_IN_PROGESS ?
                     LOC_SESS_INTERMEDIATE : LOC_SESS_SUCCESS);
                LocApiBase::reportPosition(location,
                                           locationExtended,
                                           (void*)location_report_ptr,
                                           fixStatus,
                                           tech_Mask);
            }
        }
        else
        {
            LocApiBase::reportPosition(location,
                                       locationExtended,
                                       NULL,
                                       LOC_SESS_FAILURE);
            LOC_LOGV("loc_eng_report_position: ignore position report "
                     "when session status = %d\n",
                     location_report_ptr->session_status);
        }
    }
    else
    {
        LOC_LOGV("loc_eng_report_position: ignore position report "
                 "when session status is not set\n");
    }
}

void LocApiRpc::reportSv(const rpc_loc_gnss_info_s_type *gnss_report_ptr)
{
    QtiGnssSvStatus     SvStatus = {0};
    GpsLocationExtended locationExtended = {0};
    locationExtended.size = sizeof(locationExtended);
    int             num_svs_max = 0;
    const rpc_loc_sv_info_s_type *sv_info_ptr;

    if (gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_SV_COUNT)
    {
        num_svs_max = gnss_report_ptr->sv_count;
        if (num_svs_max > LOC_GPS_MAX_SVS)
        {
            num_svs_max = LOC_GPS_MAX_SVS;
        }
    }

    if (gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_SV_LIST)
    {
        SvStatus.num_svs = 0;

        for (int i = 0; i < num_svs_max; i++)
        {
            sv_info_ptr = &(gnss_report_ptr->sv_list.sv_list_val[i]);
            if (sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_SYSTEM)
            {
                if (sv_info_ptr->system == RPC_LOC_SV_SYSTEM_GPS)
                {
                    SvStatus.sv_list[SvStatus.num_svs].size = sizeof(LocGpsSvInfo);
                    SvStatus.sv_list[SvStatus.num_svs].prn = sv_info_ptr->prn;

                    // We only have the data field to report gps eph and alm mask
                    if ((sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_HAS_EPH) &&
                        (sv_info_ptr->has_eph == 1))
                    {
                        SvStatus.ephemeris_mask |= (1 << (sv_info_ptr->prn-1));
                    }

                    if ((sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_HAS_ALM) &&
                        (sv_info_ptr->has_alm == 1))
                    {
                        SvStatus.almanac_mask |= (1 << (sv_info_ptr->prn-1));
                    }

                    if ((sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_PROCESS_STATUS) &&
                        (sv_info_ptr->process_status == RPC_LOC_SV_STATUS_TRACK))
                    {
                        SvStatus.gps_used_in_fix_mask |= (1 << (sv_info_ptr->prn-1));
                    }
                }
                // SBAS: GPS RPN: 120-151,
                // In exteneded measurement report, we follow nmea standard, which is from 33-64.
                else if (sv_info_ptr->system == RPC_LOC_SV_SYSTEM_SBAS)
                {
                    SvStatus.sv_list[SvStatus.num_svs].prn = sv_info_ptr->prn + 33 - 120;
                }
                // Gloness: Slot id: 1-32
                // In extended measurement report, we follow nmea standard, which is 65-96
                else if (sv_info_ptr->system == RPC_LOC_SV_SYSTEM_GLONASS)
                {
                    if ((sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_PROCESS_STATUS) &&
                        (sv_info_ptr->process_status == RPC_LOC_SV_STATUS_TRACK))
                    {
                        SvStatus.glo_used_in_fix_mask |= (1 << (sv_info_ptr->prn-1));
                    }

                    SvStatus.sv_list[SvStatus.num_svs].prn = sv_info_ptr->prn + (65-1);
                }
                // Unsupported SV system
                else
                {
                    continue;
                }
            }

            if (sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_SNR)
            {
                SvStatus.sv_list[SvStatus.num_svs].snr = sv_info_ptr->snr;
            }

            if (sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_ELEVATION)
            {
                SvStatus.sv_list[SvStatus.num_svs].elevation = sv_info_ptr->elevation;
            }

            if (sv_info_ptr->valid_mask & RPC_LOC_SV_INFO_VALID_AZIMUTH)
            {
                SvStatus.sv_list[SvStatus.num_svs].azimuth = sv_info_ptr->azimuth;
            }

            SvStatus.num_svs++;
        }
    }

    if ((gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_POS_DOP) &&
        (gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_HOR_DOP) &&
        (gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_VERT_DOP))
    {
        locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_DOP;
        locationExtended.pdop = gnss_report_ptr->position_dop;
        locationExtended.hdop = gnss_report_ptr->horizontal_dop;
        locationExtended.vdop = gnss_report_ptr->vertical_dop;
    }

    if (SvStatus.num_svs >= 0)
    {
        LocApiBase::reportSv(SvStatus,
                             locationExtended,
                             (void*)gnss_report_ptr);
    }
}

void LocApiRpc::reportStatus(const rpc_loc_status_event_s_type *status_report_ptr)
{

    if (status_report_ptr->event == RPC_LOC_STATUS_EVENT_ENGINE_STATE) {
        if (status_report_ptr->payload.rpc_loc_status_event_payload_u_type_u.engine_state == RPC_LOC_ENGINE_STATE_ON)
        {
            LocApiBase::reportStatus(LOC_GPS_STATUS_ENGINE_ON);
            LocApiBase::reportStatus(LOC_GPS_STATUS_SESSION_BEGIN);
        }
        else if (status_report_ptr->payload.rpc_loc_status_event_payload_u_type_u.engine_state == RPC_LOC_ENGINE_STATE_OFF)
        {
            LocApiBase::reportStatus(LOC_GPS_STATUS_SESSION_END);
            LocApiBase::reportStatus(LOC_GPS_STATUS_ENGINE_OFF);
        }
        else
        {
            LocApiBase::reportStatus(LOC_GPS_STATUS_NONE);
        }
    }

}

void LocApiRpc::reportNmea(const rpc_loc_nmea_report_s_type *nmea_report_ptr)
{

#if (AMSS_VERSION==3200)
    LocApiBase::reportNmea(nmea_report_ptr->nmea_sentences.nmea_sentences_val,
                           nmea_report_ptr->nmea_sentences.nmea_sentences_len);
#else
    LocApiBase::reportNmea(nmea_report_ptr->nmea_sentences,
                           nmea_report_ptr->length);
    LOC_LOGD("loc_eng_report_nmea: $%c%c%c\n",
             nmea_report_ptr->nmea_sentences[3],
             nmea_report_ptr->nmea_sentences[4],
             nmea_report_ptr->nmea_sentences[5]);
#endif /* #if (AMSS_VERSION==3200) */
}

enum loc_api_adapter_err
LocApiRpc::setXtraData(char* data, int length)
{
    int     rpc_ret_val = RPC_LOC_API_GENERAL_FAILURE;
    int     total_parts;
    uint8   part;
    uint16  part_len;
    uint16  len_injected;
    rpc_loc_ioctl_data_u_type            ioctl_data;
    rpc_loc_ioctl_e_type                 ioctl_type = RPC_LOC_IOCTL_INJECT_PREDICTED_ORBITS_DATA;
    rpc_loc_predicted_orbits_data_s_type *predicted_orbits_data_ptr;

    LOC_LOGD("qct_loc_eng_inject_xtra_data, xtra size = %d, data ptr = 0x%lx\n", length, (long) data);

    predicted_orbits_data_ptr = &ioctl_data.rpc_loc_ioctl_data_u_type_u.predicted_orbits_data;
    predicted_orbits_data_ptr->format_type = RPC_LOC_PREDICTED_ORBITS_XTRA;
    predicted_orbits_data_ptr->total_size = length;
    total_parts = (length - 1) / XTRA_BLOCK_SIZE + 1;
    predicted_orbits_data_ptr->total_parts = total_parts;

    len_injected = 0; // O bytes injected
    ioctl_data.disc = ioctl_type;

    // XTRA injection starts with part 1
    for (part = 1; part <= total_parts; part++)
    {
        predicted_orbits_data_ptr->part = part;
        predicted_orbits_data_ptr->part_len = XTRA_BLOCK_SIZE;
        if (XTRA_BLOCK_SIZE > (length - len_injected))
        {
            predicted_orbits_data_ptr->part_len = length - len_injected;
        }
        predicted_orbits_data_ptr->data_ptr.data_ptr_len = predicted_orbits_data_ptr->part_len;
        predicted_orbits_data_ptr->data_ptr.data_ptr_val = data + len_injected;

        LOC_LOGD("qct_loc_eng_inject_xtra_data, part %d/%d, len = %d, total = %d\n",
                 predicted_orbits_data_ptr->part,
                 total_parts,
                 predicted_orbits_data_ptr->part_len,
                 len_injected);

        if (part < total_parts)
        {
            // No callback in this case
            rpc_ret_val = loc_ioctl (client_handle,
                                     ioctl_type,
                                     &ioctl_data);

            if (rpc_ret_val != RPC_LOC_API_SUCCESS)
            {
                LOC_LOGE("loc_ioctl for xtra error: %s\n", loc_get_ioctl_status_name(rpc_ret_val));
                break;
            }
            //Add a delay of 10 ms so that repeated RPC calls dont starve the modem processor
            usleep(10 * 1000);
        }
        else // part == total_parts
        {
            // Last part injection, will need to wait for callback
            if (!loc_eng_ioctl(client_handle,
                               ioctl_type,
                               &ioctl_data,
                               LOC_XTRA_INJECT_DEFAULT_TIMEOUT,
                               NULL))
            {
                rpc_ret_val = RPC_LOC_API_GENERAL_FAILURE;
            }
            break; // done with injection
        }

        len_injected += predicted_orbits_data_ptr->part_len;
        LOC_LOGD("loc_ioctl XTRA injected length: %d\n", len_injected);
    }

    return convertErr(rpc_ret_val);
}

/* Request the Xtra Server Url from the modem */
enum loc_api_adapter_err
LocApiRpc::requestXtraServer()
{
    loc_api_adapter_err           err;
    rpc_loc_ioctl_data_u_type     data;
    rpc_loc_ioctl_callback_s_type callback_data;

    err = convertErr(loc_eng_ioctl(client_handle,
                                   RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE,
                                   &data,
                                   LOC_IOCTL_DEFAULT_TIMEOUT,
                                   &callback_data));

    if (LOC_API_ADAPTER_ERR_SUCCESS != err)
    {
        LOC_LOGE("RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE failed!: err=%d\n", err);
        return err;
    }
    else if (RPC_LOC_SESS_STATUS_SUCCESS != callback_data.status)
    {
        LOC_LOGE("RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE failed!: status=%ld\n", callback_data.status);
        return LOC_API_ADAPTER_ERR_GENERAL_FAILURE;
    }
    else if (RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE != callback_data.type)
    {
        LOC_LOGE("RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE is not the type expected! type=%d\n", callback_data.type);
        return LOC_API_ADAPTER_ERR_GENERAL_FAILURE;
    }
    else if (RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE != callback_data.data.disc)
    {
        LOC_LOGE("RPC_LOC_IOCTL_QUERY_PREDICTED_ORBITS_DATA_SOURCE is not the disc expected! disc=%d\n", callback_data.data.disc);
        return LOC_API_ADAPTER_ERR_GENERAL_FAILURE;
    }

    reportXtraServer(callback_data.data.rpc_loc_ioctl_callback_data_u_type_u.
                     predicted_orbits_data_source.servers[0],
                     callback_data.data.rpc_loc_ioctl_callback_data_u_type_u.
                     predicted_orbits_data_source.servers[1],
                     callback_data.data.rpc_loc_ioctl_callback_data_u_type_u.
                     predicted_orbits_data_source.servers[2],
                     255);

    return LOC_API_ADAPTER_ERR_SUCCESS;
}

enum loc_api_adapter_err
LocApiRpc::atlOpenStatus(int handle, int is_succ, char* apn, AGpsBearerType bearer, LocAGpsType agpsType)
{
    rpc_loc_server_open_status_e_type open_status = is_succ ? RPC_LOC_SERVER_OPEN_SUCCESS : RPC_LOC_SERVER_OPEN_FAIL;
   rpc_loc_ioctl_data_u_type           ioctl_data;

    if (LOC_AGPS_TYPE_INVALID == agpsType) {
        rpc_loc_server_open_status_s_type  *conn_open_status_ptr =
            &ioctl_data.rpc_loc_ioctl_data_u_type_u.conn_open_status;

        // Fill in data
        ioctl_data.disc = RPC_LOC_IOCTL_INFORM_SERVER_OPEN_STATUS;
        conn_open_status_ptr->conn_handle = handle;
        conn_open_status_ptr->open_status = open_status;
#if (AMSS_VERSION==3200)
        conn_open_status_ptr->apn_name = apn; /* requires APN */
#else
        if (is_succ) {
            strlcpy(conn_open_status_ptr->apn_name, apn,
                    sizeof conn_open_status_ptr->apn_name);
        } else {
            conn_open_status_ptr->apn_name[0] = 0;
        }
#endif /* #if (AMSS_VERSION==3200) */

        LOC_LOGD("ATL RPC_LOC_IOCTL_INFORM_SERVER_OPEN_STATUS open %s, APN name = [%s]\n",
                 log_succ_fail_string(is_succ),
                 apn);
    } else {
        rpc_loc_server_multi_open_status_s_type  *conn_multi_open_status_ptr =
            &ioctl_data.rpc_loc_ioctl_data_u_type_u.multi_conn_open_status;

        // Fill in data
        ioctl_data.disc = RPC_LOC_IOCTL_INFORM_SERVER_MULTI_OPEN_STATUS;
        conn_multi_open_status_ptr->conn_handle = handle;
        conn_multi_open_status_ptr->open_status = open_status;
        if (is_succ) {
            strlcpy(conn_multi_open_status_ptr->apn_name, apn,
                    sizeof conn_multi_open_status_ptr->apn_name);
        } else {
            conn_multi_open_status_ptr->apn_name[0] = 0;
        }

        switch(bearer)
        {
        case AGPS_APN_BEARER_IPV4:
            conn_multi_open_status_ptr->pdp_type = RPC_LOC_SERVER_PDP_IP;
            break;
        case AGPS_APN_BEARER_IPV6:
            conn_multi_open_status_ptr->pdp_type = RPC_LOC_SERVER_PDP_IPV6;
            break;
        case AGPS_APN_BEARER_IPV4V6:
            conn_multi_open_status_ptr->pdp_type = RPC_LOC_SERVER_PDP_IPV4V6;
            break;
        default:
            conn_multi_open_status_ptr->pdp_type = RPC_LOC_SERVER_PDP_PPP;
        }

        LOC_LOGD("ATL RPC_LOC_IOCTL_INFORM_SERVER_MULTI_OPEN_STATUS open %s, APN name = [%s], pdp_type = %d\n",
                 log_succ_fail_string(is_succ),
                 apn,
                 conn_multi_open_status_ptr->pdp_type);
    }

    // Make the IOCTL call
    return convertErr(
        loc_eng_ioctl(client_handle,
                      ioctl_data.disc,
                      &ioctl_data,
                      LOC_IOCTL_DEFAULT_TIMEOUT,
                      NULL)
        );
}

enum loc_api_adapter_err
LocApiRpc::atlCloseStatus(int handle, int is_succ)
{
    rpc_loc_ioctl_data_u_type           ioctl_data;
    ioctl_data.disc = RPC_LOC_IOCTL_INFORM_SERVER_CLOSE_STATUS;

    rpc_loc_server_close_status_s_type *conn_close_status_ptr =
        &ioctl_data.rpc_loc_ioctl_data_u_type_u.conn_close_status;
    conn_close_status_ptr->conn_handle = handle;
    conn_close_status_ptr->close_status = is_succ ? RPC_LOC_SERVER_CLOSE_SUCCESS : RPC_LOC_SERVER_CLOSE_FAIL;

    // Make the IOCTL call
    return convertErr(
        loc_eng_ioctl(client_handle,
                      ioctl_data.disc,
                      &ioctl_data,
                      LOC_IOCTL_DEFAULT_TIMEOUT,
                      NULL)
        );
}

void LocApiRpc::ATLEvent(const rpc_loc_server_request_s_type *server_request_ptr)
{
    int connHandle;
    LocAGpsType agps_type;

    LOC_LOGV("RPC_LOC_EVENT_ASSISTANCE_DATA_REQUEST event %s)",
             loc_get_event_atl_open_name(server_request_ptr->event));
    switch (server_request_ptr->event)
    {
    case RPC_LOC_SERVER_REQUEST_MULTI_OPEN:
        connHandle = server_request_ptr->payload.rpc_loc_server_request_u_type_u.multi_open_req.conn_handle;
        if (server_request_ptr->payload.rpc_loc_server_request_u_type_u.multi_open_req.connection_type
            == RPC_LOC_SERVER_CONNECTION_LBS) {
            agps_type = LOC_AGPS_TYPE_SUPL;
            LOC_LOGV("ATLEvent: event - RPC_LOC_SERVER_REQUEST_MULTI_OPEN\n            type - LOC_AGPS_TYPE_SUPL\n            handle - %d", connHandle);
        } else {
            agps_type = LOC_AGPS_TYPE_WWAN_ANY;
            LOC_LOGV("ATLEvent: event - RPC_LOC_SERVER_REQUEST_MULTI_OPEN\n            type - LOC_AGPS_TYPE_WWAN_ANY\n            handle - %d", connHandle);
        }
        requestATL(connHandle, agps_type);
        break;
    case RPC_LOC_SERVER_REQUEST_OPEN:
        connHandle = server_request_ptr->payload.rpc_loc_server_request_u_type_u.open_req.conn_handle;
        LOC_LOGV("ATLEvent: event - RPC_LOC_SERVER_REQUEST_OPEN\n            handle - %d", connHandle);
        requestATL(connHandle, LOC_AGPS_TYPE_INVALID);
        break;
    case RPC_LOC_SERVER_REQUEST_CLOSE:
        connHandle = server_request_ptr->payload.rpc_loc_server_request_u_type_u.close_req.conn_handle;
        LOC_LOGV("ATLEvent: event - RPC_LOC_SERVER_REQUEST_CLOSE\n            handle - %d", connHandle);
        releaseATL(connHandle);
        break;
    default:
        LOC_LOGE("ATLEvent: event type %d invalid", server_request_ptr->event);
   }
}

void LocApiRpc::NIEvent(const rpc_loc_ni_event_s_type *ni_req)
{
    LocGpsNiNotification notif = {0};

    switch (ni_req->event)
    {
    case RPC_LOC_NI_EVENT_VX_NOTIFY_VERIFY_REQ:
    {
        const rpc_loc_ni_vx_notify_verify_req_s_type *vx_req =
            &ni_req->payload.rpc_loc_ni_event_payload_u_type_u.vx_req;
        LOC_LOGI("VX Notification");
        notif.ni_type = LOC_GPS_NI_TYPE_VOICE;
        // Requestor ID
        hexcode(notif.requestor_id, sizeof notif.requestor_id,
                vx_req->requester_id.requester_id,
                vx_req->requester_id.requester_id_length);
        notif.text_encoding = 0; // No text and no encoding
        notif.requestor_id_encoding = convertNiEncodingType(vx_req->encoding_scheme);
        NIEventFillVerfiyType(notif, vx_req->notification_priv_type);
    }
        break;

    case RPC_LOC_NI_EVENT_UMTS_CP_NOTIFY_VERIFY_REQ:
    {
        const rpc_loc_ni_umts_cp_notify_verify_req_s_type *umts_cp_req =
            &ni_req->payload.rpc_loc_ni_event_payload_u_type_u.umts_cp_req;
        LOC_LOGI("UMTS CP Notification\n");
        notif.ni_type= LOC_GPS_NI_TYPE_UMTS_CTRL_PLANE;         // Stores notification text
#if (AMSS_VERSION==3200)
        hexcode(notif.text, sizeof notif.text,
                umts_cp_req->notification_text.notification_text_val,
                umts_cp_req->notification_length);
        hexcode(notif.requestor_id, sizeof notif.requestor_id,
                umts_cp_req->requestor_id.requestor_id_string.requestor_id_string_val,
                umts_cp_req->requestor_id.string_len);
#else
        hexcode(notif.text, sizeof notif.text,
                umts_cp_req->notification_text,
                umts_cp_req->notification_length);
        hexcode(notif.requestor_id, sizeof notif.requestor_id,
                umts_cp_req->requestor_id.requestor_id_string,
                umts_cp_req->requestor_id.string_len);
#endif
        notif.text_encoding = convertNiEncodingType(umts_cp_req->datacoding_scheme);
        notif.requestor_id_encoding = notif.text_encoding;
        NIEventFillVerfiyType(notif, umts_cp_req->notification_priv_type);

        // LCS address (using extras field)
        if (umts_cp_req->ext_client_address_data.ext_client_address_len != 0)
        {
            // Copy LCS Address into notif.extras in the format: Address = 012345
            strlcat(notif.extras, LOC_NI_NOTIF_KEY_ADDRESS, sizeof notif.extras);
            strlcat(notif.extras, " = ", sizeof notif.extras);
            int addr_len = 0;
            const char *address_source = NULL;

#if (AMSS_VERSION==3200)
            address_source = umts_cp_req->ext_client_address_data.ext_client_address.ext_client_address_val;
#else
            address_source = umts_cp_req->ext_client_address_data.ext_client_address;
#endif /* #if (AMSS_VERSION==3200) */

            char lcs_addr[32]; // Decoded LCS address for UMTS CP NI
            addr_len = decodeAddress(lcs_addr, sizeof lcs_addr, address_source,
                                     umts_cp_req->ext_client_address_data.ext_client_address_len);

            // The address is ASCII string
            if (addr_len)
            {
                strlcat(notif.extras, lcs_addr, sizeof notif.extras);
            }
        }
    }
        break;

    case RPC_LOC_NI_EVENT_SUPL_NOTIFY_VERIFY_REQ:
    {
        const rpc_loc_ni_supl_notify_verify_req_s_type *supl_req =
            &ni_req->payload.rpc_loc_ni_event_payload_u_type_u.supl_req;
        LOC_LOGI("SUPL Notification\n");
        notif.ni_type = LOC_GPS_NI_TYPE_UMTS_SUPL;

        if (supl_req->flags & RPC_LOC_NI_CLIENT_NAME_PRESENT)
        {
#if (AMSS_VERSION==3200)
            hexcode(notif.text, sizeof notif.text,
                    supl_req->client_name.client_name_string.client_name_string_val,   /* buffer */
                    supl_req->client_name.string_len                                   /* length */
            );
#else
            hexcode(notif.text, sizeof notif.text,
                            supl_req->client_name.client_name_string,   /* buffer */
                            supl_req->client_name.string_len            /* length */
            );
#endif /* #if (AMSS_VERSION==3200) */
            LOC_LOGV("SUPL NI: client_name: %s len=%d", notif.text, supl_req->client_name.string_len);
        }
        else {
            LOC_LOGV("SUPL NI: client_name not present.");
        }

        // Requestor ID
        if (supl_req->flags & RPC_LOC_NI_REQUESTOR_ID_PRESENT)
        {
#if (AMSS_VERSION==3200)
            hexcode(notif.requestor_id, sizeof notif.requestor_id,
                    supl_req->requestor_id.requestor_id_string.requestor_id_string_val,  /* buffer */
                    supl_req->requestor_id.string_len                                    /* length */
                );
#else
            hexcode(notif.requestor_id, sizeof notif.requestor_id,
                    supl_req->requestor_id.requestor_id_string,  /* buffer */
                    supl_req->requestor_id.string_len            /* length */
                );
#endif /* #if (AMSS_VERSION==3200) */
            LOC_LOGV("SUPL NI: requestor_id: %s len=%d", notif.requestor_id, supl_req->requestor_id.string_len);
        }
        else {
            LOC_LOGV("SUPL NI: requestor_id not present.");
        }

        // Encoding type
        if (supl_req->flags & RPC_LOC_NI_ENCODING_TYPE_PRESENT)
        {
            notif.text_encoding = convertNiEncodingType(supl_req->datacoding_scheme);
            notif.requestor_id_encoding = notif.text_encoding;
        }
        else {
            notif.text_encoding = notif.requestor_id_encoding = LOC_GPS_ENC_UNKNOWN;
        }

        NIEventFillVerfiyType(notif, ni_req->payload.rpc_loc_ni_event_payload_u_type_u.supl_req.notification_priv_type);
    }
        break;

    default:
        LOC_LOGE("Unknown NI event: %x\n", (int) ni_req->event);
        return;
    }

    // this copy will get freed in loc_eng_ni when loc_ni_respond() is called
    rpc_loc_ni_event_s_type *copy = (rpc_loc_ni_event_s_type *)malloc(sizeof(*copy));
    memcpy(copy, ni_req, sizeof(*copy));
    requestNiNotify(notif, (const void*)copy);
}

int LocApiRpc::NIEventFillVerfiyType(LocGpsNiNotification &notif,
                                rpc_loc_ni_notify_verify_e_type notif_priv)
{
   switch (notif_priv)
   {
   case RPC_LOC_NI_USER_NO_NOTIFY_NO_VERIFY:
       notif.notify_flags = 0;
       notif.default_response = LOC_GPS_NI_RESPONSE_NORESP;
       return 1;
   case RPC_LOC_NI_USER_NOTIFY_ONLY:
       notif.notify_flags = LOC_GPS_NI_NEED_NOTIFY;
       notif.default_response = LOC_GPS_NI_RESPONSE_NORESP;
       return 1;
   case RPC_LOC_NI_USER_NOTIFY_VERIFY_ALLOW_NO_RESP:
       notif.notify_flags = LOC_GPS_NI_NEED_NOTIFY | LOC_GPS_NI_NEED_VERIFY;
       notif.default_response = LOC_GPS_NI_RESPONSE_ACCEPT;
       return 1;
   case RPC_LOC_NI_USER_NOTIFY_VERIFY_NOT_ALLOW_NO_RESP:
       notif.notify_flags = LOC_GPS_NI_NEED_NOTIFY | LOC_GPS_NI_NEED_VERIFY;
       notif.default_response = LOC_GPS_NI_RESPONSE_DENY;
       return 1;
   case RPC_LOC_NI_USER_PRIVACY_OVERRIDE:
       notif.notify_flags = LOC_GPS_NI_PRIVACY_OVERRIDE;
       notif.default_response = LOC_GPS_NI_RESPONSE_NORESP;
       return 1;
   default:
      return 0;
   }
}

enum loc_api_adapter_err
LocApiRpc::setSUPLVersion(uint32_t version)
{
   rpc_loc_ioctl_data_u_type ioctl_data = {RPC_LOC_IOCTL_SET_SUPL_VERSION, {0}};
   ioctl_data.rpc_loc_ioctl_data_u_type_u.supl_version = (int)version;
   return convertErr(
       loc_eng_ioctl (client_handle,
                      RPC_LOC_IOCTL_SET_SUPL_VERSION,
                      &ioctl_data,
                      LOC_IOCTL_DEFAULT_TIMEOUT,
                      NULL)
       );
}

LocGpsNiEncodingType LocApiRpc::convertNiEncodingType(int loc_encoding)
{
   switch (loc_encoding)
   {
   case RPC_LOC_NI_SUPL_UTF8:
       return LOC_GPS_ENC_SUPL_UTF8;
   case RPC_LOC_NI_SUPL_UCS2:
       return LOC_GPS_ENC_SUPL_UCS2;
   case RPC_LOC_NI_SUPL_GSM_DEFAULT:
      return LOC_GPS_ENC_SUPL_GSM_DEFAULT;
   case RPC_LOC_NI_SS_LANGUAGE_UNSPEC:
      return LOC_GPS_ENC_SUPL_GSM_DEFAULT; // SS_LANGUAGE_UNSPEC = GSM
   default:
       return LOC_GPS_ENC_UNKNOWN;
   }
}

LocApiBase* getLocApi(const MsgTask* msgTask,
                      LOC_API_ADAPTER_EVENT_MASK_T exMask,
                      ContextBase *context) {
    return new LocApiRpc(msgTask, exMask, context);
}

/*Values for lock
  1 = Do not lock any position sessions
  2 = Lock MI position sessions
  3 = Lock MT position sessions
  4 = Lock all position sessions
*/
int LocApiRpc::setGpsLock(LOC_GPS_LOCK_MASK lockMask)
{
    rpc_loc_ioctl_data_u_type    ioctl_data;
    boolean ret_val;
    LOC_LOGD("%s:%d]: lock: %x\n", __func__, __LINE__, lockMask);
    ioctl_data.rpc_loc_ioctl_data_u_type_u.engine_lock = convertGpsLockMask(lockMask);
    ioctl_data.disc = RPC_LOC_IOCTL_SET_ENGINE_LOCK;
    ret_val = loc_eng_ioctl (loc_eng_data.client_handle,
                            RPC_LOC_IOCTL_SET_ENGINE_LOCK,
                            &ioctl_data,
                            LOC_IOCTL_DEFAULT_TIMEOUT,
                            NULL /* No output information is expected*/);

    LOC_LOGD("%s:%d]: ret_val: %d\n", __func__, __LINE__, (int)ret_val);
    return (ret_val == TRUE ? 0 : -1);
}

/*
  Returns
  Current value of GPS lock on success
  -1 on failure
*/
int LocApiRpc :: getGpsLock()
{
    rpc_loc_ioctl_data_u_type    ioctl_data;
    rpc_loc_ioctl_callback_s_type callback_payload;
    boolean ret_val;
    int ret=0;
    LOC_LOGD("%s:%d]: Enter\n", __func__, __LINE__);
    ret_val = loc_eng_ioctl (loc_eng_data.client_handle,
                            RPC_LOC_IOCTL_GET_ENGINE_LOCK,
                            &ioctl_data,
                            LOC_IOCTL_DEFAULT_TIMEOUT,
                            &callback_payload);
    if(ret_val == TRUE) {
        ret = (int)callback_payload.data.engine_lock;
        LOC_LOGD("%s:%d]: Lock type: %d\n", __func__, __LINE__, ret);
    }
    else {
        LOC_LOGE("%s:%d]: Ioctl failed", __func__, __LINE__);
        ret = -1;
    }
    LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
    return ret;
}
