summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp12
-rw-r--r--android/README98
-rw-r--r--android/gpsd.rc5
-rw-r--r--android/gpsd_wrapper.c24
-rw-r--r--android/hal/Android.bp24
-rw-r--r--android/hal/Gnss.cpp381
-rw-r--r--android/hal/Gnss.h107
-rw-r--r--android/hal/GnssConfiguration.cpp84
-rw-r--r--android/hal/GnssConfiguration.h73
-rw-r--r--android/hal/GnssMeasurement.cpp35
-rw-r--r--android/hal/GnssMeasurement.h42
-rw-r--r--android/hal/android.hardware.gnss@1.1-service.gpsd.rc4
-rw-r--r--android/hal/service.cpp25
-rw-r--r--android/manifest.xml11
-rw-r--r--android/sepolicy/file_contexts3
-rw-r--r--android/sepolicy/gpsd.te16
-rw-r--r--android/sepolicy/hal_gnss.te6
17 files changed, 944 insertions, 6 deletions
diff --git a/Android.bp b/Android.bp
index e307e342..fbdaf9f0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22,6 +22,7 @@ cc_binary {
cc_binary {
name: "gpsd",
vendor: true,
+ required: ["gpsd_wrapper"],
generated_headers: [
"gpsd_timebase_h",
"gpsd_revision_h",
@@ -45,6 +46,17 @@ cc_binary {
]
}
+cc_binary {
+ name: "gpsd_wrapper",
+ vendor: true,
+ init_rc: ["android/gpsd.rc"],
+ srcs: ["android/gpsd_wrapper.c"],
+ shared_libs: [
+ "liblog",
+ "libcutils"
+ ]
+}
+
cc_library_shared {
name: "libgps",
vendor: true,
diff --git a/android/README b/android/README
index 4f4b3753..1824ae64 100644
--- a/android/README
+++ b/android/README
@@ -4,10 +4,12 @@ GPSd on Android/AOSP:
The files in this path, as well as the Android.bp in the path below,
are used for building the GPSd client as a platform library for AOSP.
-Three components are currently implemented;
+Five components are currently implemented;
+gpsd executable,
Shared library, libgps (builds libgps.so)
Static library, libgps_static
Test client, gps_test (builds executable gps_test)
+Android GNSS 1.1 HAL, (builds android.hardware.gnss@1.1-service.gpsd)
Build instructions:
@@ -43,6 +45,12 @@ Following this, performing a full build for your device will
incorporate the component(s) into the vendor partition image for
your device.
+Note that for the HAL, you also have to include some additional
+components;
+PRODUCT_PACKAGES += android.hardware.gnss@1.1 \
+ android.hardware.gnss@1.1-impl \
+ android.hardware.gnss@1.1-service.gpsd
+
Running gps_test:
-----------------
@@ -62,11 +70,89 @@ shared_libs that your module depends on. For static, use
"libgps_static" in the list of static_libs.
-Future work:
-------------
-I am currently in the process of writing a GNSS 1.1 HAL for Android
-based on libgps. Once this is working, I will move on to getting
-GPSd itself to build.
+GPSd:
+-----
+The only configuration needed for GPSd is to set a system property
+with the launch parameters to use. This should be appended to the
+PRODUCT_PROPERTY_OVERRIDES variable in an appropriate device specific
+makefile. This is a comma-separated list of parameters.
+
+For example (note that this is the default if unspecified);
+PRODUCT_PROPERTY_OVERRIDES += \
+ service.gpsd.parameters=-Nn,-D2,/dev/ttyACM0,/dev/ttyACM1
+
+
+GNSS 1.1 HAL for Android:
+-------------------------
+This is the part that wraps everything together on Android. It makes
+it possible for location services on Android to be fed location data
+by gpsd.
+
+The HAL is able to operate with or without gpsd running on the same
+device. By default, the HAL will look to localhost:2947 for gpsd,
+however, it is possible to change both the host and port by setting
+the system properties "service.gpsd.host" and "service.gpsd.port"
+to reflect the hostname or ip address of the gpsd host, and the
+port number it is listening on.
+
+The HAL also has a special automotive mode that can be activated
+by setting the system property "service.gpsd.automotive" to any
+value. When the automotive mode is activated, the HAL will feed
+the location services with the last known fix coordinates until
+the first valid fix has been received. If the fix is later lost,
+it will correctly NOT continue to feed a fix. The updated fix will
+be stored in persistent system properties every 30 seconds while
+there is a valid fix.
+
+The purpose of automotive mode is to make it possible to immediately
+input a destination into navigation software without having to wait
+for a valid fix. A valid fix can take minutes to establish, even with
+a clear view of the sky, but worse, a fix can even be impossible if
+the vehicle is parked underground or in any structure that
+effectively blocks the signals. The consequence is that without
+automotive mode, it would be necessary to drive the vehicle from
+its current location to somewhere with a clear view of the sky,
+and then wait potentially several minutes for a valid fix before
+being able to enter a destination. With automotive mode, it is
+possible to immediately enter a destination, and begin driving.
+Once a fix has been established, the navigation software will
+update to the current location on the route.
+
+Note that in addition to including the components, it is also
+necessary to make adjustments to the device's selinux policy in
+order for the HAL to properly interact with the network. A typical
+policy file will be provided.
+
+
+VERY IMPORTANT: SEPOLICY!
+-------------------------
+In the old days prior to Android 8, it was possible to achieve full
+system functionality by simply disabling selinux enforcement. This
+is no longer enough with Android 8+, since the init process does
+its own selinux checks before it will even attempt to launch a daemon.
+
+And not only this, but it is also very important to enforce a certain
+degree of security. As such, appropriate policy for the GPSd process
+and the GNSS HAL are included, however, it is necessary for it to be
+included in the device configuration.
+
+Add the following to an appropriate device specific makefile;
+BOARD_SEPOLICY_DIRS += external/gpsd/android/sepolicy
+
+
+Putting it all together:
+------------------------
+Adding the following to your device's makefile will build and install
+GPSd and its GNSS HAL:
+
+PRODUCT_PACKAGES += android.hardware.gnss@1.1 \
+ android.hardware.gnss@1.1-impl \
+ android.hardware.gnss@1.1-service.gpsd \
+ gpsd
+PRODUCT_PROPERTY_OVERRIDES += \
+ service.gpsd.parameters=-Nn,-G,/dev/ttyACM0
+BOARD_SEPOLICY_DIRS += external/gpsd/android/sepolicy
+DEVICE_MANIFEST_FILE += external/gpsd/android/manifest.xml
Contact: Adam Serbinski <aserbinski@gmail.com>
diff --git a/android/gpsd.rc b/android/gpsd.rc
new file mode 100644
index 00000000..6d8969ab
--- /dev/null
+++ b/android/gpsd.rc
@@ -0,0 +1,5 @@
+service gpsd /vendor/bin/gpsd_wrapper
+ class main
+ user root
+ group root
+ oneshot
diff --git a/android/gpsd_wrapper.c b/android/gpsd_wrapper.c
new file mode 100644
index 00000000..12520382
--- /dev/null
+++ b/android/gpsd_wrapper.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <android/log.h>
+#include <cutils/properties.h>
+#include <sys/system_properties.h>
+
+int main (){
+ char gpsd_params[PROP_VALUE_MAX];
+ char cmd[1024];
+ int i = 0;
+ property_get("service.gpsd.parameters", gpsd_params, "-Nn,-D2,/dev/ttyACM0,/dev/ttyACM1");
+ while (gpsd_params[i] != 0){
+ if (gpsd_params[i] == ',') gpsd_params[i] = ' ';
+ i++;
+ }
+
+ sprintf(cmd, "/vendor/bin/logwrapper /vendor/bin/gpsd %s", gpsd_params);
+
+ __android_log_print(ANDROID_LOG_DEBUG, "gpsd_wrapper", "Starting gpsd: %s", cmd);
+
+ system(cmd);
+ return 0;
+}
+
diff --git a/android/hal/Android.bp b/android/hal/Android.bp
new file mode 100644
index 00000000..21cd123e
--- /dev/null
+++ b/android/hal/Android.bp
@@ -0,0 +1,24 @@
+cc_binary {
+ name: "android.hardware.gnss@1.1-service.gpsd",
+ init_rc: ["android.hardware.gnss@1.1-service.gpsd.rc"],
+ relative_install_path: "hw",
+ vendor: true,
+ srcs: [
+ "Gnss.cpp",
+ "GnssConfiguration.cpp",
+ "GnssMeasurement.cpp",
+ "service.cpp",
+ ],
+ shared_libs: [
+ "libhidlbase",
+ "libhidltransport",
+ "libutils",
+ "liblog",
+ "libcutils",
+ "android.hardware.gnss@1.1",
+ "android.hardware.gnss@1.0",
+ ],
+ static_libs: [
+ "libgps_static"
+ ]
+}
diff --git a/android/hal/Gnss.cpp b/android/hal/Gnss.cpp
new file mode 100644
index 00000000..eb8e7050
--- /dev/null
+++ b/android/hal/Gnss.cpp
@@ -0,0 +1,381 @@
+#define LOG_TAG "GPSd_HAL"
+
+#include <android/hardware/gnss/1.0/types.h>
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "Gnss.h"
+#include "GnssMeasurement.h"
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V1_1 {
+namespace implementation {
+
+using GnssSvFlags = IGnssCallback::GnssSvFlags;
+
+const uint32_t MIN_INTERVAL_MILLIS = 100;
+sp<::android::hardware::gnss::V1_1::IGnssCallback> Gnss::sGnssCallback = nullptr;
+
+Gnss::Gnss() : mMinIntervalMs(1000), mGnssConfiguration{new GnssConfiguration()} {}
+
+Gnss::~Gnss() {
+ stop();
+}
+
+// Methods from ::android::hardware::gnss::V1_0::IGnss follow.
+Return<bool> Gnss::setCallback(const sp<::android::hardware::gnss::V1_0::IGnssCallback>&) {
+ // Mock handles only new callback (see setCallback1_1) coming from Android P+
+ return false;
+}
+
+Return<bool> Gnss::start() {
+ if (mIsActive) {
+ ALOGW("Gnss has started. Restarting...");
+ stop();
+ }
+
+ mIsActive = true;
+ mThread = std::thread([this]() {
+ struct gps_data_t gps_data;
+ int gpsopen = -1;
+ char gpsdhost[PROP_VALUE_MAX];
+ char gpsdport[PROP_VALUE_MAX];
+ char gpsdauto[PROP_VALUE_MAX];
+ int is_automotive;
+ char gpslat[PROP_VALUE_MAX];
+ char gpslon[PROP_VALUE_MAX];
+ long last_recorded_fix = 0;
+ char dtos[100];
+ GnssLocation location;
+
+ // Normally, GPSd will be running on localhost, but we can set a system property
+ // "service.gpsd.host" to some other hostname in order to open a GPSd instance
+ // running on a different host.
+ property_get("service.gpsd.host", gpsdhost, "localhost");
+ property_get("service.gpsd.port", gpsdport, "2947");
+ is_automotive = (property_get("service.gpsd.automotive", gpsdauto, "") > 0);
+
+ // Load coordinates stored in persist properties as current location
+ // This is to provide instantaneous fix to the last good location
+ // in order to provide instantaneous ability to begin navigator routing.
+ if (is_automotive && property_get("persist.service.gpsd.latitude", gpslat, "") > 0
+ && property_get("persist.service.gpsd.longitude", gpslon, "") > 0){
+ location = {
+ .gnssLocationFlags = 0xDD,
+ .latitudeDegrees = atof(gpslat),
+ .longitudeDegrees = atof(gpslon),
+ .speedMetersPerSec = 0.0,
+ .bearingDegrees = 0.0,
+ .horizontalAccuracyMeters = 0.0,
+ .speedAccuracyMetersPerSecond = 0.0,
+ .bearingAccuracyDegrees = 0.0,
+ .timestamp = (long) time(NULL)
+ };
+ this->reportLocation(location);
+ }
+
+ memset(&gps_data, 0, sizeof(gps_data));
+
+ while (mIsActive == true) {
+ // If the connection to GPSd is not open, try to open it.
+ // If the attempt to open it fails, sleep 5 seconds and try again.
+ // Note the continue; statement that will skip the reading in the
+ // event that the connection to GPSd cannot be established.
+ if (gpsopen != 0){
+ ALOGD("%s: gpsd_host: %s, gpsd_port: %s", __func__, gpsdhost, gpsdport);
+ if ((gpsopen = gps_open(gpsdhost, gpsdport, &gps_data)) == 0){
+ ALOGD("%s: gps_open SUCCESS", __func__);
+ gps_stream(&gps_data, WATCH_ENABLE, NULL);
+ } else {
+ ALOGD("%s: gps_open FAIL (%d). Trying again in 5 seconds.", __func__, gpsopen);
+ sleep(5);
+ continue;
+ }
+ }
+
+ // Wait for data from gpsd, then process it.
+ if (gps_waiting (&gps_data, 2000000)) {
+ errno = 0;
+ if (gps_read (&gps_data, NULL, 0) != -1) {
+
+ if (gps_data.status >= 1 && gps_data.fix.mode >= 2){
+
+ // Every 30 seconds, store current coordinates to persist property.
+ if (is_automotive && ((long) gps_data.fix.time) > last_recorded_fix + 30){
+ last_recorded_fix = (long) gps_data.fix.time;
+ sprintf(dtos, "%lf", gps_data.fix.latitude);
+ property_set("persist.service.gpsd.latitude", dtos);
+ sprintf(dtos, "%lf", gps_data.fix.longitude);
+ property_set("persist.service.gpsd.longitude", dtos);
+ }
+
+ unsigned short flags =
+ V1_0::GnssLocationFlags::HAS_LAT_LONG |
+ V1_0::GnssLocationFlags::HAS_SPEED |
+ V1_0::GnssLocationFlags::HAS_BEARING |
+ V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY |
+ V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY |
+ V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY;
+
+ location = {
+ .gnssLocationFlags = flags,
+ .latitudeDegrees = (double) gps_data.fix.latitude,
+ .longitudeDegrees = (double) gps_data.fix.longitude,
+ .speedMetersPerSec = (float) gps_data.fix.speed,
+ .bearingDegrees = (float) gps_data.fix.track,
+ .horizontalAccuracyMeters = (float) gps_data.fix.epy,
+ .speedAccuracyMetersPerSecond = (float) gps_data.fix.eps,
+ .bearingAccuracyDegrees = (float) gps_data.fix.epd,
+ .timestamp = (long) gps_data.fix.time
+ };
+
+ if (gps_data.fix.mode == 3){
+ flags |= V1_0::GnssLocationFlags::HAS_ALTITUDE |
+ V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY;
+
+ location.altitudeMeters = gps_data.fix.altitude;
+ location.verticalAccuracyMeters = gps_data.fix.epv;
+ }
+
+ this->reportLocation(location);
+ } else if (is_automotive && last_recorded_fix == 0){
+ this->reportLocation(location);
+ }
+
+ GnssSvStatus svStatus = {.numSvs = (uint32_t) gps_data.satellites_visible};
+ for (int i = 0; i < gps_data.satellites_visible; i++){
+ GnssConstellationType constellation_type = GnssConstellationType::UNKNOWN;
+ switch (gps_data.skyview[i].gnssid){
+ case 0:
+ constellation_type = GnssConstellationType::GPS;
+ break;
+ case 1:
+ constellation_type = GnssConstellationType::SBAS;
+ break;
+ case 2:
+ constellation_type = GnssConstellationType::GALILEO;
+ break;
+ case 3:
+ constellation_type = GnssConstellationType::BEIDOU;
+ break;
+ case 4:
+ constellation_type = GnssConstellationType::UNKNOWN;
+ break;
+ case 5:
+ constellation_type = GnssConstellationType::QZSS;
+ break;
+ case 6:
+ constellation_type = GnssConstellationType::GLONASS;
+ break;
+ }
+ svStatus.gnssSvList[i] = getSvInfo(
+ gps_data.skyview[i].svid,
+ constellation_type,
+ gps_data.skyview[i].ss,
+ gps_data.skyview[i].elevation,
+ gps_data.skyview[i].azimuth,
+ gps_data.skyview[i].used
+ );
+
+ svStatus.gnssSvList[i].svFlag = 0;
+ if (gps_data.skyview[i].used == 1) svStatus.gnssSvList[i].svFlag |= GnssSvFlags::USED_IN_FIX;
+ }
+ this->reportSvStatus(svStatus);
+ }
+ }
+ }
+
+ // Close the GPS
+ gps_stream(&gps_data, WATCH_DISABLE, NULL);
+ gps_close (&gps_data);
+ });
+
+ return true;
+}
+
+Return<bool> Gnss::stop() {
+ mIsActive = false;
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+ return true;
+}
+
+Return<void> Gnss::cleanup() {
+ // TODO implement
+ return Void();
+}
+
+Return<bool> Gnss::injectTime(int64_t, int64_t, int32_t) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> Gnss::injectLocation(double, double, float) {
+ // TODO implement
+ return bool{};
+}
+
+Return<void> Gnss::deleteAidingData(::android::hardware::gnss::V1_0::IGnss::GnssAidingData) {
+ return Void();
+}
+
+Return<bool> Gnss::setPositionMode(::android::hardware::gnss::V1_0::IGnss::GnssPositionMode,
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionRecurrence,
+ uint32_t, uint32_t, uint32_t) {
+ // TODO implement
+ return bool{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IAGnssRil>> Gnss::getExtensionAGnssRil() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IAGnssRil>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssGeofencing>> Gnss::getExtensionGnssGeofencing() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IGnssGeofencing>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IAGnss>> Gnss::getExtensionAGnss() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IAGnss>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssNi>> Gnss::getExtensionGnssNi() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IGnssNi>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssMeasurement>> Gnss::getExtensionGnssMeasurement() {
+ // TODO implement
+ return new GnssMeasurement();
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssNavigationMessage>>
+Gnss::getExtensionGnssNavigationMessage() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IGnssNavigationMessage>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssXtra>> Gnss::getExtensionXtra() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IGnssXtra>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssConfiguration>>
+Gnss::getExtensionGnssConfiguration() {
+ // TODO implement
+ return new GnssConfiguration();
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssDebug>> Gnss::getExtensionGnssDebug() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IGnssDebug>{};
+}
+
+Return<sp<::android::hardware::gnss::V1_0::IGnssBatching>> Gnss::getExtensionGnssBatching() {
+ // TODO implement
+ return ::android::sp<::android::hardware::gnss::V1_0::IGnssBatching>{};
+}
+
+// Methods from ::android::hardware::gnss::V1_1::IGnss follow.
+Return<bool> Gnss::setCallback_1_1(
+ const sp<::android::hardware::gnss::V1_1::IGnssCallback>& callback) {
+ if (callback == nullptr) {
+ ALOGE("%s: Null callback ignored", __func__);
+ return false;
+ }
+
+ sGnssCallback = callback;
+
+ uint32_t capabilities = 0x0;
+ auto ret = sGnssCallback->gnssSetCapabilitesCb(capabilities);
+ if (!ret.isOk()) {
+ ALOGE("%s: Unable to invoke callback", __func__);
+ }
+
+ IGnssCallback::GnssSystemInfo gnssInfo = {.yearOfHw = 2018};
+
+ ret = sGnssCallback->gnssSetSystemInfoCb(gnssInfo);
+ if (!ret.isOk()) {
+ ALOGE("%s: Unable to invoke callback", __func__);
+ }
+
+ auto gnssName = "GPSd GNSS Implementation v1.1";
+ ret = sGnssCallback->gnssNameCb(gnssName);
+ if (!ret.isOk()) {
+ ALOGE("%s: Unable to invoke callback", __func__);
+ }
+
+ return true;
+}
+
+Return<bool> Gnss::setPositionMode_1_1(
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionMode,
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionRecurrence, uint32_t minIntervalMs,
+ uint32_t, uint32_t, bool) {
+ mMinIntervalMs = (minIntervalMs < MIN_INTERVAL_MILLIS) ? MIN_INTERVAL_MILLIS : minIntervalMs;
+ return true;
+}
+
+Return<sp<::android::hardware::gnss::V1_1::IGnssConfiguration>>
+Gnss::getExtensionGnssConfiguration_1_1() {
+ return mGnssConfiguration;
+}
+
+Return<sp<::android::hardware::gnss::V1_1::IGnssMeasurement>>
+Gnss::getExtensionGnssMeasurement_1_1() {
+ // TODO implement
+ return new GnssMeasurement();
+}
+
+Return<bool> Gnss::injectBestLocation(const GnssLocation&) {
+ return true;
+}
+
+Return<GnssSvInfo> Gnss::getSvInfo(int16_t svid, GnssConstellationType type, float cN0DbHz,
+ float elevationDegrees, float azimuthDegrees, int16_t used) const {
+ GnssSvInfo svInfo = {.svid = svid,
+ .constellation = type,
+ .cN0Dbhz = cN0DbHz,
+ .elevationDegrees = elevationDegrees,
+ .azimuthDegrees = azimuthDegrees,
+ .svFlag = 0};
+ if (used)
+ svInfo.svFlag |= GnssSvFlags::USED_IN_FIX;
+ if (elevationDegrees > 0 && azimuthDegrees > 0)
+ svInfo.svFlag |= GnssSvFlags::HAS_EPHEMERIS_DATA | GnssSvFlags::HAS_ALMANAC_DATA;
+
+ return svInfo;
+}
+
+Return<void> Gnss::reportLocation(const GnssLocation& location) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (sGnssCallback == nullptr) {
+ ALOGE("%s: sGnssCallback is null.", __func__);
+ return Void();
+ }
+ sGnssCallback->gnssLocationCb(location);
+ return Void();
+}
+
+Return<void> Gnss::reportSvStatus(const GnssSvStatus& svStatus) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (sGnssCallback == nullptr) {
+ ALOGE("%s: sGnssCallback is null.", __func__);
+ return Void();
+ }
+ sGnssCallback->gnssSvStatusCb(svStatus);
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
diff --git a/android/hal/Gnss.h b/android/hal/Gnss.h
new file mode 100644
index 00000000..da0c6975
--- /dev/null
+++ b/android/hal/Gnss.h
@@ -0,0 +1,107 @@
+#ifndef ANDROID_HARDWARE_GNSS_V1_1_GNSS_H
+#define ANDROID_HARDWARE_GNSS_V1_1_GNSS_H
+
+#include <android/hardware/gnss/1.1/IGnss.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <atomic>
+#include <mutex>
+#include <thread>
+#include "GnssConfiguration.h"
+#include "gps.h"
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+using GnssConstellationType = V1_0::GnssConstellationType;
+using GnssLocation = V1_0::GnssLocation;
+using GnssSvInfo = V1_0::IGnssCallback::GnssSvInfo;
+using GnssSvStatus = V1_0::IGnssCallback::GnssSvStatus;
+
+/**
+ * Unlike the gnss/1.0/default implementation, which is a shim layer to the legacy gps.h, this
+ * default implementation serves as a mock implementation for emulators
+ */
+struct Gnss : public IGnss {
+ Gnss();
+ ~Gnss();
+ // Methods from ::android::hardware::gnss::V1_0::IGnss follow.
+ Return<bool> setCallback(
+ const sp<::android::hardware::gnss::V1_0::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(
+ ::android::hardware::gnss::V1_0::IGnss::GnssAidingData aidingDataFlags) override;
+ Return<bool> setPositionMode(
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionMode mode,
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionRecurrence recurrence,
+ uint32_t minIntervalMs, uint32_t preferredAccuracyMeters,
+ uint32_t preferredTimeMs) override;
+ Return<sp<::android::hardware::gnss::V1_0::IAGnssRil>> getExtensionAGnssRil() override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssGeofencing>> getExtensionGnssGeofencing()
+ override;
+ Return<sp<::android::hardware::gnss::V1_0::IAGnss>> getExtensionAGnss() override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssNi>> getExtensionGnssNi() override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssMeasurement>> getExtensionGnssMeasurement()
+ override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssNavigationMessage>>
+ getExtensionGnssNavigationMessage() override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssXtra>> getExtensionXtra() override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssConfiguration>> getExtensionGnssConfiguration()
+ override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssDebug>> getExtensionGnssDebug() override;
+ Return<sp<::android::hardware::gnss::V1_0::IGnssBatching>> getExtensionGnssBatching() override;
+
+ // Methods from ::android::hardware::gnss::V1_1::IGnss follow.
+ Return<bool> setCallback_1_1(
+ const sp<::android::hardware::gnss::V1_1::IGnssCallback>& callback) override;
+ Return<bool> setPositionMode_1_1(
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionMode mode,
+ ::android::hardware::gnss::V1_0::IGnss::GnssPositionRecurrence recurrence,
+ uint32_t minIntervalMs, uint32_t preferredAccuracyMeters, uint32_t preferredTimeMs,
+ bool lowPowerMode) override;
+ Return<sp<::android::hardware::gnss::V1_1::IGnssConfiguration>>
+ getExtensionGnssConfiguration_1_1() override;
+ Return<sp<::android::hardware::gnss::V1_1::IGnssMeasurement>> getExtensionGnssMeasurement_1_1()
+ override;
+ Return<bool> injectBestLocation(
+ const ::android::hardware::gnss::V1_0::GnssLocation& location) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ private:
+ Return<GnssSvInfo> getSvInfo(int16_t svid, GnssConstellationType type, float cN0DbHz,
+ float elevationDegress, float azimuthDegress, int16_t used) const;
+ Return<void> reportLocation(const GnssLocation&) const;
+ Return<void> reportSvStatus(const GnssSvStatus&) const;
+
+ static sp<IGnssCallback> sGnssCallback;
+ std::atomic<long> mMinIntervalMs;
+ sp<GnssConfiguration> mGnssConfiguration;
+ std::atomic<bool> mIsActive;
+ std::thread mThread;
+ mutable std::mutex mMutex;
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GNSS_V1_1_GNSS_H
diff --git a/android/hal/GnssConfiguration.cpp b/android/hal/GnssConfiguration.cpp
new file mode 100644
index 00000000..2717571c
--- /dev/null
+++ b/android/hal/GnssConfiguration.cpp
@@ -0,0 +1,84 @@
+#define LOG_TAG "GnssConfiguration"
+
+#include "GnssConfiguration.h"
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V1_1 {
+namespace implementation {
+
+// Methods from ::android::hardware::gnss::V1_0::IGnssConfiguration follow.
+Return<bool> GnssConfiguration::setSuplEs(bool) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> GnssConfiguration::setSuplVersion(uint32_t) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> GnssConfiguration::setSuplMode(hidl_bitfield<SuplMode>) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> GnssConfiguration::setGpsLock(hidl_bitfield<GpsLock>) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> GnssConfiguration::setLppProfile(hidl_bitfield<LppProfile>) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> GnssConfiguration::setGlonassPositioningProtocol(hidl_bitfield<GlonassPosProtocol>) {
+ // TODO implement
+ return bool{};
+}
+
+Return<bool> GnssConfiguration::setEmergencySuplPdn(bool) {
+ // TODO implement
+ return bool{};
+}
+
+// Methods from ::android::hardware::gnss::V1_1::IGnssConfiguration follow.
+Return<bool> GnssConfiguration::setBlacklist(const hidl_vec<BlacklistedSource>& sourceList) {
+ std::unique_lock<std::recursive_mutex> lock(mMutex);
+ mBlacklistedConstellationSet.clear();
+ mBlacklistedSourceSet.clear();
+ for (auto source : sourceList) {
+ if (source.svid == 0) {
+ // Wildcard blacklist, i.e., blacklist entire constellation.
+ mBlacklistedConstellationSet.insert(source.constellation);
+ } else {
+ mBlacklistedSourceSet.insert(source);
+ }
+ }
+ return true;
+}
+
+Return<bool> GnssConfiguration::isBlacklisted(const GnssSvInfo& gnssSvInfo) const {
+ std::unique_lock<std::recursive_mutex> lock(mMutex);
+ if (mBlacklistedConstellationSet.find(gnssSvInfo.constellation) !=
+ mBlacklistedConstellationSet.end()) {
+ return true;
+ }
+ BlacklistedSource source = {.constellation = gnssSvInfo.constellation, .svid = gnssSvInfo.svid};
+ return (mBlacklistedSourceSet.find(source) != mBlacklistedSourceSet.end());
+}
+
+std::recursive_mutex& GnssConfiguration::getMutex() const {
+ return mMutex;
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
diff --git a/android/hal/GnssConfiguration.h b/android/hal/GnssConfiguration.h
new file mode 100644
index 00000000..9b2699b6
--- /dev/null
+++ b/android/hal/GnssConfiguration.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_HARDWARE_GNSS_V1_1_GNSSCONFIGURATION_H
+#define ANDROID_HARDWARE_GNSS_V1_1_GNSSCONFIGURATION_H
+
+#include <android/hardware/gnss/1.1/IGnssCallback.h>
+#include <android/hardware/gnss/1.1/IGnssConfiguration.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <mutex>
+#include <unordered_set>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+using BlacklistedSource = ::android::hardware::gnss::V1_1::IGnssConfiguration::BlacklistedSource;
+using GnssConstellationType = V1_0::GnssConstellationType;
+using GnssSvInfo = V1_0::IGnssCallback::GnssSvInfo;
+
+struct BlacklistedSourceHash {
+ inline int operator()(const BlacklistedSource& source) const {
+ return int(source.constellation) * 1000 + int(source.svid);
+ }
+};
+
+struct BlacklistedSourceEqual {
+ inline bool operator()(const BlacklistedSource& s1, const BlacklistedSource& s2) const {
+ return (s1.constellation == s2.constellation) && (s1.svid == s2.svid);
+ }
+};
+
+using BlacklistedSourceSet =
+ std::unordered_set<BlacklistedSource, BlacklistedSourceHash, BlacklistedSourceEqual>;
+using BlacklistedConstellationSet = std::unordered_set<GnssConstellationType>;
+
+struct GnssConfiguration : public IGnssConfiguration {
+ // 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;
+
+ // Methods from ::android::hardware::gnss::V1_1::IGnssConfiguration follow.
+ Return<bool> setBlacklist(const hidl_vec<BlacklistedSource>& blacklist) override;
+
+ Return<bool> isBlacklisted(const GnssSvInfo& gnssSvInfo) const;
+ std::recursive_mutex& getMutex() const;
+
+ private:
+ BlacklistedSourceSet mBlacklistedSourceSet;
+ BlacklistedConstellationSet mBlacklistedConstellationSet;
+ mutable std::recursive_mutex mMutex;
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GNSS_V1_1_GNSSCONFIGURATION_H
diff --git a/android/hal/GnssMeasurement.cpp b/android/hal/GnssMeasurement.cpp
new file mode 100644
index 00000000..e88badd7
--- /dev/null
+++ b/android/hal/GnssMeasurement.cpp
@@ -0,0 +1,35 @@
+#include "GnssMeasurement.h"
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V1_1 {
+namespace implementation {
+
+// Methods from ::android::hardware::gnss::V1_0::IGnssMeasurement follow.
+Return<::android::hardware::gnss::V1_0::IGnssMeasurement::GnssMeasurementStatus>
+GnssMeasurement::setCallback(const sp<::android::hardware::gnss::V1_0::IGnssMeasurementCallback>&) {
+ // TODO implement
+ return ::android::hardware::gnss::V1_0::IGnssMeasurement::GnssMeasurementStatus{};
+}
+
+Return<void> GnssMeasurement::close() {
+ // TODO implement
+ return Void();
+}
+
+// Methods from ::android::hardware::gnss::V1_1::IGnssMeasurement follow.
+Return<::android::hardware::gnss::V1_0::IGnssMeasurement::GnssMeasurementStatus>
+GnssMeasurement::setCallback_1_1(
+ const sp<::android::hardware::gnss::V1_1::IGnssMeasurementCallback>&, bool) {
+ // TODO implement
+ return ::android::hardware::gnss::V1_0::IGnssMeasurement::GnssMeasurementStatus{};
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
diff --git a/android/hal/GnssMeasurement.h b/android/hal/GnssMeasurement.h
new file mode 100644
index 00000000..650cb272
--- /dev/null
+++ b/android/hal/GnssMeasurement.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_HARDWARE_GNSS_V1_1_GNSSMEASUREMENT_H
+#define ANDROID_HARDWARE_GNSS_V1_1_GNSSMEASUREMENT_H
+
+#include <android/hardware/gnss/1.1/IGnssMeasurement.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct GnssMeasurement : public IGnssMeasurement {
+ // Methods from ::android::hardware::gnss::V1_0::IGnssMeasurement follow.
+ Return<::android::hardware::gnss::V1_0::IGnssMeasurement::GnssMeasurementStatus> setCallback(
+ const sp<::android::hardware::gnss::V1_0::IGnssMeasurementCallback>& callback) override;
+ Return<void> close() override;
+
+ // Methods from ::android::hardware::gnss::V1_1::IGnssMeasurement follow.
+ Return<::android::hardware::gnss::V1_0::IGnssMeasurement::GnssMeasurementStatus>
+ setCallback_1_1(const sp<::android::hardware::gnss::V1_1::IGnssMeasurementCallback>& callback,
+ bool enableFullTracking) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GNSS_V1_1_GNSSMEASUREMENT_H
diff --git a/android/hal/android.hardware.gnss@1.1-service.gpsd.rc b/android/hal/android.hardware.gnss@1.1-service.gpsd.rc
new file mode 100644
index 00000000..3e13c7b1
--- /dev/null
+++ b/android/hal/android.hardware.gnss@1.1-service.gpsd.rc
@@ -0,0 +1,4 @@
+service gnss-gpsd /vendor/bin/hw/android.hardware.gnss@1.1-service.gpsd
+ class hal
+ user root
+ group system gps radio
diff --git a/android/hal/service.cpp b/android/hal/service.cpp
new file mode 100644
index 00000000..825d117c
--- /dev/null
+++ b/android/hal/service.cpp
@@ -0,0 +1,25 @@
+#define LOG_TAG "android.hardware.gnss@1.1-service.gpsd"
+
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include "Gnss.h"
+
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::gnss::V1_1::implementation::Gnss;
+using ::android::hardware::gnss::V1_1::IGnss;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::OK;
+using ::android::sp;
+
+int main(int /* argc */, char* /* argv */ []) {
+ sp<IGnss> gnss = new Gnss();
+ configureRpcThreadpool(1, true /* will join */);
+ if (gnss->registerAsService() != OK) {
+ ALOGE("Could not register gnss 1.1 service.");
+ return 1;
+ }
+ joinRpcThreadpool();
+
+ ALOGE("Service exited!");
+ return 1;
+}
diff --git a/android/manifest.xml b/android/manifest.xml
new file mode 100644
index 00000000..57bf092c
--- /dev/null
+++ b/android/manifest.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.gnss</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>IGnss</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/android/sepolicy/file_contexts b/android/sepolicy/file_contexts
new file mode 100644
index 00000000..ba71bc12
--- /dev/null
+++ b/android/sepolicy/file_contexts
@@ -0,0 +1,3 @@
+/vendor/bin/hw/android\.hardware\.gnss@1\.1-service\.gpsd u:object_r:hal_gnss_default_exec:s0
+/vendor/bin/gpsd u:object_r:gpsd_exec:s0
+/vendor/bin/gpsd_wrapper u:object_r:gpsd_exec:s0
diff --git a/android/sepolicy/gpsd.te b/android/sepolicy/gpsd.te
new file mode 100644
index 00000000..518b6166
--- /dev/null
+++ b/android/sepolicy/gpsd.te
@@ -0,0 +1,16 @@
+type gpsd, domain;
+type gpsd_exec, exec_type, vendor_file_type, file_type;
+
+init_daemon_domain(gpsd)
+net_domain(gpsd)
+
+allow gpsd gpsd:capability { net_raw };
+allow gpsd console_device:chr_file rw_file_perms;
+
+# The following are needed by logwrapper to get gpsd's output
+# to Android's logs.
+typeattribute gpsd vendor_executes_system_violators;
+allow gpsd shell_exec:file { execute execute_no_trans read open getattr };
+allow gpsd vendor_file:file { execute execute_no_trans read open getattr };
+allow gpsd devpts:chr_file { read write open getattr };
+allow gpsd gpsd_exec:file { execute execute_no_trans read open getattr };
diff --git a/android/sepolicy/hal_gnss.te b/android/sepolicy/hal_gnss.te
new file mode 100644
index 00000000..4bbbcb1d
--- /dev/null
+++ b/android/sepolicy/hal_gnss.te
@@ -0,0 +1,6 @@
+# Hijack hostapd domain and attribute to gain network access
+hal_server_domain(hal_gnss_default, hal_wifi_hostapd);
+typeattribute hal_gnss_default hal_wifi_hostapd_server;
+
+net_domain(hal_gnss_default)
+allow hal_gnss_default hal_gnss_default:capability { net_raw };