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