Add navigation methods to IGnss AIDL HAL (hardware/interfaces)
Bug: 205185251
Test: atest VtsHalGnssTargetTest
Change-Id: Ib412aeb0b302952d13e236554f00690206b98253
diff --git a/gnss/aidl/vts/gnss_hal_test.cpp b/gnss/aidl/vts/gnss_hal_test.cpp
index 2447bf8..13c32ee 100644
--- a/gnss/aidl/vts/gnss_hal_test.cpp
+++ b/gnss/aidl/vts/gnss_hal_test.cpp
@@ -14,20 +14,31 @@
* limitations under the License.
*/
+#define LOG_TAG "GnssHalTest"
+
#include "gnss_hal_test.h"
#include <hidl/ServiceManagement.h>
+#include "Utils.h"
-using GnssConstellationTypeAidl = android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::GnssLocation;
+using android::hardware::gnss::IGnss;
+using android::hardware::gnss::IGnssCallback;
+using android::hardware::gnss::common::Utils;
+using GnssConstellationTypeV2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
void GnssHalTest::SetUp() {
// Get AIDL handle
aidl_gnss_hal_ = android::waitForDeclaredService<IGnssAidl>(String16(GetParam().c_str()));
ASSERT_NE(aidl_gnss_hal_, nullptr);
+ ALOGD("AIDL Interface Version = %d", aidl_gnss_hal_->getInterfaceVersion());
- const auto& hidlInstanceNames = android::hardware::getAllHalInstanceNames(
- android::hardware::gnss::V2_1::IGnss::descriptor);
- gnss_hal_ = IGnss_V2_1::getService(hidlInstanceNames[0]);
- ASSERT_NE(gnss_hal_, nullptr);
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ const auto& hidlInstanceNames = android::hardware::getAllHalInstanceNames(
+ android::hardware::gnss::V2_1::IGnss::descriptor);
+ gnss_hal_ = IGnss_V2_1::getService(hidlInstanceNames[0]);
+ ASSERT_NE(gnss_hal_, nullptr);
+ }
SetUpGnssCallback();
}
@@ -40,7 +51,6 @@
if (!status.isOk()) {
ALOGE("Failed to setCallback");
}
-
ASSERT_TRUE(status.isOk());
/*
@@ -48,9 +58,243 @@
*/
EXPECT_TRUE(aidl_gnss_cb_->capabilities_cbq_.retrieve(aidl_gnss_cb_->last_capabilities_,
TIMEOUT_SEC));
-
EXPECT_EQ(aidl_gnss_cb_->capabilities_cbq_.calledCount(), 1);
- // Invoke the super method.
- GnssHalTestTemplate<IGnss_V2_1>::SetUpGnssCallback();
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ // Invoke the super method.
+ GnssHalTestTemplate<IGnss_V2_1>::SetUpGnssCallback();
+ }
+}
+
+void GnssHalTest::CheckLocation(const GnssLocation& location, bool check_speed) {
+ Utils::checkLocation(location, check_speed, /* check_more_accuracies= */ true);
+}
+
+void GnssHalTest::SetPositionMode(const int min_interval_msec, const bool low_power_mode) {
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ // Invoke the super method.
+ return GnssHalTestTemplate<IGnss_V2_1>::SetPositionMode(min_interval_msec, low_power_mode);
+ }
+
+ const int kPreferredAccuracy = 0; // Ideally perfect (matches GnssLocationProvider)
+ const int kPreferredTimeMsec = 0; // Ideally immediate
+
+ auto status = aidl_gnss_hal_->setPositionMode(
+ IGnss::GnssPositionMode::MS_BASED, IGnss::GnssPositionRecurrence::RECURRENCE_PERIODIC,
+ min_interval_msec, kPreferredAccuracy, kPreferredTimeMsec, low_power_mode);
+
+ ASSERT_TRUE(status.isOk());
+}
+
+bool GnssHalTest::StartAndCheckFirstLocation(const int min_interval_msec,
+ const bool low_power_mode) {
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ // Invoke the super method.
+ return GnssHalTestTemplate<IGnss_V2_1>::StartAndCheckFirstLocation(min_interval_msec,
+ low_power_mode);
+ }
+
+ SetPositionMode(min_interval_msec, low_power_mode);
+ auto result = aidl_gnss_hal_->start();
+
+ EXPECT_TRUE(result.isOk());
+
+ /*
+ * GnssLocationProvider support of AGPS SUPL & XtraDownloader is not available in VTS,
+ * so allow time to demodulate ephemeris over the air.
+ */
+ const int kFirstGnssLocationTimeoutSeconds = 75;
+
+ EXPECT_TRUE(aidl_gnss_cb_->location_cbq_.retrieve(aidl_gnss_cb_->last_location_,
+ kFirstGnssLocationTimeoutSeconds));
+ int locationCalledCount = aidl_gnss_cb_->location_cbq_.calledCount();
+ EXPECT_EQ(locationCalledCount, 1);
+
+ if (locationCalledCount > 0) {
+ // don't require speed on first fix
+ CheckLocation(aidl_gnss_cb_->last_location_, false);
+ return true;
+ }
+ return false;
+}
+
+void GnssHalTest::StopAndClearLocations() {
+ ALOGD("StopAndClearLocations");
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ // Invoke the super method.
+ return GnssHalTestTemplate<IGnss_V2_1>::StopAndClearLocations();
+ }
+
+ auto status = aidl_gnss_hal_->stop();
+ EXPECT_TRUE(status.isOk());
+
+ /*
+ * Clear notify/waiting counter, allowing up till the timeout after
+ * the last reply for final startup messages to arrive (esp. system
+ * info.)
+ */
+ while (aidl_gnss_cb_->location_cbq_.retrieve(aidl_gnss_cb_->last_location_, TIMEOUT_SEC)) {
+ }
+ aidl_gnss_cb_->location_cbq_.reset();
+}
+
+void GnssHalTest::StartAndCheckLocations(int count) {
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ // Invoke the super method.
+ return GnssHalTestTemplate<IGnss_V2_1>::StartAndCheckLocations(count);
+ }
+ const int kMinIntervalMsec = 500;
+ const int kLocationTimeoutSubsequentSec = 2;
+ const bool kLowPowerMode = false;
+
+ EXPECT_TRUE(StartAndCheckFirstLocation(kMinIntervalMsec, kLowPowerMode));
+
+ for (int i = 1; i < count; i++) {
+ EXPECT_TRUE(aidl_gnss_cb_->location_cbq_.retrieve(aidl_gnss_cb_->last_location_,
+ kLocationTimeoutSubsequentSec));
+ int locationCalledCount = aidl_gnss_cb_->location_cbq_.calledCount();
+ EXPECT_EQ(locationCalledCount, i + 1);
+ // Don't cause confusion by checking details if no location yet
+ if (locationCalledCount > 0) {
+ // Should be more than 1 location by now, but if not, still don't check first fix speed
+ CheckLocation(aidl_gnss_cb_->last_location_, locationCalledCount > 1);
+ }
+ }
+}
+
+std::list<std::vector<IGnssCallback::GnssSvInfo>> GnssHalTest::convertToAidl(
+ const std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>>& sv_info_list) {
+ std::list<std::vector<IGnssCallback::GnssSvInfo>> aidl_sv_info_list;
+ for (const auto& sv_info_vec : sv_info_list) {
+ std::vector<IGnssCallback::GnssSvInfo> aidl_sv_info_vec;
+ for (const auto& sv_info : sv_info_vec) {
+ IGnssCallback::GnssSvInfo aidl_sv_info;
+ aidl_sv_info.svid = sv_info.v2_0.v1_0.svid;
+ aidl_sv_info.constellation =
+ static_cast<GnssConstellationType>(sv_info.v2_0.constellation);
+ aidl_sv_info.cN0Dbhz = sv_info.v2_0.v1_0.cN0Dbhz;
+ aidl_sv_info.basebandCN0DbHz = sv_info.basebandCN0DbHz;
+ aidl_sv_info.elevationDegrees = sv_info.v2_0.v1_0.elevationDegrees;
+ aidl_sv_info.azimuthDegrees = sv_info.v2_0.v1_0.azimuthDegrees;
+ aidl_sv_info.carrierFrequencyHz = (int64_t)sv_info.v2_0.v1_0.carrierFrequencyHz;
+ aidl_sv_info.svFlag = (int)sv_info.v2_0.v1_0.svFlag;
+ aidl_sv_info_vec.push_back(aidl_sv_info);
+ }
+ aidl_sv_info_list.push_back(aidl_sv_info_vec);
+ }
+ return aidl_sv_info_list;
+}
+
+/*
+ * FindStrongFrequentNonGpsSource:
+ *
+ * Search through a GnssSvStatus list for the strongest non-GPS satellite observed enough times
+ *
+ * returns the strongest source,
+ * or a source with constellation == UNKNOWN if none are found sufficient times
+ */
+BlocklistedSource GnssHalTest::FindStrongFrequentNonGpsSource(
+ const std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> sv_info_list,
+ const int min_observations) {
+ return FindStrongFrequentNonGpsSource(convertToAidl(sv_info_list), min_observations);
+}
+
+BlocklistedSource GnssHalTest::FindStrongFrequentNonGpsSource(
+ const std::list<std::vector<IGnssCallback::GnssSvInfo>> sv_info_list,
+ const int min_observations) {
+ std::map<ComparableBlocklistedSource, SignalCounts> mapSignals;
+
+ for (const auto& sv_info_vec : sv_info_list) {
+ for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
+ const auto& gnss_sv = sv_info_vec[iSv];
+ if ((gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
+ (gnss_sv.constellation != GnssConstellationType::GPS)) {
+ ComparableBlocklistedSource source;
+ source.id.svid = gnss_sv.svid;
+ source.id.constellation = gnss_sv.constellation;
+
+ const auto& itSignal = mapSignals.find(source);
+ if (itSignal == mapSignals.end()) {
+ SignalCounts counts;
+ counts.observations = 1;
+ counts.max_cn0_dbhz = gnss_sv.cN0Dbhz;
+ mapSignals.insert(
+ std::pair<ComparableBlocklistedSource, SignalCounts>(source, counts));
+ } else {
+ itSignal->second.observations++;
+ if (itSignal->second.max_cn0_dbhz < gnss_sv.cN0Dbhz) {
+ itSignal->second.max_cn0_dbhz = gnss_sv.cN0Dbhz;
+ }
+ }
+ }
+ }
+ }
+
+ float max_cn0_dbhz_with_sufficient_count = 0.;
+ int total_observation_count = 0;
+ int blocklisted_source_count_observation = 0;
+
+ ComparableBlocklistedSource source_to_blocklist; // initializes to zero = UNKNOWN constellation
+ for (auto const& pairSignal : mapSignals) {
+ total_observation_count += pairSignal.second.observations;
+ if ((pairSignal.second.observations >= min_observations) &&
+ (pairSignal.second.max_cn0_dbhz > max_cn0_dbhz_with_sufficient_count)) {
+ source_to_blocklist = pairSignal.first;
+ blocklisted_source_count_observation = pairSignal.second.observations;
+ max_cn0_dbhz_with_sufficient_count = pairSignal.second.max_cn0_dbhz;
+ }
+ }
+ ALOGD("Among %d observations, chose svid %d, constellation %d, "
+ "with %d observations at %.1f max CNo",
+ total_observation_count, source_to_blocklist.id.svid,
+ (int)source_to_blocklist.id.constellation, blocklisted_source_count_observation,
+ max_cn0_dbhz_with_sufficient_count);
+
+ return source_to_blocklist.id;
+}
+
+GnssConstellationType GnssHalTest::startLocationAndGetNonGpsConstellation(
+ const int locations_to_await, const int gnss_sv_info_list_timeout) {
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ return static_cast<GnssConstellationType>(
+ GnssHalTestTemplate<IGnss_V2_1>::startLocationAndGetNonGpsConstellation(
+ locations_to_await, gnss_sv_info_list_timeout));
+ }
+ aidl_gnss_cb_->location_cbq_.reset();
+ StartAndCheckLocations(locations_to_await);
+ const int location_called_count = aidl_gnss_cb_->location_cbq_.calledCount();
+
+ // Tolerate 1 less sv status to handle edge cases in reporting.
+ int sv_info_list_cbq_size = aidl_gnss_cb_->sv_info_list_cbq_.size();
+ EXPECT_GE(sv_info_list_cbq_size + 1, locations_to_await);
+ ALOGD("Observed %d GnssSvInfo, while awaiting %d Locations (%d received)",
+ sv_info_list_cbq_size, locations_to_await, location_called_count);
+
+ // Find first non-GPS constellation to blocklist
+ GnssConstellationType constellation_to_blocklist = GnssConstellationType::UNKNOWN;
+ for (int i = 0; i < sv_info_list_cbq_size; ++i) {
+ std::vector<IGnssCallback::GnssSvInfo> sv_info_vec;
+ aidl_gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, gnss_sv_info_list_timeout);
+ for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
+ auto& gnss_sv = sv_info_vec[iSv];
+ if ((gnss_sv.svFlag & (uint32_t)IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
+ (gnss_sv.constellation != GnssConstellationType::UNKNOWN) &&
+ (gnss_sv.constellation != GnssConstellationType::GPS)) {
+ // found a non-GPS constellation
+ constellation_to_blocklist = gnss_sv.constellation;
+ break;
+ }
+ }
+ if (constellation_to_blocklist != GnssConstellationType::UNKNOWN) {
+ break;
+ }
+ }
+
+ if (constellation_to_blocklist == GnssConstellationType::UNKNOWN) {
+ ALOGI("No non-GPS constellations found, constellation blocklist test less effective.");
+ // Proceed functionally to blocklist something.
+ constellation_to_blocklist = GnssConstellationType::GLONASS;
+ }
+
+ return constellation_to_blocklist;
}