code:
/* Copyright (c) 2017-2020, 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_MeasurementAPIClient"

#include <log_util.h>
#include <loc_cfg.h>
#include <inttypes.h>

#include "LocationUtil.h"
#include "MeasurementAPIClient.h"
#include <loc_misc_utils.h>

namespace android {
namespace hardware {
namespace gnss {
namespace V2_1 {
namespace implementation {

using ::android::hardware::gnss::V1_0::IGnssMeasurement;
using ::android::hardware::gnss::V2_0::IGnssMeasurementCallback;

static void convertGnssData(GnssMeasurementsNotification& in,
        V1_0::IGnssMeasurementCallback::GnssData& out);
static void convertGnssData_1_1(GnssMeasurementsNotification& in,
        V1_1::IGnssMeasurementCallback::GnssData& out);
static void convertGnssData_2_0(GnssMeasurementsNotification& in,
        V2_0::IGnssMeasurementCallback::GnssData& out);
static void convertGnssData_2_1(GnssMeasurementsNotification& in,
        V2_1::IGnssMeasurementCallback::GnssData& out);
static void convertGnssMeasurement(GnssMeasurementsData& in,
        V1_0::IGnssMeasurementCallback::GnssMeasurement& out);
static void convertGnssClock(GnssMeasurementsClock& in, IGnssMeasurementCallback::GnssClock& out);
static void convertGnssClock_2_1(GnssMeasurementsClock& in,
        V2_1::IGnssMeasurementCallback::GnssClock& out);
static void convertGnssMeasurementsCodeType(GnssMeasurementsCodeType& inCodeType,
        char* inOtherCodeTypeName,
        ::android::hardware::hidl_string& out);
static void convertGnssMeasurementsAccumulatedDeltaRangeState(GnssMeasurementsAdrStateMask& in,
        ::android::hardware::hidl_bitfield
                <V1_1::IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState>& out);
static void convertGnssMeasurementsState(GnssMeasurementsStateMask& in,
        ::android::hardware::hidl_bitfield
                <V2_0::IGnssMeasurementCallback::GnssMeasurementState>& out);
static void convertElapsedRealtimeNanos(GnssMeasurementsNotification& in,
        ::android::hardware::gnss::V2_0::ElapsedRealtime& elapsedRealtimeNanos);

MeasurementAPIClient::MeasurementAPIClient() :
    mGnssMeasurementCbIface(nullptr),
    mGnssMeasurementCbIface_1_1(nullptr),
    mGnssMeasurementCbIface_2_0(nullptr),
    mGnssMeasurementCbIface_2_1(nullptr),
    mTracking(false)
{
    LOC_LOGD("%s]: ()", __FUNCTION__);
}

MeasurementAPIClient::~MeasurementAPIClient()
{
    LOC_LOGD("%s]: ()", __FUNCTION__);
}

void MeasurementAPIClient::clearInterfaces()
{
    mGnssMeasurementCbIface = nullptr;
    mGnssMeasurementCbIface_1_1 = nullptr;
    mGnssMeasurementCbIface_2_0 = nullptr;
    mGnssMeasurementCbIface_2_1 = nullptr;
}

// for GpsInterface
Return<IGnssMeasurement::GnssMeasurementStatus>
MeasurementAPIClient::measurementSetCallback(const sp<V1_0::IGnssMeasurementCallback>& callback)
{
    LOC_LOGD("%s]: (%p)", __FUNCTION__, &callback);

    mMutex.lock();
    clearInterfaces();
    mGnssMeasurementCbIface = callback;
    mMutex.unlock();

    return startTracking();
}

Return<IGnssMeasurement::GnssMeasurementStatus>
MeasurementAPIClient::measurementSetCallback_1_1(
        const sp<V1_1::IGnssMeasurementCallback>& callback,
        GnssPowerMode powerMode, uint32_t timeBetweenMeasurement)
{
    LOC_LOGD("%s]: (%p) (powermode: %d) (tbm: %d)",
            __FUNCTION__, &callback, (int)powerMode, timeBetweenMeasurement);

    mMutex.lock();
    clearInterfaces();
    mGnssMeasurementCbIface_1_1 = callback;
    mMutex.unlock();

    return startTracking(powerMode, timeBetweenMeasurement);
}

Return<IGnssMeasurement::GnssMeasurementStatus>
MeasurementAPIClient::measurementSetCallback_2_0(
    const sp<V2_0::IGnssMeasurementCallback>& callback,
    GnssPowerMode powerMode, uint32_t timeBetweenMeasurement)
{
    LOC_LOGD("%s]: (%p) (powermode: %d) (tbm: %d)",
        __FUNCTION__, &callback, (int)powerMode, timeBetweenMeasurement);

    mMutex.lock();
    clearInterfaces();
    mGnssMeasurementCbIface_2_0 = callback;
    mMutex.unlock();

    return startTracking(powerMode, timeBetweenMeasurement);
}

Return<IGnssMeasurement::GnssMeasurementStatus> MeasurementAPIClient::measurementSetCallback_2_1(
        const sp<V2_1::IGnssMeasurementCallback>& callback,
        GnssPowerMode powerMode, uint32_t timeBetweenMeasurement) {
    LOC_LOGD("%s]: (%p) (powermode: %d) (tbm: %d)",
        __FUNCTION__, &callback, (int)powerMode, timeBetweenMeasurement);

    mMutex.lock();
    clearInterfaces();
    mGnssMeasurementCbIface_2_1 = callback;
    mMutex.unlock();

    return startTracking(powerMode, timeBetweenMeasurement);
}
Return<IGnssMeasurement::GnssMeasurementStatus>
MeasurementAPIClient::startTracking(
        GnssPowerMode powerMode, uint32_t timeBetweenMeasurement)
{
    LocationCallbacks locationCallbacks;
    memset(&locationCallbacks, 0, sizeof(LocationCallbacks));
    locationCallbacks.size = sizeof(LocationCallbacks);

    locationCallbacks.trackingCb = nullptr;
    locationCallbacks.batchingCb = nullptr;
    locationCallbacks.geofenceBreachCb = nullptr;
    locationCallbacks.geofenceStatusCb = nullptr;
    locationCallbacks.gnssLocationInfoCb = nullptr;
    locationCallbacks.gnssNiCb = nullptr;
    locationCallbacks.gnssSvCb = nullptr;
    locationCallbacks.gnssNmeaCb = nullptr;

    locationCallbacks.gnssMeasurementsCb = nullptr;
    if (mGnssMeasurementCbIface_2_1 != nullptr ||
        mGnssMeasurementCbIface_2_0 != nullptr ||
        mGnssMeasurementCbIface_1_1 != nullptr ||
        mGnssMeasurementCbIface != nullptr) {
        locationCallbacks.gnssMeasurementsCb =
            [this](GnssMeasurementsNotification gnssMeasurementsNotification) {
                onGnssMeasurementsCb(gnssMeasurementsNotification);
            };
    }

    locAPISetCallbacks(locationCallbacks);

    TrackingOptions options = {};
    memset(&options, 0, sizeof(TrackingOptions));
    options.size = sizeof(TrackingOptions);
    options.minInterval = 1000;
    options.mode = GNSS_SUPL_MODE_STANDALONE;
    if (GNSS_POWER_MODE_INVALID != powerMode) {
        options.powerMode = powerMode;
        options.tbm = timeBetweenMeasurement;
    }

    mTracking = true;
    LOC_LOGD("%s]: start tracking session", __FUNCTION__);
    locAPIStartTracking(options);
    return IGnssMeasurement::GnssMeasurementStatus::SUCCESS;
}

// for GpsMeasurementInterface
void MeasurementAPIClient::measurementClose() {
    LOC_LOGD("%s]: ()", __FUNCTION__);
    mTracking = false;
    locAPIStopTracking();
}

// callbacks
void MeasurementAPIClient::onGnssMeasurementsCb(
        GnssMeasurementsNotification gnssMeasurementsNotification)
{
    LOC_LOGD("%s]: (count: %u active: %d)",
            __FUNCTION__, gnssMeasurementsNotification.count, mTracking);
    if (mTracking) {
        mMutex.lock();
        sp<V1_0::IGnssMeasurementCallback> gnssMeasurementCbIface = nullptr;
        sp<V1_1::IGnssMeasurementCallback> gnssMeasurementCbIface_1_1 = nullptr;
        sp<V2_0::IGnssMeasurementCallback> gnssMeasurementCbIface_2_0 = nullptr;
        sp<V2_1::IGnssMeasurementCallback> gnssMeasurementCbIface_2_1 = nullptr;
        if (mGnssMeasurementCbIface_2_1 != nullptr) {
            gnssMeasurementCbIface_2_1 = mGnssMeasurementCbIface_2_1;
        } else if (mGnssMeasurementCbIface_2_0 != nullptr) {
            gnssMeasurementCbIface_2_0 = mGnssMeasurementCbIface_2_0;
        } else if (mGnssMeasurementCbIface_1_1 != nullptr) {
            gnssMeasurementCbIface_1_1 = mGnssMeasurementCbIface_1_1;
        } else if (mGnssMeasurementCbIface != nullptr) {
            gnssMeasurementCbIface = mGnssMeasurementCbIface;
        }
        mMutex.unlock();

        if (gnssMeasurementCbIface_2_1 != nullptr) {
            V2_1::IGnssMeasurementCallback::GnssData gnssData;
            convertGnssData_2_1(gnssMeasurementsNotification, gnssData);
            auto r = gnssMeasurementCbIface_2_1->gnssMeasurementCb_2_1(gnssData);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssMeasurementCb description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssMeasurementCbIface_2_0 != nullptr) {
            V2_0::IGnssMeasurementCallback::GnssData gnssData;
            convertGnssData_2_0(gnssMeasurementsNotification, gnssData);
            auto r = gnssMeasurementCbIface_2_0->gnssMeasurementCb_2_0(gnssData);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssMeasurementCb description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssMeasurementCbIface_1_1 != nullptr) {
            V1_1::IGnssMeasurementCallback::GnssData gnssData;
            convertGnssData_1_1(gnssMeasurementsNotification, gnssData);
            auto r = gnssMeasurementCbIface_1_1->gnssMeasurementCb(gnssData);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssMeasurementCb description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssMeasurementCbIface != nullptr) {
            V1_0::IGnssMeasurementCallback::GnssData gnssData;
            convertGnssData(gnssMeasurementsNotification, gnssData);
            auto r = gnssMeasurementCbIface->GnssMeasurementCb(gnssData);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from GnssMeasurementCb description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

static void convertGnssMeasurement(GnssMeasurementsData& in,
        V1_0::IGnssMeasurementCallback::GnssMeasurement& out)
{
    memset(&out, 0, sizeof(out));
    if (in.flags & GNSS_MEASUREMENTS_DATA_SIGNAL_TO_NOISE_RATIO_BIT)
        out.flags |= IGnssMeasurementCallback::GnssMeasurementFlags::HAS_SNR;
    if (in.flags & GNSS_MEASUREMENTS_DATA_CARRIER_FREQUENCY_BIT)
        out.flags |= IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_FREQUENCY;
    if (in.flags & GNSS_MEASUREMENTS_DATA_CARRIER_CYCLES_BIT)
        out.flags |= IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_CYCLES;
    if (in.flags & GNSS_MEASUREMENTS_DATA_CARRIER_PHASE_BIT)
        out.flags |= IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_PHASE;
    if (in.flags & GNSS_MEASUREMENTS_DATA_CARRIER_PHASE_UNCERTAINTY_BIT)
        out.flags |= IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_PHASE_UNCERTAINTY;
    if (in.flags & GNSS_MEASUREMENTS_DATA_AUTOMATIC_GAIN_CONTROL_BIT)
        out.flags |= IGnssMeasurementCallback::GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL;
    convertGnssSvid(in, out.svid);
    convertGnssConstellationType(in.svType, out.constellation);
    out.timeOffsetNs = in.timeOffsetNs;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_CODE_LOCK_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_CODE_LOCK;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_BIT_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_BIT_SYNC;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_SUBFRAME_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_SUBFRAME_SYNC;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_TOW_DECODED_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_TOW_DECODED;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_MSEC_AMBIGUOUS_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_MSEC_AMBIGUOUS;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_SYMBOL_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_SYMBOL_SYNC;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_GLO_STRING_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GLO_STRING_SYNC;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_GLO_TOD_DECODED_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GLO_TOD_DECODED;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_BDS_D2_BIT_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_BDS_D2_BIT_SYNC;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_BDS_D2_SUBFRAME_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_BDS_D2_SUBFRAME_SYNC;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_GAL_E1BC_CODE_LOCK_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GAL_E1BC_CODE_LOCK;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_GAL_E1C_2ND_CODE_LOCK_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GAL_E1C_2ND_CODE_LOCK;
    if (in.stateMask & GNSS_MEASUREMENTS_STATE_GAL_E1B_PAGE_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GAL_E1B_PAGE_SYNC;
    if (in.stateMask &  GNSS_MEASUREMENTS_STATE_SBAS_SYNC_BIT)
        out.state |= IGnssMeasurementCallback::GnssMeasurementState::STATE_SBAS_SYNC;
    out.receivedSvTimeInNs = in.receivedSvTimeNs;
    out.receivedSvTimeUncertaintyInNs = in.receivedSvTimeUncertaintyNs;
    out.cN0DbHz = in.carrierToNoiseDbHz;
    out.pseudorangeRateMps = in.pseudorangeRateMps;
    out.pseudorangeRateUncertaintyMps = in.pseudorangeRateUncertaintyMps;
    if (in.adrStateMask & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_VALID_BIT)
        out.accumulatedDeltaRangeState |=
            IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_VALID;
    if (in.adrStateMask & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_RESET_BIT)
        out.accumulatedDeltaRangeState |=
            IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_RESET;
    if (in.adrStateMask & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_CYCLE_SLIP_BIT)
        out.accumulatedDeltaRangeState |=
            IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_CYCLE_SLIP;
    out.accumulatedDeltaRangeM = in.adrMeters;
    out.accumulatedDeltaRangeUncertaintyM = in.adrUncertaintyMeters;
    out.carrierFrequencyHz = in.carrierFrequencyHz;
    out.carrierCycles = in.carrierCycles;
    out.carrierPhase = in.carrierPhase;
    out.carrierPhaseUncertainty = in.carrierPhaseUncertainty;
    uint8_t indicator =
        static_cast<uint8_t>(IGnssMeasurementCallback::GnssMultipathIndicator::INDICATOR_UNKNOWN);
    if (in.multipathIndicator & GNSS_MEASUREMENTS_MULTIPATH_INDICATOR_PRESENT)
        indicator |= IGnssMeasurementCallback::GnssMultipathIndicator::INDICATOR_PRESENT;
    if (in.multipathIndicator & GNSS_MEASUREMENTS_MULTIPATH_INDICATOR_NOT_PRESENT)
        indicator |= IGnssMeasurementCallback::GnssMultipathIndicator::INDICATIOR_NOT_PRESENT;
    out.multipathIndicator =
        static_cast<IGnssMeasurementCallback::GnssMultipathIndicator>(indicator);
    out.snrDb = in.signalToNoiseRatioDb;
    out.agcLevelDb = in.agcLevelDb;
}

static void convertGnssClock(GnssMeasurementsClock& in, IGnssMeasurementCallback::GnssClock& out)
{
    memset(&out, 0, sizeof(out));
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_LEAP_SECOND_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_LEAP_SECOND;
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_TIME_UNCERTAINTY_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_TIME_UNCERTAINTY;
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_FULL_BIAS_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_FULL_BIAS;
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_BIAS_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_BIAS;
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_BIAS_UNCERTAINTY_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_BIAS_UNCERTAINTY;
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_DRIFT_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_DRIFT;
    if (in.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_DRIFT_UNCERTAINTY_BIT)
        out.gnssClockFlags |= IGnssMeasurementCallback::GnssClockFlags::HAS_DRIFT_UNCERTAINTY;
    out.leapSecond = in.leapSecond;
    out.timeNs = in.timeNs;
    out.timeUncertaintyNs = in.timeUncertaintyNs;
    out.fullBiasNs = in.fullBiasNs;
    out.biasNs = in.biasNs;
    out.biasUncertaintyNs = in.biasUncertaintyNs;
    out.driftNsps = in.driftNsps;
    out.driftUncertaintyNsps = in.driftUncertaintyNsps;
    out.hwClockDiscontinuityCount = in.hwClockDiscontinuityCount;
}

static void convertGnssClock_2_1(GnssMeasurementsClock& in,
        V2_1::IGnssMeasurementCallback::GnssClock& out)
{
    memset(&out, 0, sizeof(out));
    convertGnssClock(in, out.v1_0);
    convertGnssConstellationType(in.referenceSignalTypeForIsb.svType,
            out.referenceSignalTypeForIsb.constellation);
    out.referenceSignalTypeForIsb.carrierFrequencyHz =
            in.referenceSignalTypeForIsb.carrierFrequencyHz;
    convertGnssMeasurementsCodeType(in.referenceSignalTypeForIsb.codeType,
            in.referenceSignalTypeForIsb.otherCodeTypeName,
            out.referenceSignalTypeForIsb.codeType);
}

static void convertGnssData(GnssMeasurementsNotification& in,
        V1_0::IGnssMeasurementCallback::GnssData& out)
{
    memset(&out, 0, sizeof(out));
    out.measurementCount = in.count;
    if (out.measurementCount > static_cast<uint32_t>(V1_0::GnssMax::SVS_COUNT)) {
        LOC_LOGW("%s]: Too many measurement %u. Clamps to %d.",
                __FUNCTION__,  out.measurementCount, V1_0::GnssMax::SVS_COUNT);
        out.measurementCount = static_cast<uint32_t>(V1_0::GnssMax::SVS_COUNT);
    }
    for (size_t i = 0; i < out.measurementCount; i++) {
        convertGnssMeasurement(in.measurements[i], out.measurements[i]);
    }
    convertGnssClock(in.clock, out.clock);
}

static void convertGnssData_1_1(GnssMeasurementsNotification& in,
        V1_1::IGnssMeasurementCallback::GnssData& out)
{
    memset(&out, 0, sizeof(out));
    out.measurements.resize(in.count);
    for (size_t i = 0; i < in.count; i++) {
        convertGnssMeasurement(in.measurements[i], out.measurements[i].v1_0);
        convertGnssMeasurementsAccumulatedDeltaRangeState(in.measurements[i].adrStateMask,
                out.measurements[i].accumulatedDeltaRangeState);
    }
    convertGnssClock(in.clock, out.clock);
}

static void convertGnssData_2_0(GnssMeasurementsNotification& in,
        V2_0::IGnssMeasurementCallback::GnssData& out)
{
    memset(&out, 0, sizeof(out));
    out.measurements.resize(in.count);
    for (size_t i = 0; i < in.count; i++) {
        convertGnssMeasurement(in.measurements[i], out.measurements[i].v1_1.v1_0);
        convertGnssConstellationType(in.measurements[i].svType, out.measurements[i].constellation);
        convertGnssMeasurementsCodeType(in.measurements[i].codeType,
            in.measurements[i].otherCodeTypeName,
            out.measurements[i].codeType);
        convertGnssMeasurementsAccumulatedDeltaRangeState(in.measurements[i].adrStateMask,
                out.measurements[i].v1_1.accumulatedDeltaRangeState);
        convertGnssMeasurementsState(in.measurements[i].stateMask, out.measurements[i].state);
    }
    convertGnssClock(in.clock, out.clock);
    convertElapsedRealtimeNanos(in, out.elapsedRealtime);
}

static void convertGnssMeasurementsCodeType(GnssMeasurementsCodeType& inCodeType,
        char* inOtherCodeTypeName, ::android::hardware::hidl_string& out)
{
    memset(&out, 0, sizeof(out));
    switch(inCodeType) {
        case GNSS_MEASUREMENTS_CODE_TYPE_A:
            out = "A";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_B:
            out = "B";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_C:
            out = "C";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_I:
            out = "I";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_L:
            out = "L";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_M:
            out = "M";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_P:
            out = "P";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_Q:
            out = "Q";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_S:
            out = "S";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_W:
            out = "W";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_X:
            out = "X";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_Y:
            out = "Y";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_Z:
            out = "Z";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_N:
            out = "N";
            break;
        case GNSS_MEASUREMENTS_CODE_TYPE_OTHER:
        default:
            out = inOtherCodeTypeName;
            break;
    }
}

static void convertGnssMeasurementsAccumulatedDeltaRangeState(GnssMeasurementsAdrStateMask& in,
        ::android::hardware::hidl_bitfield
                <V1_1::IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState>& out)
{
    memset(&out, 0, sizeof(out));
    if (in & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_VALID_BIT)
        out |= IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_VALID;
    if (in & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_RESET_BIT)
        out |= IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_RESET;
    if (in & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_CYCLE_SLIP_BIT)
        out |= IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_CYCLE_SLIP;
    if (in & GNSS_MEASUREMENTS_ACCUMULATED_DELTA_RANGE_STATE_HALF_CYCLE_RESOLVED_BIT)
        out |= IGnssMeasurementCallback::
                GnssAccumulatedDeltaRangeState::ADR_STATE_HALF_CYCLE_RESOLVED;
}

static void convertGnssMeasurementsState(GnssMeasurementsStateMask& in,
        ::android::hardware::hidl_bitfield
                <V2_0::IGnssMeasurementCallback::GnssMeasurementState>& out)
{
    memset(&out, 0, sizeof(out));
    if (in & GNSS_MEASUREMENTS_STATE_CODE_LOCK_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_CODE_LOCK;
    if (in & GNSS_MEASUREMENTS_STATE_BIT_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_BIT_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_SUBFRAME_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_SUBFRAME_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_TOW_DECODED_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_TOW_DECODED;
    if (in & GNSS_MEASUREMENTS_STATE_MSEC_AMBIGUOUS_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_MSEC_AMBIGUOUS;
    if (in & GNSS_MEASUREMENTS_STATE_SYMBOL_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_SYMBOL_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_GLO_STRING_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GLO_STRING_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_GLO_TOD_DECODED_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GLO_TOD_DECODED;
    if (in & GNSS_MEASUREMENTS_STATE_BDS_D2_BIT_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_BDS_D2_BIT_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_BDS_D2_SUBFRAME_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_BDS_D2_SUBFRAME_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_GAL_E1BC_CODE_LOCK_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GAL_E1BC_CODE_LOCK;
    if (in & GNSS_MEASUREMENTS_STATE_GAL_E1C_2ND_CODE_LOCK_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GAL_E1C_2ND_CODE_LOCK;
    if (in & GNSS_MEASUREMENTS_STATE_GAL_E1B_PAGE_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GAL_E1B_PAGE_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_SBAS_SYNC_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_SBAS_SYNC;
    if (in & GNSS_MEASUREMENTS_STATE_TOW_KNOWN_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_TOW_KNOWN;
    if (in & GNSS_MEASUREMENTS_STATE_GLO_TOD_KNOWN_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_GLO_TOD_KNOWN;
    if (in & GNSS_MEASUREMENTS_STATE_2ND_CODE_LOCK_BIT)
        out |= IGnssMeasurementCallback::GnssMeasurementState::STATE_2ND_CODE_LOCK;
}

static void convertGnssData_2_1(GnssMeasurementsNotification& in,
        V2_1::IGnssMeasurementCallback::GnssData& out)
{
    memset(&out, 0, sizeof(out));
    out.measurements.resize(in.count);
    for (size_t i = 0; i < in.count; i++) {
        out.measurements[i].flags = 0;
        convertGnssMeasurement(in.measurements[i], out.measurements[i].v2_0.v1_1.v1_0);
        convertGnssConstellationType(in.measurements[i].svType,
                out.measurements[i].v2_0.constellation);
        convertGnssMeasurementsCodeType(in.measurements[i].codeType,
                in.measurements[i].otherCodeTypeName,
                out.measurements[i].v2_0.codeType);
        convertGnssMeasurementsAccumulatedDeltaRangeState(in.measurements[i].adrStateMask,
                out.measurements[i].v2_0.v1_1.accumulatedDeltaRangeState);
        convertGnssMeasurementsState(in.measurements[i].stateMask,
                out.measurements[i].v2_0.state);
        out.measurements[i].basebandCN0DbHz = in.measurements[i].basebandCarrierToNoiseDbHz;

        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_SIGNAL_TO_NOISE_RATIO_BIT) {
            out.measurements[i].flags |=
                V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_SNR;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_CARRIER_FREQUENCY_BIT) {
            out.measurements[i].flags |=
                V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_FREQUENCY;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_CARRIER_CYCLES_BIT) {
            out.measurements[i].flags |=
                V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_CYCLES;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_CARRIER_PHASE_BIT) {
            out.measurements[i].flags |=
                V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_CARRIER_PHASE;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_CARRIER_PHASE_UNCERTAINTY_BIT) {
            out.measurements[i].flags |=
                V2_1::IGnssMeasurementCallback::
                        GnssMeasurementFlags::HAS_CARRIER_PHASE_UNCERTAINTY;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_AUTOMATIC_GAIN_CONTROL_BIT) {
            out.measurements[i].flags |=
                V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_FULL_ISB_BIT) {
            out.measurements[i].fullInterSignalBiasNs = in.measurements[i].fullInterSignalBiasNs;
            out.measurements[i].flags |=
                    V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_FULL_ISB;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_FULL_ISB_UNCERTAINTY_BIT) {
            out.measurements[i].fullInterSignalBiasUncertaintyNs =
                    in.measurements[i].fullInterSignalBiasUncertaintyNs;
            out.measurements[i].flags |=
                    V2_1::IGnssMeasurementCallback::
                            GnssMeasurementFlags::HAS_FULL_ISB_UNCERTAINTY;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_SATELLITE_ISB_BIT) {
            out.measurements[i].satelliteInterSignalBiasNs =
                    in.measurements[i].satelliteInterSignalBiasNs;
            out.measurements[i].flags |=
                    V2_1::IGnssMeasurementCallback::GnssMeasurementFlags::HAS_SATELLITE_ISB;
        }
        if (in.measurements[i].flags & GNSS_MEASUREMENTS_DATA_SATELLITE_ISB_UNCERTAINTY_BIT) {
            out.measurements[i].satelliteInterSignalBiasUncertaintyNs =
                    in.measurements[i].satelliteInterSignalBiasUncertaintyNs;
            out.measurements[i].flags |=
                    V2_1::IGnssMeasurementCallback::
                            GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY;
        }
    }
    convertGnssClock_2_1(in.clock, out.clock);
    convertElapsedRealtimeNanos(in, out.elapsedRealtime);
}

static void convertElapsedRealtimeNanos(GnssMeasurementsNotification& in,
        ::android::hardware::gnss::V2_0::ElapsedRealtime& elapsedRealtime)
{
    if (in.clock.flags & GNSS_MEASUREMENTS_CLOCK_FLAGS_ELAPSED_REAL_TIME_BIT) {
        elapsedRealtime.flags |= V2_0::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS;
        elapsedRealtime.timestampNs = in.clock.elapsedRealTime;
        elapsedRealtime.flags |= V2_0::ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS;
        elapsedRealtime.timeUncertaintyNs = in.clock.elapsedRealTimeUnc;
        LOC_LOGd("elapsedRealtime.timestampNs=%" PRIi64 ""
                 " elapsedRealtime.timeUncertaintyNs=%" PRIi64 " elapsedRealtime.flags=0x%X",
                 elapsedRealtime.timestampNs,
                 elapsedRealtime.timeUncertaintyNs, elapsedRealtime.flags);
    }
}

}  // namespace implementation
}  // namespace V2_1
}  // namespace gnss
}  // namespace hardware
}  // namespace android


code:
/* Copyright (c) 2017-2020, 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.
 *
 */

#ifndef MEASUREMENT_API_CLINET_H
#define MEASUREMENT_API_CLINET_H

#include <mutex>
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
//#include <android/hardware/gnss/1.1/IGnssMeasurementCallback.h>
#include <android/hardware/gnss/2.1/IGnssMeasurementCallback.h>
#include <LocationAPIClientBase.h>
#include <hidl/Status.h>
#include <gps_extended_c.h>

namespace android {
namespace hardware {
namespace gnss {
namespace V2_1 {
namespace implementation {

using ::android::sp;

class MeasurementAPIClient : public LocationAPIClientBase
{
public:
    MeasurementAPIClient();
    MeasurementAPIClient(const MeasurementAPIClient&) = delete;
    MeasurementAPIClient& operator=(const MeasurementAPIClient&) = delete;

    // for GpsMeasurementInterface
    Return<V1_0::IGnssMeasurement::GnssMeasurementStatus> measurementSetCallback(
            const sp<V1_0::IGnssMeasurementCallback>& callback);
    Return<V1_0::IGnssMeasurement::GnssMeasurementStatus> measurementSetCallback_1_1(
            const sp<V1_1::IGnssMeasurementCallback>& callback,
            GnssPowerMode powerMode = GNSS_POWER_MODE_INVALID,
            uint32_t timeBetweenMeasurement = GPS_DEFAULT_FIX_INTERVAL_MS);
    Return<V1_0::IGnssMeasurement::GnssMeasurementStatus> measurementSetCallback_2_0(
            const sp<V2_0::IGnssMeasurementCallback>& callback,
            GnssPowerMode powerMode = GNSS_POWER_MODE_INVALID,
            uint32_t timeBetweenMeasurement = GPS_DEFAULT_FIX_INTERVAL_MS);
    Return<V1_0::IGnssMeasurement::GnssMeasurementStatus> measurementSetCallback_2_1(
            const sp<V2_1::IGnssMeasurementCallback>& callback,
            GnssPowerMode powerMode = GNSS_POWER_MODE_INVALID,
            uint32_t timeBetweenMeasurement = GPS_DEFAULT_FIX_INTERVAL_MS);
    void measurementClose();
    Return<IGnssMeasurement::GnssMeasurementStatus> startTracking(
            GnssPowerMode powerMode = GNSS_POWER_MODE_INVALID,
            uint32_t timeBetweenMeasurement = GPS_DEFAULT_FIX_INTERVAL_MS);

    // callbacks we are interested in
    void onGnssMeasurementsCb(GnssMeasurementsNotification gnssMeasurementsNotification) final;

private:
    virtual ~MeasurementAPIClient();

    std::mutex mMutex;
    sp<V1_0::IGnssMeasurementCallback> mGnssMeasurementCbIface;
    sp<V1_1::IGnssMeasurementCallback> mGnssMeasurementCbIface_1_1;
    sp<V2_0::IGnssMeasurementCallback> mGnssMeasurementCbIface_2_0;
    sp<V2_1::IGnssMeasurementCallback> mGnssMeasurementCbIface_2_1;
    bool mTracking;
    void clearInterfaces();
};

}  // namespace implementation
}  // namespace V2_1
}  // namespace gnss
}  // namespace hardware
}  // namespace android
#endif // MEASUREMENT_API_CLINET_H

license: unknown


code:
/*
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 * Not a Contribution
 */
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2_0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2_0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "android.hardware.gnss@2.1-service-qti"

#include <android/hardware/gnss/2.1/IGnss.h>
#include <hidl/LegacySupport.h>
#include "loc_cfg.h"
#include "loc_misc_utils.h"

extern "C" {
#include "vndfwk-detect.h"
}

#ifdef ARCH_ARM_32
#define DEFAULT_HW_BINDER_MEM_SIZE 65536
#endif

using android::hardware::gnss::V2_1::IGnss;

using android::hardware::configureRpcThreadpool;
using android::hardware::registerPassthroughServiceImplementation;
using android::hardware::joinRpcThreadpool;

using android::status_t;
using android::OK;

typedef int vendorEnhancedServiceMain(int /* argc */, char* /* argv */ []);

int main() {

    ALOGI("%s", __FUNCTION__);

    int vendorInfo = getVendorEnhancedInfo();
    bool vendorEnhanced = ( 1 == vendorInfo || 3 == vendorInfo );
    setVendorEnhanced(vendorEnhanced);

#ifdef ARCH_ARM_32
    android::hardware::ProcessState::initWithMmapSize((size_t)(DEFAULT_HW_BINDER_MEM_SIZE));
#endif
    configureRpcThreadpool(1, true);
    status_t status;

    status = registerPassthroughServiceImplementation<IGnss>();
    if (status == OK) {
    #ifdef LOC_HIDL_VERSION
        #define VENDOR_ENHANCED_LIB "vendor.qti.gnss@" LOC_HIDL_VERSION "-service.so"

        void* libHandle = NULL;
        vendorEnhancedServiceMain* vendorEnhancedMainMethod = (vendorEnhancedServiceMain*)
                dlGetSymFromLib(libHandle, VENDOR_ENHANCED_LIB, "main");
        if (NULL != vendorEnhancedMainMethod) {
            (*vendorEnhancedMainMethod)(0, NULL);
        }
    #else
        ALOGI("LOC_HIDL_VERSION not defined.");
    #endif
        joinRpcThreadpool();
    } else {
        ALOGE("Error while registering IGnss 2.1 service: %d", status);
    }

    return 0;
}


code:
LOCAL_PATH := $(call my-dir)
include $(call all-subdir-makefiles)


code:
cc_library_static {

    name: "liblocbatterylistener",
    vendor: true,

    cflags: GNSS_CFLAGS + ["-DBATTERY_LISTENER_ENABLED"],
    local_include_dirs: ["."],

    srcs: ["battery_listener.cpp"],

    shared_libs: [
        "liblog",
        "libhidlbase",
        "libcutils",
        "libutils",
        "android.hardware.health@1.0",
        "android.hardware.health@2.0",
        "android.hardware.health@2.1",
        "android.hardware.power@1.2",
        "libbase",
    ],

    static_libs: ["libhealthhalutils"],

    header_libs: [
        "libgps.utils_headers",
        "libloc_pla_headers",
    ],
}

cc_library_headers {

    name: "liblocbatterylistener_headers",
    export_include_dirs: ["."],
}


code:
/*
* Copyright (c) 2019-2020, 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.
*/
#include "battery_listener.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "LocSvc_BatteryListener"
#define LOG_NDEBUG 0

#include <android/hidl/manager/1.0/IServiceManager.h>
#include <android/hardware/health/2.1/IHealth.h>
#include <android/hardware/health/2.1/IHealthInfoCallback.h>
#include <healthhalutils/HealthHalUtils.h>
#include <hidl/HidlTransportSupport.h>
#include <thread>
#include <log_util.h>

using android::hardware::interfacesEqual;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::health::V1_0::BatteryStatus;
using android::hardware::health::V2_1::HealthInfo;
using android::hardware::health::V2_1::IHealthInfoCallback;
using android::hardware::health::V2_1::IHealth;
using android::hardware::health::V2_0::Result;
using android::hidl::manager::V1_0::IServiceManager;
using namespace std::literals::chrono_literals;

static bool sIsBatteryListened = false;
namespace android {

#define GET_HEALTH_SVC_RETRY_CNT 5
#define GET_HEALTH_SVC_WAIT_TIME_MS 500

struct BatteryListenerImpl : public hardware::health::V2_1::IHealthInfoCallback,
                             public hardware::hidl_death_recipient {
    typedef std::function<void(bool)> cb_fn_t;
    BatteryListenerImpl(cb_fn_t cb);
    virtual ~BatteryListenerImpl ();
    virtual hardware::Return<void> healthInfoChanged(
            const hardware::health::V2_0::HealthInfo& info);
    virtual hardware::Return<void> healthInfoChanged_2_1(
            const hardware::health::V2_1::HealthInfo& info);
    virtual void serviceDied(uint64_t cookie,
                             const wp<hidl::base::V1_0::IBase>& who);
    bool isCharging() {
        std::lock_guard<std::mutex> _l(mLock);
        return statusToBool(mStatus);
    }
  private:
    sp<hardware::health::V2_1::IHealth> mHealth;
    status_t init();
    BatteryStatus mStatus;
    cb_fn_t mCb;
    std::mutex mLock;
    std::condition_variable mCond;
    std::unique_ptr<std::thread> mThread;
    bool mDone;
    bool statusToBool(const BatteryStatus &s) const {
        return (s == BatteryStatus::CHARGING) ||
               (s ==  BatteryStatus::FULL);
    }
};

status_t BatteryListenerImpl::init()
{
    int tries = 0;

    if (mHealth != NULL)
        return INVALID_OPERATION;

    do {
        mHealth = IHealth::getService();
        if (mHealth != NULL)
            break;
        usleep(GET_HEALTH_SVC_WAIT_TIME_MS * 1000);
        tries++;
    } while(tries < GET_HEALTH_SVC_RETRY_CNT);

    if (mHealth == NULL) {
        LOC_LOGe("no health service found, retries %d", tries);
        return NO_INIT;
    } else {
        LOC_LOGi("Get health service in %d tries", tries);
    }
    mStatus = BatteryStatus::UNKNOWN;
    auto ret = mHealth->getChargeStatus([&](Result r, BatteryStatus status) {
        if (r != Result::SUCCESS) {
            LOC_LOGe("batterylistener: cannot get battery status");
            return;
        }
        mStatus = status;
    });
    if (!ret.isOk()) {
        LOC_LOGe("batterylistener: get charge status transaction error");
    }
    if (mStatus == BatteryStatus::UNKNOWN) {
        LOC_LOGw("batterylistener: init: invalid battery status");
    }
    mDone = false;
    mThread = std::make_unique<std::thread>([this]() {
            std::unique_lock<std::mutex> l(mLock);
            BatteryStatus local_status = mStatus;
            while (!mDone) {
                if (local_status == mStatus) {
                    mCond.wait(l);
                    continue;
                }
                local_status = mStatus;
                switch (local_status) {
                    // NOT_CHARGING is a special event that indicates, a battery is connected,
                    // but not charging. This is seen for approx a second
                    // after charger is plugged in. A charging event is eventually received.
                    // We must try to avoid an unnecessary cb to HAL
                    // only to call it again shortly.
                    // An option to deal with this transient event would be to ignore this.
                    // Or process this event with a slight delay (i.e cancel this event
                    // if a different event comes in within a timeout
                    case BatteryStatus::NOT_CHARGING : {
                        auto mStatusnot_ncharging =
                                [this, local_status]() { return mStatus != local_status; };
                        mCond.wait_for(l, 3s, mStatusnot_ncharging);
                        if (mStatusnot_ncharging()) // i.e event changed
                            break;
                        [[clang::fallthrough]]; //explicit fall-through between switch labels
                    }
                    default:
                        bool c = statusToBool(local_status);
                        LOC_LOGi("healthInfo cb thread: cb %s", c ? "CHARGING" : "NOT CHARGING");
                        l.unlock();
                        mCb(c);
                        l.lock();
                        break;
                }
            }
    });
    auto reg = mHealth->registerCallback(this);
    if (!reg.isOk()) {
        LOC_LOGe("Transaction error in registeringCb to HealthHAL death: %s",
                reg.description().c_str());
    }

    auto linked = mHealth->linkToDeath(this, 0 /* cookie */);
    if (!linked.isOk() || linked == false) {
        LOC_LOGe("Transaction error in linking to HealthHAL death: %s",
                linked.description().c_str());
    }
    return NO_ERROR;
}

BatteryListenerImpl::BatteryListenerImpl(cb_fn_t cb) :
        mCb(cb)
{
    init();
}

BatteryListenerImpl::~BatteryListenerImpl()
{
    {
        std::lock_guard<std::mutex> _l(mLock);
        if (mHealth != NULL)
            mHealth->unregisterCallback(this);
            auto r = mHealth->unlinkToDeath(this);
            if (!r.isOk() || r == false) {
                LOC_LOGe("Transaction error in unregister to HealthHAL death: %s",
                        r.description().c_str());
            }
    }
    mDone = true;
    mThread->join();
}

void BatteryListenerImpl::serviceDied(uint64_t cookie __unused,
                                     const wp<hidl::base::V1_0::IBase>& who)
{
    {
        std::lock_guard<std::mutex> _l(mLock);
        if (mHealth == NULL || !interfacesEqual(mHealth, who.promote())) {
            LOC_LOGe("health not initialized or unknown interface died");
            return;
        }
        LOC_LOGi("health service died, reinit");
        mDone = true;
    }
    mHealth = NULL;
    mCond.notify_one();
    mThread->join();
    std::lock_guard<std::mutex> _l(mLock);
    init();
}

// this callback seems to be a SYNC callback and so
// waits for return before next event is issued.
// therefore we need not have a queue to process
// NOT_CHARGING and CHARGING concurrencies.
// Replace single var by a list if this assumption is broken
Return<void> BatteryListenerImpl::healthInfoChanged(
        const hardware::health::V2_0::HealthInfo& info) {
    LOC_LOGv("healthInfoChanged: %d", info.legacy.batteryStatus);
    std::unique_lock<std::mutex> l(mLock);
    if (info.legacy.batteryStatus != mStatus) {
        mStatus = info.legacy.batteryStatus;
        mCond.notify_one();
    }
    return Void();
}

Return<void> BatteryListenerImpl::healthInfoChanged_2_1(
        const hardware::health::V2_1::HealthInfo& info) {
    LOC_LOGv("healthInfoChanged_2_1: %d", info.legacy.legacy.batteryStatus);
    healthInfoChanged(info.legacy);
    return Void();
}

static sp<BatteryListenerImpl> batteryListener;

bool batteryPropertiesListenerIsCharging() {
    return batteryListener->isCharging();
}

status_t batteryPropertiesListenerInit(BatteryListenerImpl::cb_fn_t cb) {
    batteryListener = new BatteryListenerImpl(cb);
    bool isCharging = batteryPropertiesListenerIsCharging();
    LOC_LOGv("charging status: %s charging", isCharging ? "" : "not");;
    if (isCharging) {
        cb(isCharging);
    }
    return NO_ERROR;
}

status_t batteryPropertiesListenerDeinit() {
    batteryListener.clear();
    return OK;
}

} // namespace android

void loc_extn_battery_properties_listener_init(battery_status_change_fn_t fn) {
    LOC_LOGv("loc_extn_battery_properties_listener_init entry");
    if (!sIsBatteryListened) {
        std::thread t1(android::batteryPropertiesListenerInit,
                [=](bool charging) { fn(charging); });
        t1.detach();
        sIsBatteryListened = true;
    }
}

void loc_extn_battery_properties_listener_deinit() {
    android::batteryPropertiesListenerDeinit();
}

bool loc_extn_battery_properties_is_charging() {
    return android::batteryPropertiesListenerIsCharging();
}


code:
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of The Linux Foundation nor the names of its
*       contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
typedef void (* battery_status_change_fn_t)(bool);
void loc_extn_battery_properties_listener_init(battery_status_change_fn_t fn);
void loc_extn_battery_properties_listener_deinit();
bool loc_extn_battery_properties_is_charging();


code:
cc_library_shared {

    name: "libbatching",
    vendor: true,

    shared_libs: [
        "libutils",
        "libcutils",
        "liblog",
        "libloc_core",
        "libgps.utils",
        "libdl",
    ],

    srcs: [
        "location_batching.cpp",
        "BatchingAdapter.cpp",
    ],

    header_libs: [
        "libgps.utils_headers",
        "libloc_core_headers",
        "libloc_pla_headers",
        "liblocation_api_headers",
    ],

    cflags: GNSS_CFLAGS,
}


code:
/* Copyright (c) 2017-2020, 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_BatchingAdapter"

#include <loc_pla.h>
#include <log_util.h>
#include <LocContext.h>
#include <BatchingAdapter.h>

using namespace loc_core;

BatchingAdapter::BatchingAdapter() :
    LocAdapterBase(0,
                   LocContext::getLocContext(LocContext::mLocationHalName),
                   false, nullptr, true),
    mOngoingTripDistance(0),
    mOngoingTripTBFInterval(0),
    mTripWithOngoingTBFDropped(false),
    mTripWithOngoingTripDistanceDropped(false),
    mBatchingTimeout(0),
    mBatchingAccuracy(1),
    mBatchSize(0),
    mTripBatchSize(0)
{
    LOC_LOGD("%s]: Constructor", __func__);
    readConfigCommand();
    setConfigCommand();

    // at last step, let us inform adapater base that we are done
    // with initialization, e.g.: ready to process handleEngineUpEvent
    doneInit();
}

void
BatchingAdapter::readConfigCommand()
{
    LOC_LOGD("%s]: ", __func__);

    struct MsgReadConfig : public LocMsg {
        BatchingAdapter& mAdapter;
        inline MsgReadConfig(BatchingAdapter& adapter) :
            LocMsg(),
            mAdapter(adapter) {}
        inline virtual void proc() const {
            uint32_t batchingTimeout = 0;
            uint32_t batchingAccuracy = 0;
            uint32_t batchSize = 0;
            uint32_t tripBatchSize = 0;
            static const loc_param_s_type flp_conf_param_table[] =
            {
                {"BATCH_SIZE", &batchSize, NULL, 'n'},
                {"OUTDOOR_TRIP_BATCH_SIZE", &tripBatchSize, NULL, 'n'},
                {"BATCH_SESSION_TIMEOUT", &batchingTimeout, NULL, 'n'},
                {"ACCURACY", &batchingAccuracy, NULL, 'n'},
            };
            UTIL_READ_CONF(LOC_PATH_FLP_CONF, flp_conf_param_table);

            LOC_LOGD("%s]: batchSize %u tripBatchSize %u batchingAccuracy %u batchingTimeout %u ",
                     __func__, batchSize, tripBatchSize, batchingAccuracy, batchingTimeout);

             mAdapter.setBatchSize(batchSize);
             mAdapter.setTripBatchSize(tripBatchSize);
             mAdapter.setBatchingTimeout(batchingTimeout);
             mAdapter.setBatchingAccuracy(batchingAccuracy);
        }
    };

    sendMsg(new MsgReadConfig(*this));

}

void
BatchingAdapter::setConfigCommand()
{
    LOC_LOGD("%s]: ", __func__);

    struct MsgSetConfig : public LocMsg {
        BatchingAdapter& mAdapter;
        LocApiBase& mApi;
        inline MsgSetConfig(BatchingAdapter& adapter,
                            LocApiBase& api) :
            LocMsg(),
            mAdapter(adapter),
            mApi(api) {}
        inline virtual void proc() const {
            mApi.setBatchSize(mAdapter.getBatchSize());
            mApi.setTripBatchSize(mAdapter.getTripBatchSize());
        }
    };

    sendMsg(new MsgSetConfig(*this, *mLocApi));
}

void
BatchingAdapter::stopClientSessions(LocationAPI* client)
{
    LOC_LOGD("%s]: client %p", __func__, client);

    typedef struct pairKeyBatchMode {
        LocationAPI* client;
        uint32_t id;
        BatchingMode batchingMode;
        inline pairKeyBatchMode(LocationAPI* _client, uint32_t _id, BatchingMode _bMode) :
            client(_client), id(_id), batchingMode(_bMode) {}
    } pairKeyBatchMode;
    std::vector<pairKeyBatchMode> vBatchingClient;
    for (auto it : mBatchingSessions) {
        if (client == it.first.client) {
            vBatchingClient.emplace_back(it.first.client, it.first.id, it.second.batchingMode);
        }
    }
    for (auto keyBatchingMode : vBatchingClient) {
        if (keyBatchingMode.batchingMode != BATCHING_MODE_TRIP) {
            stopBatching(keyBatchingMode.client, keyBatchingMode.id);
        } else {
            stopTripBatchingMultiplex(keyBatchingMode.client, keyBatchingMode.id);
        }
    }
}

void
BatchingAdapter::updateClientsEventMask()
{
    LOC_API_ADAPTER_EVENT_MASK_T mask = 0;
    for (auto it=mClientData.begin(); it != mClientData.end(); ++it) {
        // we don't register LOC_API_ADAPTER_BIT_BATCH_FULL until we
        // start batching with ROUTINE or TRIP option
        if (it->second.batchingCb != nullptr) {
            mask |= LOC_API_ADAPTER_BIT_BATCH_STATUS;
        }
    }
    if (autoReportBatchingSessionsCount() > 0) {
        mask |= LOC_API_ADAPTER_BIT_BATCH_FULL;
    }
    updateEvtMask(mask, LOC_REGISTRATION_MASK_SET);
}

void
BatchingAdapter::handleEngineUpEvent()
{
    struct MsgSSREvent : public LocMsg {
        BatchingAdapter& mAdapter;
        LocApiBase& mApi;
        inline MsgSSREvent(BatchingAdapter& adapter,
                           LocApiBase& api) :
            LocMsg(),
            mAdapter(adapter),
            mApi(api) {}
        virtual void proc() const {
            mAdapter.setEngineCapabilitiesKnown(true);
            mAdapter.broadcastCapabilities(mAdapter.getCapabilities());
            mApi.setBatchSize(mAdapter.getBatchSize());
            mApi.setTripBatchSize(mAdapter.getTripBatchSize());
            mAdapter.restartSessions();
            for (auto msg: mAdapter.mPendingMsgs) {
                mAdapter.sendMsg(msg);
            }
            mAdapter.mPendingMsgs.clear();
        }
    };

    sendMsg(new MsgSSREvent(*this, *mLocApi));
}

void
BatchingAdapter::restartSessions()
{
    LOC_LOGD("%s]: ", __func__);

    if (autoReportBatchingSessionsCount() > 0) {
        updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                      LOC_REGISTRATION_MASK_ENABLED);
    }
    for (auto it = mBatchingSessions.begin();
              it != mBatchingSessions.end(); ++it) {
        if (it->second.batchingMode != BATCHING_MODE_TRIP) {
            mLocApi->startBatching(it->first.id, it->second,
                                    getBatchingAccuracy(), getBatchingTimeout(),
                                    new LocApiResponse(*getContext(),
                                    [] (LocationError /*err*/) {}));
        }
    }

    if (mTripSessions.size() > 0) {
        // restart outdoor trip batching session if any.
        mOngoingTripDistance = 0;
        mOngoingTripTBFInterval = 0;

        // record the min trip distance and min tbf interval of all ongoing sessions
        for (auto tripSession : mTripSessions) {

            TripSessionStatus &tripSessStatus = tripSession.second;

            if ((0 == mOngoingTripDistance) ||
                (mOngoingTripDistance >
                 (tripSessStatus.tripDistance - tripSessStatus.accumulatedDistanceThisTrip))) {
                mOngoingTripDistance = tripSessStatus.tripDistance -
                    tripSessStatus.accumulatedDistanceThisTrip;
            }

            if ((0 == mOngoingTripTBFInterval) ||
                (mOngoingTripTBFInterval > tripSessStatus.tripTBFInterval)) {
                mOngoingTripTBFInterval = tripSessStatus.tripTBFInterval;
            }

            // reset the accumulatedDistanceOngoingBatch for each session
            tripSessStatus.accumulatedDistanceOngoingBatch = 0;

        }

        mLocApi->startOutdoorTripBatching(mOngoingTripDistance, mOngoingTripTBFInterval,
                getBatchingTimeout(), new LocApiResponse(*getContext(), [this] (LocationError err) {
            if (LOCATION_ERROR_SUCCESS != err) {
                mOngoingTripDistance = 0;
                mOngoingTripTBFInterval = 0;
            }
            printTripReport();
        }));
    }
}

bool
BatchingAdapter::hasBatchingCallback(LocationAPI* client)
{
    auto it = mClientData.find(client);
    return (it != mClientData.end() && it->second.batchingCb);
}

bool
BatchingAdapter::isBatchingSession(LocationAPI* client, uint32_t sessionId)
{
    LocationSessionKey key(client, sessionId);
    return (mBatchingSessions.find(key) != mBatchingSessions.end());
}

bool
BatchingAdapter::isTripSession(uint32_t sessionId) {
    return (mTripSessions.find(sessionId) != mTripSessions.end());
}

void
BatchingAdapter::saveBatchingSession(LocationAPI* client, uint32_t sessionId,
        const BatchingOptions& batchingOptions)
{
    LocationSessionKey key(client, sessionId);
    mBatchingSessions[key] = batchingOptions;
}

void
BatchingAdapter::eraseBatchingSession(LocationAPI* client, uint32_t sessionId)
{
    LocationSessionKey key(client, sessionId);
    auto it = mBatchingSessions.find(key);
    if (it != mBatchingSessions.end()) {
        mBatchingSessions.erase(it);
    }
}

void
BatchingAdapter::reportResponse(LocationAPI* client, LocationError err, uint32_t sessionId)
{
    LOC_LOGD("%s]: client %p id %u err %u", __func__, client, sessionId, err);

    auto it = mClientData.find(client);
    if (it != mClientData.end() &&
        it->second.responseCb != nullptr) {
        it->second.responseCb(err, sessionId);
    } else {
        LOC_LOGE("%s]: client %p id %u not found in data", __func__, client, sessionId);
    }
}

uint32_t
BatchingAdapter::autoReportBatchingSessionsCount()
{
    uint32_t count = 0;
    for (auto batchingSession: mBatchingSessions) {
        if (batchingSession.second.batchingMode != BATCHING_MODE_NO_AUTO_REPORT) {
            count++;
        }
    }
    count += mTripSessions.size();
    return count;
}

uint32_t
BatchingAdapter::startBatchingCommand(
        LocationAPI* client, BatchingOptions& batchOptions)
{
    uint32_t sessionId = generateSessionId();
    LOC_LOGD("%s]: client %p id %u minInterval %u minDistance %u mode %u Batching Mode %d",
             __func__, client, sessionId, batchOptions.minInterval, batchOptions.minDistance,
             batchOptions.mode,batchOptions.batchingMode);

    struct MsgStartBatching : public LocMsg {
        BatchingAdapter& mAdapter;
        LocApiBase& mApi;
        LocationAPI* mClient;
        uint32_t mSessionId;
        BatchingOptions mBatchingOptions;
        inline MsgStartBatching(BatchingAdapter& adapter,
                               LocApiBase& api,
                               LocationAPI* client,
                               uint32_t sessionId,
                               BatchingOptions batchOptions) :
            LocMsg(),
            mAdapter(adapter),
            mApi(api),
            mClient(client),
            mSessionId(sessionId),
            mBatchingOptions(batchOptions) {}
        inline virtual void proc() const {
            if (!mAdapter.isEngineCapabilitiesKnown()) {
                mAdapter.mPendingMsgs.push_back(new MsgStartBatching(*this));
                return;
            }
            LocationError err = LOCATION_ERROR_SUCCESS;

            if (!mAdapter.hasBatchingCallback(mClient)) {
                err = LOCATION_ERROR_CALLBACK_MISSING;
            } else if (0 == mBatchingOptions.size) {
                err = LOCATION_ERROR_INVALID_PARAMETER;
            } else if (!ContextBase::isMessageSupported(
                       LOC_API_ADAPTER_MESSAGE_DISTANCE_BASE_LOCATION_BATCHING)) {
                err = LOCATION_ERROR_NOT_SUPPORTED;
            }
            if (LOCATION_ERROR_SUCCESS == err) {
                if (mBatchingOptions.batchingMode == BATCHING_MODE_ROUTINE ||
                    mBatchingOptions.batchingMode == BATCHING_MODE_NO_AUTO_REPORT) {
                    mAdapter.startBatching(mClient, mSessionId, mBatchingOptions);
                } else if (mBatchingOptions.batchingMode == BATCHING_MODE_TRIP) {
                    mAdapter.startTripBatchingMultiplex(mClient, mSessionId, mBatchingOptions);
                } else {
                    mAdapter.reportResponse(mClient, LOCATION_ERROR_INVALID_PARAMETER, mSessionId);
                }
            }
        }
    };

    sendMsg(new MsgStartBatching(*this, *mLocApi, client, sessionId, batchOptions));

    return sessionId;
}

void
BatchingAdapter::startBatching(LocationAPI* client, uint32_t sessionId,
        const BatchingOptions& batchingOptions)
{
    if (batchingOptions.batchingMode != BATCHING_MODE_NO_AUTO_REPORT &&
        0 == autoReportBatchingSessionsCount()) {
        // if there is currenty no batching sessions interested in batch full event, then this
        // new session will need to register for batch full event
        updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                      LOC_REGISTRATION_MASK_ENABLED);
    }

    // Assume start will be OK, remove session if not
    saveBatchingSession(client, sessionId, batchingOptions);
    mLocApi->startBatching(sessionId, batchingOptions, getBatchingAccuracy(), getBatchingTimeout(),
            new LocApiResponse(*getContext(),
            [this, client, sessionId, batchingOptions] (LocationError err) {
        if (LOCATION_ERROR_SUCCESS != err) {
            eraseBatchingSession(client, sessionId);
        }

        if (LOCATION_ERROR_SUCCESS != err &&
            batchingOptions.batchingMode != BATCHING_MODE_NO_AUTO_REPORT &&
            0 == autoReportBatchingSessionsCount()) {
            // if we fail to start batching and we have already registered batch full event
            // we need to undo that since no sessions are now interested in batch full event
            updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                          LOC_REGISTRATION_MASK_DISABLED);
        }

        reportResponse(client, err, sessionId);
    }));
}

void
BatchingAdapter::updateBatchingOptionsCommand(LocationAPI* client, uint32_t id,
        BatchingOptions& batchOptions)
{
    LOC_LOGD("%s]: client %p id %u minInterval %u minDistance %u mode %u batchMode %u",
             __func__, client, id, batchOptions.minInterval,
             batchOptions.minDistance, batchOptions.mode,
             batchOptions.batchingMode);

    struct MsgUpdateBatching : public LocMsg {
        BatchingAdapter& mAdapter;
        LocApiBase& mApi;
        LocationAPI* mClient;
        uint32_t mSessionId;
        BatchingOptions mBatchOptions;
        inline MsgUpdateBatching(BatchingAdapter& adapter,
                                LocApiBase& api,
                                LocationAPI* client,
                                uint32_t sessionId,
                                BatchingOptions batchOptions) :
            LocMsg(),
            mAdapter(adapter),
            mApi(api),
            mClient(client),
            mSessionId(sessionId),
            mBatchOptions(batchOptions) {}
        inline virtual void proc() const {
            if (!mAdapter.isEngineCapabilitiesKnown()) {
                mAdapter.mPendingMsgs.push_back(new MsgUpdateBatching(*this));
                return;
            }
            LocationError err = LOCATION_ERROR_SUCCESS;
            if (!mAdapter.isBatchingSession(mClient, mSessionId)) {
                err = LOCATION_ERROR_ID_UNKNOWN;
            } else if ((0 == mBatchOptions.size) ||
                       (mBatchOptions.batchingMode > BATCHING_MODE_NO_AUTO_REPORT)) {
                err = LOCATION_ERROR_INVALID_PARAMETER;
            }
            if (LOCATION_ERROR_SUCCESS == err) {
                if (!mAdapter.isTripSession(mSessionId)) {
                    mAdapter.stopBatching(mClient, mSessionId, true, mBatchOptions);
                } else {
                    mAdapter.stopTripBatchingMultiplex(mClient, mSessionId, true, mBatchOptions);
                }
           }
        }
    };

    sendMsg(new MsgUpdateBatching(*this, *mLocApi, client, id, batchOptions));
}

void
BatchingAdapter::stopBatchingCommand(LocationAPI* client, uint32_t id)
{
    LOC_LOGD("%s]: client %p id %u", __func__, client, id);

    struct MsgStopBatching : public LocMsg {
        BatchingAdapter& mAdapter;
        LocApiBase& mApi;
        LocationAPI* mClient;
        uint32_t mSessionId;
        inline MsgStopBatching(BatchingAdapter& adapter,
                               LocApiBase& api,
                               LocationAPI* client,
                               uint32_t sessionId) :
            LocMsg(),
            mAdapter(adapter),
            mApi(api),
            mClient(client),
            mSessionId(sessionId) {}
        inline virtual void proc() const {
            if (!mAdapter.isEngineCapabilitiesKnown()) {
                mAdapter.mPendingMsgs.push_back(new MsgStopBatching(*this));
                return;
            }
            LocationError err = LOCATION_ERROR_SUCCESS;
            if (!mAdapter.isBatchingSession(mClient, mSessionId)) {
                err = LOCATION_ERROR_ID_UNKNOWN;
            }
            if (LOCATION_ERROR_SUCCESS == err) {
                if (mAdapter.isTripSession(mSessionId)) {
                    mAdapter.stopTripBatchingMultiplex(mClient, mSessionId);
                } else {
                    mAdapter.stopBatching(mClient, mSessionId);
                }
            }
        }
    };

    sendMsg(new MsgStopBatching(*this, *mLocApi, client, id));
}

void
BatchingAdapter::stopBatching(LocationAPI* client, uint32_t sessionId, bool restartNeeded,
        const BatchingOptions& batchOptions)
{
    LocationSessionKey key(client, sessionId);
    auto it = mBatchingSessions.find(key);
    if (it != mBatchingSessions.end()) {
        auto flpOptions = it->second;
        // Assume stop will be OK, restore session if not
        eraseBatchingSession(client, sessionId);
        mLocApi->stopBatching(sessionId,
                new LocApiResponse(*getContext(),
                [this, client, sessionId, flpOptions, restartNeeded, batchOptions]
                (LocationError err) {
            if (LOCATION_ERROR_SUCCESS != err) {
                saveBatchingSession(client, sessionId, batchOptions);
            } else {
                // if stopBatching is success, unregister for batch full event if this was the last
                // batching session that is interested in batch full event
                if (0 == autoReportBatchingSessionsCount() &&
                    flpOptions.batchingMode != BATCHING_MODE_NO_AUTO_REPORT) {
                    updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                                  LOC_REGISTRATION_MASK_DISABLED);
                }

                if (restartNeeded) {
                    if (batchOptions.batchingMode == BATCHING_MODE_ROUTINE ||
                            batchOptions.batchingMode == BATCHING_MODE_NO_AUTO_REPORT) {
                        startBatching(client, sessionId, batchOptions);
                    } else if (batchOptions.batchingMode == BATCHING_MODE_TRIP) {
                        startTripBatchingMultiplex(client, sessionId, batchOptions);
                    }
                }
            }
            reportResponse(client, err, sessionId);
        }));
    }
}

void
BatchingAdapter::getBatchedLocationsCommand(LocationAPI* client, uint32_t id, size_t count)
{
    LOC_LOGD("%s]: client %p id %u count %zu", __func__, client, id, count);

    struct MsgGetBatchedLocations : public LocMsg {
        BatchingAdapter& mAdapter;
        LocApiBase& mApi;
        LocationAPI* mClient;
        uint32_t mSessionId;
        size_t mCount;
        inline MsgGetBatchedLocations(BatchingAdapter& adapter,
                                     LocApiBase& api,
                                     LocationAPI* client,
                                     uint32_t sessionId,
                                     size_t count) :
            LocMsg(),
            mAdapter(adapter),
            mApi(api),
            mClient(client),
            mSessionId(sessionId),
            mCount(count) {}
        inline virtual void proc() const {
            if (!mAdapter.isEngineCapabilitiesKnown()) {
                mAdapter.mPendingMsgs.push_back(new MsgGetBatchedLocations(*this));
                return;
            }
            LocationError err = LOCATION_ERROR_SUCCESS;
            if (!mAdapter.hasBatchingCallback(mClient)) {
                err = LOCATION_ERROR_CALLBACK_MISSING;
            } else if (!mAdapter.isBatchingSession(mClient, mSessionId)) {
                err = LOCATION_ERROR_ID_UNKNOWN;
            }
            if (LOCATION_ERROR_SUCCESS == err) {
                if (mAdapter.isTripSession(mSessionId)) {
                    mApi.getBatchedTripLocations(mCount, 0,
                            new LocApiResponse(*mAdapter.getContext(),
                            [&mAdapter = mAdapter, mSessionId = mSessionId,
                            mClient = mClient] (LocationError err) {
                        mAdapter.reportResponse(mClient, err, mSessionId);
                    }));
                } else {
                    mApi.getBatchedLocations(mCount, new LocApiResponse(*mAdapter.getContext(),
                            [&mAdapter = mAdapter, mSessionId = mSessionId,
                            mClient = mClient] (LocationError err) {
                        mAdapter.reportResponse(mClient, err, mSessionId);
                    }));
                }
            } else {
                mAdapter.reportResponse(mClient, err, mSessionId);
            }
        }
    };

    sendMsg(new MsgGetBatchedLocations(*this, *mLocApi, client, id, count));
}

void
BatchingAdapter::reportLocationsEvent(const Location* locations, size_t count,
        BatchingMode batchingMode)
{
    LOC_LOGD("%s]: count %zu batchMode %d", __func__, count, batchingMode);

    struct MsgReportLocations : public LocMsg {
        BatchingAdapter& mAdapter;
        Location* mLocations;
        size_t mCount;
        BatchingMode mBatchingMode;
        inline MsgReportLocations(BatchingAdapter& adapter,
                                  const Location* locations,
                                  size_t count,
                                  BatchingMode batchingMode) :
            LocMsg(),
            mAdapter(adapter),
            mLocations(new Location[count]),
            mCount(count),
            mBatchingMode(batchingMode)
        {
            if (nullptr == mLocations) {
                LOC_LOGE("%s]: new failed to allocate mLocations", __func__);
                return;
            }
            for (size_t i=0; i < mCount; ++i) {
                mLocations[i] = locations[i];
            }
        }
        inline virtual ~MsgReportLocations() {
            if (nullptr != mLocations)
                delete[] mLocations;
        }
        inline virtual void proc() const {
            mAdapter.reportLocations(mLocations, mCount, mBatchingMode);
        }
    };

    sendMsg(new MsgReportLocations(*this, locations, count, batchingMode));
}

void
BatchingAdapter::reportLocations(Location* locations, size_t count, BatchingMode batchingMode)
{
    BatchingOptions batchOptions = {sizeof(BatchingOptions), batchingMode};

    for (auto it=mClientData.begin(); it != mClientData.end(); ++it) {
        if (nullptr != it->second.batchingCb) {
            it->second.batchingCb(count, locations, batchOptions);
        }
    }
}

void
BatchingAdapter::reportCompletedTripsEvent(uint32_t accumulated_distance)
{
    struct MsgReportCompletedTrips : public LocMsg {
        BatchingAdapter& mAdapter;
        uint32_t mAccumulatedDistance;
        inline MsgReportCompletedTrips(BatchingAdapter& adapter,
                                  uint32_t accumulated_distance) :
            LocMsg(),
            mAdapter(adapter),
            mAccumulatedDistance(accumulated_distance)
        {
        }
        inline virtual ~MsgReportCompletedTrips() {
        }
        inline virtual void proc() const {

            // Check if any trips are completed
            std::list<uint32_t> completedTripsList;
            completedTripsList.clear();

            for(auto itt = mAdapter.mTripSessions.begin(); itt != mAdapter.mTripSessions.end();)
            {
                TripSessionStatus &tripSession = itt->second;

                tripSession.accumulatedDistanceThisTrip =
                        tripSession.accumulatedDistanceOnTripRestart
                        + (mAccumulatedDistance - tripSession.accumulatedDistanceOngoingBatch);
                if (tripSession.tripDistance <= tripSession.accumulatedDistanceThisTrip) {
                    // trip is completed
                    completedTripsList.push_back(itt->first);
                    itt = mAdapter.mTripSessions.erase(itt);

                    if (tripSession.tripTBFInterval == mAdapter.mOngoingTripTBFInterval) {
                        // trip with ongoing TBF interval is completed
                        mAdapter.mTripWithOngoingTBFDropped = true;
                    }

                    if (tripSession.tripDistance == mAdapter.mOngoingTripDistance) {
                        // trip with ongoing trip distance is completed
                        mAdapter.mTripWithOngoingTripDistanceDropped = true;
                    }
                } else {
                    itt++;
                }
            }

            if (completedTripsList.size() > 0) {
                mAdapter.reportBatchStatusChange(BATCHING_STATUS_TRIP_COMPLETED,
                        completedTripsList);
                mAdapter.restartTripBatching(false, mAccumulatedDistance, 0);
            } else {
                mAdapter.printTripReport();
            }
        }
    };

    LOC_LOGD("%s]: Accumulated Distance so far: %u",
               __func__,  accumulated_distance);

    sendMsg(new MsgReportCompletedTrips(*this, accumulated_distance));
}

void
BatchingAdapter::reportBatchStatusChange(BatchingStatus batchStatus,
        std::list<uint32_t> & completedTripsList)
{
    BatchingStatusInfo batchStatusInfo =
            {sizeof(BatchingStatusInfo), batchStatus};

    for (auto it=mClientData.begin(); it != mClientData.end(); ++it) {
        if (nullptr != it->second.batchingStatusCb) {
            it->second.batchingStatusCb(batchStatusInfo, completedTripsList);
        }
    }
}

void
BatchingAdapter::reportBatchStatusChangeEvent(BatchingStatus batchStatus)
{
    struct MsgReportBatchStatus : public LocMsg {
        BatchingAdapter& mAdapter;
        BatchingStatus mBatchStatus;
        inline MsgReportBatchStatus(BatchingAdapter& adapter,
                BatchingStatus batchStatus) :
            LocMsg(),
            mAdapter(adapter),
            mBatchStatus(batchStatus)
        {
        }
        inline virtual ~MsgReportBatchStatus() {
        }
        inline virtual void proc() const {
            std::list<uint32_t> tempList;
            tempList.clear();
            mAdapter.reportBatchStatusChange(mBatchStatus, tempList);
        }
    };

    sendMsg(new MsgReportBatchStatus(*this, batchStatus));
}

void
BatchingAdapter::startTripBatchingMultiplex(LocationAPI* client, uint32_t sessionId,
        const BatchingOptions& batchingOptions)
{
    if (mTripSessions.size() == 0) {
        // if there is currenty no batching sessions interested in batch full event, then this
        // new session will need to register for batch full event
        if (0 == autoReportBatchingSessionsCount()) {
            updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                          LOC_REGISTRATION_MASK_ENABLED);
        }

        // Assume start will be OK, remove session if not
        saveBatchingSession(client, sessionId, batchingOptions);

        mTripSessions[sessionId] = { 0, 0, 0, batchingOptions.minDistance,
                batchingOptions.minInterval};
        mLocApi->startOutdoorTripBatching(batchingOptions.minDistance,
                batchingOptions.minInterval, getBatchingTimeout(), new LocApiResponse(*getContext(),
                [this, client, sessionId, batchingOptions] (LocationError err) {
            if (err == LOCATION_ERROR_SUCCESS) {
                mOngoingTripDistance = batchingOptions.minDistance;
                mOngoingTripTBFInterval = batchingOptions.minInterval;
                LOC_LOGD("%s] New Trip started ...", __func__);
                printTripReport();
            } else {
                eraseBatchingSession(client, sessionId);
                mTripSessions.erase(sessionId);
                // if we fail to start batching and we have already registered batch full event
                // we need to undo that since no sessions are now interested in batch full event
                if (0 == autoReportBatchingSessionsCount()) {
                    updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                                  LOC_REGISTRATION_MASK_DISABLED);
                }
            }
            reportResponse(client, err, sessionId);
        }));
    } else {
        // query accumulated distance
        mLocApi->queryAccumulatedTripDistance(
                new LocApiResponseData<LocApiBatchData>(*getContext(),
                [this, batchingOptions, sessionId, client]
                (LocationError err, LocApiBatchData data) {
            uint32_t accumulatedDistanceOngoingBatch = 0;
            uint32_t numOfBatchedPositions = 0;
            uint32_t ongoingTripDistance = mOngoingTripDistance;
            uint32_t ongoingTripInterval = mOngoingTripTBFInterval;
            bool needsRestart = false;

            // check if TBF of new session is lesser than ongoing TBF interval
            if (ongoingTripInterval > batchingOptions.minInterval) {
                ongoingTripInterval = batchingOptions.minInterval;
                needsRestart = true;
            }
            accumulatedDistanceOngoingBatch = data.accumulatedDistance;
            numOfBatchedPositions = data.numOfBatchedPositions;
            TripSessionStatus newTripSession = { accumulatedDistanceOngoingBatch, 0, 0,
                                                 batchingOptions.minDistance,
                                                 batchingOptions.minInterval};
            if (err != LOCATION_ERROR_SUCCESS) {
                // unable to query accumulated distance, assume remaining distance in
                // ongoing batch is mongoingTripDistance.
                if (batchingOptions.minDistance < ongoingTripDistance) {
                    ongoingTripDistance = batchingOptions.minDistance;
                    needsRestart = true;
                }
            } else {
                // compute the remaining distance
                uint32_t ongoing_trip_remaining_distance = ongoingTripDistance -
                        accumulatedDistanceOngoingBatch;

                // check if new trip distance is lesser than the ongoing batch remaining distance
                if (batchingOptions.minDistance < ongoing_trip_remaining_distance) {
                    ongoingTripDistance = batchingOptions.minDistance;
                    needsRestart = true;
                } else if (needsRestart == true) {
                    // needsRestart is anyways true , may be because of lesser TBF of new session.
                    ongoingTripDistance = ongoing_trip_remaining_distance;
                }
                mTripSessions[sessionId] = newTripSession;
                LOC_LOGD("%s] New Trip started ...", __func__);
                printTripReport();
            }

            if (needsRestart) {
                mOngoingTripDistance = ongoingTripDistance;
                mOngoingTripTBFInterval = ongoingTripInterval;

                // reset the accumulatedDistanceOngoingBatch for each session,
                // and record the total accumulated distance so far for the session.
                for (auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) {
                    TripSessionStatus &tripSessStatus = itt->second;
                    tripSessStatus.accumulatedDistanceOngoingBatch = 0;
                    tripSessStatus.accumulatedDistanceOnTripRestart =
                            tripSessStatus.accumulatedDistanceThisTrip;
                }
                mLocApi->reStartOutdoorTripBatching(ongoingTripDistance, ongoingTripInterval,
                        getBatchingTimeout(), new LocApiResponse(*getContext(),
                        [this, client, sessionId] (LocationError err) {
                    if (err != LOCATION_ERROR_SUCCESS) {
                        LOC_LOGE("%s] New Trip restart failed!", __func__);
                    }
                    reportResponse(client, err, sessionId);
                }));
            } else {
                reportResponse(client, LOCATION_ERROR_SUCCESS, sessionId);
            }
        }));
    }
}

void
BatchingAdapter::stopTripBatchingMultiplex(LocationAPI* client, uint32_t sessionId,
        bool restartNeeded, const BatchingOptions& batchOptions)
{
    LocationError err = LOCATION_ERROR_SUCCESS;

    if (mTripSessions.size() == 1) {
        mLocApi->stopOutdoorTripBatching(true, new LocApiResponse(*getContext(),
                [this, restartNeeded, client, sessionId, batchOptions]
                (LocationError err) {
            if (LOCATION_ERROR_SUCCESS == err) {
                // if stopOutdoorTripBatching is success, unregister for batch full event if this
                // was the last batching session that is interested in batch full event
                if (1 == autoReportBatchingSessionsCount()) {
                    updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                                  LOC_REGISTRATION_MASK_DISABLED);
                }
            }
            stopTripBatchingMultiplexCommon(err, client, sessionId, restartNeeded, batchOptions);
        }));
        return;
    }

    stopTripBatchingMultiplexCommon(err, client, sessionId, restartNeeded, batchOptions);
}

void
BatchingAdapter::stopTripBatchingMultiplexCommon(LocationError err, LocationAPI* client,
        uint32_t sessionId, bool restartNeeded, const BatchingOptions& batchOptions)
{
    auto itt = mTripSessions.find(sessionId);
    TripSessionStatus tripSess = itt->second;
    if (tripSess.tripTBFInterval == mOngoingTripTBFInterval) {
        // trip with ongoing trip interval is stopped
        mTripWithOngoingTBFDropped = true;
    }

    if (tripSess.tripDistance == mOngoingTripDistance) {
        // trip with ongoing trip distance is stopped
        mTripWithOngoingTripDistanceDropped = true;
    }

    mTripSessions.erase(sessionId);

    if (mTripSessions.size() == 0) {
        mOngoingTripDistance = 0;
        mOngoingTripTBFInterval = 0;
    } else {
        restartTripBatching(true);
    }

    if (restartNeeded) {
        eraseBatchingSession(client, sessionId);
        if (batchOptions.batchingMode == BATCHING_MODE_ROUTINE ||
                batchOptions.batchingMode == BATCHING_MODE_NO_AUTO_REPORT) {
            startBatching(client, sessionId, batchOptions);
        } else if (batchOptions.batchingMode == BATCHING_MODE_TRIP) {
            startTripBatchingMultiplex(client, sessionId, batchOptions);
        }
    }
    reportResponse(client, err, sessionId);
}

void
BatchingAdapter::restartTripBatching(bool queryAccumulatedDistance, uint32_t accDist,
        uint32_t numbatchedPos)
{
    // does batch need restart with new trip distance / TBF interval
    uint32_t minRemainingDistance = 0;
    uint32_t minTBFInterval = 0;

    // if no more trips left, stop the ongoing trip
    if (mTripSessions.size() == 0) {
        mLocApi->stopOutdoorTripBatching(true, new LocApiResponse(*getContext(),
                                               [] (LocationError /*err*/) {}));
        mOngoingTripDistance = 0;
        mOngoingTripTBFInterval = 0;
        // unregister for batch full event if there are no more
        // batching session that is interested in batch full event
        if (0 == autoReportBatchingSessionsCount()) {
                updateEvtMask(LOC_API_ADAPTER_BIT_BATCH_FULL,
                              LOC_REGISTRATION_MASK_DISABLED);
        }
        return;
    }

    // record the min trip distance and min tbf interval of all ongoing sessions
    for (auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) {

        TripSessionStatus tripSessStatus = itt->second;

        if ((minRemainingDistance == 0) ||
                (minRemainingDistance > (tripSessStatus.tripDistance
                - tripSessStatus.accumulatedDistanceThisTrip))) {
            minRemainingDistance = tripSessStatus.tripDistance -
                    tripSessStatus.accumulatedDistanceThisTrip;
        }

        if ((minTBFInterval == 0) ||
            (minTBFInterval > tripSessStatus.tripTBFInterval)) {
            minTBFInterval = tripSessStatus.tripTBFInterval;
        }
    }

    mLocApi->queryAccumulatedTripDistance(
            new LocApiResponseData<LocApiBatchData>(*getContext(),
            [this, queryAccumulatedDistance, minRemainingDistance, minTBFInterval, accDist,
            numbatchedPos] (LocationError /*err*/, LocApiBatchData data) {
        bool needsRestart = false;

        uint32_t ongoingTripDistance = mOngoingTripDistance;
        uint32_t ongoingTripInterval = mOngoingTripTBFInterval;
        uint32_t accumulatedDistance = accDist;
        uint32_t numOfBatchedPositions = numbatchedPos;

        if (queryAccumulatedDistance) {
            accumulatedDistance = data.accumulatedDistance;
            numOfBatchedPositions = data.numOfBatchedPositions;
        }

        if ((!mTripWithOngoingTripDistanceDropped) &&
                (ongoingTripDistance - accumulatedDistance != 0)) {
            // if ongoing trip is already not completed still,
            // check the min distance against the remaining distance
            if (minRemainingDistance <
                    (ongoingTripDistance - accumulatedDistance)) {
                ongoingTripDistance = minRemainingDistance;
                needsRestart = true;
            }
        } else if (minRemainingDistance != 0) {
            // else if ongoing trip is already completed / dropped,
            // use the minRemainingDistance of ongoing sessions
            ongoingTripDistance = minRemainingDistance;
            needsRestart = true;
        }

         if ((minTBFInterval < ongoingTripInterval) ||
                    ((minTBFInterval != ongoingTripInterval) &&
                    (mTripWithOngoingTBFDropped))) {
            ongoingTripInterval = minTBFInterval;
            needsRestart = true;
        }

        if (needsRestart) {
            mLocApi->reStartOutdoorTripBatching(ongoingTripDistance, ongoingTripInterval,
                    getBatchingTimeout(), new LocApiResponse(*getContext(),
                    [this, accumulatedDistance, ongoingTripDistance, ongoingTripInterval]
                    (LocationError err) {

                if (err == LOCATION_ERROR_SUCCESS) {
                    for(auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) {
                        TripSessionStatus &tripSessStatus = itt->second;
                        tripSessStatus.accumulatedDistanceThisTrip =
                                tripSessStatus.accumulatedDistanceOnTripRestart +
                                (accumulatedDistance -
                                 tripSessStatus.accumulatedDistanceOngoingBatch);

                        tripSessStatus.accumulatedDistanceOngoingBatch = 0;
                        tripSessStatus.accumulatedDistanceOnTripRestart =
                                tripSessStatus.accumulatedDistanceThisTrip;
                    }

                    mOngoingTripDistance = ongoingTripDistance;
                    mOngoingTripTBFInterval = ongoingTripInterval;
                }
            }));
        }
    }));
}

void
BatchingAdapter::printTripReport()
{
    IF_LOC_LOGD {
        LOC_LOGD("Ongoing Trip Distance = %u, Ongoing Trip TBF Interval = %u",
                mOngoingTripDistance, mOngoingTripTBFInterval);

        for (auto itt = mTripSessions.begin(); itt != mTripSessions.end(); itt++) {
            TripSessionStatus tripSessStatus = itt->second;

            LOC_LOGD("tripDistance:%u tripTBFInterval:%u"
                    " trip accumulated Distance:%u"
                    " trip accumualted distance ongoing batch:%u"
                    " trip accumulated distance on trip restart %u \r\n",
                    tripSessStatus.tripDistance, tripSessStatus.tripTBFInterval,
                    tripSessStatus.accumulatedDistanceThisTrip,
                    tripSessStatus.accumulatedDistanceOngoingBatch,
                    tripSessStatus.accumulatedDistanceOnTripRestart);
        }
    }
}