1 /* 2 * Copyright (C) 2017 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 17 #define EGMOCK_VERBOSE 1 18 19 #include <VtsHalHidlTargetTestBase.h> 20 #include <android-base/logging.h> 21 #include <android-base/strings.h> 22 #include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h> 23 #include <android/hardware/broadcastradio/2.0/ITunerCallback.h> 24 #include <android/hardware/broadcastradio/2.0/ITunerSession.h> 25 #include <android/hardware/broadcastradio/2.0/types.h> 26 #include <broadcastradio-utils-2x/Utils.h> 27 #include <broadcastradio-vts-utils/call-barrier.h> 28 #include <broadcastradio-vts-utils/environment-utils.h> 29 #include <broadcastradio-vts-utils/mock-timeout.h> 30 #include <broadcastradio-vts-utils/pointer-utils.h> 31 #include <cutils/bitops.h> 32 #include <gmock/gmock.h> 33 34 #include <chrono> 35 #include <optional> 36 #include <regex> 37 38 namespace android { 39 namespace hardware { 40 namespace broadcastradio { 41 namespace V2_0 { 42 namespace vts { 43 44 using namespace std::chrono_literals; 45 46 using std::unordered_set; 47 using std::vector; 48 using testing::_; 49 using testing::AnyNumber; 50 using testing::ByMove; 51 using testing::DoAll; 52 using testing::Invoke; 53 using testing::SaveArg; 54 55 using broadcastradio::vts::BroadcastRadioHidlEnvironment; 56 using broadcastradio::vts::CallBarrier; 57 using broadcastradio::vts::clearAndWait; 58 using utils::make_identifier; 59 using utils::make_selector_amfm; 60 61 namespace timeout { 62 63 static constexpr auto tune = 30s; 64 static constexpr auto programListScan = 5min; 65 66 } // namespace timeout 67 68 static constexpr auto gTuneWorkaround = 200ms; 69 70 static const ConfigFlag gConfigFlagValues[] = { 71 ConfigFlag::FORCE_MONO, 72 ConfigFlag::FORCE_ANALOG, 73 ConfigFlag::FORCE_DIGITAL, 74 ConfigFlag::RDS_AF, 75 ConfigFlag::RDS_REG, 76 ConfigFlag::DAB_DAB_LINKING, 77 ConfigFlag::DAB_FM_LINKING, 78 ConfigFlag::DAB_DAB_SOFT_LINKING, 79 ConfigFlag::DAB_FM_SOFT_LINKING, 80 }; 81 82 class TunerCallbackMock : public ITunerCallback { 83 public: 84 TunerCallbackMock(); 85 86 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&)); 87 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged_, Return<void>(const ProgramInfo&)); 88 virtual Return<void> onCurrentProgramInfoChanged(const ProgramInfo& info); 89 Return<void> onProgramListUpdated(const ProgramListChunk& chunk); 90 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected)); 91 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters)); 92 93 MOCK_TIMEOUT_METHOD0(onProgramListReady, void()); 94 95 std::mutex mLock; 96 utils::ProgramInfoSet mProgramList; 97 }; 98 99 struct AnnouncementListenerMock : public IAnnouncementListener { 100 MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&)); 101 }; 102 103 static BroadcastRadioHidlEnvironment<IBroadcastRadio>* gEnv = nullptr; 104 105 class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase { 106 protected: 107 virtual void SetUp() override; 108 virtual void TearDown() override; 109 110 bool openSession(); 111 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config); 112 std::optional<utils::ProgramInfoSet> getProgramList(); 113 114 sp<IBroadcastRadio> mModule; 115 Properties mProperties; 116 sp<ITunerSession> mSession; 117 sp<TunerCallbackMock> mCallback = new TunerCallbackMock(); 118 }; 119 120 static void printSkipped(std::string msg) { 121 std::cout << "[ SKIPPED ] " << msg << std::endl; 122 } 123 124 MATCHER_P(InfoHasId, id, 125 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) { 126 auto ids = utils::getAllIds(arg.selector, utils::getType(id)); 127 return ids.end() != find(ids.begin(), ids.end(), id.value); 128 } 129 130 TunerCallbackMock::TunerCallbackMock() { 131 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged_, _).Times(AnyNumber()); 132 133 // we expect the antenna is connected through the whole test 134 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0); 135 } 136 137 Return<void> TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) { 138 for (auto&& id : info.selector) { 139 EXPECT_NE(IdentifierType::INVALID, utils::getType(id)); 140 } 141 142 auto logically = utils::getType(info.logicallyTunedTo); 143 /* This field is required for currently tuned program and should be INVALID 144 * for entries from the program list. 145 */ 146 EXPECT_TRUE( 147 logically == IdentifierType::AMFM_FREQUENCY || logically == IdentifierType::RDS_PI || 148 logically == IdentifierType::HD_STATION_ID_EXT || 149 logically == IdentifierType::DAB_SID_EXT || logically == IdentifierType::DRMO_SERVICE_ID || 150 logically == IdentifierType::SXM_SERVICE_ID || 151 (logically >= IdentifierType::VENDOR_START && logically <= IdentifierType::VENDOR_END) || 152 logically > IdentifierType::SXM_CHANNEL); 153 154 auto physically = utils::getType(info.physicallyTunedTo); 155 // ditto (see "logically" above) 156 EXPECT_TRUE( 157 physically == IdentifierType::AMFM_FREQUENCY || 158 physically == IdentifierType::DAB_ENSEMBLE || 159 physically == IdentifierType::DRMO_FREQUENCY || physically == IdentifierType::SXM_CHANNEL || 160 (physically >= IdentifierType::VENDOR_START && physically <= IdentifierType::VENDOR_END) || 161 physically > IdentifierType::SXM_CHANNEL); 162 163 if (logically == IdentifierType::AMFM_FREQUENCY) { 164 auto ps = utils::getMetadataString(info, MetadataKey::RDS_PS); 165 if (ps.has_value()) { 166 EXPECT_NE("", android::base::Trim(*ps)) 167 << "Don't use empty RDS_PS as an indicator of missing RSD PS data."; 168 } 169 } 170 171 return onCurrentProgramInfoChanged_(info); 172 } 173 174 Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) { 175 std::lock_guard<std::mutex> lk(mLock); 176 177 updateProgramList(mProgramList, chunk); 178 179 if (chunk.complete) onProgramListReady(); 180 181 return {}; 182 } 183 184 void BroadcastRadioHalTest::SetUp() { 185 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open"; 186 187 // lookup HIDL service (radio module) 188 mModule = getService<IBroadcastRadio>(gEnv->getServiceName<IBroadcastRadio>()); 189 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation"; 190 191 // get module properties 192 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; }); 193 ASSERT_TRUE(propResult.isOk()); 194 195 EXPECT_FALSE(mProperties.maker.empty()); 196 EXPECT_FALSE(mProperties.product.empty()); 197 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u); 198 } 199 200 void BroadcastRadioHalTest::TearDown() { 201 mSession.clear(); 202 mModule.clear(); 203 clearAndWait(mCallback, 1s); 204 } 205 206 bool BroadcastRadioHalTest::openSession() { 207 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open"; 208 209 Result halResult = Result::UNKNOWN_ERROR; 210 auto openCb = [&](Result result, const sp<ITunerSession>& session) { 211 halResult = result; 212 if (result != Result::OK) return; 213 mSession = session; 214 }; 215 auto hidlResult = mModule->openSession(mCallback, openCb); 216 217 EXPECT_TRUE(hidlResult.isOk()); 218 EXPECT_EQ(Result::OK, halResult); 219 EXPECT_NE(nullptr, mSession.get()); 220 221 return nullptr != mSession.get(); 222 } 223 224 bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) { 225 auto halResult = Result::UNKNOWN_ERROR; 226 auto cb = [&](Result result, AmFmRegionConfig configCb) { 227 halResult = result; 228 if (config) *config = configCb; 229 }; 230 231 auto hidlResult = mModule->getAmFmRegionConfig(full, cb); 232 EXPECT_TRUE(hidlResult.isOk()); 233 234 if (halResult == Result::NOT_SUPPORTED) return false; 235 236 EXPECT_EQ(Result::OK, halResult); 237 return halResult == Result::OK; 238 } 239 240 std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() { 241 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber()); 242 243 auto startResult = mSession->startProgramListUpdates({}); 244 if (startResult == Result::NOT_SUPPORTED) { 245 printSkipped("Program list not supported"); 246 return nullopt; 247 } 248 EXPECT_EQ(Result::OK, startResult); 249 if (startResult != Result::OK) return nullopt; 250 251 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan); 252 253 auto stopResult = mSession->stopProgramListUpdates(); 254 EXPECT_TRUE(stopResult.isOk()); 255 256 return mCallback->mProgramList; 257 } 258 259 /** 260 * Test session opening. 261 * 262 * Verifies that: 263 * - the method succeeds on a first and subsequent calls; 264 * - the method succeeds when called for the second time without 265 * closing previous session. 266 */ 267 TEST_F(BroadcastRadioHalTest, OpenSession) { 268 // simply open session for the first time 269 ASSERT_TRUE(openSession()); 270 271 // drop (without explicit close) and re-open the session 272 mSession.clear(); 273 ASSERT_TRUE(openSession()); 274 275 // open the second session (the first one should be forcibly closed) 276 auto secondSession = mSession; 277 mSession.clear(); 278 ASSERT_TRUE(openSession()); 279 } 280 281 static bool isValidAmFmFreq(uint64_t freq) { 282 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq); 283 return utils::isValid(id); 284 } 285 286 static void validateRange(const AmFmBandRange& range) { 287 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound)); 288 EXPECT_TRUE(isValidAmFmFreq(range.upperBound)); 289 EXPECT_LT(range.lowerBound, range.upperBound); 290 EXPECT_GT(range.spacing, 0u); 291 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing); 292 } 293 294 static bool supportsFM(const AmFmRegionConfig& config) { 295 for (auto&& range : config.ranges) { 296 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true; 297 } 298 return false; 299 } 300 301 /** 302 * Test fetching AM/FM regional configuration. 303 * 304 * Verifies that: 305 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware; 306 * - there is at least one AM/FM band configured; 307 * - FM Deemphasis and RDS are correctly configured for FM-capable radio; 308 * - all channel grids (frequency ranges and spacings) are valid; 309 * - seek spacing is a multiple of the manual spacing value. 310 */ 311 TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) { 312 AmFmRegionConfig config; 313 bool supported = getAmFmRegionConfig(false, &config); 314 if (!supported) { 315 printSkipped("AM/FM not supported"); 316 return; 317 } 318 319 EXPECT_GT(config.ranges.size(), 0u); 320 EXPECT_LE(popcountll(config.fmDeemphasis), 1); 321 EXPECT_LE(popcountll(config.fmRds), 1); 322 323 for (auto&& range : config.ranges) { 324 validateRange(range); 325 EXPECT_EQ(0u, range.scanSpacing % range.spacing); 326 EXPECT_GE(range.scanSpacing, range.spacing); 327 } 328 329 if (supportsFM(config)) { 330 EXPECT_EQ(popcountll(config.fmDeemphasis), 1); 331 } 332 } 333 334 /** 335 * Test fetching AM/FM regional capabilities. 336 * 337 * Verifies that: 338 * - AM/FM regional capabilities are either available or not supported at all by the hardware; 339 * - there is at least one AM/FM range supported; 340 * - there is at least one de-emphasis filter mode supported for FM-capable radio; 341 * - all channel grids (frequency ranges and spacings) are valid; 342 * - seek spacing is not set. 343 */ 344 TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) { 345 AmFmRegionConfig config; 346 bool supported = getAmFmRegionConfig(true, &config); 347 if (!supported) { 348 printSkipped("AM/FM not supported"); 349 return; 350 } 351 352 EXPECT_GT(config.ranges.size(), 0u); 353 354 for (auto&& range : config.ranges) { 355 validateRange(range); 356 EXPECT_EQ(0u, range.scanSpacing); 357 } 358 359 if (supportsFM(config)) { 360 EXPECT_GE(popcountll(config.fmDeemphasis), 1); 361 } 362 } 363 364 /** 365 * Test fetching DAB regional configuration. 366 * 367 * Verifies that: 368 * - DAB regional configuration is either set at startup or not supported at all by the hardware; 369 * - all channel labels match correct format; 370 * - all channel frequencies are in correct range. 371 */ 372 TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) { 373 Result halResult; 374 hidl_vec<DabTableEntry> config; 375 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) { 376 halResult = result; 377 config = configCb; 378 }; 379 auto hidlResult = mModule->getDabRegionConfig(cb); 380 ASSERT_TRUE(hidlResult.isOk()); 381 382 if (halResult == Result::NOT_SUPPORTED) { 383 printSkipped("DAB not supported"); 384 return; 385 } 386 ASSERT_EQ(Result::OK, halResult); 387 388 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$"); 389 // double-check correctness of the test 390 ASSERT_TRUE(std::regex_match("5A", re)); 391 ASSERT_FALSE(std::regex_match("5a", re)); 392 ASSERT_FALSE(std::regex_match("1234ABCD", re)); 393 ASSERT_TRUE(std::regex_match("CN 12D", re)); 394 ASSERT_FALSE(std::regex_match(" 5A", re)); 395 396 for (auto&& entry : config) { 397 EXPECT_TRUE(std::regex_match(std::string(entry.label), re)); 398 399 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency); 400 EXPECT_TRUE(utils::isValid(id)); 401 } 402 } 403 404 /** 405 * Test tuning with FM selector. 406 * 407 * Verifies that: 408 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED; 409 * - if it is supported, the method succeeds; 410 * - after a successful tune call, onCurrentProgramInfoChanged callback is 411 * invoked carrying a proper selector; 412 * - program changes exactly to what was requested. 413 */ 414 TEST_F(BroadcastRadioHalTest, FmTune) { 415 ASSERT_TRUE(openSession()); 416 417 uint64_t freq = 100100; // 100.1 FM 418 auto sel = make_selector_amfm(freq); 419 420 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged 421 * callback setting infoCb, because egmock cannot distinguish calls with different matchers 422 * (there is one here and one in callback constructor). 423 * 424 * This sleep workaround will fix default implementation, but the real HW tests will still be 425 * flaky. We probably need to implement egmock alternative based on actions. 426 */ 427 std::this_thread::sleep_for(gTuneWorkaround); 428 429 // try tuning 430 ProgramInfo infoCb = {}; 431 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, 432 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq))) 433 .Times(AnyNumber()) 434 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void())))); 435 auto result = mSession->tune(sel); 436 437 // expect a failure if it's not supported 438 if (!utils::isSupported(mProperties, sel)) { 439 EXPECT_EQ(Result::NOT_SUPPORTED, result); 440 return; 441 } 442 443 // expect a callback if it succeeds 444 EXPECT_EQ(Result::OK, result); 445 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune); 446 447 LOG(DEBUG) << "current program info: " << toString(infoCb); 448 449 // it should tune exactly to what was requested 450 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY); 451 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq)); 452 } 453 454 /** 455 * Test tuning with invalid selectors. 456 * 457 * Verifies that: 458 * - if the selector is not supported, it's ignored; 459 * - if it is supported, an invalid value results with INVALID_ARGUMENTS; 460 */ 461 TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) { 462 ASSERT_TRUE(openSession()); 463 464 vector<ProgramIdentifier> invalid = { 465 make_identifier(IdentifierType::AMFM_FREQUENCY, 0), 466 make_identifier(IdentifierType::RDS_PI, 0x10000), 467 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000), 468 make_identifier(IdentifierType::DAB_SID_EXT, 0), 469 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000), 470 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000), 471 }; 472 473 for (auto&& id : invalid) { 474 ProgramSelector sel{id, {}}; 475 476 auto result = mSession->tune(sel); 477 478 if (utils::isSupported(mProperties, sel)) { 479 EXPECT_EQ(Result::INVALID_ARGUMENTS, result); 480 } else { 481 EXPECT_EQ(Result::NOT_SUPPORTED, result); 482 } 483 } 484 } 485 486 /** 487 * Test tuning with empty program selector. 488 * 489 * Verifies that: 490 * - tune fails with NOT_SUPPORTED when program selector is not initialized. 491 */ 492 TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) { 493 ASSERT_TRUE(openSession()); 494 495 // Program type is 1-based, so 0 will always be invalid. 496 ProgramSelector sel = {}; 497 auto result = mSession->tune(sel); 498 ASSERT_EQ(Result::NOT_SUPPORTED, result); 499 } 500 501 /** 502 * Test seeking to next/prev station via ITunerSession::scan(). 503 * 504 * Verifies that: 505 * - the method succeeds; 506 * - the program info is changed within timeout::tune; 507 * - works both directions and with or without skipping sub-channel. 508 */ 509 TEST_F(BroadcastRadioHalTest, Seek) { 510 ASSERT_TRUE(openSession()); 511 512 // TODO(b/69958777): see FmTune workaround 513 std::this_thread::sleep_for(gTuneWorkaround); 514 515 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber()); 516 auto result = mSession->scan(true /* up */, true /* skip subchannel */); 517 EXPECT_EQ(Result::OK, result); 518 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune); 519 520 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber()); 521 result = mSession->scan(false /* down */, false /* don't skip subchannel */); 522 EXPECT_EQ(Result::OK, result); 523 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune); 524 } 525 526 /** 527 * Test step operation. 528 * 529 * Verifies that: 530 * - the method succeeds or returns NOT_SUPPORTED; 531 * - the program info is changed within timeout::tune if the method succeeded; 532 * - works both directions. 533 */ 534 TEST_F(BroadcastRadioHalTest, Step) { 535 ASSERT_TRUE(openSession()); 536 537 // TODO(b/69958777): see FmTune workaround 538 std::this_thread::sleep_for(gTuneWorkaround); 539 540 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber()); 541 auto result = mSession->step(true /* up */); 542 if (result == Result::NOT_SUPPORTED) { 543 printSkipped("step not supported"); 544 return; 545 } 546 EXPECT_EQ(Result::OK, result); 547 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune); 548 549 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber()); 550 result = mSession->step(false /* down */); 551 EXPECT_EQ(Result::OK, result); 552 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune); 553 } 554 555 /** 556 * Test tune cancellation. 557 * 558 * Verifies that: 559 * - the method does not crash after being invoked multiple times. 560 */ 561 TEST_F(BroadcastRadioHalTest, Cancel) { 562 ASSERT_TRUE(openSession()); 563 564 for (int i = 0; i < 10; i++) { 565 auto result = mSession->scan(true /* up */, true /* skip subchannel */); 566 ASSERT_EQ(Result::OK, result); 567 568 auto cancelResult = mSession->cancel(); 569 ASSERT_TRUE(cancelResult.isOk()); 570 } 571 } 572 573 /** 574 * Test IBroadcastRadio::get|setParameters() methods called with no parameters. 575 * 576 * Verifies that: 577 * - callback is called for empty parameters set. 578 */ 579 TEST_F(BroadcastRadioHalTest, NoParameters) { 580 ASSERT_TRUE(openSession()); 581 582 hidl_vec<VendorKeyValue> halResults = {}; 583 bool wasCalled = false; 584 auto cb = [&](hidl_vec<VendorKeyValue> results) { 585 wasCalled = true; 586 halResults = results; 587 }; 588 589 auto hidlResult = mSession->setParameters({}, cb); 590 ASSERT_TRUE(hidlResult.isOk()); 591 ASSERT_TRUE(wasCalled); 592 ASSERT_EQ(0u, halResults.size()); 593 594 wasCalled = false; 595 hidlResult = mSession->getParameters({}, cb); 596 ASSERT_TRUE(hidlResult.isOk()); 597 ASSERT_TRUE(wasCalled); 598 ASSERT_EQ(0u, halResults.size()); 599 } 600 601 /** 602 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters. 603 * 604 * Verifies that: 605 * - unknown parameters are ignored; 606 * - callback is called also for empty results set. 607 */ 608 TEST_F(BroadcastRadioHalTest, UnknownParameters) { 609 ASSERT_TRUE(openSession()); 610 611 hidl_vec<VendorKeyValue> halResults = {}; 612 bool wasCalled = false; 613 auto cb = [&](hidl_vec<VendorKeyValue> results) { 614 wasCalled = true; 615 halResults = results; 616 }; 617 618 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb); 619 ASSERT_TRUE(hidlResult.isOk()); 620 ASSERT_TRUE(wasCalled); 621 ASSERT_EQ(0u, halResults.size()); 622 623 wasCalled = false; 624 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb); 625 ASSERT_TRUE(hidlResult.isOk()); 626 ASSERT_TRUE(wasCalled); 627 ASSERT_EQ(0u, halResults.size()); 628 } 629 630 /** 631 * Test session closing. 632 * 633 * Verifies that: 634 * - the method does not crash after being invoked multiple times. 635 */ 636 TEST_F(BroadcastRadioHalTest, Close) { 637 ASSERT_TRUE(openSession()); 638 639 for (int i = 0; i < 10; i++) { 640 auto cancelResult = mSession->close(); 641 ASSERT_TRUE(cancelResult.isOk()); 642 } 643 } 644 645 /** 646 * Test geting image of invalid ID. 647 * 648 * Verifies that: 649 * - getImage call handles argument 0 gracefully. 650 */ 651 TEST_F(BroadcastRadioHalTest, GetNoImage) { 652 size_t len = 0; 653 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); }); 654 655 ASSERT_TRUE(result.isOk()); 656 ASSERT_EQ(0u, len); 657 } 658 659 /** 660 * Test getting config flags. 661 * 662 * Verifies that: 663 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE; 664 * - call success or failure is consistent with setConfigFlag. 665 */ 666 TEST_F(BroadcastRadioHalTest, FetchConfigFlags) { 667 ASSERT_TRUE(openSession()); 668 669 for (auto flag : gConfigFlagValues) { 670 auto halResult = Result::UNKNOWN_ERROR; 671 auto cb = [&](Result result, bool) { halResult = result; }; 672 auto hidlResult = mSession->isConfigFlagSet(flag, cb); 673 EXPECT_TRUE(hidlResult.isOk()); 674 675 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) { 676 ASSERT_EQ(Result::OK, halResult); 677 } 678 679 // set must fail or succeed the same way as get 680 auto setResult = mSession->setConfigFlag(flag, false); 681 EXPECT_EQ(halResult, setResult); 682 setResult = mSession->setConfigFlag(flag, true); 683 EXPECT_EQ(halResult, setResult); 684 } 685 } 686 687 /** 688 * Test setting config flags. 689 * 690 * Verifies that: 691 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE; 692 * - isConfigFlagSet reflects the state requested immediately after the set call. 693 */ 694 TEST_F(BroadcastRadioHalTest, SetConfigFlags) { 695 ASSERT_TRUE(openSession()); 696 697 auto get = [&](ConfigFlag flag) { 698 auto halResult = Result::UNKNOWN_ERROR; 699 bool gotValue = false; 700 auto cb = [&](Result result, bool value) { 701 halResult = result; 702 gotValue = value; 703 }; 704 auto hidlResult = mSession->isConfigFlagSet(flag, cb); 705 EXPECT_TRUE(hidlResult.isOk()); 706 EXPECT_EQ(Result::OK, halResult); 707 return gotValue; 708 }; 709 710 for (auto flag : gConfigFlagValues) { 711 auto result = mSession->setConfigFlag(flag, false); 712 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) { 713 // setting to true must result in the same error as false 714 auto secondResult = mSession->setConfigFlag(flag, true); 715 EXPECT_EQ(result, secondResult); 716 continue; 717 } 718 ASSERT_EQ(Result::OK, result); 719 720 // verify false is set 721 auto value = get(flag); 722 EXPECT_FALSE(value); 723 724 // try setting true this time 725 result = mSession->setConfigFlag(flag, true); 726 ASSERT_EQ(Result::OK, result); 727 value = get(flag); 728 EXPECT_TRUE(value); 729 730 // false again 731 result = mSession->setConfigFlag(flag, false); 732 ASSERT_EQ(Result::OK, result); 733 value = get(flag); 734 EXPECT_FALSE(value); 735 } 736 } 737 738 /** 739 * Test getting program list. 740 * 741 * Verifies that: 742 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED; 743 * - the complete list is fetched within timeout::programListScan; 744 * - stopProgramListUpdates does not crash. 745 */ 746 TEST_F(BroadcastRadioHalTest, GetProgramList) { 747 ASSERT_TRUE(openSession()); 748 749 getProgramList(); 750 } 751 752 /** 753 * Test HD_STATION_NAME correctness. 754 * 755 * Verifies that if a program on the list contains HD_STATION_NAME identifier: 756 * - the program provides station name in its metadata; 757 * - the identifier matches the name; 758 * - there is only one identifier of that type. 759 */ 760 TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) { 761 ASSERT_TRUE(openSession()); 762 763 auto list = getProgramList(); 764 if (!list) return; 765 766 for (auto&& program : *list) { 767 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME); 768 EXPECT_LE(nameIds.size(), 1u); 769 if (nameIds.size() == 0) continue; 770 771 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME); 772 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS); 773 ASSERT_TRUE(name.has_value()); 774 775 auto expectedId = utils::make_hdradio_station_name(*name); 776 EXPECT_EQ(expectedId.value, nameIds[0]); 777 } 778 } 779 780 /** 781 * Test announcement listener registration. 782 * 783 * Verifies that: 784 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED; 785 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise); 786 * - closing handle does not crash. 787 */ 788 TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) { 789 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock(); 790 791 Result halResult = Result::UNKNOWN_ERROR; 792 sp<ICloseHandle> closeHandle = nullptr; 793 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) { 794 halResult = result; 795 closeHandle = closeHandle_; 796 }; 797 798 auto hidlResult = 799 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb); 800 ASSERT_TRUE(hidlResult.isOk()); 801 802 if (halResult == Result::NOT_SUPPORTED) { 803 ASSERT_EQ(nullptr, closeHandle.get()); 804 printSkipped("Announcements not supported"); 805 return; 806 } 807 808 ASSERT_EQ(Result::OK, halResult); 809 ASSERT_NE(nullptr, closeHandle.get()); 810 811 closeHandle->close(); 812 } 813 814 } // namespace vts 815 } // namespace V2_0 816 } // namespace broadcastradio 817 } // namespace hardware 818 } // namespace android 819 820 int main(int argc, char** argv) { 821 using android::hardware::broadcastradio::V2_0::vts::gEnv; 822 using android::hardware::broadcastradio::V2_0::IBroadcastRadio; 823 using android::hardware::broadcastradio::vts::BroadcastRadioHidlEnvironment; 824 android::base::SetDefaultTag("BcRadio.vts"); 825 android::base::SetMinimumLogSeverity(android::base::VERBOSE); 826 gEnv = new BroadcastRadioHidlEnvironment<IBroadcastRadio>; 827 ::testing::AddGlobalTestEnvironment(gEnv); 828 ::testing::InitGoogleTest(&argc, argv); 829 gEnv->init(&argc, argv); 830 return RUN_ALL_TESTS(); 831 } 832