Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Yu-Han Yang | 1afbd5f | 2021-11-24 16:39:13 -0800 | [diff] [blame] | 17 | #define LOG_TAG "GnssHalTest" |
| 18 | |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 19 | #include "gnss_hal_test.h" |
| 20 | #include <hidl/ServiceManagement.h> |
Yu-Han Yang | 1afbd5f | 2021-11-24 16:39:13 -0800 | [diff] [blame] | 21 | #include "Utils.h" |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 22 | |
Yu-Han Yang | 1afbd5f | 2021-11-24 16:39:13 -0800 | [diff] [blame] | 23 | using android::hardware::gnss::GnssConstellationType; |
| 24 | using android::hardware::gnss::GnssLocation; |
| 25 | using android::hardware::gnss::IGnss; |
| 26 | using android::hardware::gnss::IGnssCallback; |
| 27 | using android::hardware::gnss::common::Utils; |
| 28 | using GnssConstellationTypeV2_0 = android::hardware::gnss::V2_0::GnssConstellationType; |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 29 | |
| 30 | void GnssHalTest::SetUp() { |
| 31 | // Get AIDL handle |
| 32 | aidl_gnss_hal_ = android::waitForDeclaredService<IGnssAidl>(String16(GetParam().c_str())); |
| 33 | ASSERT_NE(aidl_gnss_hal_, nullptr); |
Yu-Han Yang | 1afbd5f | 2021-11-24 16:39:13 -0800 | [diff] [blame] | 34 | ALOGD("AIDL Interface Version = %d", aidl_gnss_hal_->getInterfaceVersion()); |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 35 | |
Yu-Han Yang | 1afbd5f | 2021-11-24 16:39:13 -0800 | [diff] [blame] | 36 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 37 | const auto& hidlInstanceNames = android::hardware::getAllHalInstanceNames( |
| 38 | android::hardware::gnss::V2_1::IGnss::descriptor); |
| 39 | gnss_hal_ = IGnss_V2_1::getService(hidlInstanceNames[0]); |
| 40 | ASSERT_NE(gnss_hal_, nullptr); |
| 41 | } |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 42 | |
| 43 | SetUpGnssCallback(); |
| 44 | } |
| 45 | |
| 46 | void GnssHalTest::SetUpGnssCallback() { |
| 47 | aidl_gnss_cb_ = new GnssCallbackAidl(); |
| 48 | ASSERT_NE(aidl_gnss_cb_, nullptr); |
| 49 | |
| 50 | auto status = aidl_gnss_hal_->setCallback(aidl_gnss_cb_); |
| 51 | if (!status.isOk()) { |
| 52 | ALOGE("Failed to setCallback"); |
| 53 | } |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 54 | ASSERT_TRUE(status.isOk()); |
| 55 | |
| 56 | /* |
| 57 | * Capabilities callback should trigger. |
| 58 | */ |
| 59 | EXPECT_TRUE(aidl_gnss_cb_->capabilities_cbq_.retrieve(aidl_gnss_cb_->last_capabilities_, |
| 60 | TIMEOUT_SEC)); |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 61 | EXPECT_EQ(aidl_gnss_cb_->capabilities_cbq_.calledCount(), 1); |
| 62 | |
Yu-Han Yang | 1afbd5f | 2021-11-24 16:39:13 -0800 | [diff] [blame] | 63 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 64 | // Invoke the super method. |
| 65 | GnssHalTestTemplate<IGnss_V2_1>::SetUpGnssCallback(); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | void GnssHalTest::CheckLocation(const GnssLocation& location, bool check_speed) { |
| 70 | Utils::checkLocation(location, check_speed, /* check_more_accuracies= */ true); |
| 71 | } |
| 72 | |
| 73 | void GnssHalTest::SetPositionMode(const int min_interval_msec, const bool low_power_mode) { |
| 74 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 75 | // Invoke the super method. |
| 76 | return GnssHalTestTemplate<IGnss_V2_1>::SetPositionMode(min_interval_msec, low_power_mode); |
| 77 | } |
| 78 | |
| 79 | const int kPreferredAccuracy = 0; // Ideally perfect (matches GnssLocationProvider) |
| 80 | const int kPreferredTimeMsec = 0; // Ideally immediate |
| 81 | |
| 82 | auto status = aidl_gnss_hal_->setPositionMode( |
| 83 | IGnss::GnssPositionMode::MS_BASED, IGnss::GnssPositionRecurrence::RECURRENCE_PERIODIC, |
| 84 | min_interval_msec, kPreferredAccuracy, kPreferredTimeMsec, low_power_mode); |
| 85 | |
| 86 | ASSERT_TRUE(status.isOk()); |
| 87 | } |
| 88 | |
| 89 | bool GnssHalTest::StartAndCheckFirstLocation(const int min_interval_msec, |
| 90 | const bool low_power_mode) { |
| 91 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 92 | // Invoke the super method. |
| 93 | return GnssHalTestTemplate<IGnss_V2_1>::StartAndCheckFirstLocation(min_interval_msec, |
| 94 | low_power_mode); |
| 95 | } |
| 96 | |
| 97 | SetPositionMode(min_interval_msec, low_power_mode); |
| 98 | auto result = aidl_gnss_hal_->start(); |
| 99 | |
| 100 | EXPECT_TRUE(result.isOk()); |
| 101 | |
| 102 | /* |
| 103 | * GnssLocationProvider support of AGPS SUPL & XtraDownloader is not available in VTS, |
| 104 | * so allow time to demodulate ephemeris over the air. |
| 105 | */ |
| 106 | const int kFirstGnssLocationTimeoutSeconds = 75; |
| 107 | |
| 108 | EXPECT_TRUE(aidl_gnss_cb_->location_cbq_.retrieve(aidl_gnss_cb_->last_location_, |
| 109 | kFirstGnssLocationTimeoutSeconds)); |
| 110 | int locationCalledCount = aidl_gnss_cb_->location_cbq_.calledCount(); |
| 111 | EXPECT_EQ(locationCalledCount, 1); |
| 112 | |
| 113 | if (locationCalledCount > 0) { |
| 114 | // don't require speed on first fix |
| 115 | CheckLocation(aidl_gnss_cb_->last_location_, false); |
| 116 | return true; |
| 117 | } |
| 118 | return false; |
| 119 | } |
| 120 | |
| 121 | void GnssHalTest::StopAndClearLocations() { |
| 122 | ALOGD("StopAndClearLocations"); |
| 123 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 124 | // Invoke the super method. |
| 125 | return GnssHalTestTemplate<IGnss_V2_1>::StopAndClearLocations(); |
| 126 | } |
| 127 | |
| 128 | auto status = aidl_gnss_hal_->stop(); |
| 129 | EXPECT_TRUE(status.isOk()); |
| 130 | |
| 131 | /* |
| 132 | * Clear notify/waiting counter, allowing up till the timeout after |
| 133 | * the last reply for final startup messages to arrive (esp. system |
| 134 | * info.) |
| 135 | */ |
| 136 | while (aidl_gnss_cb_->location_cbq_.retrieve(aidl_gnss_cb_->last_location_, TIMEOUT_SEC)) { |
| 137 | } |
| 138 | aidl_gnss_cb_->location_cbq_.reset(); |
| 139 | } |
| 140 | |
| 141 | void GnssHalTest::StartAndCheckLocations(int count) { |
| 142 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 143 | // Invoke the super method. |
| 144 | return GnssHalTestTemplate<IGnss_V2_1>::StartAndCheckLocations(count); |
| 145 | } |
| 146 | const int kMinIntervalMsec = 500; |
| 147 | const int kLocationTimeoutSubsequentSec = 2; |
| 148 | const bool kLowPowerMode = false; |
| 149 | |
| 150 | EXPECT_TRUE(StartAndCheckFirstLocation(kMinIntervalMsec, kLowPowerMode)); |
| 151 | |
| 152 | for (int i = 1; i < count; i++) { |
| 153 | EXPECT_TRUE(aidl_gnss_cb_->location_cbq_.retrieve(aidl_gnss_cb_->last_location_, |
| 154 | kLocationTimeoutSubsequentSec)); |
| 155 | int locationCalledCount = aidl_gnss_cb_->location_cbq_.calledCount(); |
| 156 | EXPECT_EQ(locationCalledCount, i + 1); |
| 157 | // Don't cause confusion by checking details if no location yet |
| 158 | if (locationCalledCount > 0) { |
| 159 | // Should be more than 1 location by now, but if not, still don't check first fix speed |
| 160 | CheckLocation(aidl_gnss_cb_->last_location_, locationCalledCount > 1); |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | std::list<std::vector<IGnssCallback::GnssSvInfo>> GnssHalTest::convertToAidl( |
| 166 | const std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>>& sv_info_list) { |
| 167 | std::list<std::vector<IGnssCallback::GnssSvInfo>> aidl_sv_info_list; |
| 168 | for (const auto& sv_info_vec : sv_info_list) { |
| 169 | std::vector<IGnssCallback::GnssSvInfo> aidl_sv_info_vec; |
| 170 | for (const auto& sv_info : sv_info_vec) { |
| 171 | IGnssCallback::GnssSvInfo aidl_sv_info; |
| 172 | aidl_sv_info.svid = sv_info.v2_0.v1_0.svid; |
| 173 | aidl_sv_info.constellation = |
| 174 | static_cast<GnssConstellationType>(sv_info.v2_0.constellation); |
| 175 | aidl_sv_info.cN0Dbhz = sv_info.v2_0.v1_0.cN0Dbhz; |
| 176 | aidl_sv_info.basebandCN0DbHz = sv_info.basebandCN0DbHz; |
| 177 | aidl_sv_info.elevationDegrees = sv_info.v2_0.v1_0.elevationDegrees; |
| 178 | aidl_sv_info.azimuthDegrees = sv_info.v2_0.v1_0.azimuthDegrees; |
| 179 | aidl_sv_info.carrierFrequencyHz = (int64_t)sv_info.v2_0.v1_0.carrierFrequencyHz; |
| 180 | aidl_sv_info.svFlag = (int)sv_info.v2_0.v1_0.svFlag; |
| 181 | aidl_sv_info_vec.push_back(aidl_sv_info); |
| 182 | } |
| 183 | aidl_sv_info_list.push_back(aidl_sv_info_vec); |
| 184 | } |
| 185 | return aidl_sv_info_list; |
| 186 | } |
| 187 | |
| 188 | /* |
| 189 | * FindStrongFrequentNonGpsSource: |
| 190 | * |
| 191 | * Search through a GnssSvStatus list for the strongest non-GPS satellite observed enough times |
| 192 | * |
| 193 | * returns the strongest source, |
| 194 | * or a source with constellation == UNKNOWN if none are found sufficient times |
| 195 | */ |
| 196 | BlocklistedSource GnssHalTest::FindStrongFrequentNonGpsSource( |
| 197 | const std::list<hidl_vec<IGnssCallback_2_1::GnssSvInfo>> sv_info_list, |
| 198 | const int min_observations) { |
| 199 | return FindStrongFrequentNonGpsSource(convertToAidl(sv_info_list), min_observations); |
| 200 | } |
| 201 | |
| 202 | BlocklistedSource GnssHalTest::FindStrongFrequentNonGpsSource( |
| 203 | const std::list<std::vector<IGnssCallback::GnssSvInfo>> sv_info_list, |
| 204 | const int min_observations) { |
| 205 | std::map<ComparableBlocklistedSource, SignalCounts> mapSignals; |
| 206 | |
| 207 | for (const auto& sv_info_vec : sv_info_list) { |
| 208 | for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) { |
| 209 | const auto& gnss_sv = sv_info_vec[iSv]; |
| 210 | if ((gnss_sv.svFlag & (int)IGnssCallback::GnssSvFlags::USED_IN_FIX) && |
| 211 | (gnss_sv.constellation != GnssConstellationType::GPS)) { |
| 212 | ComparableBlocklistedSource source; |
| 213 | source.id.svid = gnss_sv.svid; |
| 214 | source.id.constellation = gnss_sv.constellation; |
| 215 | |
| 216 | const auto& itSignal = mapSignals.find(source); |
| 217 | if (itSignal == mapSignals.end()) { |
| 218 | SignalCounts counts; |
| 219 | counts.observations = 1; |
| 220 | counts.max_cn0_dbhz = gnss_sv.cN0Dbhz; |
| 221 | mapSignals.insert( |
| 222 | std::pair<ComparableBlocklistedSource, SignalCounts>(source, counts)); |
| 223 | } else { |
| 224 | itSignal->second.observations++; |
| 225 | if (itSignal->second.max_cn0_dbhz < gnss_sv.cN0Dbhz) { |
| 226 | itSignal->second.max_cn0_dbhz = gnss_sv.cN0Dbhz; |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | float max_cn0_dbhz_with_sufficient_count = 0.; |
| 234 | int total_observation_count = 0; |
| 235 | int blocklisted_source_count_observation = 0; |
| 236 | |
| 237 | ComparableBlocklistedSource source_to_blocklist; // initializes to zero = UNKNOWN constellation |
| 238 | for (auto const& pairSignal : mapSignals) { |
| 239 | total_observation_count += pairSignal.second.observations; |
| 240 | if ((pairSignal.second.observations >= min_observations) && |
| 241 | (pairSignal.second.max_cn0_dbhz > max_cn0_dbhz_with_sufficient_count)) { |
| 242 | source_to_blocklist = pairSignal.first; |
| 243 | blocklisted_source_count_observation = pairSignal.second.observations; |
| 244 | max_cn0_dbhz_with_sufficient_count = pairSignal.second.max_cn0_dbhz; |
| 245 | } |
| 246 | } |
| 247 | ALOGD("Among %d observations, chose svid %d, constellation %d, " |
| 248 | "with %d observations at %.1f max CNo", |
| 249 | total_observation_count, source_to_blocklist.id.svid, |
| 250 | (int)source_to_blocklist.id.constellation, blocklisted_source_count_observation, |
| 251 | max_cn0_dbhz_with_sufficient_count); |
| 252 | |
| 253 | return source_to_blocklist.id; |
| 254 | } |
| 255 | |
| 256 | GnssConstellationType GnssHalTest::startLocationAndGetNonGpsConstellation( |
| 257 | const int locations_to_await, const int gnss_sv_info_list_timeout) { |
| 258 | if (aidl_gnss_hal_->getInterfaceVersion() == 1) { |
| 259 | return static_cast<GnssConstellationType>( |
| 260 | GnssHalTestTemplate<IGnss_V2_1>::startLocationAndGetNonGpsConstellation( |
| 261 | locations_to_await, gnss_sv_info_list_timeout)); |
| 262 | } |
| 263 | aidl_gnss_cb_->location_cbq_.reset(); |
| 264 | StartAndCheckLocations(locations_to_await); |
| 265 | const int location_called_count = aidl_gnss_cb_->location_cbq_.calledCount(); |
| 266 | |
| 267 | // Tolerate 1 less sv status to handle edge cases in reporting. |
| 268 | int sv_info_list_cbq_size = aidl_gnss_cb_->sv_info_list_cbq_.size(); |
| 269 | EXPECT_GE(sv_info_list_cbq_size + 1, locations_to_await); |
| 270 | ALOGD("Observed %d GnssSvInfo, while awaiting %d Locations (%d received)", |
| 271 | sv_info_list_cbq_size, locations_to_await, location_called_count); |
| 272 | |
| 273 | // Find first non-GPS constellation to blocklist |
| 274 | GnssConstellationType constellation_to_blocklist = GnssConstellationType::UNKNOWN; |
| 275 | for (int i = 0; i < sv_info_list_cbq_size; ++i) { |
| 276 | std::vector<IGnssCallback::GnssSvInfo> sv_info_vec; |
| 277 | aidl_gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, gnss_sv_info_list_timeout); |
| 278 | for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) { |
| 279 | auto& gnss_sv = sv_info_vec[iSv]; |
| 280 | if ((gnss_sv.svFlag & (uint32_t)IGnssCallback::GnssSvFlags::USED_IN_FIX) && |
| 281 | (gnss_sv.constellation != GnssConstellationType::UNKNOWN) && |
| 282 | (gnss_sv.constellation != GnssConstellationType::GPS)) { |
| 283 | // found a non-GPS constellation |
| 284 | constellation_to_blocklist = gnss_sv.constellation; |
| 285 | break; |
| 286 | } |
| 287 | } |
| 288 | if (constellation_to_blocklist != GnssConstellationType::UNKNOWN) { |
| 289 | break; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | if (constellation_to_blocklist == GnssConstellationType::UNKNOWN) { |
| 294 | ALOGI("No non-GPS constellations found, constellation blocklist test less effective."); |
| 295 | // Proceed functionally to blocklist something. |
| 296 | constellation_to_blocklist = GnssConstellationType::GLONASS; |
| 297 | } |
| 298 | |
| 299 | return constellation_to_blocklist; |
Yu-Han Yang | 1e1a676 | 2020-09-30 17:01:53 -0700 | [diff] [blame] | 300 | } |