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 "broadcastradio.vts" 18 19 #include <VtsHalHidlTargetTestBase.h> 20 #include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h> 21 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h> 22 #include <android/hardware/broadcastradio/1.1/ITuner.h> 23 #include <android/hardware/broadcastradio/1.1/ITunerCallback.h> 24 #include <android/hardware/broadcastradio/1.1/types.h> 25 #include <android-base/logging.h> 26 #include <broadcastradio-utils/Utils.h> 27 #include <broadcastradio-vts-utils/call-barrier.h> 28 #include <broadcastradio-vts-utils/mock-timeout.h> 29 #include <cutils/native_handle.h> 30 #include <cutils/properties.h> 31 #include <gmock/gmock.h> 32 #include <hidl/HidlTransportSupport.h> 33 #include <utils/threads.h> 34 35 #include <chrono> 36 37 namespace android { 38 namespace hardware { 39 namespace broadcastradio { 40 namespace V1_1 { 41 namespace vts { 42 43 using namespace std::chrono_literals; 44 45 using testing::_; 46 using testing::AnyNumber; 47 using testing::ByMove; 48 using testing::DoAll; 49 using testing::Invoke; 50 using testing::SaveArg; 51 52 using broadcastradio::vts::CallBarrier; 53 using V1_0::BandConfig; 54 using V1_0::Class; 55 using V1_0::MetaData; 56 using V1_0::MetadataKey; 57 using V1_0::MetadataType; 58 59 using std::chrono::steady_clock; 60 using std::this_thread::sleep_for; 61 62 static constexpr auto kConfigTimeout = 10s; 63 static constexpr auto kConnectModuleTimeout = 1s; 64 static constexpr auto kTuneTimeout = 30s; 65 static constexpr auto kEventPropagationTimeout = 1s; 66 static constexpr auto kFullScanTimeout = 1min; 67 68 static constexpr ProgramType kStandardProgramTypes[] = { 69 ProgramType::AM, ProgramType::FM, ProgramType::AM_HD, ProgramType::FM_HD, 70 ProgramType::DAB, ProgramType::DRMO, ProgramType::SXM}; 71 72 static void printSkipped(std::string msg) { 73 std::cout << "[ SKIPPED ] " << msg << std::endl; 74 } 75 76 struct TunerCallbackMock : public ITunerCallback { 77 TunerCallbackMock() { EXPECT_CALL(*this, hardwareFailure()).Times(0); } 78 79 MOCK_METHOD0(hardwareFailure, Return<void>()); 80 MOCK_TIMEOUT_METHOD2(configChange, Return<void>(Result, const BandConfig&)); 81 MOCK_METHOD2(tuneComplete, Return<void>(Result, const V1_0::ProgramInfo&)); 82 MOCK_TIMEOUT_METHOD2(tuneComplete_1_1, Return<void>(Result, const ProgramSelector&)); 83 MOCK_METHOD1(afSwitch, Return<void>(const V1_0::ProgramInfo&)); 84 MOCK_METHOD1(antennaStateChange, Return<void>(bool connected)); 85 MOCK_METHOD1(trafficAnnouncement, Return<void>(bool active)); 86 MOCK_METHOD1(emergencyAnnouncement, Return<void>(bool active)); 87 MOCK_METHOD3(newMetadata, Return<void>(uint32_t ch, uint32_t subCh, const hidl_vec<MetaData>&)); 88 MOCK_METHOD1(backgroundScanAvailable, Return<void>(bool)); 89 MOCK_TIMEOUT_METHOD1(backgroundScanComplete, Return<void>(ProgramListResult)); 90 MOCK_METHOD0(programListChanged, Return<void>()); 91 MOCK_TIMEOUT_METHOD1(currentProgramInfoChanged, Return<void>(const ProgramInfo&)); 92 }; 93 94 class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase, 95 public ::testing::WithParamInterface<Class> { 96 protected: 97 virtual void SetUp() override; 98 virtual void TearDown() override; 99 100 bool openTuner(); 101 bool nextBand(); 102 bool getProgramList(std::function<void(const hidl_vec<ProgramInfo>& list)> cb); 103 104 Class radioClass; 105 bool skipped = false; 106 107 sp<IBroadcastRadio> mRadioModule; 108 sp<ITuner> mTuner; 109 sp<TunerCallbackMock> mCallback = new TunerCallbackMock(); 110 111 private: 112 const BandConfig& getBand(unsigned idx); 113 114 unsigned currentBandIndex = 0; 115 hidl_vec<BandConfig> mBands; 116 }; 117 118 /** 119 * Clears strong pointer and waits until the object gets destroyed. 120 * 121 * @param ptr The pointer to get cleared. 122 * @param timeout Time to wait for other references. 123 */ 124 template <typename T> 125 static void clearAndWait(sp<T>& ptr, std::chrono::milliseconds timeout) { 126 wp<T> wptr = ptr; 127 ptr.clear(); 128 auto limit = steady_clock::now() + timeout; 129 while (wptr.promote() != nullptr) { 130 constexpr auto step = 10ms; 131 if (steady_clock::now() + step > limit) { 132 FAIL() << "Pointer was not released within timeout"; 133 break; 134 } 135 sleep_for(step); 136 } 137 } 138 139 void BroadcastRadioHalTest::SetUp() { 140 radioClass = GetParam(); 141 142 // lookup HIDL service 143 auto factory = getService<IBroadcastRadioFactory>(); 144 ASSERT_NE(nullptr, factory.get()); 145 146 // connect radio module 147 Result connectResult; 148 CallBarrier onConnect; 149 factory->connectModule(radioClass, [&](Result ret, const sp<V1_0::IBroadcastRadio>& radio) { 150 connectResult = ret; 151 if (ret == Result::OK) mRadioModule = IBroadcastRadio::castFrom(radio); 152 onConnect.call(); 153 }); 154 ASSERT_TRUE(onConnect.waitForCall(kConnectModuleTimeout)); 155 156 if (connectResult == Result::INVALID_ARGUMENTS) { 157 printSkipped("This device class is not supported."); 158 skipped = true; 159 return; 160 } 161 ASSERT_EQ(connectResult, Result::OK); 162 ASSERT_NE(nullptr, mRadioModule.get()); 163 164 // get module properties 165 Properties prop11; 166 auto& prop10 = prop11.base; 167 auto propResult = 168 mRadioModule->getProperties_1_1([&](const Properties& properties) { prop11 = properties; }); 169 170 ASSERT_TRUE(propResult.isOk()); 171 EXPECT_EQ(radioClass, prop10.classId); 172 EXPECT_GT(prop10.numTuners, 0u); 173 EXPECT_GT(prop11.supportedProgramTypes.size(), 0u); 174 EXPECT_GT(prop11.supportedIdentifierTypes.size(), 0u); 175 if (radioClass == Class::AM_FM) { 176 EXPECT_GT(prop10.bands.size(), 0u); 177 } 178 mBands = prop10.bands; 179 } 180 181 void BroadcastRadioHalTest::TearDown() { 182 mTuner.clear(); 183 mRadioModule.clear(); 184 clearAndWait(mCallback, 1s); 185 } 186 187 bool BroadcastRadioHalTest::openTuner() { 188 EXPECT_EQ(nullptr, mTuner.get()); 189 190 if (radioClass == Class::AM_FM) { 191 EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _); 192 } 193 194 Result halResult = Result::NOT_INITIALIZED; 195 auto openCb = [&](Result result, const sp<V1_0::ITuner>& tuner) { 196 halResult = result; 197 if (result != Result::OK) return; 198 mTuner = ITuner::castFrom(tuner); 199 }; 200 currentBandIndex = 0; 201 auto hidlResult = mRadioModule->openTuner(getBand(0), true, mCallback, openCb); 202 203 EXPECT_TRUE(hidlResult.isOk()); 204 EXPECT_EQ(Result::OK, halResult); 205 EXPECT_NE(nullptr, mTuner.get()); 206 if (radioClass == Class::AM_FM && mTuner != nullptr) { 207 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout); 208 209 BandConfig halConfig; 210 Result halResult = Result::NOT_INITIALIZED; 211 mTuner->getConfiguration([&](Result result, const BandConfig& config) { 212 halResult = result; 213 halConfig = config; 214 }); 215 EXPECT_EQ(Result::OK, halResult); 216 EXPECT_TRUE(halConfig.antennaConnected); 217 } 218 219 EXPECT_NE(nullptr, mTuner.get()); 220 return nullptr != mTuner.get(); 221 } 222 223 const BandConfig& BroadcastRadioHalTest::getBand(unsigned idx) { 224 static const BandConfig dummyBandConfig = {}; 225 226 if (radioClass != Class::AM_FM) { 227 ALOGD("Not AM/FM radio, returning dummy band config"); 228 return dummyBandConfig; 229 } 230 231 EXPECT_GT(mBands.size(), idx); 232 if (mBands.size() <= idx) { 233 ALOGD("Band index out of bound, returning dummy band config"); 234 return dummyBandConfig; 235 } 236 237 auto& band = mBands[idx]; 238 ALOGD("Returning %s band", toString(band.type).c_str()); 239 return band; 240 } 241 242 bool BroadcastRadioHalTest::nextBand() { 243 if (currentBandIndex + 1 >= mBands.size()) return false; 244 currentBandIndex++; 245 246 BandConfig bandCb; 247 EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _) 248 .WillOnce(DoAll(SaveArg<1>(&bandCb), testing::Return(ByMove(Void())))); 249 auto hidlResult = mTuner->setConfiguration(getBand(currentBandIndex)); 250 EXPECT_EQ(Result::OK, hidlResult); 251 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout); 252 EXPECT_EQ(getBand(currentBandIndex), bandCb); 253 254 return true; 255 } 256 257 bool BroadcastRadioHalTest::getProgramList( 258 std::function<void(const hidl_vec<ProgramInfo>& list)> cb) { 259 ProgramListResult getListResult = ProgramListResult::NOT_INITIALIZED; 260 bool isListEmpty = true; 261 auto getListCb = [&](ProgramListResult result, const hidl_vec<ProgramInfo>& list) { 262 ALOGD("getListCb(%s, ProgramInfo[%zu])", toString(result).c_str(), list.size()); 263 getListResult = result; 264 if (result != ProgramListResult::OK) return; 265 isListEmpty = (list.size() == 0); 266 if (!isListEmpty) cb(list); 267 }; 268 269 // first try... 270 EXPECT_TIMEOUT_CALL(*mCallback, backgroundScanComplete, ProgramListResult::OK) 271 .Times(AnyNumber()); 272 auto hidlResult = mTuner->getProgramList({}, getListCb); 273 EXPECT_TRUE(hidlResult.isOk()); 274 if (!hidlResult.isOk()) return false; 275 276 if (getListResult == ProgramListResult::NOT_STARTED) { 277 auto result = mTuner->startBackgroundScan(); 278 EXPECT_EQ(ProgramListResult::OK, result); 279 getListResult = ProgramListResult::NOT_READY; // continue as in NOT_READY case 280 } 281 if (getListResult == ProgramListResult::NOT_READY) { 282 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, backgroundScanComplete, kFullScanTimeout); 283 284 // second (last) try... 285 hidlResult = mTuner->getProgramList({}, getListCb); 286 EXPECT_TRUE(hidlResult.isOk()); 287 if (!hidlResult.isOk()) return false; 288 EXPECT_EQ(ProgramListResult::OK, getListResult); 289 } 290 291 return !isListEmpty; 292 } 293 294 /** 295 * Test IBroadcastRadio::openTuner() method called twice. 296 * 297 * Verifies that: 298 * - the openTuner method succeeds when called for the second time without 299 * deleting previous ITuner instance. 300 * 301 * This is a more strict requirement than in 1.0, where a second openTuner 302 * might fail. 303 */ 304 TEST_P(BroadcastRadioHalTest, OpenTunerTwice) { 305 if (skipped) return; 306 307 ASSERT_TRUE(openTuner()); 308 309 auto secondTuner = mTuner; 310 mTuner.clear(); 311 312 ASSERT_TRUE(openTuner()); 313 } 314 315 /** 316 * Test tuning to program list entry. 317 * 318 * Verifies that: 319 * - getProgramList either succeeds or returns NOT_STARTED/NOT_READY status; 320 * - if the program list is NOT_STARTED, startBackgroundScan makes it completed 321 * within a full scan timeout and the next getProgramList call succeeds; 322 * - if the program list is not empty, tuneByProgramSelector call succeeds; 323 * - getProgramInformation_1_1 returns the same selector as returned in tuneComplete_1_1 call. 324 */ 325 TEST_P(BroadcastRadioHalTest, TuneFromProgramList) { 326 if (skipped) return; 327 ASSERT_TRUE(openTuner()); 328 329 ProgramInfo firstProgram; 330 bool foundAny = false; 331 do { 332 auto getCb = [&](const hidl_vec<ProgramInfo>& list) { 333 // don't copy the whole list out, it might be heavy 334 firstProgram = list[0]; 335 }; 336 if (getProgramList(getCb)) foundAny = true; 337 } while (nextBand()); 338 if (HasFailure()) return; 339 if (!foundAny) { 340 printSkipped("Program list is empty."); 341 return; 342 } 343 344 ProgramInfo infoCb; 345 ProgramSelector selCb; 346 EXPECT_CALL(*mCallback, tuneComplete(_, _)).Times(0); 347 EXPECT_TIMEOUT_CALL(*mCallback, tuneComplete_1_1, Result::OK, _) 348 .WillOnce(DoAll(SaveArg<1>(&selCb), testing::Return(ByMove(Void())))); 349 EXPECT_TIMEOUT_CALL(*mCallback, currentProgramInfoChanged, _) 350 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void())))); 351 auto tuneResult = mTuner->tuneByProgramSelector(firstProgram.selector); 352 ASSERT_EQ(Result::OK, tuneResult); 353 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, tuneComplete_1_1, kTuneTimeout); 354 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, currentProgramInfoChanged, kEventPropagationTimeout); 355 EXPECT_EQ(firstProgram.selector.primaryId, selCb.primaryId); 356 EXPECT_EQ(infoCb.selector, selCb); 357 358 bool called = false; 359 auto getResult = mTuner->getProgramInformation_1_1([&](Result result, ProgramInfo info) { 360 called = true; 361 EXPECT_EQ(Result::OK, result); 362 EXPECT_EQ(selCb, info.selector); 363 }); 364 ASSERT_TRUE(getResult.isOk()); 365 ASSERT_TRUE(called); 366 } 367 368 /** 369 * Test that primary vendor identifier isn't used for standard program types. 370 * 371 * Verifies that: 372 * - tuneByProgramSelector fails when VENDORn_PRIMARY is set as a primary 373 * identifier for program types other than VENDORn. 374 */ 375 TEST_P(BroadcastRadioHalTest, TuneFailsForPrimaryVendor) { 376 if (skipped) return; 377 ASSERT_TRUE(openTuner()); 378 379 for (auto ptype : kStandardProgramTypes) { 380 ALOGD("Checking %s...", toString(ptype).c_str()); 381 ProgramSelector sel = {}; 382 sel.programType = static_cast<uint32_t>(ptype); 383 sel.primaryId.type = static_cast<uint32_t>(IdentifierType::VENDOR_PRIMARY_START); 384 385 auto tuneResult = mTuner->tuneByProgramSelector(sel); 386 ASSERT_NE(Result::OK, tuneResult); 387 } 388 } 389 390 /** 391 * Test that tune with unknown program type fails. 392 * 393 * Verifies that: 394 * - tuneByProgramSelector fails with INVALID_ARGUMENT when unknown program type is passed. 395 */ 396 TEST_P(BroadcastRadioHalTest, TuneFailsForUnknownProgram) { 397 if (skipped) return; 398 ASSERT_TRUE(openTuner()); 399 400 // Program type is 1-based, so 0 will be always invalid. 401 ProgramSelector sel = {}; 402 auto tuneResult = mTuner->tuneByProgramSelector(sel); 403 ASSERT_EQ(Result::INVALID_ARGUMENTS, tuneResult); 404 } 405 406 /** 407 * Test cancelling announcement. 408 * 409 * Verifies that: 410 * - cancelAnnouncement succeeds either when there is an announcement or there is none. 411 */ 412 TEST_P(BroadcastRadioHalTest, CancelAnnouncement) { 413 if (skipped) return; 414 ASSERT_TRUE(openTuner()); 415 416 auto hidlResult = mTuner->cancelAnnouncement(); 417 EXPECT_EQ(Result::OK, hidlResult); 418 } 419 420 /** 421 * Test getImage call with invalid image ID. 422 * 423 * Verifies that: 424 * - getImage call handles argument 0 gracefully. 425 */ 426 TEST_P(BroadcastRadioHalTest, GetNoImage) { 427 if (skipped) return; 428 429 size_t len = 0; 430 auto hidlResult = 431 mRadioModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); }); 432 433 ASSERT_TRUE(hidlResult.isOk()); 434 ASSERT_EQ(0u, len); 435 } 436 437 /** 438 * Test proper image format in metadata. 439 * 440 * Verifies that: 441 * - all images in metadata are provided out-of-band (by id, not as a binary blob); 442 * - images are available for getImage call. 443 */ 444 TEST_P(BroadcastRadioHalTest, OobImagesOnly) { 445 if (skipped) return; 446 ASSERT_TRUE(openTuner()); 447 448 std::vector<int> imageIds; 449 450 do { 451 auto getCb = [&](const hidl_vec<ProgramInfo>& list) { 452 for (auto&& program : list) { 453 for (auto&& entry : program.base.metadata) { 454 EXPECT_NE(MetadataType::RAW, entry.type); 455 if (entry.key != MetadataKey::ICON && entry.key != MetadataKey::ART) continue; 456 EXPECT_NE(0, entry.intValue); 457 EXPECT_EQ(0u, entry.rawValue.size()); 458 if (entry.intValue != 0) imageIds.push_back(entry.intValue); 459 } 460 } 461 }; 462 getProgramList(getCb); 463 } while (nextBand()); 464 465 if (imageIds.size() == 0) { 466 printSkipped("No images found"); 467 return; 468 } 469 470 for (auto id : imageIds) { 471 ALOGD("Checking image %d", id); 472 473 size_t len = 0; 474 auto hidlResult = 475 mRadioModule->getImage(id, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); }); 476 477 ASSERT_TRUE(hidlResult.isOk()); 478 ASSERT_GT(len, 0u); 479 } 480 } 481 482 /** 483 * Test AnalogForced switch. 484 * 485 * Verifies that: 486 * - setAnalogForced results either with INVALID_STATE, or isAnalogForced replying the same. 487 */ 488 TEST_P(BroadcastRadioHalTest, AnalogForcedSwitch) { 489 if (skipped) return; 490 ASSERT_TRUE(openTuner()); 491 492 bool forced; 493 Result halIsResult; 494 auto isCb = [&](Result result, bool isForced) { 495 halIsResult = result; 496 forced = isForced; 497 }; 498 499 // set analog mode 500 auto setResult = mTuner->setAnalogForced(true); 501 ASSERT_TRUE(setResult.isOk()); 502 if (Result::INVALID_STATE == setResult) { 503 // if setter fails, getter should fail too - it means the switch is not supported at all 504 auto isResult = mTuner->isAnalogForced(isCb); 505 ASSERT_TRUE(isResult.isOk()); 506 EXPECT_EQ(Result::INVALID_STATE, halIsResult); 507 return; 508 } 509 ASSERT_EQ(Result::OK, setResult); 510 511 // check, if it's analog 512 auto isResult = mTuner->isAnalogForced(isCb); 513 ASSERT_TRUE(isResult.isOk()); 514 EXPECT_EQ(Result::OK, halIsResult); 515 ASSERT_TRUE(forced); 516 517 // set digital mode 518 setResult = mTuner->setAnalogForced(false); 519 ASSERT_EQ(Result::OK, setResult); 520 521 // check, if it's digital 522 isResult = mTuner->isAnalogForced(isCb); 523 ASSERT_TRUE(isResult.isOk()); 524 EXPECT_EQ(Result::OK, halIsResult); 525 ASSERT_FALSE(forced); 526 } 527 528 INSTANTIATE_TEST_CASE_P(BroadcastRadioHalTestCases, BroadcastRadioHalTest, 529 ::testing::Values(Class::AM_FM, Class::SAT, Class::DT)); 530 531 } // namespace vts 532 } // namespace V1_1 533 } // namespace broadcastradio 534 } // namespace hardware 535 } // namespace android 536 537 int main(int argc, char** argv) { 538 ::testing::InitGoogleTest(&argc, argv); 539 int status = RUN_ALL_TESTS(); 540 ALOGI("Test result = %d", status); 541 return status; 542 } 543