diff options
-rw-r--r-- | Android.bp | 12 | ||||
-rw-r--r-- | android/README | 98 | ||||
-rw-r--r-- | android/gpsd.rc | 5 | ||||
-rw-r--r-- | android/gpsd_wrapper.c | 24 | ||||
-rw-r--r-- | android/hal/Android.bp | 24 | ||||
-rw-r--r-- | android/hal/Gnss.cpp | 381 | ||||
-rw-r--r-- | android/hal/Gnss.h | 107 | ||||
-rw-r--r-- | android/hal/GnssConfiguration.cpp | 84 | ||||
-rw-r--r-- | android/hal/GnssConfiguration.h | 73 | ||||
-rw-r--r-- | android/hal/GnssMeasurement.cpp | 35 | ||||
-rw-r--r-- | android/hal/GnssMeasurement.h | 42 | ||||
-rw-r--r-- | android/hal/android.hardware.gnss@1.1-service.gpsd.rc | 4 | ||||
-rw-r--r-- | android/hal/service.cpp | 25 | ||||
-rw-r--r-- | android/manifest.xml | 11 | ||||
-rw-r--r-- | android/sepolicy/file_contexts | 3 | ||||
-rw-r--r-- | android/sepolicy/gpsd.te | 16 | ||||
-rw-r--r-- | android/sepolicy/hal_gnss.te | 6 |
17 files changed, 944 insertions, 6 deletions
@@ -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 }; |