Add GNSS satellite blacklist VTS 2.0 test

- Use IGnssCallback@2.0::gnssSvStatusCb_2_0() method to receive SV
  info.
- Run the test only if SATELLITE_BLACKLIST capability is supported.
- IRNSS is excluded from blacklisting as IGnssConfiguration@2.0 is
  not updated to support the blacklisting new constellation type.
- Modify the corresponding VTS 1.1 test to run only if the GNSS
  HAL version is 1.1.

Bug: 128028791
Test: atest VtsHalGnssV2_0Target
Change-Id: Idd44a5a1c9d54d7434da945ecbd1ee90007f5054
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp
index 381ac1d..f3b376e 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp
@@ -16,10 +16,16 @@
 
 #define LOG_TAG "GnssHalTest"
 
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
+
 #include <gnss_hal_test.h>
 #include <chrono>
 #include "Utils.h"
 
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
 using ::android::hardware::gnss::common::Utils;
 
 // Implementations for the main test class for GNSS HAL
@@ -149,6 +155,28 @@
     }
 }
 
+bool GnssHalTest::IsGnssHalVersion_1_1() const {
+    using ::android::hidl::manager::V1_2::IServiceManager;
+    sp<IServiceManager> manager = ::android::hardware::defaultServiceManager1_2();
+
+    bool hasGnssHalVersion_1_1 = false;
+    manager->listManifestByInterface(
+            "android.hardware.gnss@1.1::IGnss",
+            [&hasGnssHalVersion_1_1](const hidl_vec<hidl_string>& registered) {
+                ASSERT_EQ(1, registered.size());
+                hasGnssHalVersion_1_1 = true;
+            });
+
+    bool hasGnssHalVersion_2_0 = false;
+    manager->listManifestByInterface(
+            "android.hardware.gnss@2.0::IGnss",
+            [&hasGnssHalVersion_2_0](const hidl_vec<hidl_string>& registered) {
+                hasGnssHalVersion_2_0 = registered.size() != 0;
+            });
+
+    return hasGnssHalVersion_1_1 && !hasGnssHalVersion_2_0;
+}
+
 void GnssHalTest::notify() {
     std::unique_lock<std::mutex> lock(mtx_);
     notify_count_++;
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.h b/gnss/1.1/vts/functional/gnss_hal_test.h
index 64478b5..84a9f84 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.h
+++ b/gnss/1.1/vts/functional/gnss_hal_test.h
@@ -145,6 +145,12 @@
      */
     void SetPositionMode(const int min_interval_msec, const bool low_power_mode);
 
+    /*
+     * IsGnssHalVersion_1_1:
+     * returns  true if the GNSS HAL version is exactly 1.1.
+     */
+    bool IsGnssHalVersion_1_1() const;
+
     sp<IGnss> gnss_hal_;         // GNSS HAL to call into
     sp<IGnssCallback> gnss_cb_;  // Primary callback interface
 
diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
index 2d901f3..4288d0d 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
@@ -202,6 +202,11 @@
  * formerly strongest satellite
  */
 TEST_F(GnssHalTest, BlacklistIndividualSatellites) {
+    if (!IsGnssHalVersion_1_1()) {
+        ALOGI("Test BlacklistIndividualSatellites skipped. GNSS HAL version is greater than 1.1.");
+        return;
+    }
+
     const int kLocationsToAwait = 3;
     const int kRetriesToUnBlacklist = 10;
 
@@ -323,6 +328,11 @@
  * 4a & b) Clean up by turning off location, and send in empty blacklist.
  */
 TEST_F(GnssHalTest, BlacklistConstellation) {
+    if (!IsGnssHalVersion_1_1()) {
+        ALOGI("Test BlacklistConstellation skipped. GNSS HAL version is greater than 1.1.");
+        return;
+    }
+
     const int kLocationsToAwait = 3;
 
     StartAndCheckLocations(kLocationsToAwait);
diff --git a/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp b/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp
index a8e40ba..ae36c50 100644
--- a/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp
+++ b/gnss/2.0/vts/functional/VtsHalGnssV2_0TargetTest.cpp
@@ -23,7 +23,6 @@
     ::testing::AddGlobalTestEnvironment(GnssHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
     GnssHidlEnvironment::Instance()->init(&argc, argv);
-    // TODO (b/122463165): Expand coverage to include 1.1 and 1.0 VTS tests.
     int status = RUN_ALL_TESTS();
     ALOGI("Test result = %d", status);
     return status;
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp
index a9f858c..14ae43c 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp
@@ -152,7 +152,7 @@
       name_cbq_("name"),
       capabilities_cbq_("capabilities"),
       location_cbq_("location"),
-      sv_info_cbq_("sv_info") {}
+      sv_info_list_cbq_("sv_info") {}
 
 Return<void> GnssHalTest::GnssCallback::gnssSetSystemInfoCb(
         const IGnssCallback_1_0::GnssSystemInfo& info) {
@@ -204,7 +204,7 @@
 Return<void> GnssHalTest::GnssCallback::gnssSvStatusCb_2_0(
         const hidl_vec<IGnssCallback_2_0::GnssSvInfo>& svInfoList) {
     ALOGI("gnssSvStatusCb_2_0. Size = %d", (int)svInfoList.size());
-    sv_info_cbq_.store(svInfoList);
+    sv_info_list_cbq_.store(svInfoList);
     return Void();
 }
 
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h
index 05e37d3..90a7866 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.h
+++ b/gnss/2.0/vts/functional/gnss_hal_test.h
@@ -23,6 +23,7 @@
 
 #include <condition_variable>
 #include <deque>
+#include <list>
 #include <mutex>
 
 using android::hardware::hidl_vec;
@@ -85,6 +86,14 @@
          */
         bool retrieve(T& event, int timeout_seconds);
 
+        /*
+         * Removes parameter count number of callack events at the front of the queue, stores
+         * them in event_list parameter and returns the number of events retrieved. Waits up to
+         * timeout_seconds to retrieve each event. If timeout occurs, it returns the number of
+         * items retrieved which will be less than count.
+         */
+        int retrieve(list<T>& event_list, int count, int timeout_seconds);
+
         /* Returns the number of events pending to be retrieved from the callback event queue. */
         int size() const;
 
@@ -117,7 +126,7 @@
         CallbackQueue<android::hardware::hidl_string> name_cbq_;
         CallbackQueue<uint32_t> capabilities_cbq_;
         CallbackQueue<GnssLocation_2_0> location_cbq_;
-        CallbackQueue<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_cbq_;
+        CallbackQueue<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> sv_info_list_cbq_;
 
         GnssCallback();
         virtual ~GnssCallback() = default;
@@ -265,6 +274,19 @@
 }
 
 template <class T>
+int GnssHalTest::CallbackQueue<T>::retrieve(list<T>& event_list, int count, int timeout_seconds) {
+    for (int i = 0; i < count; ++i) {
+        T event;
+        if (!retrieve(event, timeout_seconds)) {
+            return i;
+        }
+        event_list.push_back(event);
+    }
+
+    return count;
+}
+
+template <class T>
 int GnssHalTest::CallbackQueue<T>::size() const {
     std::unique_lock<std::recursive_mutex> lock(mtx_);
     return events_.size();
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 155afd6..6e0887f 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -23,7 +23,10 @@
 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;
@@ -33,15 +36,12 @@
 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 IGnssBatching_V1_0 = android::hardware::gnss::V1_0::IGnssBatching;
-using IGnssBatching_V2_0 = android::hardware::gnss::V2_0::IGnssBatching;
 
 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::GnssConstellationType;
 using android::hardware::gnss::V2_0::IGnssCallback;
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
 
@@ -209,9 +209,9 @@
 
         // Verify ConstellationType is valid.
         ASSERT_TRUE(static_cast<uint8_t>(measurement.constellation) >=
-                            static_cast<uint8_t>(GnssConstellationType::UNKNOWN) &&
+                            static_cast<uint8_t>(GnssConstellationType_2_0::UNKNOWN) &&
                     static_cast<uint8_t>(measurement.constellation) <=
-                            static_cast<uint8_t>(GnssConstellationType::IRNSS));
+                            static_cast<uint8_t>(GnssConstellationType_2_0::IRNSS));
 
         // Verify State is valid.
         ASSERT_TRUE(
@@ -414,3 +414,356 @@
     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);
+}
\ No newline at end of file