Upgrade gnss to @2.0

Bug: 152575718
Test: atest VtsHalGnssV2_0TargetTest
Signed-off-by: Roman Kiryanov <rkir@google.com>
Change-Id: I6ea8562da77a6b12d53e5d92ceed9456a71ad699
diff --git a/gnss/Android.bp b/gnss/Android.bp
new file mode 100644
index 0000000..dbee0de
--- /dev/null
+++ b/gnss/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+cc_binary {
+    name: "android.hardware.gnss@2.0-service.ranchu",
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.gnss@2.0-service.ranchu.rc"],
+    vintf_fragments: ["android.hardware.gnss@2.0-service.ranchu.xml"],
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "agnss.cpp",
+        "gnss_configuration.cpp",
+        "gnss_measurement.cpp",
+        "gnss_hw_conn.cpp",
+        "gnss_hw_listener.cpp",
+        "data_sink.cpp",
+        "gnss.cpp",
+        "main.cpp",
+        "util.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+        "android.hardware.gnss@2.0",
+        "android.hardware.gnss@1.1",
+        "android.hardware.gnss@1.0",
+        "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss.visibility_control@1.0",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"android.hardware.gnss@2.0-service.ranchu\"",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+    ],
+}
diff --git a/gnss/agnss.cpp b/gnss/agnss.cpp
new file mode 100644
index 0000000..ae7a0aa
--- /dev/null
+++ b/gnss/agnss.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "agnss.h"
+
+namespace goldfish {
+
+Return<void> AGnss20::setCallback(const sp<ahg20::IAGnssCallback>& callback) {
+    (void)callback;
+    return {};
+}
+
+Return<bool> AGnss20::dataConnClosed() {
+    return false;
+}
+
+Return<bool> AGnss20::dataConnFailed() {
+    return false;
+}
+
+Return<bool> AGnss20::setServer(ahg20::IAGnssCallback::AGnssType type,
+                                const hidl_string& hostname,
+                                int32_t port) {
+    (void)type;
+    (void)hostname;
+    (void)port;
+    return true;
+}
+
+Return<bool> AGnss20::dataConnOpen(uint64_t networkHandle,
+                                   const hidl_string& apn,
+                                   ahg20::IAGnss::ApnIpType apnIpType) {
+    (void)networkHandle;
+    (void)apn;
+    (void)apnIpType;
+    return false;
+}
+
+}  // namespace goldfish
diff --git a/gnss/agnss.h b/gnss/agnss.h
new file mode 100644
index 0000000..e9df28b
--- /dev/null
+++ b/gnss/agnss.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/gnss/1.0/IAGnss.h>
+#include <android/hardware/gnss/2.0/IAGnss.h>
+
+namespace goldfish {
+namespace ahg = ::android::hardware::gnss;
+namespace ahg10 = ahg::V1_0;
+namespace ahg20 = ahg::V2_0;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+
+struct AGnss20 : public ahg20::IAGnss {
+    Return<void> setCallback(const sp<ahg20::IAGnssCallback>& callback) override;
+    Return<bool> dataConnClosed() override;
+    Return<bool> dataConnFailed() override;
+    Return<bool> setServer(ahg20::IAGnssCallback::AGnssType type,
+                           const hidl_string& hostname,
+                           int32_t port) override;
+    Return<bool> dataConnOpen(uint64_t networkHandle, const hidl_string& apn,
+                              ahg20::IAGnss::ApnIpType apnIpType) override;
+};
+
+}  // namespace goldfish
diff --git a/gnss/android.hardware.gnss@2.0-service.ranchu.rc b/gnss/android.hardware.gnss@2.0-service.ranchu.rc
new file mode 100644
index 0000000..77dca15
--- /dev/null
+++ b/gnss/android.hardware.gnss@2.0-service.ranchu.rc
@@ -0,0 +1,9 @@
+service vendor.gnss-2-0 /vendor/bin/hw/android.hardware.gnss@2.0-service.ranchu
+    interface android.hardware.gnss@2.0::IGnss default
+    interface android.hardware.gnss@1.1::IGnss default
+    interface android.hardware.gnss@1.0::IGnss default
+    class hal
+    user gps
+    group system gps radio
+    oneshot
+    disabled
diff --git a/manifest.gnss.xml b/gnss/android.hardware.gnss@2.0-service.ranchu.xml
similarity index 69%
rename from manifest.gnss.xml
rename to gnss/android.hardware.gnss@2.0-service.ranchu.xml
index 69a178f..5b417f6 100644
--- a/manifest.gnss.xml
+++ b/gnss/android.hardware.gnss@2.0-service.ranchu.xml
@@ -1,8 +1,9 @@
-<manifest version="1.0" type="device" target-level="3">
+<manifest version="1.0" type="device">
     <hal format="hidl">
         <name>android.hardware.gnss</name>
         <transport>hwbinder</transport>
-        <version>1.0</version>
+        <version>2.0</version>
+        <version>1.1</version>
         <interface>
             <name>IGnss</name>
             <instance>default</instance>
diff --git a/gnss/data_sink.cpp b/gnss/data_sink.cpp
new file mode 100644
index 0000000..4b0c52c
--- /dev/null
+++ b/gnss/data_sink.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+#include "data_sink.h"
+
+namespace goldfish {
+
+void DataSink::gnssLocation(const ahg20::GnssLocation& loc) const {
+    std::unique_lock<std::mutex> lock(mtx);
+    if (cb20) {
+        cb20->gnssLocationCb_2_0(loc);
+    }
+}
+
+void DataSink::gnssSvStatus(const hidl_vec<ahg20::IGnssCallback::GnssSvInfo>& svInfoList20) const {
+    std::unique_lock<std::mutex> lock(mtx);
+    if (cb20) {
+        cb20->gnssSvStatusCb_2_0(svInfoList20);
+    }
+}
+
+void DataSink::gnssStatus(const ahg10::IGnssCallback::GnssStatusValue status) const {
+    std::unique_lock<std::mutex> lock(mtx);
+    if (cb20) {
+        cb20->gnssStatusCb(status);
+    }
+}
+
+void DataSink::gnssNmea(const ahg10::GnssUtcTime t,
+                        const hidl_string& nmea) const {
+    std::unique_lock<std::mutex> lock(mtx);
+    if (cb20) {
+        cb20->gnssNmeaCb(t, nmea);
+    }
+}
+
+void DataSink::setCallback20(sp<ahg20::IGnssCallback> cb) {
+    std::unique_lock<std::mutex> lock(mtx);
+    cb20 = std::move(cb);
+}
+
+void DataSink::cleanup() {
+    std::unique_lock<std::mutex> lock(mtx);
+    cb20 = nullptr;
+}
+
+}  // namespace goldfish
diff --git a/gnss/data_sink.h b/gnss/data_sink.h
new file mode 100644
index 0000000..d182f2d
--- /dev/null
+++ b/gnss/data_sink.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/gnss/2.0/IGnss.h>
+#include <mutex>
+
+namespace goldfish {
+namespace ahg = ::android::hardware::gnss;
+namespace ahg20 = ahg::V2_0;
+namespace ahg10 = ahg::V1_0;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+class DataSink {
+public:
+    void gnssLocation(const ahg20::GnssLocation&) const;
+    void gnssSvStatus(const hidl_vec<ahg20::IGnssCallback::GnssSvInfo>&) const;
+    void gnssStatus(const ahg10::IGnssCallback::GnssStatusValue) const;
+    void gnssNmea(const ahg10::GnssUtcTime, const hidl_string&) const;
+
+    void setCallback20(sp<ahg20::IGnssCallback>);
+    void cleanup();
+
+private:
+    sp<ahg20::IGnssCallback> cb20;
+    mutable std::mutex       mtx;
+};
+
+}  // namespace goldfish
diff --git a/gnss/gnss.cpp b/gnss/gnss.cpp
new file mode 100644
index 0000000..8fa81fc
--- /dev/null
+++ b/gnss/gnss.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+
+#include "gnss.h"
+#include "gnss_configuration.h"
+#include "gnss_measurement.h"
+#include "agnss.h"
+
+namespace {
+constexpr char kGnssDeviceName[] = "Android Studio Emulator GPS";
+};
+
+namespace goldfish {
+
+Return<sp<ahg20::IGnssConfiguration>> Gnss20::getExtensionGnssConfiguration_2_0() {
+    return new GnssConfiguration20();
+}
+
+Return<sp<ahg20::IGnssDebug>> Gnss20::getExtensionGnssDebug_2_0() {
+    return nullptr;
+}
+
+Return<sp<ahg20::IAGnss>> Gnss20::getExtensionAGnss_2_0() {
+    return new AGnss20();
+}
+
+Return<sp<ahg20::IAGnssRil>> Gnss20::getExtensionAGnssRil_2_0() {
+    return nullptr;
+}
+
+Return<sp<ahg20::IGnssMeasurement>> Gnss20::getExtensionGnssMeasurement_2_0() {
+    return new GnssMeasurement20();
+}
+
+Return<bool> Gnss20::setCallback_2_0(const sp<ahg20::IGnssCallback>& callback) {
+    if (callback == nullptr) {
+        return false;
+    } else if (open()) {
+        using Caps = ahg20::IGnssCallback::Capabilities;
+        callback->gnssSetCapabilitiesCb_2_0(Caps::MEASUREMENTS | 0);
+        callback->gnssNameCb(kGnssDeviceName);
+        callback->gnssSetSystemInfoCb({.yearOfHw = 2020});
+
+        m_dataSink.setCallback20(callback);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+Return<sp<ahgmc10::IMeasurementCorrections>> Gnss20::getExtensionMeasurementCorrections() {
+    return nullptr;
+}
+
+Return<sp<ahgvc10::IGnssVisibilityControl>> Gnss20::getExtensionVisibilityControl() {
+    return nullptr;
+}
+
+Return<sp<ahg20::IGnssBatching>> Gnss20::getExtensionGnssBatching_2_0() {
+    return nullptr;
+}
+
+Return<bool> Gnss20::injectBestLocation_2_0(const ahg20::GnssLocation& location) {
+    (void)location;
+    return true;
+}
+
+Return<bool> Gnss20::setPositionMode_1_1(ahg10::IGnss::GnssPositionMode mode,
+                                         ahg10::IGnss::GnssPositionRecurrence recurrence,
+                                         uint32_t minIntervalMs, uint32_t preferredAccuracyMeters,
+                                         uint32_t preferredTimeMs, bool lowPowerMode) {
+    (void)mode;
+    (void)recurrence;
+    (void)minIntervalMs;
+    (void)preferredAccuracyMeters;
+    (void)preferredTimeMs;
+    (void)lowPowerMode;
+    return true;
+}
+
+Return<bool> Gnss20::start() {
+    std::unique_lock<std::mutex> lock(m_gnssHwConnMtx);
+    if (m_gnssHwConn) {
+        return m_gnssHwConn->start();
+    } else {
+        return false;
+    }
+}
+
+Return<bool> Gnss20::stop() {
+    std::unique_lock<std::mutex> lock(m_gnssHwConnMtx);
+    if (m_gnssHwConn) {
+        return m_gnssHwConn->stop();
+    } else {
+        return false;
+    }
+}
+
+Return<void> Gnss20::cleanup() {
+    {
+        std::unique_lock<std::mutex> lock(m_gnssHwConnMtx);
+        m_gnssHwConn.reset();
+    }
+
+    m_dataSink.cleanup();
+
+    return {};
+}
+
+Return<bool> Gnss20::injectTime(int64_t timeMs, int64_t timeReferenceMs,
+                                int32_t uncertaintyMs) {
+    (void)timeMs;
+    (void)timeReferenceMs;
+    (void)uncertaintyMs;
+    return true;
+}
+
+Return<bool> Gnss20::injectLocation(double latitudeDegrees, double longitudeDegrees,
+                                    float accuracyMeters) {
+    (void)latitudeDegrees;
+    (void)longitudeDegrees;
+    (void)accuracyMeters;
+    return false;
+}
+
+Return<void> Gnss20::deleteAidingData(ahg10::IGnss::GnssAidingData aidingDataFlags) {
+    (void)aidingDataFlags;
+    return {};
+}
+
+Return<sp<ahg10::IGnssGeofencing>> Gnss20::getExtensionGnssGeofencing() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IGnssNavigationMessage>> Gnss20::getExtensionGnssNavigationMessage() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IGnssXtra>> Gnss20::getExtensionXtra() {
+    return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool Gnss20::open() {
+    std::unique_lock<std::mutex> lock(m_gnssHwConnMtx);
+    if (m_gnssHwConn) {
+        return true;
+    } else {
+        auto conn = std::make_unique<GnssHwConn>(&m_dataSink);
+        if (conn->ok()) {
+            m_gnssHwConn = std::move(conn);
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+
+//// deprecated and old versions ///////////////////////////////////////////////
+Return<bool> Gnss20::setCallback_1_1(const sp<ahg11::IGnssCallback>&) {
+    return false;
+}
+
+Return<sp<ahg11::IGnssMeasurement>> Gnss20::getExtensionGnssMeasurement_1_1() {
+    return nullptr;
+}
+
+Return<sp<ahg11::IGnssConfiguration>> Gnss20::getExtensionGnssConfiguration_1_1() {
+    return nullptr;
+}
+
+Return<bool> Gnss20::setCallback(const sp<ahg10::IGnssCallback>&) {
+    return false;
+}
+
+Return<sp<ahg10::IGnssMeasurement>> Gnss20::getExtensionGnssMeasurement() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IAGnss>> Gnss20::getExtensionAGnss() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IGnssNi>> Gnss20::getExtensionGnssNi() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IGnssDebug>> Gnss20::getExtensionGnssDebug() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IGnssBatching>> Gnss20::getExtensionGnssBatching() {
+    return nullptr;
+}
+
+Return<bool> Gnss20::injectBestLocation(const ahg10::GnssLocation&) {
+    return false;
+}
+
+Return<bool> Gnss20::setPositionMode(ahg10::IGnss::GnssPositionMode,
+                                     ahg10::IGnss::GnssPositionRecurrence,
+                                     uint32_t, uint32_t, uint32_t) {
+    return false;
+}
+
+Return<sp<ahg10::IGnssConfiguration>> Gnss20::getExtensionGnssConfiguration() {
+    return nullptr;
+}
+
+Return<sp<ahg10::IAGnssRil>> Gnss20::getExtensionAGnssRil() {
+    return nullptr;
+}
+
+}  // namespace goldfish
diff --git a/gnss/gnss.h b/gnss/gnss.h
new file mode 100644
index 0000000..bc8ccfc
--- /dev/null
+++ b/gnss/gnss.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/gnss/2.0/IGnss.h>
+#include <mutex>
+#include <memory>
+#include "data_sink.h"
+#include "gnss_hw_conn.h"
+
+namespace goldfish {
+namespace ahg = ::android::hardware::gnss;
+namespace ahg20 = ahg::V2_0;
+namespace ahg11 = ahg::V1_1;
+namespace ahg10 = ahg::V1_0;
+namespace ahgmc10 = ahg::measurement_corrections::V1_0;
+namespace ahgvc10 = ahg::visibility_control::V1_0;
+
+using ::android::sp;
+using ::android::hardware::Return;
+
+struct Gnss20 : public ahg20::IGnss {
+    // Methods from V2_0::IGnss follow.
+    Return<sp<ahg20::IGnssConfiguration>> getExtensionGnssConfiguration_2_0() override;
+    Return<sp<ahg20::IGnssDebug>> getExtensionGnssDebug_2_0() override;
+    Return<sp<ahg20::IAGnss>> getExtensionAGnss_2_0() override;
+    Return<sp<ahg20::IAGnssRil>> getExtensionAGnssRil_2_0() override;
+    Return<sp<ahg20::IGnssMeasurement>> getExtensionGnssMeasurement_2_0() override;
+    Return<bool> setCallback_2_0(const sp<ahg20::IGnssCallback>& callback) override;
+    Return<sp<ahgmc10::IMeasurementCorrections>>
+    getExtensionMeasurementCorrections() override;
+    Return<sp<ahgvc10::IGnssVisibilityControl>> getExtensionVisibilityControl() override;
+    Return<sp<ahg20::IGnssBatching>> getExtensionGnssBatching_2_0() override;
+    Return<bool> injectBestLocation_2_0(const ahg20::GnssLocation& location) override;
+
+    // Methods from V1_1::IGnss follow.
+    Return<bool> setCallback_1_1(const sp<ahg11::IGnssCallback>& callback) override;
+    Return<bool> setPositionMode_1_1(ahg10::IGnss::GnssPositionMode mode,
+                                     ahg10::IGnss::GnssPositionRecurrence recurrence,
+                                     uint32_t minIntervalMs, uint32_t preferredAccuracyMeters,
+                                     uint32_t preferredTimeMs, bool lowPowerMode) override;
+    Return<sp<ahg11::IGnssConfiguration>> getExtensionGnssConfiguration_1_1() override;
+    Return<sp<ahg11::IGnssMeasurement>> getExtensionGnssMeasurement_1_1() override;
+    Return<bool> injectBestLocation(const ahg10::GnssLocation& location) override;
+
+    // Methods from V1_0::IGnss follow.
+    Return<bool> setCallback(const sp<ahg10::IGnssCallback>& callback) override;
+    Return<bool> start() override;
+    Return<bool> stop() override;
+    Return<void> cleanup() override;
+    Return<bool> injectTime(int64_t timeMs, int64_t timeReferenceMs,
+                            int32_t uncertaintyMs) override;
+    Return<bool> injectLocation(double latitudeDegrees, double longitudeDegrees,
+                                float accuracyMeters) override;
+    Return<void> deleteAidingData(ahg10::IGnss::GnssAidingData aidingDataFlags) override;
+    Return<bool> setPositionMode(ahg10::IGnss::GnssPositionMode mode,
+                                 ahg10::IGnss::GnssPositionRecurrence recurrence,
+                                 uint32_t minIntervalMs, uint32_t preferredAccuracyMeters,
+                                 uint32_t preferredTimeMs) override;
+    Return<sp<ahg10::IAGnssRil>> getExtensionAGnssRil() override;
+    Return<sp<ahg10::IGnssGeofencing>> getExtensionGnssGeofencing() override;
+    Return<sp<ahg10::IAGnss>> getExtensionAGnss() override;
+    Return<sp<ahg10::IGnssNi>> getExtensionGnssNi() override;
+    Return<sp<ahg10::IGnssMeasurement>> getExtensionGnssMeasurement() override;
+    Return<sp<ahg10::IGnssNavigationMessage>> getExtensionGnssNavigationMessage() override;
+    Return<sp<ahg10::IGnssXtra>> getExtensionXtra() override;
+    Return<sp<ahg10::IGnssConfiguration>> getExtensionGnssConfiguration() override;
+    Return<sp<ahg10::IGnssDebug>> getExtensionGnssDebug() override;
+    Return<sp<ahg10::IGnssBatching>> getExtensionGnssBatching() override;
+
+private:
+    bool open();
+    void cleanupImpl();
+    bool injectBestLocationImpl(const ahg10::GnssLocation&,
+                                const ahg20::ElapsedRealtime);
+
+
+    DataSink m_dataSink;    // all updates go here
+
+    std::unique_ptr<GnssHwConn> m_gnssHwConn;
+    mutable std::mutex          m_gnssHwConnMtx;
+};
+
+}  // namespace goldfish
diff --git a/gnss/gnss_configuration.cpp b/gnss/gnss_configuration.cpp
new file mode 100644
index 0000000..49ef6e1
--- /dev/null
+++ b/gnss/gnss_configuration.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "gnss_configuration.h"
+
+namespace goldfish {
+
+Return<bool> GnssConfiguration20::setEsExtensionSec(uint32_t emergencyExtensionSeconds) {
+    (void)emergencyExtensionSeconds;
+    return false;
+}
+
+Return<bool> GnssConfiguration20::setBlacklist(const hidl_vec<ahg11::IGnssConfiguration::BlacklistedSource>& blacklist) {
+    (void)blacklist;
+    return false;
+}
+
+Return<bool> GnssConfiguration20::setSuplVersion(uint32_t version) {
+    (void)version;
+    return true;
+}
+
+Return<bool> GnssConfiguration20::setSuplMode(hidl_bitfield<SuplMode> mode) {
+    (void)mode;
+    return true;
+}
+
+Return<bool> GnssConfiguration20::setGpsLock(hidl_bitfield<GpsLock> lock) {
+    (void)lock;
+    return false;
+}
+
+Return<bool> GnssConfiguration20::setLppProfile(hidl_bitfield<LppProfile> lppProfile) {
+    (void)lppProfile;
+    return true;
+}
+
+Return<bool> GnssConfiguration20::setGlonassPositioningProtocol(hidl_bitfield<GlonassPosProtocol> protocol) {
+    (void)protocol;
+    return true;
+}
+
+Return<bool> GnssConfiguration20::setEmergencySuplPdn(bool enable) {
+    (void)enable;
+    return true;
+}
+
+/// old and deprecated /////////////////////////////////////////////////////////
+Return<bool> GnssConfiguration20::setSuplEs(bool enable) {
+    (void)enable;
+    return false;
+}
+
+}  // namespace goldfish
diff --git a/gnss/gnss_configuration.h b/gnss/gnss_configuration.h
new file mode 100644
index 0000000..36e71ca
--- /dev/null
+++ b/gnss/gnss_configuration.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android/hardware/gnss/2.0/IGnssConfiguration.h>
+
+namespace goldfish {
+namespace ahg = ::android::hardware::gnss;
+namespace ahg20 = ahg::V2_0;
+namespace ahg11 = ahg::V1_1;
+namespace ahg10 = ahg::V1_0;
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+struct Gnss20;
+
+struct GnssConfiguration20 : public ahg20::IGnssConfiguration {
+    // Methods from ::android::hardware::gnss::V2_0::IGnssConfiguration follow.
+    Return<bool> setEsExtensionSec(uint32_t emergencyExtensionSeconds) override;
+
+    // Methods from ::android::hardware::gnss::V1_1::IGnssConfiguration follow.
+    Return<bool> setBlacklist(const hidl_vec<ahg11::IGnssConfiguration::BlacklistedSource>& blacklist) override;
+
+    // Methods from ::android::hardware::gnss::V1_0::IGnssConfiguration follow.
+    Return<bool> setSuplEs(bool enabled) override;
+    Return<bool> setSuplVersion(uint32_t version) override;
+    Return<bool> setSuplMode(hidl_bitfield<SuplMode> mode) override;
+    Return<bool> setGpsLock(hidl_bitfield<GpsLock> lock) override;
+    Return<bool> setLppProfile(hidl_bitfield<LppProfile> lppProfile) override;
+    Return<bool> setGlonassPositioningProtocol(hidl_bitfield<GlonassPosProtocol> protocol) override;
+    Return<bool> setEmergencySuplPdn(bool enable) override;
+};
+
+}  // namespace goldfish
diff --git a/gnss/gnss_hw_conn.cpp b/gnss/gnss_hw_conn.cpp
new file mode 100644
index 0000000..371e4db
--- /dev/null
+++ b/gnss/gnss_hw_conn.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <log/log.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include "gnss_hw_conn.h"
+#include "gnss_hw_listener.h"
+
+namespace {
+constexpr char kCMD_QUIT = 'q';
+constexpr char kCMD_START = 'a';
+constexpr char kCMD_STOP = 'o';
+
+ssize_t qemu_pipe_write_fully(int pipe, const void* buffer, ssize_t len) {
+    const char* p = (const char*)buffer;
+
+    while (len > 0) {
+      ssize_t n = TEMP_FAILURE_RETRY(write(pipe, p, len));
+      if (n < 0) return n;
+
+      p += n;
+      len -= n;
+    }
+
+    return 0;
+}
+
+int qemu_pipe_open_ns(const char* ns, const char* pipeName, const int flags) {
+    int fd = TEMP_FAILURE_RETRY(open("/dev/goldfish_pipe", flags));
+    if (fd < 0) {
+        return -1;
+    }
+
+    char buff[64];
+    int len = snprintf(buff, sizeof(buff), "pipe:%s:%s", ns, pipeName);
+    if (qemu_pipe_write_fully(fd, buff, len + 1)) {
+        close(fd);
+        return -1;
+    }
+
+    return fd;
+}
+
+int epollCtlAdd(int epollFd, int fd) {
+    int ret;
+
+    /* make the fd non-blocking */
+    ret = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+    if (ret < 0) {
+        return ret;
+    }
+    ret = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, ret | O_NONBLOCK));
+    if (ret < 0) {
+        return ret;
+    }
+
+    struct epoll_event ev;
+    ev.events  = EPOLLIN;
+    ev.data.fd = fd;
+
+    return TEMP_FAILURE_RETRY(epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ev));
+}
+}  // namespace
+
+namespace goldfish {
+
+GnssHwConn::GnssHwConn(const DataSink* sink) {
+    m_devFd.reset(qemu_pipe_open_ns("qemud", "gps", O_RDWR));
+    if (!m_devFd.ok()) {
+        ALOGE("%s:%d: qemu_pipe_open_ns failed", __PRETTY_FUNCTION__, __LINE__);
+        return;
+    }
+
+    if (!::android::base::Socketpair(AF_LOCAL, SOCK_STREAM, 0,
+                                     &m_callersFd, &m_threadsFd)) {
+        ALOGE("%s:%d: Socketpair failed", __PRETTY_FUNCTION__, __LINE__);
+        m_devFd.reset();
+        return;
+    }
+
+    m_thread = std::thread([this, sink]() {
+        sink->gnssStatus(ahg10::IGnssCallback::GnssStatusValue::ENGINE_ON);
+        workerThread(m_devFd.get(), m_threadsFd.get(), sink);
+        sink->gnssStatus(ahg10::IGnssCallback::GnssStatusValue::ENGINE_OFF);
+    });
+}
+
+GnssHwConn::~GnssHwConn() {
+    if (m_thread.joinable()) {
+        sendWorkerThreadCommand(kCMD_QUIT);
+        m_thread.join();
+    }
+}
+
+bool GnssHwConn::ok() const {
+    return m_thread.joinable();
+}
+
+bool GnssHwConn::start() {
+    return ok() && sendWorkerThreadCommand(kCMD_START);
+}
+
+bool GnssHwConn::stop() {
+    return ok() && sendWorkerThreadCommand(kCMD_STOP);
+}
+
+void GnssHwConn::workerThread(int devFd, int threadsFd, const DataSink* sink) {
+    const unique_fd epollFd(epoll_create1(0));
+    if (!epollFd.ok()) {
+        ALOGE("%s:%d: epoll_create1 failed", __PRETTY_FUNCTION__, __LINE__);
+        ::abort();
+    }
+
+    epollCtlAdd(epollFd.get(), devFd);
+    epollCtlAdd(epollFd.get(), threadsFd);
+
+    GnssHwListener listener(sink);
+    bool running = false;
+
+    while (true) {
+        struct epoll_event events[2];
+        const int kTimeoutMs = 60000;
+        const int n = TEMP_FAILURE_RETRY(epoll_wait(epollFd.get(),
+                                                    events, 2,
+                                                    kTimeoutMs));
+        if (n < 0) {
+            ALOGE("%s:%d: epoll_wait failed with '%s'",
+                  __PRETTY_FUNCTION__, __LINE__, strerror(errno));
+            continue;
+        }
+
+        for (int i = 0; i < n; ++i) {
+            const struct epoll_event* ev = &events[i];
+            const int fd = ev->data.fd;
+            const int ev_events = ev->events;
+
+            if (fd == devFd) {
+                if (ev_events & (EPOLLERR | EPOLLHUP)) {
+                    ALOGE("%s:%d: epoll_wait: devFd has an error, ev_events=%x",
+                          __PRETTY_FUNCTION__, __LINE__, ev_events);
+                    ::abort();
+                } else if (ev_events & EPOLLIN) {
+                    char buf[64];
+                    while (true) {
+                        int n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
+                        if (n > 0) {
+                            if (running) {
+                                for (int i = 0; i < n; ++i) {
+                                    listener.consume(buf[i]);
+                                }
+                            }
+                        } else {
+                            break;
+                        }
+                    }
+                }
+            } else if (fd == threadsFd) {
+                if (ev_events & (EPOLLERR | EPOLLHUP)) {
+                    ALOGE("%s:%d: epoll_wait: threadsFd has an error, ev_events=%x",
+                          __PRETTY_FUNCTION__, __LINE__, ev_events);
+                    ::abort();
+                } else if (ev_events & EPOLLIN) {
+                    const int cmd = workerThreadRcvCommand(fd);
+                    switch (cmd) {
+                        case kCMD_QUIT:
+                            return;
+
+                        case kCMD_START:
+                            if (!running) {
+                                listener.reset();
+                                sink->gnssStatus(ahg10::IGnssCallback::GnssStatusValue::SESSION_BEGIN);
+                                running = true;
+                            }
+                            break;
+
+                        case kCMD_STOP:
+                            if (running) {
+                                running = false;
+                                sink->gnssStatus(ahg10::IGnssCallback::GnssStatusValue::SESSION_END);
+                            }
+                            break;
+
+                        default:
+                            ALOGE("%s:%d: workerThreadRcvCommand returned unexpected command, cmd=%d",
+                                  __PRETTY_FUNCTION__, __LINE__, cmd);
+                            ::abort();
+                            break;
+                    }
+                }
+            } else {
+                ALOGE("%s:%d: epoll_wait() returned unexpected fd",
+                      __PRETTY_FUNCTION__, __LINE__);
+            }
+        }
+    }
+}
+
+int GnssHwConn::workerThreadRcvCommand(const int fd) {
+    char buf;
+    if (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) {
+        return buf;
+    } else {
+        return -1;
+    }
+}
+
+bool GnssHwConn::sendWorkerThreadCommand(char cmd) const {
+    return TEMP_FAILURE_RETRY(write(m_callersFd.get(), &cmd, 1)) == 1;
+}
+
+}  // namespace goldfish
diff --git a/gnss/gnss_hw_conn.h b/gnss/gnss_hw_conn.h
new file mode 100644
index 0000000..06bc530
--- /dev/null
+++ b/gnss/gnss_hw_conn.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <android-base/unique_fd.h>
+#include <mutex>
+#include <thread>
+#include "data_sink.h"
+
+namespace goldfish {
+using ::android::base::unique_fd;
+
+class GnssHwConn {
+public:
+    explicit GnssHwConn(const DataSink* sink);
+    ~GnssHwConn();
+
+    bool ok() const;
+    bool start();
+    bool stop();
+
+private:
+    static void workerThread(int devFd, int threadsFd, const DataSink* sink);
+    static int workerThreadRcvCommand(int fd);
+    bool sendWorkerThreadCommand(char cmd) const;
+
+    unique_fd m_devFd;      // Goldfish GPS QEMU device
+    // a pair of connected sockets to talk to the worker thread
+    unique_fd m_callersFd;  // a caller writes here
+    unique_fd m_threadsFd;  // the worker thread listens from here
+    std::thread m_thread;
+};
+
+}  // namespace goldfish
diff --git a/gnss/gnss_hw_listener.cpp b/gnss/gnss_hw_listener.cpp
new file mode 100644
index 0000000..cde8c57
--- /dev/null
+++ b/gnss/gnss_hw_listener.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <chrono>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include "gnss_hw_listener.h"
+#include "util.h"
+
+namespace goldfish {
+namespace {
+const char* testNmeaField(const char* i, const char* end,
+                          const char* v,
+                          const char sep) {
+    while (i < end) {
+        if (*v == 0) {
+            return (*i == sep) ? (i + 1) : nullptr;
+        } else if (*v == *i) {
+            ++v;
+            ++i;
+        } else {
+            return nullptr;
+        }
+    }
+
+    return nullptr;
+}
+
+const char* skipAfter(const char* i, const char* end, const char c) {
+    for (; i < end; ++i) {
+        if (*i == c) {
+            return i + 1;
+        }
+    }
+    return nullptr;
+}
+
+double convertDMMF(const int dmm, const int f, int p10) {
+    const int d = dmm / 100;
+    const int m = dmm % 100;
+    int base10 = 1;
+    for (; p10 > 0; --p10) { base10 *= 10; }
+
+    return double(d) + (m + (f / double(base10))) / 60.0;
+}
+
+double sign(char m, char positive) { return (m == positive) ? 1.0 : -1; }
+
+}  // namespace
+
+GnssHwListener::GnssHwListener(const DataSink* sink): m_sink(sink) {
+    m_buffer.reserve(256);
+}
+
+void GnssHwListener::reset() {
+    m_buffer.clear();
+}
+
+void GnssHwListener::consume(char c) {
+    if (c == '$' || !m_buffer.empty()) {
+        m_buffer.push_back(c);
+    }
+    if (c == '\n') {
+        const ahg20::ElapsedRealtime ts = util::makeElapsedRealtime(util::nowNanos());
+
+        if (parse(m_buffer.data() + 1, m_buffer.data() + m_buffer.size() - 2, ts)) {
+            m_sink->gnssNmea(ts.timestampNs / 1000000,
+                             hidl_string(m_buffer.data(), m_buffer.size()));
+        } else {
+            m_buffer.back() = 0;
+            ALOGW("%s:%d: failed to parse an NMEA message, '%s'",
+                  __PRETTY_FUNCTION__, __LINE__, m_buffer.data());
+        }
+        m_buffer.clear();
+    } else if (m_buffer.size() >= 1024) {
+        ALOGW("%s:%d buffer was too long, dropped", __PRETTY_FUNCTION__, __LINE__);
+        m_buffer.clear();
+    }
+}
+
+bool GnssHwListener::parse(const char* begin, const char* end,
+                           const ahg20::ElapsedRealtime& ts) {
+    if (const char* fields = testNmeaField(begin, end, "GPRMC", ',')) {
+        return parseGPRMC(fields, end, ts);
+    } else if (const char* fields = testNmeaField(begin, end, "GPGGA", ',')) {
+        return parseGPGGA(fields, end, ts);
+    } else {
+        return false;
+    }
+}
+
+//        begin                                                          end
+// $GPRMC,195206,A,1000.0000,N,10000.0000,E,173.8,231.8,010420,004.2,W*47
+//          1    2    3      4    5       6     7     8      9    10 11 12
+//      1  195206     Time Stamp
+//      2  A          validity - A-ok, V-invalid
+//      3  1000.0000  current Latitude
+//      4  N          North/South
+//      5  10000.0000 current Longitude
+//      6  E          East/West
+//      7  173.8      Speed in knots
+//      8  231.8      True course
+//      9  010420     Date Stamp (13 June 1994)
+//     10  004.2      Variation
+//     11  W          East/West
+//     12  *70        checksum
+bool GnssHwListener::parseGPRMC(const char* begin, const char*,
+                                const ahg20::ElapsedRealtime& ts) {
+    double speedKnots = 0;
+    double course = 0;
+    double variation = 0;
+    int latdmm = 0;
+    int londmm = 0;
+    int latf = 0;
+    int lonf = 0;
+    int latdmmConsumed = 0;
+    int latfConsumed = 0;
+    int londmmConsumed = 0;
+    int lonfConsumed = 0;
+    int hhmmss = -1;
+    int ddmoyy = 0;
+    char validity = 0;
+    char ns = 0;    // north/south
+    char ew = 0;    // east/west
+    char var_ew = 0;
+
+    if (sscanf(begin, "%06d,%c,%d.%n%d%n,%c,%d.%n%d%n,%c,%lf,%lf,%d,%lf,%c*",
+               &hhmmss, &validity,
+               &latdmm, &latdmmConsumed, &latf, &latfConsumed, &ns,
+               &londmm, &londmmConsumed, &lonf, &lonfConsumed, &ew,
+               &speedKnots, &course,
+               &ddmoyy,
+               &variation, &var_ew) != 13) {
+        return false;
+    }
+    if (validity != 'A') {
+        return false;
+    }
+
+    const double lat = convertDMMF(latdmm, latf, latfConsumed - latdmmConsumed) * sign(ns, 'N');
+    const double lon = convertDMMF(londmm, lonf, lonfConsumed - londmmConsumed) * sign(ew, 'E');
+    const double speed = speedKnots * 0.514444;
+
+    ahg20::GnssLocation loc20;
+    loc20.elapsedRealtime = ts;
+
+    auto& loc10 = loc20.v1_0;
+
+    loc10.latitudeDegrees = lat;
+    loc10.longitudeDegrees = lon;
+    loc10.speedMetersPerSec = speed;
+    loc10.bearingDegrees = course;
+    loc10.horizontalAccuracyMeters = 5;
+    loc10.speedAccuracyMetersPerSecond = .5;
+    loc10.bearingAccuracyDegrees = 30;
+    loc10.timestamp = ts.timestampNs / 1000000;
+
+    using ahg10::GnssLocationFlags;
+    loc10.gnssLocationFlags =
+        GnssLocationFlags::HAS_LAT_LONG |
+        GnssLocationFlags::HAS_SPEED |
+        GnssLocationFlags::HAS_BEARING |
+        GnssLocationFlags::HAS_HORIZONTAL_ACCURACY |
+        GnssLocationFlags::HAS_SPEED_ACCURACY |
+        GnssLocationFlags::HAS_BEARING_ACCURACY;
+
+    if (m_flags & GnssLocationFlags::HAS_ALTITUDE) {
+        loc10.altitudeMeters = m_altitude;
+        loc10.verticalAccuracyMeters = .5;
+        loc10.gnssLocationFlags |= GnssLocationFlags::HAS_ALTITUDE |
+                                   GnssLocationFlags::HAS_VERTICAL_ACCURACY;
+
+    }
+
+    m_sink->gnssLocation(loc20);
+    return true;
+}
+
+// $GPGGA,123519,4807.0382,N,12204.9799,W,1,6,,4.2,M,0.,M,,,*47
+//    time of fix      123519     12:35:19 UTC
+//    latitude         4807.0382  48 degrees, 07.0382 minutes
+//    north/south      N or S
+//    longitude        12204.9799 122 degrees, 04.9799 minutes
+//    east/west        E or W
+//    fix quality      1          standard GPS fix
+//    satellites       1 to 12    number of satellites being tracked
+//    HDOP             <dontcare> horizontal dilution
+//    altitude         4.2        altitude above sea-level
+//    altitude units   M          to indicate meters
+//    diff             <dontcare> height of sea-level above ellipsoid
+//    diff units       M          to indicate meters (should be <dontcare>)
+//    dgps age         <dontcare> time in seconds since last DGPS fix
+//    dgps sid         <dontcare> DGPS station id
+bool GnssHwListener::parseGPGGA(const char* begin, const char* end,
+                                const ahg20::ElapsedRealtime&) {
+    double altitude = 0;
+    int latdmm = 0;
+    int londmm = 0;
+    int latf = 0;
+    int lonf = 0;
+    int latdmmConsumed = 0;
+    int latfConsumed = 0;
+    int londmmConsumed = 0;
+    int lonfConsumed = 0;
+    int hhmmss = 0;
+    int fixQuality = 0;
+    int nSatellites = 0;
+    int consumed = 0;
+    char ns = 0;
+    char ew = 0;
+    char altitudeUnit = 0;
+
+    if (sscanf(begin, "%06d,%d.%n%d%n,%c,%d.%n%d%n,%c,%d,%d,%n",
+               &hhmmss,
+               &latdmm, &latdmmConsumed, &latf, &latfConsumed, &ns,
+               &londmm, &londmmConsumed, &lonf, &lonfConsumed, &ew,
+               &fixQuality,
+               &nSatellites,
+               &consumed) != 9) {
+        return false;
+    }
+
+    begin = skipAfter(begin + consumed, end, ',');  // skip HDOP
+    if (!begin) {
+        return false;
+    }
+    if (sscanf(begin, "%lf,%c,", &altitude, &altitudeUnit) != 2) {
+        return false;
+    }
+    if (altitudeUnit != 'M') {
+        return false;
+    }
+
+    m_altitude = altitude;
+    m_flags |= ahg10::GnssLocationFlags::HAS_ALTITUDE;
+
+    hidl_vec<ahg20::IGnssCallback::GnssSvInfo> svInfo(nSatellites);
+    for (int i = 0; i < nSatellites; ++i) {
+        auto* info20 = &svInfo[i];
+        auto* info10 = &info20->v1_0;
+
+        info20->constellation = ahg20::GnssConstellationType::GPS;
+        info10->svid = i + 3;
+        info10->constellation = ahg10::GnssConstellationType::GPS;
+        info10->cN0Dbhz = 30;
+        info10->elevationDegrees = 0;
+        info10->azimuthDegrees = 0;
+        info10->carrierFrequencyHz = 1.59975e+09;
+        info10->svFlag = ahg10::IGnssCallback::GnssSvFlags::HAS_CARRIER_FREQUENCY | 0;
+    }
+
+    m_sink->gnssSvStatus(svInfo);
+
+    return true;
+}
+
+}  // namespace goldfish
diff --git a/gnss/gnss_hw_listener.h b/gnss/gnss_hw_listener.h
new file mode 100644
index 0000000..234908b
--- /dev/null
+++ b/gnss/gnss_hw_listener.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <vector>
+#include "data_sink.h"
+
+namespace goldfish {
+using ::android::hardware::hidl_bitfield;
+
+class GnssHwListener {
+public:
+    explicit GnssHwListener(const DataSink* sink);
+    void reset();
+    void consume(char);
+
+private:
+    bool parse(const char* begin, const char* end, const ahg20::ElapsedRealtime&);
+    bool parseGPRMC(const char* begin, const char* end, const ahg20::ElapsedRealtime&);
+    bool parseGPGGA(const char* begin, const char* end, const ahg20::ElapsedRealtime&);
+
+    const DataSink*     m_sink;
+    std::vector<char>   m_buffer;
+
+    double              m_altitude = 0;
+
+    hidl_bitfield<ahg10::GnssLocationFlags> m_flags;
+};
+
+}  // namespace goldfish
diff --git a/gnss/gnss_measurement.cpp b/gnss/gnss_measurement.cpp
new file mode 100644
index 0000000..746167d
--- /dev/null
+++ b/gnss/gnss_measurement.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <chrono>
+#include "gnss_measurement.h"
+#include "util.h"
+
+namespace goldfish {
+using ::android::hardware::hidl_vec;
+
+GnssMeasurement20::~GnssMeasurement20() {
+    if (m_isRunning) {
+        stopLocked();
+    }
+}
+
+Return<GnssMeasurementStatus10>
+GnssMeasurement20::setCallback_2_0(const sp<ahg20::IGnssMeasurementCallback>& callback,
+                                   bool enableFullTracking) {
+    (void)enableFullTracking;
+    if (callback == nullptr) {
+        return GnssMeasurementStatus10::ERROR_GENERIC;
+    }
+
+    std::unique_lock<std::mutex> lock(m_mtx);
+    if (m_isRunning) {
+        stopLocked();
+    }
+
+    m_callback = callback;
+    startLocked();
+
+    return GnssMeasurementStatus10::SUCCESS;
+}
+
+Return<void> GnssMeasurement20::close() {
+    std::unique_lock<std::mutex> lock(m_mtx);
+    if (m_isRunning) {
+        stopLocked();
+    }
+
+    m_callback = nullptr;
+    return {};
+}
+
+void GnssMeasurement20::startLocked() {
+    m_thread = std::thread([this](){
+        while (m_isRunning) {
+            update();
+            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+        }
+    });
+    m_isRunning = true;
+}
+
+void GnssMeasurement20::stopLocked() {
+    m_isRunning = false;
+    m_thread.join();
+}
+
+void GnssMeasurement20::update() {
+    using GnssMeasurement20 = ahg20::IGnssMeasurementCallback::GnssMeasurement;
+    using GnssAccumulatedDeltaRangeState10 = ahg10::IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState;
+    using GnssMeasurementFlags10 = ahg10::IGnssMeasurementCallback::GnssMeasurementFlags;
+    using GnssMultipathIndicator10 = ahg10::IGnssMeasurementCallback::GnssMultipathIndicator;
+    using GnssMeasurementState20 = ahg20::IGnssMeasurementCallback::GnssMeasurementState;
+    using GnssData = ahg20::IGnssMeasurementCallback::GnssData;
+
+    ahg10::IGnssMeasurementCallback::GnssMeasurement measurement10 = {
+        .flags = GnssMeasurementFlags10::HAS_CARRIER_FREQUENCY | 0,
+        .svid = 6,
+        .constellation = ahg10::GnssConstellationType::GPS,
+        .timeOffsetNs = 0.0,
+        .receivedSvTimeInNs = 8195997131077,
+        .receivedSvTimeUncertaintyInNs = 15,
+        .cN0DbHz = 30.0,
+        .pseudorangeRateMps = -484.13739013671875,
+        .pseudorangeRateUncertaintyMps = 1.0379999876022339,
+        .accumulatedDeltaRangeState = GnssAccumulatedDeltaRangeState10::ADR_STATE_UNKNOWN | 0,
+        .accumulatedDeltaRangeM = 0.0,
+        .accumulatedDeltaRangeUncertaintyM = 0.0,
+        .carrierFrequencyHz = 1.59975e+09,
+        .multipathIndicator = GnssMultipathIndicator10::INDICATOR_UNKNOWN
+    };
+    ahg11::IGnssMeasurementCallback::GnssMeasurement measurement11 = {
+        .v1_0 = measurement10,
+        .accumulatedDeltaRangeState = 0
+    };
+
+    ahg20::IGnssMeasurementCallback::GnssMeasurement measurement20 = {
+        .v1_1 = measurement11,
+        .codeType = "C",
+        .state = GnssMeasurementState20::STATE_CODE_LOCK |
+                 GnssMeasurementState20::STATE_BIT_SYNC |
+                 GnssMeasurementState20::STATE_SUBFRAME_SYNC |
+                 GnssMeasurementState20::STATE_TOW_DECODED |
+                 GnssMeasurementState20::STATE_GLO_STRING_SYNC |
+                 GnssMeasurementState20::STATE_GLO_TOD_DECODED,
+        .constellation = ahg20::GnssConstellationType::GPS,
+    };
+
+    hidl_vec<GnssMeasurement20> measurements(1);
+    measurements[0] = measurement20;
+
+    const int64_t nowNs = util::nowNanos();
+    const int64_t fullBiasNs = (nowNs % 15331) * ((nowNs & 1) ? -1 : 1);
+    const int64_t hwTimeNs = nowNs + fullBiasNs; // local hardware clock
+
+    ahg10::IGnssMeasurementCallback::GnssClock clock10 = {
+        .gnssClockFlags = 0,
+        .leapSecond = 0,
+        .timeNs = hwTimeNs,
+        .timeUncertaintyNs = 4.5,
+        .fullBiasNs = fullBiasNs,
+        .biasNs = 1.5,
+        .biasUncertaintyNs = .7,
+        .driftNsps = -51.757811607455452,
+        .driftUncertaintyNsps = 310.64968328491528,
+        .hwClockDiscontinuityCount = 1
+    };
+
+    GnssData gnssData = {
+        .measurements = measurements,
+        .clock = clock10,
+        .elapsedRealtime = util::makeElapsedRealtime(util::nowNanos())
+    };
+
+    std::unique_lock<std::mutex> lock(m_mtx);
+    m_callback->gnssMeasurementCb_2_0(gnssData);
+}
+
+/// old and deprecated /////////////////////////////////////////////////////////
+Return<GnssMeasurementStatus10> GnssMeasurement20::setCallback_1_1(const sp<ahg11::IGnssMeasurementCallback>&, bool) {
+    return GnssMeasurementStatus10::ERROR_GENERIC;
+}
+
+Return<GnssMeasurementStatus10> GnssMeasurement20::setCallback(const sp<ahg10::IGnssMeasurementCallback>&) {
+    return GnssMeasurementStatus10::ERROR_GENERIC;
+}
+
+
+}  // namespace goldfish
diff --git a/gnss/gnss_measurement.h b/gnss/gnss_measurement.h
new file mode 100644
index 0000000..8da3b4c
--- /dev/null
+++ b/gnss/gnss_measurement.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+#include <atomic>
+#include <mutex>
+#include <thread>
+#include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+
+namespace goldfish {
+namespace ahg = ::android::hardware::gnss;
+namespace ahg20 = ahg::V2_0;
+namespace ahg11 = ahg::V1_1;
+namespace ahg10 = ahg::V1_0;
+using GnssMeasurementStatus10 = ahg10::IGnssMeasurement::GnssMeasurementStatus;
+
+using ::android::sp;
+using ::android::hardware::Return;
+
+struct GnssMeasurement20 : public ahg20::IGnssMeasurement {
+    ~GnssMeasurement20();
+
+    // Methods from V2_0::IGnssMeasurement follow.
+    Return<GnssMeasurementStatus10> setCallback_2_0(const sp<ahg20::IGnssMeasurementCallback>& callback, bool enableFullTracking) override;
+
+    // Methods from V1_1::IGnssMeasurement follow.
+    Return<GnssMeasurementStatus10> setCallback_1_1(const sp<ahg11::IGnssMeasurementCallback>& callback, bool enableFullTracking) override;
+
+    // Methods from V1_0::IGnssMeasurement follow.
+    Return<GnssMeasurementStatus10> setCallback(const sp<ahg10::IGnssMeasurementCallback>& callback) override;
+    Return<void> close() override;
+
+private:
+    void startLocked();
+    void stopLocked();
+    void update();
+
+    sp<ahg20::IGnssMeasurementCallback> m_callback;
+    std::thread                         m_thread;
+    std::atomic<bool>                   m_isRunning;
+    mutable std::mutex                  m_mtx;
+};
+
+}  // namespace goldfish
diff --git a/gnss/main.cpp b/gnss/main.cpp
new file mode 100644
index 0000000..ede611b
--- /dev/null
+++ b/gnss/main.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <android-base/logging.h>
+#include <hidl/HidlLazyUtils.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "gnss.h"
+
+int main(int /* argc */, char* /* argv */ []) {
+    ::android::sp<goldfish::Gnss20> gnss(new goldfish::Gnss20);
+
+    ::android::hardware::configureRpcThreadpool(1, true);
+
+    auto serviceRegistrar = ::android::hardware::LazyServiceRegistrar::getInstance();
+    CHECK_EQ(serviceRegistrar.registerService(gnss), ::android::OK)
+        << "Failed to register Gnss HAL";
+
+    ::android::hardware::joinRpcThreadpool();
+}
diff --git a/gnss/util.cpp b/gnss/util.cpp
new file mode 100644
index 0000000..d0be41b
--- /dev/null
+++ b/gnss/util.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <chrono>
+#include "util.h"
+
+namespace goldfish {
+namespace util {
+
+int64_t nowNanos() {
+    using namespace std::chrono;
+    return time_point_cast<nanoseconds>(system_clock::now()).time_since_epoch().count();
+}
+
+ahg20::ElapsedRealtime makeElapsedRealtime(long long timestampNs) {
+    ahg20::ElapsedRealtime ts = {
+        .flags = ahg20::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
+                 ahg20::ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS,
+        .timestampNs = static_cast<uint64_t>(timestampNs),
+        .timeUncertaintyNs = 1000000
+    };
+
+    return ts;
+}
+
+}  // namespace util
+}  // namespace goldfish
diff --git a/gnss/util.h b/gnss/util.h
new file mode 100644
index 0000000..ba1b5a4
--- /dev/null
+++ b/gnss/util.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <android/hardware/gnss/2.0/types.h>
+
+namespace goldfish {
+namespace ahg20 = ::android::hardware::gnss::V2_0;
+
+namespace util {
+
+int64_t nowNanos();
+
+ahg20::ElapsedRealtime makeElapsedRealtime(long long timestampNs);
+
+}  // namespace util
+}  // namespace goldfish
diff --git a/gps/Android.mk b/gps/Android.mk
deleted file mode 100644
index 7196f5f..0000000
--- a/gps/Android.mk
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright (C) 2010 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.
-
-
-# We're moving the emulator-specific platform libs to
-# development.git/tools/emulator/. The following test is to ensure
-# smooth builds even if the tree contains both versions.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# HAL module implemenation stored in
-# hw/<GPS_HARDWARE_MODULE_ID>.<ro.hardware>.so
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_CFLAGS += -DQEMU_HARDWARE
-LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
-LOCAL_C_INCLUDES += \
-	$(LOCAL_PATH)/../include \
-	$(LOCAL_PATH)/../../goldfish-opengl/shared/OpenglCodecCommon
-LOCAL_SRC_FILES := gps_qemu.c
-ifeq ($(TARGET_PRODUCT),vbox_x86)
-LOCAL_MODULE := gps.vbox_x86
-else
-LOCAL_MODULE := gps.goldfish
-endif
-include $(BUILD_SHARED_LIBRARY)
-
-
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_CFLAGS += -DQEMU_HARDWARE
-LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
-LOCAL_C_INCLUDES += \
-	$(LOCAL_PATH)/../include \
-	$(LOCAL_PATH)/../../goldfish-opengl/shared/OpenglCodecCommon
-LOCAL_SRC_FILES := gps_qemu.c
-LOCAL_MODULE := gps.ranchu
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/gps/gps_qemu.c b/gps/gps_qemu.c
deleted file mode 100644
index cfb8e36..0000000
--- a/gps/gps_qemu.c
+++ /dev/null
@@ -1,1150 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-/* this implements a GPS hardware library for the Android emulator.
- * the following code should be built as a shared library that will be
- * placed into /system/lib/hw/gps.goldfish.so
- *
- * it will be loaded by the code in hardware/libhardware/hardware.c
- * which is itself called from android_location_GpsLocationProvider.cpp
- */
-
-
-#include <errno.h>
-#include <pthread.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sys/epoll.h>
-#include <math.h>
-#include <time.h>
-
-#define  LOG_TAG  "gps_qemu"
-#include <log/log.h>
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
-#include <hardware/gps.h>
-#include "qemud.h"
-
-/* the name of the qemu-controlled pipe */
-#define  QEMUD_CHANNEL_NAME  "gps"
-
-#define  GPS_DEBUG  0
-
-#undef D
-#if GPS_DEBUG
-#  define  D(...)   ALOGD(__VA_ARGS__)
-#else
-#  define  D(...)   ((void)0)
-#endif
-
-/*****************************************************************/
-/*****************************************************************/
-/*****                                                       *****/
-/*****       N M E A   T O K E N I Z E R                     *****/
-/*****                                                       *****/
-/*****************************************************************/
-/*****************************************************************/
-
-typedef struct {
-    const char*  p;
-    const char*  end;
-} Token;
-
-#define  MAX_NMEA_TOKENS  64
-
-typedef struct {
-    int     count;
-    Token   tokens[ MAX_NMEA_TOKENS ];
-} NmeaTokenizer;
-
-/* this is the state of our connection to the qemu_gpsd daemon */
-typedef struct {
-    int                     init;
-    int                     fd;
-    GpsCallbacks            callbacks;
-    pthread_t               thread;
-    int                     control[2];
-    pthread_mutex_t         lock;
-    GpsMeasurementCallbacks*   measurement_callbacks; /* protected by lock:
-                                                         accessed by main and child threads */
-    bool                    gnss_enabled; /* set by ro.kernel.qemu.gps.gnss_enabled=1 */
-    bool                    fix_provided_by_gnss; /* set by ro.kernel.qemu.gps.fix_by_gnss=1 */
-} GpsState;
-
-static GpsState  _gps_state[1];
-
-static int
-nmea_tokenizer_init( NmeaTokenizer*  t, const char*  p, const char*  end )
-{
-    int    count = 0;
-
-    // the initial '$' is optional
-    if (p < end && p[0] == '$')
-        p += 1;
-
-    // remove trailing newline
-    if (end > p && end[-1] == '\n') {
-        end -= 1;
-        if (end > p && end[-1] == '\r')
-            end -= 1;
-    }
-
-    // get rid of checksum at the end of the sentecne
-    if (end >= p+3 && end[-3] == '*') {
-        end -= 3;
-    }
-
-    while (p < end) {
-        const char*  q = p;
-
-        q = memchr(p, ',', end-p);
-        if (q == NULL)
-            q = end;
-
-        if (count < MAX_NMEA_TOKENS) {
-            t->tokens[count].p   = p;
-            t->tokens[count].end = q;
-            count += 1;
-        }
-        if (q < end)
-            q += 1;
-
-        p = q;
-    }
-
-    t->count = count;
-    return count;
-}
-
-static Token
-nmea_tokenizer_get( NmeaTokenizer*  t, int  index )
-{
-    Token  tok;
-    static const char*  dummy = "";
-
-    if (index < 0 || index >= t->count) {
-        tok.p = tok.end = dummy;
-    } else
-        tok = t->tokens[index];
-
-    return tok;
-}
-
-
-static int64_t
-str2int64( const char*  p, const char*  end )
-{
-    int64_t   result = 0;
-
-#if GPS_DEBUG
-    char temp[1024];
-    snprintf(temp, sizeof(temp), "'%.*s'", end-p, p);
-#endif
-
-    bool is_negative = false;
-    if (end > p && *p == '-') {
-        is_negative = true;
-        ++p;
-    }
-
-    int   len    = end - p;
-
-    for ( ; len > 0; len--, p++ )
-    {
-        int  c;
-
-        if (p >= end) {
-            ALOGE("parse error at func %s line %d", __func__, __LINE__);
-            goto Fail;
-        }
-
-        c = *p - '0';
-        if ((unsigned)c >= 10) {
-            ALOGE("parse error at func %s line %d on %c", __func__, __LINE__, c);
-            goto Fail;
-        }
-
-        result = result*10 + c;
-    }
-    if (is_negative) {
-        result = - result;
-    }
-#if GPS_DEBUG
-    ALOGD("%s ==> %" PRId64, temp, result);
-#endif
-    return  result;
-
-Fail:
-    return -1;
-}
-
-static int
-str2int( const char*  p, const char*  end )
-{
-    /* danger: downward convert to 32bit */
-    return str2int64(p, end);
-}
-
-static double
-str2float( const char*  p, const char*  end )
-{
-    int   len    = end - p;
-    char  temp[64];
-
-    if (len >= (int)sizeof(temp)) {
-        ALOGE("%s %d input is too long: '%.*s'", __func__, __LINE__, end-p, p);
-        return 0.;
-    }
-
-    memcpy( temp, p, len );
-    temp[len] = 0;
-    return strtod( temp, NULL );
-}
-
-/*****************************************************************/
-/*****************************************************************/
-/*****                                                       *****/
-/*****       N M E A   P A R S E R                           *****/
-/*****                                                       *****/
-/*****************************************************************/
-/*****************************************************************/
-
-#define  NMEA_MAX_SIZE  1024
-
-typedef struct {
-    int     pos;
-    int     overflow;
-    int     utc_year;
-    int     utc_mon;
-    int     utc_day;
-    GpsLocation  fix;
-    gps_location_callback  callback;
-    GnssData    gnss_data;
-    int         gnss_count;
-
-    char    in[ NMEA_MAX_SIZE+1 ];
-    bool    gnss_enabled; /* passed in from _gps_state */
-    bool    fix_provided_by_gnss; /* passed in from _gps_state */
-} NmeaReader;
-
-static void
-nmea_reader_init( NmeaReader*  r )
-{
-    memset( r, 0, sizeof(*r) );
-
-    r->pos      = 0;
-    r->overflow = 0;
-    r->utc_year = -1;
-    r->utc_mon  = -1;
-    r->utc_day  = -1;
-    r->callback = NULL;
-    r->fix.size = sizeof(r->fix);
-
-    GpsState*  s = _gps_state;
-    r->gnss_enabled = s->gnss_enabled;
-    r->fix_provided_by_gnss = s->fix_provided_by_gnss;
-
-}
-
-
-static int
-nmea_reader_update_time( NmeaReader*  r, Token  tok )
-{
-    int        hour, minute;
-    double     seconds;
-    struct tm  tm;
-    time_t     fix_time;
-
-    if (tok.p + 6 > tok.end)
-        return -1;
-
-    if (r->utc_year < 0) {
-        // no date yet, get current one
-        time_t  now = time(NULL);
-        gmtime_r( &now, &tm );
-        r->utc_year = tm.tm_year + 1900;
-        r->utc_mon  = tm.tm_mon + 1;
-        r->utc_day  = tm.tm_mday;
-    }
-
-    hour    = str2int(tok.p,   tok.p+2);
-    minute  = str2int(tok.p+2, tok.p+4);
-    seconds = str2float(tok.p+4, tok.end);
-
-    tm.tm_hour  = hour;
-    tm.tm_min   = minute;
-    tm.tm_sec   = (int) seconds;
-    tm.tm_year  = r->utc_year - 1900;
-    tm.tm_mon   = r->utc_mon - 1;
-    tm.tm_mday  = r->utc_day;
-    tm.tm_isdst = -1;
-
-    fix_time = timegm( &tm );
-    r->fix.timestamp = (long long)fix_time * 1000;
-    return 0;
-}
-
-static int
-nmea_reader_update_date( NmeaReader*  r, Token  date, Token  time )
-{
-    Token  tok = date;
-    int    day, mon, year;
-
-    if (tok.p + 6 != tok.end) {
-        D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
-        return -1;
-    }
-    day  = str2int(tok.p, tok.p+2);
-    mon  = str2int(tok.p+2, tok.p+4);
-    year = str2int(tok.p+4, tok.p+6) + 2000;
-
-    if ((day|mon|year) < 0) {
-        D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
-        return -1;
-    }
-
-    r->utc_year  = year;
-    r->utc_mon   = mon;
-    r->utc_day   = day;
-
-    return nmea_reader_update_time( r, time );
-}
-
-
-static double
-convert_from_hhmm( Token  tok )
-{
-    double  val     = str2float(tok.p, tok.end);
-    int     degrees = (int)(floor(val) / 100);
-    double  minutes = val - degrees*100.;
-    double  dcoord  = degrees + minutes / 60.0;
-    return dcoord;
-}
-
-
-static int
-nmea_reader_update_latlong( NmeaReader*  r,
-                            Token        latitude,
-                            char         latitudeHemi,
-                            Token        longitude,
-                            char         longitudeHemi )
-{
-    double   lat, lon;
-    Token    tok;
-
-    r->fix.flags &= ~GPS_LOCATION_HAS_LAT_LONG;
-
-    tok = latitude;
-    if (tok.p + 6 > tok.end) {
-        D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
-        return -1;
-    }
-    lat = convert_from_hhmm(tok);
-    if (latitudeHemi == 'S')
-        lat = -lat;
-
-    tok = longitude;
-    if (tok.p + 6 > tok.end) {
-        D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
-        return -1;
-    }
-    lon = convert_from_hhmm(tok);
-    if (longitudeHemi == 'W')
-        lon = -lon;
-
-    r->fix.flags    |= GPS_LOCATION_HAS_LAT_LONG;
-    r->fix.latitude  = lat;
-    r->fix.longitude = lon;
-    return 0;
-}
-
-
-static int
-nmea_reader_update_altitude( NmeaReader* r,
-                             Token altitude,
-                             Token __unused units )
-{
-    Token   tok = altitude;
-
-    r->fix.flags &= ~GPS_LOCATION_HAS_ALTITUDE;
-
-    if (tok.p >= tok.end)
-        return -1;
-
-    r->fix.flags   |= GPS_LOCATION_HAS_ALTITUDE;
-    r->fix.altitude = str2float(tok.p, tok.end);
-    return 0;
-}
-
-
-static int
-nmea_reader_update_bearing( NmeaReader*  r,
-                            Token        bearing )
-{
-    Token   tok = bearing;
-
-    r->fix.flags &= ~GPS_LOCATION_HAS_BEARING;
-
-    if (tok.p >= tok.end)
-        return -1;
-
-    r->fix.flags   |= GPS_LOCATION_HAS_BEARING;
-    r->fix.bearing  = str2float(tok.p, tok.end);
-    return 0;
-}
-
-
-static int
-nmea_reader_update_speed( NmeaReader*  r,
-                          Token        speed )
-{
-    Token   tok = speed;
-
-    r->fix.flags &= ~GPS_LOCATION_HAS_SPEED;
-
-    if (tok.p >= tok.end)
-        return -1;
-
-    r->fix.flags   |= GPS_LOCATION_HAS_SPEED;
-    r->fix.speed    = str2float(tok.p, tok.end);
-    return 0;
-}
-
-static int
-nmea_reader_update_accuracy( NmeaReader*  r )
-{
-    // Always return 20m accuracy.
-    // Possibly parse it from the NMEA sentence in the future.
-    r->fix.flags    |= GPS_LOCATION_HAS_ACCURACY;
-    r->fix.accuracy = 20;
-    return 0;
-}
-
-static int64_t get_int64(Token tok) {
-    return str2int64(tok.p, tok.end);
-}
-
-static int get_int(Token tok) {
-    return str2int(tok.p, tok.end);
-}
-
-static double get_double(Token tok) {
-    return str2float(tok.p, tok.end);
-}
-
-static bool has_all_required_flags(GpsLocationFlags flags) {
-    return ( flags & GPS_LOCATION_HAS_LAT_LONG
-            && flags & GPS_LOCATION_HAS_ALTITUDE
-           );
-}
-
-static bool is_ready_to_send(NmeaReader* r) {
-    if (has_all_required_flags(r->fix.flags)) {
-        if (r->gnss_enabled && r->fix_provided_by_gnss) {
-            return (r->gnss_count > 2); /* required by CTS */
-        }
-        return true;
-    }
-    return false;
-}
-
-static void
-nmea_reader_set_callback( NmeaReader*  r, gps_location_callback  cb )
-{
-    r->callback = cb;
-    if (cb != NULL && is_ready_to_send(r)) {
-        D("%s: sending latest fix to new callback", __FUNCTION__);
-        r->callback( &r->fix );
-    }
-}
-
-static void
-nmea_reader_parse( NmeaReader*  r )
-{
-   /* we received a complete sentence, now parse it to generate
-    * a new GPS fix...
-    */
-    NmeaTokenizer  tzer[1];
-    Token          tok;
-
-    D("Received: '%.*s'", r->pos, r->in);
-    if (r->pos < 9) {
-        D("Too short. discarded.");
-        return;
-    }
-
-    nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
-#if GPS_DEBUG
-    {
-        int  n;
-        D("Found %d tokens", tzer->count);
-        for (n = 0; n < tzer->count; n++) {
-            Token  tok = nmea_tokenizer_get(tzer,n);
-            D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
-        }
-    }
-#endif
-
-    tok = nmea_tokenizer_get(tzer, 0);
-    if (tok.p + 5 > tok.end) {
-        D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
-        return;
-    }
-
-    // ignore first two characters.
-    tok.p += 2;
-    if ( !memcmp(tok.p, "GGA", 3) ) {
-        // GPS fix
-        Token  tok_time          = nmea_tokenizer_get(tzer,1);
-        Token  tok_latitude      = nmea_tokenizer_get(tzer,2);
-        Token  tok_latitudeHemi  = nmea_tokenizer_get(tzer,3);
-        Token  tok_longitude     = nmea_tokenizer_get(tzer,4);
-        Token  tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
-        Token  tok_altitude      = nmea_tokenizer_get(tzer,9);
-        Token  tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
-
-        nmea_reader_update_time(r, tok_time);
-        nmea_reader_update_latlong(r, tok_latitude,
-                                      tok_latitudeHemi.p[0],
-                                      tok_longitude,
-                                      tok_longitudeHemi.p[0]);
-        nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
-
-    } else if ( !memcmp(tok.p, "GNSSv1", 6) ) {
-        r->gnss_data.clock.time_ns                         = get_int64(nmea_tokenizer_get(tzer,1));
-        r->gnss_data.clock.full_bias_ns                    = get_int64(nmea_tokenizer_get(tzer,2));
-        r->gnss_data.clock.bias_ns                         = get_double(nmea_tokenizer_get(tzer,3));
-        r->gnss_data.clock.bias_uncertainty_ns             = get_double(nmea_tokenizer_get(tzer,4));
-        r->gnss_data.clock.drift_nsps                      = get_double(nmea_tokenizer_get(tzer,5));
-        r->gnss_data.clock.drift_uncertainty_nsps          = get_double(nmea_tokenizer_get(tzer,6));
-        r->gnss_data.clock.hw_clock_discontinuity_count    = get_int(nmea_tokenizer_get(tzer,7));
-        r->gnss_data.clock.flags                           = get_int(nmea_tokenizer_get(tzer,8));
-
-        r->gnss_data.measurement_count  = get_int(nmea_tokenizer_get(tzer,9));
-
-        for (int i = 0; i < r->gnss_data.measurement_count; ++i) {
-            r->gnss_data.measurements[i].svid                               = get_int(nmea_tokenizer_get(tzer,10 + i*9 + 0));
-            r->gnss_data.measurements[i].constellation                      = get_int(nmea_tokenizer_get(tzer,10 + i*9 + 1));
-            r->gnss_data.measurements[i].state                              = get_int(nmea_tokenizer_get(tzer,10 + i*9 + 2));
-            r->gnss_data.measurements[i].received_sv_time_in_ns             = get_int64(nmea_tokenizer_get(tzer,10 + i*9 + 3));
-            r->gnss_data.measurements[i].received_sv_time_uncertainty_in_ns = get_int64(nmea_tokenizer_get(tzer,10 + i*9 + 4));
-            r->gnss_data.measurements[i].c_n0_dbhz                          = get_double(nmea_tokenizer_get(tzer,10 + i*9 + 5));
-            r->gnss_data.measurements[i].pseudorange_rate_mps               = get_double(nmea_tokenizer_get(tzer,10 + i*9 + 6));
-            r->gnss_data.measurements[i].pseudorange_rate_uncertainty_mps   = get_double(nmea_tokenizer_get(tzer,10 + i*9 + 7));
-            r->gnss_data.measurements[i].carrier_frequency_hz               = get_double(nmea_tokenizer_get(tzer,10 + i*9 + 8));
-            r->gnss_data.measurements[i].flags                              = GNSS_MEASUREMENT_HAS_CARRIER_FREQUENCY;
-        }
-    } else if ( !memcmp(tok.p, "GSA", 3) ) {
-        // do something ?
-    } else if ( !memcmp(tok.p, "RMC", 3) ) {
-        Token  tok_time          = nmea_tokenizer_get(tzer,1);
-        Token  tok_fixStatus     = nmea_tokenizer_get(tzer,2);
-        Token  tok_latitude      = nmea_tokenizer_get(tzer,3);
-        Token  tok_latitudeHemi  = nmea_tokenizer_get(tzer,4);
-        Token  tok_longitude     = nmea_tokenizer_get(tzer,5);
-        Token  tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
-        Token  tok_speed         = nmea_tokenizer_get(tzer,7);
-        Token  tok_bearing       = nmea_tokenizer_get(tzer,8);
-        Token  tok_date          = nmea_tokenizer_get(tzer,9);
-
-        D("in RMC, fixStatus=%c", tok_fixStatus.p[0]);
-        if (tok_fixStatus.p[0] == 'A')
-        {
-            nmea_reader_update_date( r, tok_date, tok_time );
-
-            nmea_reader_update_latlong( r, tok_latitude,
-                                           tok_latitudeHemi.p[0],
-                                           tok_longitude,
-                                           tok_longitudeHemi.p[0] );
-
-            nmea_reader_update_bearing( r, tok_bearing );
-            nmea_reader_update_speed  ( r, tok_speed );
-        }
-    } else {
-        tok.p -= 2;
-        D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
-    }
-
-    // Always update accuracy
-    nmea_reader_update_accuracy( r );
-
-    if (is_ready_to_send(r)) {
-#if GPS_DEBUG
-        char   temp[256];
-        char*  p   = temp;
-        char*  end = p + sizeof(temp);
-        struct tm   utc;
-
-        p += snprintf( p, end-p, "sending fix" );
-        if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
-            p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
-        }
-        if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
-            p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
-        }
-        if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
-            p += snprintf(p, end-p, " speed=%g", r->fix.speed);
-        }
-        if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
-            p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
-        }
-        if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
-            p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
-        }
-        //The unit of r->fix.timestamp is millisecond.
-        time_t timestamp = r->fix.timestamp / 1000;
-        gmtime_r( (time_t*) &timestamp, &utc );
-        p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
-#endif
-        if (r->callback) {
-            D("%s", temp);
-            r->callback( &r->fix );
-            /* we have sent a complete fix, now prepare for next complete fix */
-            r->fix.flags = 0;
-        }
-        else {
-            D("no callback, keeping data until needed !");
-        }
-    }
-
-    if (r->gnss_data.measurement_count > 0) {
-        /* this runs in child thread */
-        GpsState*  s = _gps_state;
-        pthread_mutex_lock(&s->lock);
-        if (s->measurement_callbacks && s->measurement_callbacks->gnss_measurement_callback) {
-            D("sending gnss measurement data");
-            s->measurement_callbacks->gnss_measurement_callback(&r->gnss_data);
-            r->gnss_data.measurement_count = 0;
-            r->gnss_count ++;
-        } else {
-            D("no gnss measurement_callbacks, keeping data until needed !");
-        }
-        pthread_mutex_unlock(&s->lock);
-    }
-}
-
-
-static void
-nmea_reader_addc( NmeaReader*  r, int  c )
-{
-    if (r->overflow) {
-        r->overflow = (c != '\n');
-        return;
-    }
-
-    if (r->pos >= (int) sizeof(r->in)-1 ) {
-        r->overflow = 1;
-        r->pos      = 0;
-        return;
-    }
-
-    r->in[r->pos] = (char)c;
-    r->pos       += 1;
-
-    if (c == '\n') {
-        nmea_reader_parse( r );
-        r->pos = 0;
-    }
-}
-
-
-/*****************************************************************/
-/*****************************************************************/
-/*****                                                       *****/
-/*****       C O N N E C T I O N   S T A T E                 *****/
-/*****                                                       *****/
-/*****************************************************************/
-/*****************************************************************/
-
-/* commands sent to the gps thread */
-enum {
-    CMD_QUIT  = 0,
-    CMD_START = 1,
-    CMD_STOP  = 2
-};
-
-
-
-static void
-gps_state_done( GpsState*  s )
-{
-    // tell the thread to quit, and wait for it
-    char   cmd = CMD_QUIT;
-    void*  dummy;
-    write( s->control[0], &cmd, 1 );
-    pthread_join(s->thread, &dummy);
-
-    pthread_mutex_destroy(&s->lock);
-
-    // close the control socket pair
-    close( s->control[0] ); s->control[0] = -1;
-    close( s->control[1] ); s->control[1] = -1;
-
-    // close connection to the QEMU GPS daemon
-    close( s->fd ); s->fd = -1;
-    s->init = 0;
-}
-
-
-static void
-gps_state_start( GpsState*  s )
-{
-    char  cmd = CMD_START;
-    int   ret;
-
-    do { ret=write( s->control[0], &cmd, 1 ); }
-    while (ret < 0 && errno == EINTR);
-
-    if (ret != 1)
-        D("%s: could not send CMD_START command: ret=%d: %s",
-          __FUNCTION__, ret, strerror(errno));
-}
-
-
-static void
-gps_state_stop( GpsState*  s )
-{
-    char  cmd = CMD_STOP;
-    int   ret;
-
-    do { ret=write( s->control[0], &cmd, 1 ); }
-    while (ret < 0 && errno == EINTR);
-
-    if (ret != 1)
-        D("%s: could not send CMD_STOP command: ret=%d: %s",
-          __FUNCTION__, ret, strerror(errno));
-}
-
-
-static int
-epoll_register( int  epoll_fd, int  fd )
-{
-    struct epoll_event  ev;
-    int                 ret, flags;
-
-    /* important: make the fd non-blocking */
-    flags = fcntl(fd, F_GETFL);
-    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-
-    ev.events  = EPOLLIN;
-    ev.data.fd = fd;
-    do {
-        ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
-    } while (ret < 0 && errno == EINTR);
-    return ret;
-}
-
-
-// static int
-// epoll_deregister( int  epoll_fd, int  fd )
-// {
-//     int  ret;
-//     do {
-//         ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
-//     } while (ret < 0 && errno == EINTR);
-//     return ret;
-// }
-
-/* this is the main thread, it waits for commands from gps_state_start/stop and,
- * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
- * that must be parsed to be converted into GPS fixes sent to the framework
- */
-static void
-gps_state_thread( void*  arg )
-{
-    GpsState*   state = (GpsState*) arg;
-    NmeaReader  reader[1];
-    int         epoll_fd   = epoll_create(2);
-    int         started    = 0;
-    int         gps_fd     = state->fd;
-    int         control_fd = state->control[1];
-    GpsStatus gps_status;
-    gps_status.size = sizeof(gps_status);
-
-    GnssSvStatus  gnss_sv_status;
-    memset(&gnss_sv_status, 0, sizeof(gnss_sv_status));
-    gnss_sv_status.size = sizeof(gnss_sv_status);
-    gnss_sv_status.num_svs = 1;
-    gnss_sv_status.gnss_sv_list[0].size = sizeof(gnss_sv_status.gnss_sv_list[0]);
-    gnss_sv_status.gnss_sv_list[0].svid = 17;
-    gnss_sv_status.gnss_sv_list[0].constellation = GNSS_CONSTELLATION_GPS;
-    gnss_sv_status.gnss_sv_list[0].c_n0_dbhz = 60.0;
-    gnss_sv_status.gnss_sv_list[0].elevation = 30.0;
-    gnss_sv_status.gnss_sv_list[0].azimuth = 30.0;
-    gnss_sv_status.gnss_sv_list[0].flags = GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY;
-
-    nmea_reader_init( reader );
-
-    // register control file descriptors for polling
-    epoll_register( epoll_fd, control_fd );
-    epoll_register( epoll_fd, gps_fd );
-
-    D("gps thread running");
-
-    // now loop
-    for (;;) {
-        struct epoll_event   events[2];
-        int                  ne, nevents;
-
-        int timeout = -1;
-        if (gps_status.status == GPS_STATUS_SESSION_BEGIN) {
-            timeout = 10 * 1000; // 10 seconds
-        }
-        nevents = epoll_wait( epoll_fd, events, 2, timeout );
-        if (state->callbacks.gnss_sv_status_cb) {
-            state->callbacks.gnss_sv_status_cb(&gnss_sv_status);
-        }
-        // update satilite info
-        if (nevents < 0) {
-            if (errno != EINTR)
-                ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
-            continue;
-        }
-        D("gps thread received %d events", nevents);
-        for (ne = 0; ne < nevents; ne++) {
-            if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
-                ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
-                return;
-            }
-            if ((events[ne].events & EPOLLIN) != 0) {
-                int  fd = events[ne].data.fd;
-
-                if (fd == control_fd)
-                {
-                    char  cmd = 0xFF;
-                    int   ret;
-                    D("gps control fd event");
-                    do {
-                        ret = read( fd, &cmd, 1 );
-                    } while (ret < 0 && errno == EINTR);
-
-                    if (cmd == CMD_QUIT) {
-                        D("gps thread quitting on demand");
-                        return;
-                    }
-                    else if (cmd == CMD_START) {
-                        if (!started) {
-                            D("gps thread starting  location_cb=%p", state->callbacks.location_cb);
-                            started = 1;
-                            reader->gnss_count = 0;
-                            nmea_reader_set_callback( reader, state->callbacks.location_cb );
-                            gps_status.status = GPS_STATUS_SESSION_BEGIN;
-                            if (state->callbacks.status_cb) {
-                                state->callbacks.status_cb(&gps_status);
-                            }
-                        }
-                    }
-                    else if (cmd == CMD_STOP) {
-                        if (started) {
-                            D("gps thread stopping");
-                            started = 0;
-                            nmea_reader_set_callback( reader, NULL );
-                            gps_status.status = GPS_STATUS_SESSION_END;
-                            if (state->callbacks.status_cb) {
-                                state->callbacks.status_cb(&gps_status);
-                            }
-                        }
-                    }
-                }
-                else if (fd == gps_fd)
-                {
-                    char  buff[32];
-                    D("gps fd event");
-                    for (;;) {
-                        int  nn, ret;
-
-                        ret = read( fd, buff, sizeof(buff) );
-                        if (ret < 0) {
-                            if (errno == EINTR)
-                                continue;
-                            if (errno != EWOULDBLOCK)
-                                ALOGE("error while reading from gps daemon socket: %s:", strerror(errno));
-                            break;
-                        }
-                        D("received %d bytes: %.*s", ret, ret, buff);
-                        for (nn = 0; nn < ret; nn++)
-                            nmea_reader_addc( reader, buff[nn] );
-                    }
-                    D("gps fd event end");
-                }
-                else
-                {
-                    ALOGE("epoll_wait() returned unkown fd %d ?", fd);
-                }
-            }
-        }
-    }
-}
-
-#define  BUFF_SIZE   (PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2)
-static bool is_gnss_measurement_enabled() {
-    char temp[BUFF_SIZE];
-    property_get("ro.kernel.qemu.gps.gnss_enabled", temp, "");
-    return (strncmp(temp, "1", 1) == 0);
-}
-
-static bool is_fix_provided_by_gnss_measurement() {
-    char temp[BUFF_SIZE];
-    property_get("ro.kernel.qemu.gps.fix_by_gnss", temp, "");
-    return (strncmp(temp, "1", 1) == 0);
-}
-
-static void
-gps_state_init( GpsState*  state, GpsCallbacks* callbacks )
-{
-    state->init       = 1;
-    state->control[0] = -1;
-    state->control[1] = -1;
-    state->fd         = -1;
-
-    state->fd = qemud_channel_open(QEMUD_CHANNEL_NAME);
-
-    if (state->fd < 0) {
-        D("no gps emulation detected");
-        return;
-    }
-
-    D("gps emulation will read from '%s' qemud channel", QEMUD_CHANNEL_NAME );
-
-    if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
-        ALOGE("could not create thread control socket pair: %s", strerror(errno));
-        goto Fail;
-    }
-
-    state->gnss_enabled = is_gnss_measurement_enabled();
-    D("gnss_enabled:%s", state->gnss_enabled ? "yes":"no");
-    state->fix_provided_by_gnss = is_fix_provided_by_gnss_measurement();
-
-    pthread_mutex_init (&state->lock, (const pthread_mutexattr_t *) NULL);
-
-    state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state );
-
-    if ( !state->thread ) {
-        ALOGE("could not create gps thread: %s", strerror(errno));
-        goto Fail;
-    }
-
-    state->callbacks = *callbacks;
-
-    // Explicitly initialize capabilities
-    state->callbacks.set_capabilities_cb(0);
-
-
-
-
-    // Setup system info, we are pre 2016 hardware.
-    GnssSystemInfo sysinfo;
-    sysinfo.size = sizeof(GnssSystemInfo);
-    sysinfo.year_of_hw = 2015;
-    state->callbacks.set_system_info_cb(&sysinfo);
-    if (state->gnss_enabled) {
-        D("enabling GPS_CAPABILITY_MEASUREMENTS");
-        state->callbacks.set_capabilities_cb(GPS_CAPABILITY_MEASUREMENTS);
-    }
-
-    D("gps state initialized");
-    return;
-
-Fail:
-    gps_state_done( state );
-}
-
-
-/*****************************************************************/
-/*****************************************************************/
-/*****                                                       *****/
-/*****       I N T E R F A C E                               *****/
-/*****                                                       *****/
-/*****************************************************************/
-/*****************************************************************/
-
-
-static int
-qemu_gps_init(GpsCallbacks* callbacks)
-{
-    GpsState*  s = _gps_state;
-
-    if (!s->init)
-        gps_state_init(s, callbacks);
-
-    if (s->fd < 0)
-        return -1;
-
-    return 0;
-}
-
-static void
-qemu_gps_cleanup(void)
-{
-    GpsState*  s = _gps_state;
-
-    if (s->init)
-        gps_state_done(s);
-}
-
-
-static int
-qemu_gps_start()
-{
-    GpsState*  s = _gps_state;
-
-    if (!s->init) {
-        D("%s: called with uninitialized state !!", __FUNCTION__);
-        return -1;
-    }
-
-    D("%s: called", __FUNCTION__);
-    gps_state_start(s);
-    return 0;
-}
-
-
-static int
-qemu_gps_stop()
-{
-    GpsState*  s = _gps_state;
-
-    if (!s->init) {
-        D("%s: called with uninitialized state !!", __FUNCTION__);
-        return -1;
-    }
-
-    D("%s: called", __FUNCTION__);
-    gps_state_stop(s);
-    return 0;
-}
-
-
-static int
-qemu_gps_inject_time(GpsUtcTime __unused time,
-                     int64_t __unused timeReference,
-                     int __unused uncertainty)
-{
-    return 0;
-}
-
-static int
-qemu_gps_inject_location(double __unused latitude,
-                         double __unused longitude,
-                         float __unused accuracy)
-{
-    return 0;
-}
-
-static void
-qemu_gps_delete_aiding_data(GpsAidingData __unused flags)
-{
-}
-
-static int qemu_gps_set_position_mode(GpsPositionMode __unused mode,
-                                      GpsPositionRecurrence __unused recurrence,
-                                      uint32_t __unused min_interval,
-                                      uint32_t __unused preferred_accuracy,
-                                      uint32_t __unused preferred_time)
-{
-    // FIXME - support fix_frequency
-    return 0;
-}
-
-static int qemu_gps_measurement_init(GpsMeasurementCallbacks* callbacks) {
-    /* this runs in main thread */
-    D("calling %s with input %p", __func__, callbacks);
-    GpsState*  s = _gps_state;
-    pthread_mutex_lock(&s->lock);
-    s->measurement_callbacks = callbacks;
-    pthread_mutex_unlock(&s->lock);
-
-    return 0;
-}
-
-static void qemu_gps_measurement_close() {
-    /* this runs in main thread */
-    D("calling %s", __func__);
-    GpsState*  s = _gps_state;
-    pthread_mutex_lock(&s->lock);
-    s->measurement_callbacks = NULL;
-    pthread_mutex_unlock(&s->lock);
-}
-
-static const GpsMeasurementInterface qemuGpsMeasurementInterface  = {
-    sizeof(GpsMeasurementInterface),
-    qemu_gps_measurement_init,
-    qemu_gps_measurement_close,
-};
-
-static const void*
-qemu_gps_get_extension(const char* name)
-{
-    if(name && strcmp(name, GPS_MEASUREMENT_INTERFACE) == 0) {
-        /* when this is called, _gps_state is not initialized yet */
-        bool gnss_enabled = is_gnss_measurement_enabled();
-        if (gnss_enabled) {
-            D("calling %s with GPS_MEASUREMENT_INTERFACE enabled", __func__);
-            return &qemuGpsMeasurementInterface;
-        }
-    }
-    return NULL;
-}
-
-static const GpsInterface  qemuGpsInterface = {
-    sizeof(GpsInterface),
-    qemu_gps_init,
-    qemu_gps_start,
-    qemu_gps_stop,
-    qemu_gps_cleanup,
-    qemu_gps_inject_time,
-    qemu_gps_inject_location,
-    qemu_gps_delete_aiding_data,
-    qemu_gps_set_position_mode,
-    qemu_gps_get_extension,
-};
-
-const GpsInterface* gps__get_gps_interface(struct gps_device_t* __unused dev)
-{
-    return &qemuGpsInterface;
-}
-
-static int open_gps(const struct hw_module_t* module,
-                    char const* __unused name,
-                    struct hw_device_t** device)
-{
-    struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
-    memset(dev, 0, sizeof(*dev));
-
-    dev->common.tag = HARDWARE_DEVICE_TAG;
-    dev->common.version = 0;
-    dev->common.module = (struct hw_module_t*)module;
-//    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
-    dev->get_gps_interface = gps__get_gps_interface;
-
-    *device = (struct hw_device_t*)dev;
-    return 0;
-}
-
-
-static struct hw_module_methods_t gps_module_methods = {
-    .open = open_gps
-};
-
-struct hw_module_t HAL_MODULE_INFO_SYM = {
-    .tag = HARDWARE_MODULE_TAG,
-    .version_major = 1,
-    .version_minor = 0,
-    .id = GPS_HARDWARE_MODULE_ID,
-    .name = "Goldfish GPS Module",
-    .author = "The Android Open Source Project",
-    .methods = &gps_module_methods,
-};
diff --git a/sepolicy/common/file_contexts b/sepolicy/common/file_contexts
index e6afeb3..a1be172 100644
--- a/sepolicy/common/file_contexts
+++ b/sepolicy/common/file_contexts
@@ -41,6 +41,7 @@
 /vendor/bin/hw/android\.hardware\.authsecret@1\.0-service  u:object_r:hal_authsecret_default_exec:s0
 /vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default  u:object_r:hal_input_classifier_default_exec:s0
 /vendor/bin/hw/android\.hardware\.power\.stats@1\.0-service\.mock  u:object_r:hal_power_stats_default_exec:s0
+/vendor/bin/hw/android\.hardware\.gnss@2\.0-service\.ranchu        u:object_r:hal_gnss_default_exec:s0
 
 /vendor/lib(64)?/hw/vulkan\.ranchu\.so   u:object_r:same_process_hal_file:s0
 /vendor/lib(64)?/hw/gralloc\.ranchu\.so   u:object_r:same_process_hal_file:s0
diff --git a/vendor.mk b/vendor.mk
index ffc0a1c..c8e758d 100644
--- a/vendor.mk
+++ b/vendor.mk
@@ -54,8 +54,6 @@
     libOpenglSystemCommon \
     libgoldfish-ril \
     qemu-props \
-    gps.goldfish \
-    gps.ranchu \
     fingerprint.goldfish \
     audio.primary.goldfish \
     audio.primary.goldfish_legacy \
@@ -110,10 +108,7 @@
     NavigationBarMode2ButtonOverlay \
 
 ifneq ($(EMULATOR_VENDOR_NO_GNSS),true)
-PRODUCT_PACKAGES += \
-    android.hardware.gnss@1.0-service \
-    android.hardware.gnss@1.0-impl
-DEVICE_MANIFEST_FILE += device/generic/goldfish/manifest.gnss.xml
+PRODUCT_PACKAGES += android.hardware.gnss@2.0-service.ranchu
 endif
 
 ifneq ($(EMULATOR_VENDOR_NO_SENSORS),true)