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