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 BATCHING_API_CLINET_H
#define BATCHING_API_CLINET_H

#include <mutex>
#include <android/hardware/gnss/2.0/IGnssBatching.h>
#include <android/hardware/gnss/2.0/IGnssBatchingCallback.h>
#include <pthread.h>

#include <LocationAPIClientBase.h>

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

enum BATCHING_STATE { STARTED, STOPPING, STOPPED };

class BatchingAPIClient : public LocationAPIClientBase
{
public:
    BatchingAPIClient(const sp<V1_0::IGnssBatchingCallback>& callback);
    BatchingAPIClient(const sp<V2_0::IGnssBatchingCallback>& callback);
    void gnssUpdateCallbacks(const sp<V1_0::IGnssBatchingCallback>& callback);
    void gnssUpdateCallbacks_2_0(const sp<V2_0::IGnssBatchingCallback>& callback);
    int getBatchSize();
    int startSession(const V1_0::IGnssBatching::Options& options);
    int updateSessionOptions(const V1_0::IGnssBatching::Options& options);
    int stopSession();
    void getBatchedLocation(int last_n_locations);
    void flushBatchedLocations();

    inline LocationCapabilitiesMask getCapabilities() { return mLocationCapabilitiesMask; }

    // callbacks
    void onCapabilitiesCb(LocationCapabilitiesMask capabilitiesMask) final;
    void onBatchingCb(size_t count, Location* location, BatchingOptions batchOptions) final;

private:
    ~BatchingAPIClient();

    void setCallbacks();
    std::mutex mMutex;
    sp<V1_0::IGnssBatchingCallback> mGnssBatchingCbIface;
    uint32_t mDefaultId;
    LocationCapabilitiesMask mLocationCapabilitiesMask;
    sp<V2_0::IGnssBatchingCallback> mGnssBatchingCbIface_2_0;
    volatile BATCHING_STATE mState = STOPPED;

    std::vector<Location> mBatchedLocationInCache;
};

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


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_GeofenceApiClient"

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

#include "LocationUtil.h"
#include "GeofenceAPIClient.h"

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

using ::android::hardware::gnss::V1_0::IGnssGeofenceCallback;
using ::android::hardware::gnss::V1_0::GnssLocation;

GeofenceAPIClient::GeofenceAPIClient(const sp<IGnssGeofenceCallback>& callback) :
    LocationAPIClientBase(),
    mGnssGeofencingCbIface(callback)
{
    LOC_LOGD("%s]: (%p)", __FUNCTION__, &callback);

    LocationCallbacks locationCallbacks;
    memset(&locationCallbacks, 0, sizeof(LocationCallbacks));
    locationCallbacks.size = sizeof(LocationCallbacks);

    locationCallbacks.trackingCb = nullptr;
    locationCallbacks.batchingCb = nullptr;

    locationCallbacks.geofenceBreachCb = nullptr;
    if (mGnssGeofencingCbIface != nullptr) {
        locationCallbacks.geofenceBreachCb =
            [this](GeofenceBreachNotification geofenceBreachNotification) {
                onGeofenceBreachCb(geofenceBreachNotification);
            };

        locationCallbacks.geofenceStatusCb =
            [this](GeofenceStatusNotification geofenceStatusNotification) {
                onGeofenceStatusCb(geofenceStatusNotification);
            };
    }

    locationCallbacks.gnssLocationInfoCb = nullptr;
    locationCallbacks.gnssNiCb = nullptr;
    locationCallbacks.gnssSvCb = nullptr;
    locationCallbacks.gnssNmeaCb = nullptr;
    locationCallbacks.gnssMeasurementsCb = nullptr;

    locAPISetCallbacks(locationCallbacks);
}

void GeofenceAPIClient::geofenceAdd(uint32_t geofence_id, double latitude, double longitude,
        double radius_meters, int32_t last_transition, int32_t monitor_transitions,
        uint32_t notification_responsiveness_ms, uint32_t unknown_timer_ms)
{
    LOC_LOGD("%s]: (%d %f %f %f %d %d %d %d)", __FUNCTION__,
            geofence_id, latitude, longitude, radius_meters,
            last_transition, monitor_transitions, notification_responsiveness_ms, unknown_timer_ms);

    GeofenceOption options;
    memset(&options, 0, sizeof(GeofenceOption));
    options.size = sizeof(GeofenceOption);
    if (monitor_transitions & IGnssGeofenceCallback::GeofenceTransition::ENTERED)
        options.breachTypeMask |= GEOFENCE_BREACH_ENTER_BIT;
    if (monitor_transitions & IGnssGeofenceCallback::GeofenceTransition::EXITED)
        options.breachTypeMask |=  GEOFENCE_BREACH_EXIT_BIT;
    options.responsiveness = notification_responsiveness_ms;

    GeofenceInfo data;
    data.size = sizeof(GeofenceInfo);
    data.latitude = latitude;
    data.longitude = longitude;
    data.radius = radius_meters;

    LocationError err = (LocationError)locAPIAddGeofences(1, &geofence_id, &options, &data);
    if (LOCATION_ERROR_SUCCESS != err) {
        onAddGeofencesCb(1, &err, &geofence_id);
    }
}

void GeofenceAPIClient::geofencePause(uint32_t geofence_id)
{
    LOC_LOGD("%s]: (%d)", __FUNCTION__, geofence_id);
    locAPIPauseGeofences(1, &geofence_id);
}

void GeofenceAPIClient::geofenceResume(uint32_t geofence_id, int32_t monitor_transitions)
{
    LOC_LOGD("%s]: (%d %d)", __FUNCTION__, geofence_id, monitor_transitions);
    GeofenceBreachTypeMask mask = 0;
    if (monitor_transitions & IGnssGeofenceCallback::GeofenceTransition::ENTERED)
        mask |= GEOFENCE_BREACH_ENTER_BIT;
    if (monitor_transitions & IGnssGeofenceCallback::GeofenceTransition::EXITED)
        mask |=  GEOFENCE_BREACH_EXIT_BIT;
    locAPIResumeGeofences(1, &geofence_id, &mask);
}

void GeofenceAPIClient::geofenceRemove(uint32_t geofence_id)
{
    LOC_LOGD("%s]: (%d)", __FUNCTION__, geofence_id);
    locAPIRemoveGeofences(1, &geofence_id);
}

void GeofenceAPIClient::geofenceRemoveAll()
{
    LOC_LOGD("%s]", __FUNCTION__);
    // TODO locAPIRemoveAllGeofences();
}

// callbacks
void GeofenceAPIClient::onGeofenceBreachCb(GeofenceBreachNotification geofenceBreachNotification)
{
    LOC_LOGD("%s]: (%d)", __FUNCTION__, geofenceBreachNotification.count);
    if (mGnssGeofencingCbIface != nullptr) {
        for (size_t i = 0; i < geofenceBreachNotification.count; i++) {
            GnssLocation gnssLocation;
            convertGnssLocation(geofenceBreachNotification.location, gnssLocation);

            IGnssGeofenceCallback::GeofenceTransition transition;
            if (geofenceBreachNotification.type == GEOFENCE_BREACH_ENTER)
                transition = IGnssGeofenceCallback::GeofenceTransition::ENTERED;
            else if (geofenceBreachNotification.type == GEOFENCE_BREACH_EXIT)
                transition = IGnssGeofenceCallback::GeofenceTransition::EXITED;
            else {
                // continue with other breach if transition is
                // nether GPS_GEOFENCE_ENTERED nor GPS_GEOFENCE_EXITED
                continue;
            }

            auto r = mGnssGeofencingCbIface->gnssGeofenceTransitionCb(
                    geofenceBreachNotification.ids[i], gnssLocation, transition,
                    static_cast<V1_0::GnssUtcTime>(geofenceBreachNotification.timestamp));
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssGeofenceTransitionCb description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

void GeofenceAPIClient::onGeofenceStatusCb(GeofenceStatusNotification geofenceStatusNotification)
{
    LOC_LOGD("%s]: (%d)", __FUNCTION__, geofenceStatusNotification.available);
    if (mGnssGeofencingCbIface != nullptr) {
        IGnssGeofenceCallback::GeofenceAvailability status =
            IGnssGeofenceCallback::GeofenceAvailability::UNAVAILABLE;
        if (geofenceStatusNotification.available == GEOFENCE_STATUS_AVAILABILE_YES) {
            status = IGnssGeofenceCallback::GeofenceAvailability::AVAILABLE;
        }
        GnssLocation gnssLocation;
        memset(&gnssLocation, 0, sizeof(GnssLocation));
        auto r = mGnssGeofencingCbIface->gnssGeofenceStatusCb(status, gnssLocation);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssGeofenceStatusCb description=%s",
                __func__, r.description().c_str());
        }
    }
}

void GeofenceAPIClient::onAddGeofencesCb(size_t count, LocationError* errors, uint32_t* ids)
{
    LOC_LOGD("%s]: (%zu)", __FUNCTION__, count);
    if (mGnssGeofencingCbIface != nullptr) {
        for (size_t i = 0; i < count; i++) {
            IGnssGeofenceCallback::GeofenceStatus status =
                IGnssGeofenceCallback::GeofenceStatus::ERROR_GENERIC;
            if (errors[i] == LOCATION_ERROR_SUCCESS)
                status = IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS;
            else if (errors[i] == LOCATION_ERROR_ID_EXISTS)
                status = IGnssGeofenceCallback::GeofenceStatus::ERROR_ID_EXISTS;
            auto r = mGnssGeofencingCbIface->gnssGeofenceAddCb(ids[i], status);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssGeofenceAddCb description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

void GeofenceAPIClient::onRemoveGeofencesCb(size_t count, LocationError* errors, uint32_t* ids)
{
    LOC_LOGD("%s]: (%zu)", __FUNCTION__, count);
    if (mGnssGeofencingCbIface != nullptr) {
        for (size_t i = 0; i < count; i++) {
            IGnssGeofenceCallback::GeofenceStatus status =
                IGnssGeofenceCallback::GeofenceStatus::ERROR_GENERIC;
            if (errors[i] == LOCATION_ERROR_SUCCESS)
                status = IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS;
            else if (errors[i] == LOCATION_ERROR_ID_UNKNOWN)
                status = IGnssGeofenceCallback::GeofenceStatus::ERROR_ID_UNKNOWN;
            auto r = mGnssGeofencingCbIface->gnssGeofenceRemoveCb(ids[i], status);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssGeofenceRemoveCb description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

void GeofenceAPIClient::onPauseGeofencesCb(size_t count, LocationError* errors, uint32_t* ids)
{
    LOC_LOGD("%s]: (%zu)", __FUNCTION__, count);
    if (mGnssGeofencingCbIface != nullptr) {
        for (size_t i = 0; i < count; i++) {
            IGnssGeofenceCallback::GeofenceStatus status =
                IGnssGeofenceCallback::GeofenceStatus::ERROR_GENERIC;
            if (errors[i] == LOCATION_ERROR_SUCCESS)
                status = IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS;
            else if (errors[i] == LOCATION_ERROR_ID_UNKNOWN)
                status = IGnssGeofenceCallback::GeofenceStatus::ERROR_ID_UNKNOWN;
            auto r = mGnssGeofencingCbIface->gnssGeofencePauseCb(ids[i], status);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssGeofencePauseCb description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

void GeofenceAPIClient::onResumeGeofencesCb(size_t count, LocationError* errors, uint32_t* ids)
{
    LOC_LOGD("%s]: (%zu)", __FUNCTION__, count);
    if (mGnssGeofencingCbIface != nullptr) {
        for (size_t i = 0; i < count; i++) {
            IGnssGeofenceCallback::GeofenceStatus status =
                IGnssGeofenceCallback::GeofenceStatus::ERROR_GENERIC;
            if (errors[i] == LOCATION_ERROR_SUCCESS)
                status = IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS;
            else if (errors[i] == LOCATION_ERROR_ID_UNKNOWN)
                status = IGnssGeofenceCallback::GeofenceStatus::ERROR_ID_UNKNOWN;
            auto r = mGnssGeofencingCbIface->gnssGeofenceResumeCb(ids[i], status);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssGeofenceResumeCb description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

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

license: unknown


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 GEOFENCE_API_CLINET_H
#define GEOFENCE_API_CLINET_H

#include <android/hardware/gnss/1.0/IGnssGeofenceCallback.h>
#include <LocationAPIClientBase.h>

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

using ::android::sp;

class GeofenceAPIClient : public LocationAPIClientBase
{
public:
    GeofenceAPIClient(const sp<V1_0::IGnssGeofenceCallback>& callback);

    void geofenceAdd(uint32_t geofence_id, double latitude, double longitude,
            double radius_meters, int32_t last_transition, int32_t monitor_transitions,
            uint32_t notification_responsiveness_ms, uint32_t unknown_timer_ms);
    void geofencePause(uint32_t geofence_id);
    void geofenceResume(uint32_t geofence_id, int32_t monitor_transitions);
    void geofenceRemove(uint32_t geofence_id);
    void geofenceRemoveAll();

    // callbacks
    void onGeofenceBreachCb(GeofenceBreachNotification geofenceBreachNotification) final;
    void onGeofenceStatusCb(GeofenceStatusNotification geofenceStatusNotification) final;
    void onAddGeofencesCb(size_t count, LocationError* errors, uint32_t* ids) final;
    void onRemoveGeofencesCb(size_t count, LocationError* errors, uint32_t* ids) final;
    void onPauseGeofencesCb(size_t count, LocationError* errors, uint32_t* ids) final;
    void onResumeGeofencesCb(size_t count, LocationError* errors, uint32_t* ids) final;

private:
    virtual ~GeofenceAPIClient() = default;

    sp<V1_0::IGnssGeofenceCallback> mGnssGeofencingCbIface;
};

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


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_GnssAPIClient"
#define SINGLE_SHOT_MIN_TRACKING_INTERVAL_MSEC (590 * 60 * 60 * 1000) // 590 hours

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

#include "LocationUtil.h"
#include "GnssAPIClient.h"
#include <LocContext.h>

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

using ::android::hardware::gnss::V2_1::IGnss;
using ::android::hardware::gnss::V2_1::IGnssCallback;
using ::android::hardware::gnss::V1_0::IGnssNiCallback;
using ::android::hardware::gnss::V2_0::GnssLocation;

static void convertGnssSvStatus(GnssSvNotification& in, V1_0::IGnssCallback::GnssSvStatus& out);
static void convertGnssSvStatus(GnssSvNotification& in,
        hidl_vec<V2_0::IGnssCallback::GnssSvInfo>& out);
static void convertGnssSvStatus(GnssSvNotification& in,
        hidl_vec<V2_1::IGnssCallback::GnssSvInfo>& out);

GnssAPIClient::GnssAPIClient(const sp<V1_0::IGnssCallback>& gpsCb,
        const sp<V1_0::IGnssNiCallback>& niCb) :
    LocationAPIClientBase(),
    mGnssCbIface(nullptr),
    mGnssNiCbIface(nullptr),
    mControlClient(new LocationAPIControlClient()),
    mLocationCapabilitiesMask(0),
    mLocationCapabilitiesCached(false),
    mTracking(false),
    mGnssCbIface_2_0(nullptr)
{
    LOC_LOGD("%s]: (%p %p)", __FUNCTION__, &gpsCb, &niCb);

    initLocationOptions();
    gnssUpdateCallbacks(gpsCb, niCb);
}

GnssAPIClient::GnssAPIClient(const sp<V2_0::IGnssCallback>& gpsCb) :
    LocationAPIClientBase(),
    mGnssCbIface(nullptr),
    mGnssNiCbIface(nullptr),
    mControlClient(new LocationAPIControlClient()),
    mLocationCapabilitiesMask(0),
    mLocationCapabilitiesCached(false),
    mTracking(false),
    mGnssCbIface_2_0(nullptr)
{
    LOC_LOGD("%s]: (%p)", __FUNCTION__, &gpsCb);

    initLocationOptions();
    gnssUpdateCallbacks_2_0(gpsCb);
}

GnssAPIClient::GnssAPIClient(const sp<V2_1::IGnssCallback>& gpsCb) :
    LocationAPIClientBase(),
    mGnssCbIface(nullptr),
    mGnssNiCbIface(nullptr),
    mControlClient(new LocationAPIControlClient()),
    mLocationCapabilitiesMask(0),
    mLocationCapabilitiesCached(false),
    mTracking(false),
    mGnssCbIface_2_1(nullptr)
{
    LOC_LOGD("%s]: (%p)", __FUNCTION__, &gpsCb);

    initLocationOptions();
    gnssUpdateCallbacks_2_1(gpsCb);
}

GnssAPIClient::~GnssAPIClient()
{
    LOC_LOGD("%s]: ()", __FUNCTION__);
    if (mControlClient) {
        delete mControlClient;
        mControlClient = nullptr;
    }
}

void GnssAPIClient::initLocationOptions()
{
    // set default LocationOptions.
    memset(&mTrackingOptions, 0, sizeof(TrackingOptions));
    mTrackingOptions.size = sizeof(TrackingOptions);
    mTrackingOptions.minInterval = 1000;
    mTrackingOptions.minDistance = 0;
    mTrackingOptions.mode = GNSS_SUPL_MODE_STANDALONE;
}

void GnssAPIClient::setCallbacks()
{
    LocationCallbacks locationCallbacks;
    memset(&locationCallbacks, 0, sizeof(LocationCallbacks));
    locationCallbacks.size = sizeof(LocationCallbacks);

    locationCallbacks.trackingCb = nullptr;
    locationCallbacks.trackingCb = [this](Location location) {
        onTrackingCb(location);
    };

    locationCallbacks.batchingCb = nullptr;
    locationCallbacks.geofenceBreachCb = nullptr;
    locationCallbacks.geofenceStatusCb = nullptr;
    locationCallbacks.gnssLocationInfoCb = nullptr;
    locationCallbacks.gnssNiCb = nullptr;
    if (mGnssNiCbIface != nullptr) {
        loc_core::ContextBase* context =
                loc_core::LocContext::getLocContext(loc_core::LocContext::mLocationHalName);
        if (!context->hasAgpsExtendedCapabilities()) {
            LOC_LOGD("Registering NI CB");
            locationCallbacks.gnssNiCb = [this](uint32_t id, GnssNiNotification gnssNiNotify) {
                onGnssNiCb(id, gnssNiNotify);
            };
        }
    }

    locationCallbacks.gnssSvCb = nullptr;
    locationCallbacks.gnssSvCb = [this](GnssSvNotification gnssSvNotification) {
        onGnssSvCb(gnssSvNotification);
    };

    locationCallbacks.gnssNmeaCb = nullptr;
    locationCallbacks.gnssNmeaCb = [this](GnssNmeaNotification gnssNmeaNotification) {
        onGnssNmeaCb(gnssNmeaNotification);
    };

    locationCallbacks.gnssMeasurementsCb = nullptr;

    locAPISetCallbacks(locationCallbacks);
}

// for GpsInterface
void GnssAPIClient::gnssUpdateCallbacks(const sp<V1_0::IGnssCallback>& gpsCb,
    const sp<IGnssNiCallback>& niCb)
{
    LOC_LOGD("%s]: (%p %p)", __FUNCTION__, &gpsCb, &niCb);

    mMutex.lock();
    mGnssCbIface = gpsCb;
    mGnssNiCbIface = niCb;
    mMutex.unlock();

    if (mGnssCbIface != nullptr || mGnssNiCbIface != nullptr) {
        setCallbacks();
    }
}

void GnssAPIClient::gnssUpdateCallbacks_2_0(const sp<V2_0::IGnssCallback>& gpsCb)
{
    LOC_LOGD("%s]: (%p)", __FUNCTION__, &gpsCb);

    mMutex.lock();
    mGnssCbIface_2_0 = gpsCb;
    mMutex.unlock();

    if (mGnssCbIface_2_0 != nullptr) {
        setCallbacks();
    }
}

void GnssAPIClient::gnssUpdateCallbacks_2_1(const sp<V2_1::IGnssCallback>& gpsCb)
{
    LOC_LOGD("%s]: (%p)", __FUNCTION__, &gpsCb);

    mMutex.lock();
    mGnssCbIface_2_1 = gpsCb;
    mMutex.unlock();

    if (mGnssCbIface_2_1 != nullptr) {
        setCallbacks();
    }
}

bool GnssAPIClient::gnssStart()
{
    LOC_LOGD("%s]: ()", __FUNCTION__);

    mMutex.lock();
    mTracking = true;
    mMutex.unlock();

    bool retVal = true;
    locAPIStartTracking(mTrackingOptions);
    return retVal;
}

bool GnssAPIClient::gnssStop()
{
    LOC_LOGD("%s]: ()", __FUNCTION__);

    mMutex.lock();
    mTracking = false;
    mMutex.unlock();

    bool retVal = true;
    locAPIStopTracking();
    return retVal;
}

bool GnssAPIClient::gnssSetPositionMode(IGnss::GnssPositionMode mode,
        IGnss::GnssPositionRecurrence recurrence, uint32_t minIntervalMs,
        uint32_t preferredAccuracyMeters, uint32_t preferredTimeMs,
        GnssPowerMode powerMode, uint32_t timeBetweenMeasurement)
{
    LOC_LOGD("%s]: (%d %d %d %d %d %d %d)", __FUNCTION__,
            (int)mode, recurrence, minIntervalMs, preferredAccuracyMeters,
            preferredTimeMs, (int)powerMode, timeBetweenMeasurement);
    bool retVal = true;
    memset(&mTrackingOptions, 0, sizeof(TrackingOptions));
    mTrackingOptions.size = sizeof(TrackingOptions);
    mTrackingOptions.minInterval = minIntervalMs;
    if (IGnss::GnssPositionMode::MS_ASSISTED == mode ||
            IGnss::GnssPositionRecurrence::RECURRENCE_SINGLE == recurrence) {
        // We set a very large interval to simulate SINGLE mode. Once we report a fix,
        // the caller should take the responsibility to stop the session.
        // For MSA, we always treat it as SINGLE mode.
        mTrackingOptions.minInterval = SINGLE_SHOT_MIN_TRACKING_INTERVAL_MSEC;
    }
    if (mode == IGnss::GnssPositionMode::STANDALONE)
        mTrackingOptions.mode = GNSS_SUPL_MODE_STANDALONE;
    else if (mode == IGnss::GnssPositionMode::MS_BASED)
        mTrackingOptions.mode = GNSS_SUPL_MODE_MSB;
    else if (mode ==  IGnss::GnssPositionMode::MS_ASSISTED)
        mTrackingOptions.mode = GNSS_SUPL_MODE_MSA;
    else {
        LOC_LOGD("%s]: invalid GnssPositionMode: %d", __FUNCTION__, (int)mode);
        retVal = false;
    }
    if (GNSS_POWER_MODE_INVALID != powerMode) {
        mTrackingOptions.powerMode = powerMode;
        mTrackingOptions.tbm = timeBetweenMeasurement;
    }
    locAPIUpdateTrackingOptions(mTrackingOptions);
    return retVal;
}

// for GpsNiInterface
void GnssAPIClient::gnssNiRespond(int32_t notifId,
        IGnssNiCallback::GnssUserResponseType userResponse)
{
    LOC_LOGD("%s]: (%d %d)", __FUNCTION__, notifId, static_cast<int>(userResponse));
    GnssNiResponse data;
    switch (userResponse) {
    case IGnssNiCallback::GnssUserResponseType::RESPONSE_ACCEPT:
        data = GNSS_NI_RESPONSE_ACCEPT;
        break;
    case IGnssNiCallback::GnssUserResponseType::RESPONSE_DENY:
        data = GNSS_NI_RESPONSE_DENY;
        break;
    case IGnssNiCallback::GnssUserResponseType::RESPONSE_NORESP:
        data = GNSS_NI_RESPONSE_NO_RESPONSE;
        break;
    default:
        data = GNSS_NI_RESPONSE_IGNORE;
        break;
    }

    locAPIGnssNiResponse(notifId, data);
}

// these apis using LocationAPIControlClient
void GnssAPIClient::gnssDeleteAidingData(IGnss::GnssAidingData aidingDataFlags)
{
    LOC_LOGD("%s]: (%02hx)", __FUNCTION__, aidingDataFlags);
    if (mControlClient == nullptr) {
        return;
    }
    GnssAidingData data;
    memset(&data, 0, sizeof (GnssAidingData));
    data.sv.svTypeMask = GNSS_AIDING_DATA_SV_TYPE_GPS_BIT |
        GNSS_AIDING_DATA_SV_TYPE_GLONASS_BIT |
        GNSS_AIDING_DATA_SV_TYPE_QZSS_BIT |
        GNSS_AIDING_DATA_SV_TYPE_BEIDOU_BIT |
        GNSS_AIDING_DATA_SV_TYPE_GALILEO_BIT |
        GNSS_AIDING_DATA_SV_TYPE_NAVIC_BIT;
    data.posEngineMask = STANDARD_POSITIONING_ENGINE;

    if (aidingDataFlags == IGnss::GnssAidingData::DELETE_ALL)
        data.deleteAll = true;
    else {
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_EPHEMERIS)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_EPHEMERIS_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_ALMANAC)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_ALMANAC_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_POSITION)
            data.common.mask |= GNSS_AIDING_DATA_COMMON_POSITION_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_TIME)
            data.common.mask |= GNSS_AIDING_DATA_COMMON_TIME_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_IONO)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_IONOSPHERE_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_UTC)
            data.common.mask |= GNSS_AIDING_DATA_COMMON_UTC_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_HEALTH)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_HEALTH_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_SVDIR)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_DIRECTION_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_SVSTEER)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_STEER_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_SADATA)
            data.sv.svMask |= GNSS_AIDING_DATA_SV_SA_DATA_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_RTI)
            data.common.mask |= GNSS_AIDING_DATA_COMMON_RTI_BIT;
        if (aidingDataFlags & IGnss::GnssAidingData::DELETE_CELLDB_INFO)
            data.common.mask |= GNSS_AIDING_DATA_COMMON_CELLDB_BIT;
    }
    mControlClient->locAPIGnssDeleteAidingData(data);
}

void GnssAPIClient::gnssEnable(LocationTechnologyType techType)
{
    LOC_LOGD("%s]: (%0d)", __FUNCTION__, techType);
    if (mControlClient == nullptr) {
        return;
    }
    mControlClient->locAPIEnable(techType);
}

void GnssAPIClient::gnssDisable()
{
    LOC_LOGD("%s]: ()", __FUNCTION__);
    if (mControlClient == nullptr) {
        return;
    }
    mControlClient->locAPIDisable();
}

void GnssAPIClient::gnssConfigurationUpdate(const GnssConfig& gnssConfig)
{
    LOC_LOGD("%s]: (%02x)", __FUNCTION__, gnssConfig.flags);
    if (mControlClient == nullptr) {
        return;
    }
    mControlClient->locAPIGnssUpdateConfig(gnssConfig);
}

void GnssAPIClient::requestCapabilities() {
    // only send capablities if it's already cached, otherwise the first time LocationAPI
    // is initialized, capabilities will be sent by LocationAPI
    if (mLocationCapabilitiesCached) {
        onCapabilitiesCb(mLocationCapabilitiesMask);
    }
}

// callbacks
void GnssAPIClient::onCapabilitiesCb(LocationCapabilitiesMask capabilitiesMask)
{
    LOC_LOGD("%s]: (%" PRIu64 ")", __FUNCTION__, capabilitiesMask);
    mLocationCapabilitiesMask = capabilitiesMask;
    mLocationCapabilitiesCached = true;

    mMutex.lock();
    auto gnssCbIface(mGnssCbIface);
    auto gnssCbIface_2_0(mGnssCbIface_2_0);
    auto gnssCbIface_2_1(mGnssCbIface_2_1);
    mMutex.unlock();

    if (gnssCbIface_2_1 != nullptr ||gnssCbIface_2_0 != nullptr || gnssCbIface != nullptr) {

        uint32_t antennaInfoVectorSize = 0;
        uint32_t data = 0;
        loc_param_s_type ant_info_vector_table[] =
        {
            { "ANTENNA_INFO_VECTOR_SIZE", &antennaInfoVectorSize, NULL, 'n' }
        };
        UTIL_READ_CONF(LOC_PATH_ANT_CORR, ant_info_vector_table);

        if (0 != antennaInfoVectorSize) {
            data |= V2_1::IGnssCallback::Capabilities::ANTENNA_INFO;
        }

        if ((capabilitiesMask & LOCATION_CAPABILITIES_TIME_BASED_TRACKING_BIT) ||
                (capabilitiesMask & LOCATION_CAPABILITIES_TIME_BASED_BATCHING_BIT) ||
                (capabilitiesMask & LOCATION_CAPABILITIES_DISTANCE_BASED_TRACKING_BIT) ||
                (capabilitiesMask & LOCATION_CAPABILITIES_DISTANCE_BASED_BATCHING_BIT))
            data |= IGnssCallback::Capabilities::SCHEDULING;
        if (capabilitiesMask & LOCATION_CAPABILITIES_GEOFENCE_BIT)
            data |= V1_0::IGnssCallback::Capabilities::GEOFENCING;
        if (capabilitiesMask & LOCATION_CAPABILITIES_GNSS_MEASUREMENTS_BIT)
            data |= V1_0::IGnssCallback::Capabilities::MEASUREMENTS;
        if (capabilitiesMask & LOCATION_CAPABILITIES_GNSS_MSB_BIT)
            data |= IGnssCallback::Capabilities::MSB;
        if (capabilitiesMask & LOCATION_CAPABILITIES_GNSS_MSA_BIT)
            data |= IGnssCallback::Capabilities::MSA;
        if (capabilitiesMask & LOCATION_CAPABILITIES_AGPM_BIT)
            data |= IGnssCallback::Capabilities::LOW_POWER_MODE;
        if (capabilitiesMask & LOCATION_CAPABILITIES_CONSTELLATION_ENABLEMENT_BIT)
            data |= IGnssCallback::Capabilities::SATELLITE_BLACKLIST;
        if (capabilitiesMask & LOCATION_CAPABILITIES_MEASUREMENTS_CORRECTION_BIT)
            data |= V2_0::IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS;

        IGnssCallback::GnssSystemInfo gnssInfo = { .yearOfHw = 2015 };

        if (capabilitiesMask & LOCATION_CAPABILITIES_GNSS_MEASUREMENTS_BIT) {
            gnssInfo.yearOfHw++; // 2016
            if (capabilitiesMask & LOCATION_CAPABILITIES_DEBUG_NMEA_BIT) {
                gnssInfo.yearOfHw++; // 2017
                if (capabilitiesMask & LOCATION_CAPABILITIES_CONSTELLATION_ENABLEMENT_BIT ||
                    capabilitiesMask & LOCATION_CAPABILITIES_AGPM_BIT) {
                    gnssInfo.yearOfHw++; // 2018
                    if (capabilitiesMask & LOCATION_CAPABILITIES_PRIVACY_BIT) {
                        gnssInfo.yearOfHw++; // 2019
                        if (capabilitiesMask & LOCATION_CAPABILITIES_MEASUREMENTS_CORRECTION_BIT) {
                            gnssInfo.yearOfHw++; // 2020
                        }
                    }
                }
            }
        }
        LOC_LOGV("%s:%d] set_system_info_cb (%d)", __FUNCTION__, __LINE__, gnssInfo.yearOfHw);

        if (gnssCbIface_2_1 != nullptr) {
            auto r = gnssCbIface_2_1->gnssSetCapabilitiesCb_2_1(data);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssSetCapabilitiesCb_2_1 description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface_2_1->gnssSetSystemInfoCb(gnssInfo);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssSetSystemInfoCb description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssCbIface_2_0 != nullptr) {
            auto r = gnssCbIface_2_0->gnssSetCapabilitiesCb_2_0(data);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssSetCapabilitiesCb_2_0 description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface_2_0->gnssSetSystemInfoCb(gnssInfo);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssSetSystemInfoCb description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssCbIface != nullptr) {
            auto r = gnssCbIface->gnssSetCapabilitesCb(data);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssSetCapabilitesCb description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface->gnssSetSystemInfoCb(gnssInfo);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssSetSystemInfoCb description=%s",
                    __func__, r.description().c_str());
            }
        }

    }

}

void GnssAPIClient::onTrackingCb(Location location)
{
    mMutex.lock();
    auto gnssCbIface(mGnssCbIface);
    auto gnssCbIface_2_0(mGnssCbIface_2_0);
    auto gnssCbIface_2_1(mGnssCbIface_2_1);
    bool isTracking = mTracking;
    mMutex.unlock();

    LOC_LOGD("%s]: (flags: %02x isTracking: %d)", __FUNCTION__, location.flags, isTracking);

    if (!isTracking) {
        return;
    }

    if (gnssCbIface_2_1 != nullptr) {
        V2_0::GnssLocation gnssLocation;
        convertGnssLocation(location, gnssLocation);
        auto r = gnssCbIface_2_1->gnssLocationCb_2_0(gnssLocation);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssLocationCb_2_0 description=%s",
                __func__, r.description().c_str());
        }
    } else if (gnssCbIface_2_0 != nullptr) {
        V2_0::GnssLocation gnssLocation;
        convertGnssLocation(location, gnssLocation);
        auto r = gnssCbIface_2_0->gnssLocationCb_2_0(gnssLocation);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssLocationCb_2_0 description=%s",
                __func__, r.description().c_str());
        }
    } else if (gnssCbIface != nullptr) {
        V1_0::GnssLocation gnssLocation;
        convertGnssLocation(location, gnssLocation);
        auto r = gnssCbIface->gnssLocationCb(gnssLocation);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssLocationCb description=%s",
                __func__, r.description().c_str());
        }
    } else {
        LOC_LOGW("%s] No GNSS Interface ready for gnssLocationCb ", __FUNCTION__);
    }

}

void GnssAPIClient::onGnssNiCb(uint32_t id, GnssNiNotification gnssNiNotification)
{
    LOC_LOGD("%s]: (id: %d)", __FUNCTION__, id);
    mMutex.lock();
    auto gnssNiCbIface(mGnssNiCbIface);
    mMutex.unlock();

    if (gnssNiCbIface == nullptr) {
        LOC_LOGE("%s]: mGnssNiCbIface is nullptr", __FUNCTION__);
        return;
    }

    IGnssNiCallback::GnssNiNotification notificationGnss = {};

    notificationGnss.notificationId = id;

    if (gnssNiNotification.type == GNSS_NI_TYPE_VOICE)
        notificationGnss.niType = IGnssNiCallback::GnssNiType::VOICE;
    else if (gnssNiNotification.type == GNSS_NI_TYPE_SUPL)
        notificationGnss.niType = IGnssNiCallback::GnssNiType::UMTS_SUPL;
    else if (gnssNiNotification.type == GNSS_NI_TYPE_CONTROL_PLANE)
        notificationGnss.niType = IGnssNiCallback::GnssNiType::UMTS_CTRL_PLANE;
    else if (gnssNiNotification.type == GNSS_NI_TYPE_EMERGENCY_SUPL)
        notificationGnss.niType = IGnssNiCallback::GnssNiType::EMERGENCY_SUPL;

    if (gnssNiNotification.options & GNSS_NI_OPTIONS_NOTIFICATION_BIT)
        notificationGnss.notifyFlags |= IGnssNiCallback::GnssNiNotifyFlags::NEED_NOTIFY;
    if (gnssNiNotification.options & GNSS_NI_OPTIONS_VERIFICATION_BIT)
        notificationGnss.notifyFlags |= IGnssNiCallback::GnssNiNotifyFlags::NEED_VERIFY;
    if (gnssNiNotification.options & GNSS_NI_OPTIONS_PRIVACY_OVERRIDE_BIT)
        notificationGnss.notifyFlags |= IGnssNiCallback::GnssNiNotifyFlags::PRIVACY_OVERRIDE;

    notificationGnss.timeoutSec = gnssNiNotification.timeout;

    if (gnssNiNotification.timeoutResponse == GNSS_NI_RESPONSE_ACCEPT)
        notificationGnss.defaultResponse = IGnssNiCallback::GnssUserResponseType::RESPONSE_ACCEPT;
    else if (gnssNiNotification.timeoutResponse == GNSS_NI_RESPONSE_DENY)
        notificationGnss.defaultResponse = IGnssNiCallback::GnssUserResponseType::RESPONSE_DENY;
    else if (gnssNiNotification.timeoutResponse == GNSS_NI_RESPONSE_NO_RESPONSE ||
            gnssNiNotification.timeoutResponse == GNSS_NI_RESPONSE_IGNORE)
        notificationGnss.defaultResponse = IGnssNiCallback::GnssUserResponseType::RESPONSE_NORESP;

    notificationGnss.requestorId = gnssNiNotification.requestor;

    notificationGnss.notificationMessage = gnssNiNotification.message;

    if (gnssNiNotification.requestorEncoding == GNSS_NI_ENCODING_TYPE_NONE)
        notificationGnss.requestorIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_NONE;
    else if (gnssNiNotification.requestorEncoding == GNSS_NI_ENCODING_TYPE_GSM_DEFAULT)
        notificationGnss.requestorIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_SUPL_GSM_DEFAULT;
    else if (gnssNiNotification.requestorEncoding == GNSS_NI_ENCODING_TYPE_UTF8)
        notificationGnss.requestorIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_SUPL_UTF8;
    else if (gnssNiNotification.requestorEncoding == GNSS_NI_ENCODING_TYPE_UCS2)
        notificationGnss.requestorIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_SUPL_UCS2;

    if (gnssNiNotification.messageEncoding == GNSS_NI_ENCODING_TYPE_NONE)
        notificationGnss.notificationIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_NONE;
    else if (gnssNiNotification.messageEncoding == GNSS_NI_ENCODING_TYPE_GSM_DEFAULT)
        notificationGnss.notificationIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_SUPL_GSM_DEFAULT;
    else if (gnssNiNotification.messageEncoding == GNSS_NI_ENCODING_TYPE_UTF8)
        notificationGnss.notificationIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_SUPL_UTF8;
    else if (gnssNiNotification.messageEncoding == GNSS_NI_ENCODING_TYPE_UCS2)
        notificationGnss.notificationIdEncoding =
            IGnssNiCallback::GnssNiEncodingType::ENC_SUPL_UCS2;

    gnssNiCbIface->niNotifyCb(notificationGnss);
}

void GnssAPIClient::onGnssSvCb(GnssSvNotification gnssSvNotification)
{
    LOC_LOGD("%s]: (count: %u)", __FUNCTION__, gnssSvNotification.count);
    mMutex.lock();
    auto gnssCbIface(mGnssCbIface);
    auto gnssCbIface_2_0(mGnssCbIface_2_0);
    auto gnssCbIface_2_1(mGnssCbIface_2_1);
    mMutex.unlock();

    if (gnssCbIface_2_1 != nullptr) {
        hidl_vec<V2_1::IGnssCallback::GnssSvInfo> svInfoList;
        convertGnssSvStatus(gnssSvNotification, svInfoList);
        auto r = gnssCbIface_2_1->gnssSvStatusCb_2_1(svInfoList);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssSvStatusCb_2_1 description=%s",
                __func__, r.description().c_str());
        }
    } else if (gnssCbIface_2_0 != nullptr) {
        hidl_vec<V2_0::IGnssCallback::GnssSvInfo> svInfoList;
        convertGnssSvStatus(gnssSvNotification, svInfoList);
        auto r = gnssCbIface_2_0->gnssSvStatusCb_2_0(svInfoList);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssSvStatusCb_2_0 description=%s",
                __func__, r.description().c_str());
        }
    } else if (gnssCbIface != nullptr) {
        V1_0::IGnssCallback::GnssSvStatus svStatus;
        convertGnssSvStatus(gnssSvNotification, svStatus);
        auto r = gnssCbIface->gnssSvStatusCb(svStatus);
        if (!r.isOk()) {
            LOC_LOGE("%s] Error from gnssSvStatusCb description=%s",
                __func__, r.description().c_str());
        }
    }
}

void GnssAPIClient::onGnssNmeaCb(GnssNmeaNotification gnssNmeaNotification)
{
    mMutex.lock();
    auto gnssCbIface(mGnssCbIface);
    auto gnssCbIface_2_0(mGnssCbIface_2_0);
    auto gnssCbIface_2_1(mGnssCbIface_2_1);
    mMutex.unlock();

    if (gnssCbIface != nullptr || gnssCbIface_2_0 != nullptr| gnssCbIface_2_1 != nullptr) {
        const std::string s(gnssNmeaNotification.nmea);
        std::stringstream ss(s);
        std::string each;
        while(std::getline(ss, each, '\n')) {
            each += '\n';
            android::hardware::hidl_string nmeaString;
            nmeaString.setToExternal(each.c_str(), each.length());
            if (gnssCbIface_2_1 != nullptr) {
                auto r = gnssCbIface_2_1->gnssNmeaCb(
                        static_cast<V1_0::GnssUtcTime>(gnssNmeaNotification.timestamp), nmeaString);
                if (!r.isOk()) {
                    LOC_LOGE("%s] Error from gnssCbIface_2_1 nmea=%s length=%u description=%s",
                             __func__, gnssNmeaNotification.nmea, gnssNmeaNotification.length,
                             r.description().c_str());
                }
            } else if (gnssCbIface_2_0 != nullptr) {
                auto r = gnssCbIface_2_0->gnssNmeaCb(
                        static_cast<V1_0::GnssUtcTime>(gnssNmeaNotification.timestamp), nmeaString);
                if (!r.isOk()) {
                    LOC_LOGE("%s] Error from gnssCbIface_2_0 nmea=%s length=%u description=%s",
                             __func__, gnssNmeaNotification.nmea, gnssNmeaNotification.length,
                             r.description().c_str());
                }
            } else if (gnssCbIface != nullptr) {
                auto r = gnssCbIface->gnssNmeaCb(
                        static_cast<V1_0::GnssUtcTime>(gnssNmeaNotification.timestamp), nmeaString);
                if (!r.isOk()) {
                    LOC_LOGE("%s] Error from gnssNmeaCb nmea=%s length=%u description=%s",
                             __func__, gnssNmeaNotification.nmea, gnssNmeaNotification.length,
                             r.description().c_str());
                }
            }
        }
    }
}

void GnssAPIClient::onStartTrackingCb(LocationError error)
{
    LOC_LOGD("%s]: (%d)", __FUNCTION__, error);
    mMutex.lock();
    auto gnssCbIface(mGnssCbIface);
    auto gnssCbIface_2_0(mGnssCbIface_2_0);
    auto gnssCbIface_2_1(mGnssCbIface_2_1);
    mMutex.unlock();

    if (error == LOCATION_ERROR_SUCCESS) {
        if (gnssCbIface_2_1 != nullptr) {
            auto r = gnssCbIface_2_1->gnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_ON);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 ENGINE_ON description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface_2_1->gnssStatusCb(IGnssCallback::GnssStatusValue::SESSION_BEGIN);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 SESSION_BEGIN description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssCbIface_2_0 != nullptr) {
            auto r = gnssCbIface_2_0->gnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_ON);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 ENGINE_ON description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface_2_0->gnssStatusCb(IGnssCallback::GnssStatusValue::SESSION_BEGIN);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 SESSION_BEGIN description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssCbIface != nullptr) {
            auto r = gnssCbIface->gnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_ON);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb ENGINE_ON description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface->gnssStatusCb(IGnssCallback::GnssStatusValue::SESSION_BEGIN);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb SESSION_BEGIN description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

void GnssAPIClient::onStopTrackingCb(LocationError error)
{
    LOC_LOGD("%s]: (%d)", __FUNCTION__, error);
    mMutex.lock();
    auto gnssCbIface(mGnssCbIface);
    auto gnssCbIface_2_0(mGnssCbIface_2_0);
    auto gnssCbIface_2_1(mGnssCbIface_2_1);
    mMutex.unlock();

    if (error == LOCATION_ERROR_SUCCESS) {
        if (gnssCbIface_2_1 != nullptr) {
            auto r = gnssCbIface_2_1->gnssStatusCb(IGnssCallback::GnssStatusValue::SESSION_END);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 SESSION_END description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface_2_1->gnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_OFF);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 ENGINE_OFF description=%s",
                    __func__, r.description().c_str());
            }
        } else if (gnssCbIface_2_0 != nullptr) {
            auto r = gnssCbIface_2_0->gnssStatusCb(IGnssCallback::GnssStatusValue::SESSION_END);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 SESSION_END description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface_2_0->gnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_OFF);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb 2_0 ENGINE_OFF description=%s",
                    __func__, r.description().c_str());
            }

        } else if (gnssCbIface != nullptr) {
            auto r = gnssCbIface->gnssStatusCb(IGnssCallback::GnssStatusValue::SESSION_END);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb SESSION_END description=%s",
                    __func__, r.description().c_str());
            }
            r = gnssCbIface->gnssStatusCb(IGnssCallback::GnssStatusValue::ENGINE_OFF);
            if (!r.isOk()) {
                LOC_LOGE("%s] Error from gnssStatusCb ENGINE_OFF description=%s",
                    __func__, r.description().c_str());
            }
        }
    }
}

static void convertGnssSvStatus(GnssSvNotification& in, V1_0::IGnssCallback::GnssSvStatus& out)
{
    memset(&out, 0, sizeof(IGnssCallback::GnssSvStatus));
    out.numSvs = in.count;
    if (out.numSvs > static_cast<uint32_t>(V1_0::GnssMax::SVS_COUNT)) {
        LOC_LOGW("%s]: Too many satellites %u. Clamps to %d.",
                __FUNCTION__,  out.numSvs, V1_0::GnssMax::SVS_COUNT);
        out.numSvs = static_cast<uint32_t>(V1_0::GnssMax::SVS_COUNT);
    }
    for (size_t i = 0; i < out.numSvs; i++) {
        convertGnssSvid(in.gnssSvs[i], out.gnssSvList[i].svid);
        convertGnssConstellationType(in.gnssSvs[i].type, out.gnssSvList[i].constellation);
        out.gnssSvList[i].cN0Dbhz = in.gnssSvs[i].cN0Dbhz;
        out.gnssSvList[i].elevationDegrees = in.gnssSvs[i].elevation;
        out.gnssSvList[i].azimuthDegrees = in.gnssSvs[i].azimuth;
        out.gnssSvList[i].carrierFrequencyHz = in.gnssSvs[i].carrierFrequencyHz;
        out.gnssSvList[i].svFlag = static_cast<uint8_t>(IGnssCallback::GnssSvFlags::NONE);
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_EPHEMER_BIT)
            out.gnssSvList[i].svFlag |= IGnssCallback::GnssSvFlags::HAS_EPHEMERIS_DATA;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_ALMANAC_BIT)
            out.gnssSvList[i].svFlag |= IGnssCallback::GnssSvFlags::HAS_ALMANAC_DATA;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_USED_IN_FIX_BIT)
            out.gnssSvList[i].svFlag |= IGnssCallback::GnssSvFlags::USED_IN_FIX;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_CARRIER_FREQUENCY_BIT)
            out.gnssSvList[i].svFlag |= IGnssCallback::GnssSvFlags::HAS_CARRIER_FREQUENCY;
    }
}

static void convertGnssSvStatus(GnssSvNotification& in,
        hidl_vec<V2_0::IGnssCallback::GnssSvInfo>& out)
{
    out.resize(in.count);
    for (size_t i = 0; i < in.count; i++) {
        convertGnssSvid(in.gnssSvs[i], out[i].v1_0.svid);
        out[i].v1_0.cN0Dbhz = in.gnssSvs[i].cN0Dbhz;
        out[i].v1_0.elevationDegrees = in.gnssSvs[i].elevation;
        out[i].v1_0.azimuthDegrees = in.gnssSvs[i].azimuth;
        out[i].v1_0.carrierFrequencyHz = in.gnssSvs[i].carrierFrequencyHz;
        out[i].v1_0.svFlag = static_cast<uint8_t>(IGnssCallback::GnssSvFlags::NONE);
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_EPHEMER_BIT)
            out[i].v1_0.svFlag |= IGnssCallback::GnssSvFlags::HAS_EPHEMERIS_DATA;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_ALMANAC_BIT)
            out[i].v1_0.svFlag |= IGnssCallback::GnssSvFlags::HAS_ALMANAC_DATA;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_USED_IN_FIX_BIT)
            out[i].v1_0.svFlag |= IGnssCallback::GnssSvFlags::USED_IN_FIX;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_CARRIER_FREQUENCY_BIT)
            out[i].v1_0.svFlag |= IGnssCallback::GnssSvFlags::HAS_CARRIER_FREQUENCY;

        convertGnssConstellationType(in.gnssSvs[i].type, out[i].constellation);
    }
}

static void convertGnssSvStatus(GnssSvNotification& in,
        hidl_vec<V2_1::IGnssCallback::GnssSvInfo>& out)
{
    out.resize(in.count);
    for (size_t i = 0; i < in.count; i++) {
        convertGnssSvid(in.gnssSvs[i], out[i].v2_0.v1_0.svid);
        out[i].v2_0.v1_0.cN0Dbhz = in.gnssSvs[i].cN0Dbhz;
        out[i].v2_0.v1_0.elevationDegrees = in.gnssSvs[i].elevation;
        out[i].v2_0.v1_0.azimuthDegrees = in.gnssSvs[i].azimuth;
        out[i].v2_0.v1_0.carrierFrequencyHz = in.gnssSvs[i].carrierFrequencyHz;
        out[i].v2_0.v1_0.svFlag = static_cast<uint8_t>(IGnssCallback::GnssSvFlags::NONE);
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_EPHEMER_BIT)
            out[i].v2_0.v1_0.svFlag |= IGnssCallback::GnssSvFlags::HAS_EPHEMERIS_DATA;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_ALMANAC_BIT)
            out[i].v2_0.v1_0.svFlag |= IGnssCallback::GnssSvFlags::HAS_ALMANAC_DATA;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_USED_IN_FIX_BIT)
            out[i].v2_0.v1_0.svFlag |= IGnssCallback::GnssSvFlags::USED_IN_FIX;
        if (in.gnssSvs[i].gnssSvOptionsMask & GNSS_SV_OPTIONS_HAS_CARRIER_FREQUENCY_BIT)
            out[i].v2_0.v1_0.svFlag |= IGnssCallback::GnssSvFlags::HAS_CARRIER_FREQUENCY;

        convertGnssConstellationType(in.gnssSvs[i].type, out[i].v2_0.constellation);
        out[i].basebandCN0DbHz = in.gnssSvs[i].basebandCarrierToNoiseDbHz;
    }
}

}  // 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 GNSS_API_CLINET_H
#define GNSS_API_CLINET_H

#include <mutex>
#include <android/hardware/gnss/2.1/IGnss.h>
#include <android/hardware/gnss/2.1/IGnssCallback.h>
#include <LocationAPIClientBase.h>

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

using ::android::sp;

class GnssAPIClient : public LocationAPIClientBase
{
public:
    GnssAPIClient(const sp<V1_0::IGnssCallback>& gpsCb,
            const sp<V1_0::IGnssNiCallback>& niCb);
    GnssAPIClient(const sp<V2_0::IGnssCallback>& gpsCb);
    GnssAPIClient(const sp<V2_1::IGnssCallback>& gpsCb);
    GnssAPIClient(const GnssAPIClient&) = delete;
    GnssAPIClient& operator=(const GnssAPIClient&) = delete;

    // for GpsInterface
    void gnssUpdateCallbacks(const sp<V1_0::IGnssCallback>& gpsCb,
            const sp<V1_0::IGnssNiCallback>& niCb);
    void gnssUpdateCallbacks_2_0(const sp<V2_0::IGnssCallback>& gpsCb);
    void gnssUpdateCallbacks_2_1(const sp<V2_1::IGnssCallback>& gpsCb);
    bool gnssStart();
    bool gnssStop();
    bool gnssSetPositionMode(V1_0::IGnss::GnssPositionMode mode,
            V1_0::IGnss::GnssPositionRecurrence recurrence,
            uint32_t minIntervalMs,
            uint32_t preferredAccuracyMeters,
            uint32_t preferredTimeMs,
            GnssPowerMode powerMode = GNSS_POWER_MODE_INVALID,
            uint32_t timeBetweenMeasurement = 0);

    // for GpsNiInterface
    void gnssNiRespond(int32_t notifId, V1_0::IGnssNiCallback::GnssUserResponseType userResponse);

    // these apis using LocationAPIControlClient
    void gnssDeleteAidingData(V1_0::IGnss::GnssAidingData aidingDataFlags);
    void gnssEnable(LocationTechnologyType techType);
    void gnssDisable();
    void gnssConfigurationUpdate(const GnssConfig& gnssConfig);

    inline LocationCapabilitiesMask gnssGetCapabilities() const {
        return mLocationCapabilitiesMask;
    }
    void requestCapabilities();

    // callbacks we are interested in
    void onCapabilitiesCb(LocationCapabilitiesMask capabilitiesMask) final;
    void onTrackingCb(Location location) final;
    void onGnssNiCb(uint32_t id, GnssNiNotification gnssNiNotification) final;
    void onGnssSvCb(GnssSvNotification gnssSvNotification) final;
    void onGnssNmeaCb(GnssNmeaNotification gnssNmeaNotification) final;

    void onStartTrackingCb(LocationError error) final;
    void onStopTrackingCb(LocationError error) final;

private:
    virtual ~GnssAPIClient();
    void setCallbacks();
    void initLocationOptions();

    sp<V1_0::IGnssCallback> mGnssCbIface;
    sp<V1_0::IGnssNiCallback> mGnssNiCbIface;
    std::mutex mMutex;
    LocationAPIControlClient* mControlClient;
    LocationCapabilitiesMask mLocationCapabilitiesMask;
    bool mLocationCapabilitiesCached;
    TrackingOptions mTrackingOptions;
    bool mTracking;
    sp<V2_0::IGnssCallback> mGnssCbIface_2_0;
    sp<V2_1::IGnssCallback> mGnssCbIface_2_1;
};

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


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.
 *
 */

#include <LocationUtil.h>
#include <log_util.h>
#include <inttypes.h>
#include <loc_misc_utils.h>
#include <gps_extended_c.h>

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

using ::android::hardware::gnss::V2_0::GnssLocation;
using ::android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
using ::android::hardware::gnss::V2_0::GnssConstellationType;
using ::android::hardware::gnss::V1_0::GnssLocationFlags;
using ::android::hardware::gnss::measurement_corrections::V1_0::GnssSingleSatCorrectionFlags;

void convertGnssLocation(Location& in, V1_0::GnssLocation& out)
{
    memset(&out, 0, sizeof(V1_0::GnssLocation));
    if (in.flags & LOCATION_HAS_LAT_LONG_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_LAT_LONG;
        out.latitudeDegrees = in.latitude;
        out.longitudeDegrees = in.longitude;
    }
    if (in.flags & LOCATION_HAS_ALTITUDE_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_ALTITUDE;
        out.altitudeMeters = in.altitude;
    }
    if (in.flags & LOCATION_HAS_SPEED_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_SPEED;
        out.speedMetersPerSec = in.speed;
    }
    if (in.flags & LOCATION_HAS_BEARING_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_BEARING;
        out.bearingDegrees = in.bearing;
    }
    if (in.flags & LOCATION_HAS_ACCURACY_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_HORIZONTAL_ACCURACY;
        out.horizontalAccuracyMeters = in.accuracy;
    }
    if (in.flags & LOCATION_HAS_VERTICAL_ACCURACY_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_VERTICAL_ACCURACY;
        out.verticalAccuracyMeters = in.verticalAccuracy;
    }
    if (in.flags & LOCATION_HAS_SPEED_ACCURACY_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_SPEED_ACCURACY;
        out.speedAccuracyMetersPerSecond = in.speedAccuracy;
    }
    if (in.flags & LOCATION_HAS_BEARING_ACCURACY_BIT) {
        out.gnssLocationFlags |= GnssLocationFlags::HAS_BEARING_ACCURACY;
        out.bearingAccuracyDegrees = in.bearingAccuracy;
    }

    out.timestamp = static_cast<V1_0::GnssUtcTime>(in.timestamp);
}

void convertGnssLocation(Location& in, V2_0::GnssLocation& out)
{
    memset(&out, 0, sizeof(V2_0::GnssLocation));
    convertGnssLocation(in, out.v1_0);

    if (in.flags & LOCATION_HAS_ELAPSED_REAL_TIME) {
        out.elapsedRealtime.flags |= ElapsedRealtimeFlags::HAS_TIMESTAMP_NS;
        out.elapsedRealtime.timestampNs = in.elapsedRealTime;
        out.elapsedRealtime.flags |= ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS;
        out.elapsedRealtime.timeUncertaintyNs = in.elapsedRealTimeUnc;
        LOC_LOGd("out.elapsedRealtime.timestampNs=%" PRIi64 ""
                 " out.elapsedRealtime.timeUncertaintyNs=%" PRIi64 ""
                 " out.elapsedRealtime.flags=0x%X",
                 out.elapsedRealtime.timestampNs,
                 out.elapsedRealtime.timeUncertaintyNs, out.elapsedRealtime.flags);
    }
}

void convertGnssLocation(const V1_0::GnssLocation& in, Location& out)
{
    memset(&out, 0, sizeof(out));
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_LAT_LONG) {
        out.flags |= LOCATION_HAS_LAT_LONG_BIT;
        out.latitude = in.latitudeDegrees;
        out.longitude = in.longitudeDegrees;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_ALTITUDE) {
        out.flags |= LOCATION_HAS_ALTITUDE_BIT;
        out.altitude = in.altitudeMeters;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_SPEED) {
        out.flags |= LOCATION_HAS_SPEED_BIT;
        out.speed = in.speedMetersPerSec;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_BEARING) {
        out.flags |= LOCATION_HAS_BEARING_BIT;
        out.bearing = in.bearingDegrees;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
        out.flags |= LOCATION_HAS_ACCURACY_BIT;
        out.accuracy = in.horizontalAccuracyMeters;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
        out.flags |= LOCATION_HAS_VERTICAL_ACCURACY_BIT;
        out.verticalAccuracy = in.verticalAccuracyMeters;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_SPEED_ACCURACY) {
        out.flags |= LOCATION_HAS_SPEED_ACCURACY_BIT;
        out.speedAccuracy = in.speedAccuracyMetersPerSecond;
    }
    if (in.gnssLocationFlags & GnssLocationFlags::HAS_BEARING_ACCURACY) {
        out.flags |= LOCATION_HAS_BEARING_ACCURACY_BIT;
        out.bearingAccuracy = in.bearingAccuracyDegrees;
    }

    out.timestamp = static_cast<uint64_t>(in.timestamp);
}

void convertGnssLocation(const V2_0::GnssLocation& in, Location& out)
{
    memset(&out, 0, sizeof(out));
    convertGnssLocation(in.v1_0, out);
}

void convertGnssConstellationType(GnssSvType& in, V1_0::GnssConstellationType& out)
{
    switch(in) {
        case GNSS_SV_TYPE_GPS:
            out = V1_0::GnssConstellationType::GPS;
            break;
        case GNSS_SV_TYPE_SBAS:
            out = V1_0::GnssConstellationType::SBAS;
            break;
        case GNSS_SV_TYPE_GLONASS:
            out = V1_0::GnssConstellationType::GLONASS;
            break;
        case GNSS_SV_TYPE_QZSS:
            out = V1_0::GnssConstellationType::QZSS;
            break;
        case GNSS_SV_TYPE_BEIDOU:
            out = V1_0::GnssConstellationType::BEIDOU;
            break;
        case GNSS_SV_TYPE_GALILEO:
            out = V1_0::GnssConstellationType::GALILEO;
            break;
        case GNSS_SV_TYPE_UNKNOWN:
        default:
            out = V1_0::GnssConstellationType::UNKNOWN;
            break;
    }
}

void convertGnssConstellationType(GnssSvType& in, V2_0::GnssConstellationType& out)
{
    switch(in) {
        case GNSS_SV_TYPE_GPS:
            out = V2_0::GnssConstellationType::GPS;
            break;
        case GNSS_SV_TYPE_SBAS:
            out = V2_0::GnssConstellationType::SBAS;
            break;
        case GNSS_SV_TYPE_GLONASS:
            out = V2_0::GnssConstellationType::GLONASS;
            break;
        case GNSS_SV_TYPE_QZSS:
            out = V2_0::GnssConstellationType::QZSS;
            break;
        case GNSS_SV_TYPE_BEIDOU:
            out = V2_0::GnssConstellationType::BEIDOU;
            break;
        case GNSS_SV_TYPE_GALILEO:
            out = V2_0::GnssConstellationType::GALILEO;
            break;
        case GNSS_SV_TYPE_NAVIC:
            out = V2_0::GnssConstellationType::IRNSS;
            break;
        case GNSS_SV_TYPE_UNKNOWN:
        default:
            out = V2_0::GnssConstellationType::UNKNOWN;
            break;
    }
}

void convertGnssSvid(GnssSv& in, int16_t& out)
{
    switch (in.type) {
        case GNSS_SV_TYPE_GPS:
            out = in.svId;
            break;
        case GNSS_SV_TYPE_SBAS:
            out = in.svId;
            break;
        case GNSS_SV_TYPE_GLONASS:
            if (!isGloSlotUnknown(in.svId)) { // OSN is known
                out = in.svId - GLO_SV_PRN_MIN + 1;
            } else { // OSN is not known, report FCN
                out = in.gloFrequency + 92;
            }
            break;
        case GNSS_SV_TYPE_QZSS:
            out = in.svId;
            break;
        case GNSS_SV_TYPE_BEIDOU:
            out = in.svId - BDS_SV_PRN_MIN + 1;
            break;
        case GNSS_SV_TYPE_GALILEO:
            out = in.svId - GAL_SV_PRN_MIN + 1;
            break;
        case GNSS_SV_TYPE_NAVIC:
            out = in.svId - NAVIC_SV_PRN_MIN + 1;
            break;
        default:
            out = in.svId;
            break;
    }
}

void convertGnssSvid(GnssMeasurementsData& in, int16_t& out)
{
    switch (in.svType) {
        case GNSS_SV_TYPE_GPS:
            out = in.svId;
            break;
        case GNSS_SV_TYPE_SBAS:
            out = in.svId;
            break;
        case GNSS_SV_TYPE_GLONASS:
            if (!isGloSlotUnknown(in.svId)) { // OSN is known
                out = in.svId - GLO_SV_PRN_MIN + 1;
            } else { // OSN is not known, report FCN
                out = in.gloFrequency + 92;
            }
            break;
        case GNSS_SV_TYPE_QZSS:
            out = in.svId;
            break;
        case GNSS_SV_TYPE_BEIDOU:
            out = in.svId - BDS_SV_PRN_MIN + 1;
            break;
        case GNSS_SV_TYPE_GALILEO:
            out = in.svId - GAL_SV_PRN_MIN + 1;
            break;
        case GNSS_SV_TYPE_NAVIC:
            out = in.svId - NAVIC_SV_PRN_MIN + 1;
            break;
        default:
            out = in.svId;
            break;
    }
}

void convertGnssEphemerisType(GnssEphemerisType& in, GnssDebug::SatelliteEphemerisType& out)
{
    switch(in) {
        case GNSS_EPH_TYPE_EPHEMERIS:
            out = GnssDebug::SatelliteEphemerisType::EPHEMERIS;
            break;
        case GNSS_EPH_TYPE_ALMANAC:
            out = GnssDebug::SatelliteEphemerisType::ALMANAC_ONLY;
            break;
        case GNSS_EPH_TYPE_UNKNOWN:
        default:
            out = GnssDebug::SatelliteEphemerisType::NOT_AVAILABLE;
            break;
    }
}

void convertGnssEphemerisSource(GnssEphemerisSource& in, GnssDebug::SatelliteEphemerisSource& out)
{
    switch(in) {
        case GNSS_EPH_SOURCE_DEMODULATED:
            out = GnssDebug::SatelliteEphemerisSource::DEMODULATED;
            break;
        case GNSS_EPH_SOURCE_SUPL_PROVIDED:
            out = GnssDebug::SatelliteEphemerisSource::SUPL_PROVIDED;
            break;
        case GNSS_EPH_SOURCE_OTHER_SERVER_PROVIDED:
            out = GnssDebug::SatelliteEphemerisSource::OTHER_SERVER_PROVIDED;
            break;
        case GNSS_EPH_SOURCE_LOCAL:
        case GNSS_EPH_SOURCE_UNKNOWN:
        default:
            out = GnssDebug::SatelliteEphemerisSource::OTHER;
            break;
    }
}

void convertGnssEphemerisHealth(GnssEphemerisHealth& in, GnssDebug::SatelliteEphemerisHealth& out)
{
    switch(in) {
        case GNSS_EPH_HEALTH_GOOD:
            out = GnssDebug::SatelliteEphemerisHealth::GOOD;
            break;
        case GNSS_EPH_HEALTH_BAD:
            out = GnssDebug::SatelliteEphemerisHealth::BAD;
            break;
        case GNSS_EPH_HEALTH_UNKNOWN:
        default:
            out = GnssDebug::SatelliteEphemerisHealth::UNKNOWN;
            break;
    }
}

void convertSingleSatCorrections(const SingleSatCorrection& in, GnssSingleSatCorrection& out)
{
    out.flags = GNSS_MEAS_CORR_UNKNOWN_BIT;
    if (in.singleSatCorrectionFlags & (GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY)) {
        out.flags |= GNSS_MEAS_CORR_HAS_SAT_IS_LOS_PROBABILITY_BIT;
    }
    if (in.singleSatCorrectionFlags & (GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH)) {
        out.flags |= GNSS_MEAS_CORR_HAS_EXCESS_PATH_LENGTH_BIT;
    }
    if (in.singleSatCorrectionFlags & (GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC)) {
        out.flags |= GNSS_MEAS_CORR_HAS_EXCESS_PATH_LENGTH_UNC_BIT;
    }
    if (in.singleSatCorrectionFlags & (GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE)) {
        out.flags |= GNSS_MEAS_CORR_HAS_REFLECTING_PLANE_BIT;
    }
    switch (in.constellation) {
    case (::android::hardware::gnss::V1_0::GnssConstellationType::GPS):
        out.svType = GNSS_SV_TYPE_GPS;
        break;
    case (::android::hardware::gnss::V1_0::GnssConstellationType::SBAS):
        out.svType = GNSS_SV_TYPE_SBAS;
        break;
    case (::android::hardware::gnss::V1_0::GnssConstellationType::GLONASS):
        out.svType = GNSS_SV_TYPE_GLONASS;
        break;
    case (::android::hardware::gnss::V1_0::GnssConstellationType::QZSS):
        out.svType = GNSS_SV_TYPE_QZSS;
        break;
    case (::android::hardware::gnss::V1_0::GnssConstellationType::BEIDOU):
        out.svType = GNSS_SV_TYPE_BEIDOU;
        break;
    case (::android::hardware::gnss::V1_0::GnssConstellationType::GALILEO):
        out.svType = GNSS_SV_TYPE_GALILEO;
        break;
    case (::android::hardware::gnss::V1_0::GnssConstellationType::UNKNOWN):
    default:
        out.svType = GNSS_SV_TYPE_UNKNOWN;
        break;
    }
    out.svId = in.svid;
    out.carrierFrequencyHz = in.carrierFrequencyHz;
    out.probSatIsLos = in.probSatIsLos;
    out.excessPathLengthMeters = in.excessPathLengthMeters;
    out.excessPathLengthUncertaintyMeters = in.excessPathLengthUncertaintyMeters;

    out.reflectingPlane.latitudeDegrees = in.reflectingPlane.latitudeDegrees;
    out.reflectingPlane.longitudeDegrees = in.reflectingPlane.longitudeDegrees;
    out.reflectingPlane.altitudeMeters = in.reflectingPlane.altitudeMeters;
    out.reflectingPlane.azimuthDegrees = in.reflectingPlane.azimuthDegrees;
}

void convertMeasurementCorrections(const MeasurementCorrectionsV1_0& in,
                                   GnssMeasurementCorrections& out)
{
    memset(&out, 0, sizeof(GnssMeasurementCorrections));
    out.latitudeDegrees = in.latitudeDegrees;
    out.longitudeDegrees = in.longitudeDegrees;
    out.altitudeMeters = in.altitudeMeters;
    out.horizontalPositionUncertaintyMeters = in.horizontalPositionUncertaintyMeters;
    out.verticalPositionUncertaintyMeters = in.verticalPositionUncertaintyMeters;
    out.toaGpsNanosecondsOfWeek = in.toaGpsNanosecondsOfWeek;

    for (int i = 0; i < in.satCorrections.size(); i++) {
        GnssSingleSatCorrection gnssSingleSatCorrection = {};

        convertSingleSatCorrections(in.satCorrections[i], gnssSingleSatCorrection);
        out.satCorrections.push_back(gnssSingleSatCorrection);
    }
}

}  // 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 LOCATION_UTIL_H
#define LOCATION_UTIL_H

#include <android/hardware/gnss/2.0/types.h>
#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <LocationAPI.h>
#include <GnssDebug.h>

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

using MeasurementCorrectionsV1_0 =
        ::android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using ::android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;

void convertGnssLocation(Location& in, V1_0::GnssLocation& out);
void convertGnssLocation(Location& in, V2_0::GnssLocation& out);
void convertGnssLocation(const V1_0::GnssLocation& in, Location& out);
void convertGnssLocation(const V2_0::GnssLocation& in, Location& out);
void convertGnssConstellationType(GnssSvType& in, V1_0::GnssConstellationType& out);
void convertGnssConstellationType(GnssSvType& in, V2_0::GnssConstellationType& out);
void convertGnssSvid(GnssSv& in, int16_t& out);
void convertGnssSvid(GnssMeasurementsData& in, int16_t& out);
void convertGnssEphemerisType(GnssEphemerisType& in, GnssDebug::SatelliteEphemerisType& out);
void convertGnssEphemerisSource(GnssEphemerisSource& in, GnssDebug::SatelliteEphemerisSource& out);
void convertGnssEphemerisHealth(GnssEphemerisHealth& in, GnssDebug::SatelliteEphemerisHealth& out);
void convertSingleSatCorrections(const SingleSatCorrection& in, GnssSingleSatCorrection& out);
void convertMeasurementCorrections(const MeasurementCorrectionsV1_0& in,
                                   GnssMeasurementCorrections& out);

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