Home | History | Annotate | Download | only in functional
      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