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