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 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