blob: 6e0887fd9ecc191510fab0c397835bf497b7eee6 [file] [log] [blame]
/*
* Copyright (C) 2018 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.
*/
#define LOG_TAG "GnssHalTestCases"
#include <VtsHalHidlTargetTestBase.h>
#include <gnss_hal_test.h>
#include "Utils.h"
using android::hardware::hidl_string;
using android::hardware::hidl_vec;
using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
using IGnssConfiguration_2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
using IGnssConfiguration_1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
using IAGnssRil_2_0 = android::hardware::gnss::V2_0::IAGnssRil;
using IGnssMeasurement_2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
using IGnssMeasurement_1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
using IGnssMeasurement_1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
using IAGnssRil_2_0 = android::hardware::gnss::V2_0::IAGnssRil;
using IAGnssRil_1_0 = android::hardware::gnss::V1_0::IAGnssRil;
using IAGnss_2_0 = android::hardware::gnss::V2_0::IAGnss;
using IAGnss_1_0 = android::hardware::gnss::V1_0::IAGnss;
using IAGnssCallback_2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
using android::hardware::gnss::common::Utils;
using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using android::hardware::gnss::V1_0::IGnssNi;
using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
using android::hardware::gnss::V2_0::IGnssCallback;
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
/*
* SetupTeardownCreateCleanup:
* Requests the gnss HAL then calls cleanup
*
* Empty test fixture to verify basic Setup & Teardown
*/
TEST_F(GnssHalTest, SetupTeardownCreateCleanup) {}
/*
* TestGnssMeasurementExtension:
* Gets the GnssMeasurementExtension and verifies that it returns an actual extension.
*/
TEST_F(GnssHalTest, TestGnssMeasurementExtension) {
auto gnssMeasurement_2_0 = gnss_hal_->getExtensionGnssMeasurement_2_0();
auto gnssMeasurement_1_1 = gnss_hal_->getExtensionGnssMeasurement_1_1();
auto gnssMeasurement_1_0 = gnss_hal_->getExtensionGnssMeasurement();
ASSERT_TRUE(gnssMeasurement_2_0.isOk() && gnssMeasurement_1_1.isOk() &&
gnssMeasurement_1_0.isOk());
sp<IGnssMeasurement_2_0> iGnssMeas_2_0 = gnssMeasurement_2_0;
sp<IGnssMeasurement_1_1> iGnssMeas_1_1 = gnssMeasurement_1_1;
sp<IGnssMeasurement_1_0> iGnssMeas_1_0 = gnssMeasurement_1_0;
// At least one interface is non-null.
int numNonNull = (int)(iGnssMeas_2_0 != nullptr) + (int)(iGnssMeas_1_1 != nullptr) +
(int)(iGnssMeas_1_0 != nullptr);
ASSERT_TRUE(numNonNull >= 1);
}
/*
* TestGnssConfigurationExtension:
* Gets the GnssConfigurationExtension and verifies that it returns an actual extension by
* calling a method.
*
* The GNSS HAL 2.0 implementation must support @2.0::IGnssConfiguration interface due to
* the deprecation of some methods in @1.0::IGnssConfiguration interface.
*/
TEST_F(GnssHalTest, TestGnssConfigurationExtension) {
auto gnssConfiguration = gnss_hal_->getExtensionGnssConfiguration_2_0();
ASSERT_TRUE(gnssConfiguration.isOk());
sp<IGnssConfiguration_2_0> iGnssConfiguration = gnssConfiguration;
ASSERT_NE(iGnssConfiguration, nullptr);
auto result = iGnssConfiguration->setEsExtensionSec(180);
ASSERT_TRUE(result.isOk());
// Expected result can be true or false depending on whether HAL implementation supports
// detecting emergency sessions without involving the framework.
}
/*
* TestGnssConfiguration_setSuplEs_Deprecation:
* Calls setSuplEs and verifies that it returns false.
*/
TEST_F(GnssHalTest, TestGnssConfiguration_setSuplEs_Deprecation) {
auto gnssConfiguration = gnss_hal_->getExtensionGnssConfiguration_2_0();
ASSERT_TRUE(gnssConfiguration.isOk());
sp<IGnssConfiguration_2_0> iGnssConfiguration = gnssConfiguration;
ASSERT_NE(iGnssConfiguration, nullptr);
auto result = iGnssConfiguration->setSuplEs(false);
ASSERT_TRUE(result.isOk());
EXPECT_FALSE(result);
}
/*
* TestGnssConfiguration_setGpsLock_Deprecation:
* Calls setGpsLock and verifies that it returns false.
*/
TEST_F(GnssHalTest, TestGnssConfiguration_setGpsLock_Deprecation) {
auto gnssConfiguration = gnss_hal_->getExtensionGnssConfiguration_2_0();
ASSERT_TRUE(gnssConfiguration.isOk());
sp<IGnssConfiguration_2_0> iGnssConfiguration = gnssConfiguration;
ASSERT_NE(iGnssConfiguration, nullptr);
auto result = iGnssConfiguration->setGpsLock(0);
ASSERT_TRUE(result.isOk());
EXPECT_FALSE(result);
}
/*
* TestAGnssRilExtension:
* Gets the AGnssRilExtension and verifies that it returns an actual extension.
*
* If IAGnssRil interface is supported, then the GNSS HAL 2.0 implementation must support
* @2.0::IAGnssRil interface due to the deprecation of framework network API methods needed
* to support the @1.0::IAGnssRil interface.
*/
TEST_F(GnssHalTest, TestAGnssRilExtension) {
auto agnssRil_2_0 = gnss_hal_->getExtensionAGnssRil_2_0();
ASSERT_TRUE(agnssRil_2_0.isOk());
sp<IAGnssRil_2_0> iAGnssRil_2_0 = agnssRil_2_0;
if (iAGnssRil_2_0 == nullptr) {
// Verify IAGnssRil 1.0 is not supported.
auto agnssRil_1_0 = gnss_hal_->getExtensionAGnssRil();
ASSERT_TRUE(agnssRil_1_0.isOk());
sp<IAGnssRil_1_0> iAGnssRil_1_0 = agnssRil_1_0;
ASSERT_EQ(iAGnssRil_1_0, nullptr);
}
}
/*
* TestAGnssRil_UpdateNetworkState_2_0:
* 1. Updates GNSS HAL that a network has connected.
* 2. Updates GNSS HAL that network has disconnected.
*/
TEST_F(GnssHalTest, TestAGnssRil_UpdateNetworkState_2_0) {
auto agnssRil = gnss_hal_->getExtensionAGnssRil_2_0();
ASSERT_TRUE(agnssRil.isOk());
sp<IAGnssRil_2_0> iAGnssRil = agnssRil;
if (iAGnssRil == nullptr) {
return;
}
// Update GNSS HAL that a network has connected.
IAGnssRil_2_0::NetworkAttributes networkAttributes = {
.networkHandle = static_cast<uint64_t>(7700664333),
.isConnected = true,
.capabilities = static_cast<uint16_t>(IAGnssRil_2_0::NetworkCapability::NOT_ROAMING),
.apn = "dummy-apn"};
auto result = iAGnssRil->updateNetworkState_2_0(networkAttributes);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
// Update GNSS HAL that network has disconnected.
networkAttributes.isConnected = false;
result = iAGnssRil->updateNetworkState_2_0(networkAttributes);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
}
/*
* TestGnssMeasurementFields:
* Sets a GnssMeasurementCallback, waits for a measurement, and verifies
* 1. codeType is valid,
* 2. constellation is valid.
* 3. state is valid.
*/
TEST_F(GnssHalTest, TestGnssMeasurementFields) {
const int kFirstGnssMeasurementTimeoutSeconds = 10;
auto gnssMeasurement = gnss_hal_->getExtensionGnssMeasurement_2_0();
if (!gnssMeasurement.isOk()) {
return;
}
sp<IGnssMeasurement_2_0> iGnssMeasurement = gnssMeasurement;
if (iGnssMeasurement == nullptr) {
return;
}
sp<GnssMeasurementCallback> callback = new GnssMeasurementCallback();
auto result = iGnssMeasurement->setCallback_2_0(callback, /* enableFullTracking= */ true);
ASSERT_TRUE(result.isOk());
EXPECT_EQ(result, IGnssMeasurement_1_0::GnssMeasurementStatus::SUCCESS);
IGnssMeasurementCallback_2_0::GnssData lastMeasurement;
ASSERT_TRUE(callback->measurement_cbq_.retrieve(lastMeasurement,
kFirstGnssMeasurementTimeoutSeconds));
EXPECT_EQ(callback->measurement_cbq_.calledCount(), 1);
ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
for (auto measurement : lastMeasurement.measurements) {
// Verify CodeType is valid.
ASSERT_NE(measurement.codeType, "");
// Verify ConstellationType is valid.
ASSERT_TRUE(static_cast<uint8_t>(measurement.constellation) >=
static_cast<uint8_t>(GnssConstellationType_2_0::UNKNOWN) &&
static_cast<uint8_t>(measurement.constellation) <=
static_cast<uint8_t>(GnssConstellationType_2_0::IRNSS));
// Verify State is valid.
ASSERT_TRUE(
static_cast<uint32_t>(measurement.state) >=
static_cast<uint32_t>(IGnssMeasurementCallback_2_0::GnssMeasurementState::
STATE_UNKNOWN) &&
static_cast<uint32_t>(measurement.state) <=
static_cast<uint32_t>(IGnssMeasurementCallback_2_0::GnssMeasurementState::
STATE_2ND_CODE_LOCK));
}
iGnssMeasurement->close();
}
/*
* TestAGnssExtension:
* Gets the AGnssExtension and verifies that it returns an actual extension.
*
* If IAGnss interface is supported, then the GNSS HAL 2.0 implementation must support
* @2.0::IAGnss interface due to the deprecation of framework network API methods needed
* to support the @1.0::IAGnss interface.
*/
TEST_F(GnssHalTest, TestAGnssExtension) {
auto agnss_2_0 = gnss_hal_->getExtensionAGnss_2_0();
ASSERT_TRUE(agnss_2_0.isOk());
sp<IAGnss_2_0> iAGnss_2_0 = agnss_2_0;
if (iAGnss_2_0 == nullptr) {
// Verify IAGnss 1.0 is not supported.
auto agnss_1_0 = gnss_hal_->getExtensionAGnss();
ASSERT_TRUE(agnss_1_0.isOk());
sp<IAGnss_1_0> iAGnss_1_0 = agnss_1_0;
ASSERT_EQ(iAGnss_1_0, nullptr);
return;
}
// Set SUPL server host/port
auto result =
iAGnss_2_0->setServer(IAGnssCallback_2_0::AGnssType::SUPL, "supl.google.com", 7275);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
}
/*
* TestGnssNiExtension_Deprecation:
* Gets the @1.0::IGnssNi extension and verifies that it is a nullptr.
*/
TEST_F(GnssHalTest, TestGnssNiExtension_Deprecation) {
// Verify IGnssNi 1.0 is not supported.
auto gnssNi = gnss_hal_->getExtensionGnssNi();
ASSERT_TRUE(!gnssNi.isOk() || ((sp<IGnssNi>)gnssNi) == nullptr);
}
/*
* TestGnssVisibilityControlExtension:
* Gets the GnssVisibilityControlExtension and if it is not null, verifies that it supports
* the gnss.visibility_control@1.0::IGnssVisibilityControl interface by invoking a method.
*/
TEST_F(GnssHalTest, TestGnssVisibilityControlExtension) {
auto gnssVisibilityControl = gnss_hal_->getExtensionVisibilityControl();
ASSERT_TRUE(gnssVisibilityControl.isOk());
sp<IGnssVisibilityControl> iGnssVisibilityControl = gnssVisibilityControl;
if (iGnssVisibilityControl == nullptr) {
return;
}
// Set non-framework proxy apps.
hidl_vec<hidl_string> proxyApps{"com.example.ims", "com.example.mdt"};
auto result = iGnssVisibilityControl->enableNfwLocationAccess(proxyApps);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
}
/*
* TestGnssMeasurementCorrectionsCapabilities:
* If measurement corrections capability is supported, verifies that the measurement corrections
* capabilities are reported and the mandatory LOS_SATS or the EXCESS_PATH_LENGTH
* capability flag is set.
*/
TEST_F(GnssHalTest, TestGnssMeasurementCorrectionsCapabilities) {
if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) {
return;
}
auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections();
ASSERT_TRUE(measurementCorrections.isOk());
sp<IMeasurementCorrections> iMeasurementCorrections = measurementCorrections;
ASSERT_NE(iMeasurementCorrections, nullptr);
// Setup measurement corrections callback.
sp<GnssMeasurementCorrectionsCallback> callback = new GnssMeasurementCorrectionsCallback();
iMeasurementCorrections->setCallback(callback);
const int kMeasurementCorrectionsCapabilitiesTimeoutSeconds = 5;
callback->capabilities_cbq_.retrieve(callback->last_capabilities_,
kMeasurementCorrectionsCapabilitiesTimeoutSeconds);
ASSERT_TRUE(callback->capabilities_cbq_.calledCount() > 0);
using Capabilities = IMeasurementCorrectionsCallback::Capabilities;
ASSERT_TRUE((callback->last_capabilities_ &
(Capabilities::LOS_SATS | Capabilities::EXCESS_PATH_LENGTH)) != 0);
}
/*
* TestGnssMeasurementCorrections:
* If measurement corrections capability is supported, verifies that it supports the
* gnss.measurement_corrections@1.0::IMeasurementCorrections interface by invoking a method.
*/
TEST_F(GnssHalTest, TestGnssMeasurementCorrections) {
if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) {
return;
}
// Verify IMeasurementCorrections is supported.
auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections();
ASSERT_TRUE(measurementCorrections.isOk());
sp<IMeasurementCorrections> iMeasurementCorrections = measurementCorrections;
ASSERT_NE(iMeasurementCorrections, nullptr);
sp<GnssMeasurementCorrectionsCallback> callback = new GnssMeasurementCorrectionsCallback();
iMeasurementCorrections->setCallback(callback);
const int kMeasurementCorrectionsCapabilitiesTimeoutSeconds = 5;
callback->capabilities_cbq_.retrieve(callback->last_capabilities_,
kMeasurementCorrectionsCapabilitiesTimeoutSeconds);
ASSERT_TRUE(callback->capabilities_cbq_.calledCount() > 0);
// Set a mock MeasurementCorrections.
auto result = iMeasurementCorrections->setCorrections(Utils::getMockMeasurementCorrections());
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
}
/*
* TestGnssDataElapsedRealtimeFlags:
* Sets a GnssMeasurementCallback, waits for a GnssData object, and verifies the flags in member
* elapsedRealitme are valid.
*/
TEST_F(GnssHalTest, TestGnssDataElapsedRealtimeFlags) {
const int kFirstGnssMeasurementTimeoutSeconds = 10;
auto gnssMeasurement = gnss_hal_->getExtensionGnssMeasurement_2_0();
if (!gnssMeasurement.isOk()) {
return;
}
sp<IGnssMeasurement_2_0> iGnssMeasurement = gnssMeasurement;
if (iGnssMeasurement == nullptr) {
return;
}
sp<GnssMeasurementCallback> callback = new GnssMeasurementCallback();
auto result = iGnssMeasurement->setCallback_2_0(callback, /* enableFullTracking= */ true);
ASSERT_TRUE(result.isOk());
EXPECT_EQ(result, IGnssMeasurement_1_0::GnssMeasurementStatus::SUCCESS);
IGnssMeasurementCallback_2_0::GnssData lastMeasurement;
ASSERT_TRUE(callback->measurement_cbq_.retrieve(lastMeasurement,
kFirstGnssMeasurementTimeoutSeconds));
EXPECT_EQ(callback->measurement_cbq_.calledCount(), 1);
ASSERT_TRUE((int)lastMeasurement.elapsedRealtime.flags <=
(int)(ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS));
// We expect a non-zero timestamp when set.
if (lastMeasurement.elapsedRealtime.flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
ASSERT_TRUE(lastMeasurement.elapsedRealtime.timestampNs != 0);
}
iGnssMeasurement->close();
}
TEST_F(GnssHalTest, TestGnssLocationElapsedRealtime) {
StartAndCheckFirstLocation();
ASSERT_TRUE((int)gnss_cb_->last_location_.elapsedRealtime.flags <=
(int)(ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS));
// We expect a non-zero timestamp when set.
if (gnss_cb_->last_location_.elapsedRealtime.flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
ASSERT_TRUE(gnss_cb_->last_location_.elapsedRealtime.timestampNs != 0);
}
StopAndClearLocations();
}
// This test only verify that injectBestLocation_2_0 does not crash.
TEST_F(GnssHalTest, TestInjectBestLocation_2_0) {
StartAndCheckFirstLocation();
gnss_hal_->injectBestLocation_2_0(gnss_cb_->last_location_);
StopAndClearLocations();
}
/*
* TestGnssBatchingExtension:
* Gets the @2.0::IGnssBatching extension and verifies that it doesn't return an error. Support
* for this interface is optional.
*/
TEST_F(GnssHalTest, TestGnssBatchingExtension) {
auto gnssBatching_2_0 = gnss_hal_->getExtensionGnssBatching_2_0();
ASSERT_TRUE(gnssBatching_2_0.isOk());
}
/*
* MapConstellationType:
* Given a GnssConstellationType_2_0 type constellation, maps to its equivalent
* GnssConstellationType_1_0 type constellation. For constellations that do not have
* an equivalent value, maps to GnssConstellationType_1_0::UNKNOWN
*/
GnssConstellationType_1_0 MapConstellationType(GnssConstellationType_2_0 constellation) {
switch (constellation) {
case GnssConstellationType_2_0::GPS:
return GnssConstellationType_1_0::GPS;
case GnssConstellationType_2_0::SBAS:
return GnssConstellationType_1_0::SBAS;
case GnssConstellationType_2_0::GLONASS:
return GnssConstellationType_1_0::GLONASS;
case GnssConstellationType_2_0::QZSS:
return GnssConstellationType_1_0::QZSS;
case GnssConstellationType_2_0::BEIDOU:
return GnssConstellationType_1_0::BEIDOU;
case GnssConstellationType_2_0::GALILEO:
return GnssConstellationType_1_0::GALILEO;
default:
return GnssConstellationType_1_0::UNKNOWN;
}
}
/*
* 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
*/
IGnssConfiguration_1_1::BlacklistedSource FindStrongFrequentNonGpsSource(
const list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>>& sv_info_lists,
const int min_observations) {
struct ComparableBlacklistedSource {
IGnssConfiguration_1_1::BlacklistedSource id;
ComparableBlacklistedSource() {
id.constellation = GnssConstellationType_1_0::UNKNOWN;
id.svid = 0;
}
bool operator<(const ComparableBlacklistedSource& compare) const {
return ((id.svid < compare.id.svid) || ((id.svid == compare.id.svid) &&
(id.constellation < compare.id.constellation)));
}
};
struct SignalCounts {
int observations;
float max_cn0_dbhz;
};
std::map<ComparableBlacklistedSource, SignalCounts> mapSignals;
for (const auto& sv_info_list : sv_info_lists) {
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
if ((sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
(sv_info.constellation != GnssConstellationType_2_0::IRNSS) &&
(sv_info.constellation != GnssConstellationType_2_0::GPS)) {
ComparableBlacklistedSource source;
source.id.svid = sv_info.v1_0.svid;
source.id.constellation = MapConstellationType(sv_info.constellation);
const auto& itSignal = mapSignals.find(source);
if (itSignal == mapSignals.end()) {
SignalCounts counts;
counts.observations = 1;
counts.max_cn0_dbhz = sv_info.v1_0.cN0Dbhz;
mapSignals.insert(
std::pair<ComparableBlacklistedSource, SignalCounts>(source, counts));
} else {
itSignal->second.observations++;
if (itSignal->second.max_cn0_dbhz < sv_info.v1_0.cN0Dbhz) {
itSignal->second.max_cn0_dbhz = sv_info.v1_0.cN0Dbhz;
}
}
}
}
}
float max_cn0_dbhz_with_sufficient_count = 0.;
int total_observation_count = 0;
int blacklisted_source_count_observation = 0;
ComparableBlacklistedSource source_to_blacklist; // 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_blacklist = pairSignal.first;
blacklisted_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_blacklist.id.svid,
(int)source_to_blacklist.id.constellation, blacklisted_source_count_observation,
max_cn0_dbhz_with_sufficient_count);
return source_to_blacklist.id;
}
/*
* BlacklistIndividualSatellites:
*
* 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
* GnssStatus for common satellites (strongest and one other.)
* 2a & b) Turns off location, and blacklists common satellites.
* 3) Restart location, wait for 3 locations, ensuring they are valid, and checks corresponding
* GnssStatus does not use those satellites.
* 4a & b) Turns off location, and send in empty blacklist.
* 5a) Restart location, wait for 3 locations, ensuring they are valid, and checks corresponding
* GnssStatus does re-use at least the previously strongest satellite
* 5b) Retry a few times, in case GNSS search strategy takes a while to reacquire even the
* formerly strongest satellite
*/
TEST_F(GnssHalTest, BlacklistIndividualSatellites) {
if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) {
return;
}
const int kLocationsToAwait = 3;
const int kRetriesToUnBlacklist = 10;
gnss_cb_->location_cbq_.reset();
StartAndCheckLocations(kLocationsToAwait);
int location_called_count = gnss_cb_->location_cbq_.calledCount();
// Tolerate 1 less sv status to handle edge cases in reporting.
int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
/*
* Identify strongest SV seen at least kLocationsToAwait -1 times
* Why -1? To avoid test flakiness in case of (plausible) slight flakiness in strongest signal
* observability (one epoch RF null)
*/
const int kGnssSvStatusTimeout = 2;
list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_lists;
int count = gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_lists, sv_info_list_cbq_size,
kGnssSvStatusTimeout);
ASSERT_EQ(count, sv_info_list_cbq_size);
IGnssConfiguration_1_1::BlacklistedSource source_to_blacklist =
FindStrongFrequentNonGpsSource(sv_info_lists, kLocationsToAwait - 1);
if (source_to_blacklist.constellation == GnssConstellationType_1_0::UNKNOWN) {
// Cannot find a non-GPS satellite. Let the test pass.
return;
}
// Stop locations, blacklist the common SV
StopAndClearLocations();
auto gnss_configuration_hal_return = gnss_hal_->getExtensionGnssConfiguration_1_1();
ASSERT_TRUE(gnss_configuration_hal_return.isOk());
sp<IGnssConfiguration_1_1> gnss_configuration_hal = gnss_configuration_hal_return;
ASSERT_NE(gnss_configuration_hal, nullptr);
hidl_vec<IGnssConfiguration_1_1::BlacklistedSource> sources;
sources.resize(1);
sources[0] = source_to_blacklist;
auto result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
// retry and ensure satellite not used
gnss_cb_->sv_info_list_cbq_.reset();
gnss_cb_->location_cbq_.reset();
StartAndCheckLocations(kLocationsToAwait);
// early exit if test is being run with insufficient signal
location_called_count = gnss_cb_->location_cbq_.calledCount();
if (location_called_count == 0) {
ALOGE("0 Gnss locations received - ensure sufficient signal and retry");
}
ASSERT_TRUE(location_called_count > 0);
// Tolerate 1 less sv status to handle edge cases in reporting.
sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
for (int i = 0; i < sv_info_list_cbq_size; ++i) {
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
auto constellation = MapConstellationType(sv_info.constellation);
EXPECT_FALSE((sv_info.v1_0.svid == source_to_blacklist.svid) &&
(constellation == source_to_blacklist.constellation) &&
(sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
}
}
// clear blacklist and restart - this time updating the blacklist while location is still on
sources.resize(0);
result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
bool strongest_sv_is_reobserved = false;
// do several loops awaiting a few locations, allowing non-immediate reacquisition strategies
int unblacklist_loops_remaining = kRetriesToUnBlacklist;
while (!strongest_sv_is_reobserved && (unblacklist_loops_remaining-- > 0)) {
StopAndClearLocations();
gnss_cb_->sv_info_list_cbq_.reset();
gnss_cb_->location_cbq_.reset();
StartAndCheckLocations(kLocationsToAwait);
// early exit loop if test is being run with insufficient signal
location_called_count = gnss_cb_->location_cbq_.calledCount();
if (location_called_count == 0) {
ALOGE("0 Gnss locations received - ensure sufficient signal and retry");
}
ASSERT_TRUE(location_called_count > 0);
// Tolerate 1 less sv status to handle edge cases in reporting.
sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
ALOGD("Clear blacklist, observed %d GnssSvStatus, while awaiting %d Locations"
", tries remaining %d",
sv_info_list_cbq_size, kLocationsToAwait, unblacklist_loops_remaining);
for (int i = 0; i < sv_info_list_cbq_size; ++i) {
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
auto constellation = MapConstellationType(sv_info.constellation);
if ((sv_info.v1_0.svid == source_to_blacklist.svid) &&
(constellation == source_to_blacklist.constellation) &&
(sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX)) {
strongest_sv_is_reobserved = true;
break;
}
}
if (strongest_sv_is_reobserved) break;
}
}
EXPECT_TRUE(strongest_sv_is_reobserved);
StopAndClearLocations();
}
/*
* BlacklistConstellation:
*
* 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
* GnssStatus for any non-GPS constellations.
* 2a & b) Turns off location, and blacklist first non-GPS constellations.
* 3) Restart location, wait for 3 locations, ensuring they are valid, and checks corresponding
* GnssStatus does not use any constellation but GPS.
* 4a & b) Clean up by turning off location, and send in empty blacklist.
*/
TEST_F(GnssHalTest, BlacklistConstellation) {
if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) {
return;
}
const int kLocationsToAwait = 3;
gnss_cb_->location_cbq_.reset();
StartAndCheckLocations(kLocationsToAwait);
const int location_called_count = gnss_cb_->location_cbq_.calledCount();
// Tolerate 1 less sv status to handle edge cases in reporting.
int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
// Find first non-GPS constellation to blacklist. Exclude IRNSS in GnssConstellationType_2_0
// as blacklisting of this constellation is not supported in gnss@2.0.
const int kGnssSvStatusTimeout = 2;
GnssConstellationType_1_0 constellation_to_blacklist = GnssConstellationType_1_0::UNKNOWN;
for (int i = 0; i < sv_info_list_cbq_size; ++i) {
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
if ((sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
(sv_info.constellation != GnssConstellationType_2_0::UNKNOWN) &&
(sv_info.constellation != GnssConstellationType_2_0::IRNSS) &&
(sv_info.constellation != GnssConstellationType_2_0::GPS)) {
// found a non-GPS V1_0 constellation
constellation_to_blacklist = MapConstellationType(sv_info.constellation);
break;
}
}
if (constellation_to_blacklist != GnssConstellationType_1_0::UNKNOWN) {
break;
}
}
if (constellation_to_blacklist == GnssConstellationType_1_0::UNKNOWN) {
ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
// Proceed functionally to blacklist something.
constellation_to_blacklist = GnssConstellationType_1_0::GLONASS;
}
IGnssConfiguration_1_1::BlacklistedSource source_to_blacklist;
source_to_blacklist.constellation = constellation_to_blacklist;
source_to_blacklist.svid = 0; // documented wildcard for all satellites in this constellation
auto gnss_configuration_hal_return = gnss_hal_->getExtensionGnssConfiguration_1_1();
ASSERT_TRUE(gnss_configuration_hal_return.isOk());
sp<IGnssConfiguration_1_1> gnss_configuration_hal = gnss_configuration_hal_return;
ASSERT_NE(gnss_configuration_hal, nullptr);
hidl_vec<IGnssConfiguration_1_1::BlacklistedSource> sources;
sources.resize(1);
sources[0] = source_to_blacklist;
auto result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
// retry and ensure constellation not used
gnss_cb_->sv_info_list_cbq_.reset();
gnss_cb_->location_cbq_.reset();
StartAndCheckLocations(kLocationsToAwait);
// Tolerate 1 less sv status to handle edge cases in reporting.
sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", sv_info_list_cbq_size,
kLocationsToAwait);
for (int i = 0; i < sv_info_list_cbq_size; ++i) {
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
auto constellation = MapConstellationType(sv_info.constellation);
EXPECT_FALSE((constellation == source_to_blacklist.constellation) &&
(sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
}
}
// clean up
StopAndClearLocations();
sources.resize(0);
result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
}