• 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     std::optional<utils::ProgramInfoSet> getProgramList(const ProgramFilter& filter);
112 
113     sp<IBroadcastRadio> mModule;
114     Properties mProperties;
115     sp<ITunerSession> mSession;
116     sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
117 };
118 
printSkipped(std::string msg)119 static void printSkipped(std::string msg) {
120     const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
121     std::cout << "[  SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
122               << std::endl;
123     std::cout << 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 
TunerCallbackMock()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 
onCurrentProgramInfoChanged(const ProgramInfo & info)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 
onProgramListUpdated(const ProgramListChunk & chunk)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 
SetUp()186 void BroadcastRadioHalTest::SetUp() {
187     EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
188 
189     // lookup HIDL service (radio module)
190     mModule = IBroadcastRadio::getService(GetParam());
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 
TearDown()202 void BroadcastRadioHalTest::TearDown() {
203     mSession.clear();
204     mModule.clear();
205     clearAndWait(mCallback, 1s);
206 }
207 
openSession()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 
getAmFmRegionConfig(bool full,AmFmRegionConfig * config)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 
getProgramList()242 std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
243     ProgramFilter emptyFilter = {};
244     return getProgramList(emptyFilter);
245 }
246 
getProgramList(const ProgramFilter & filter)247 std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList(
248         const ProgramFilter& filter) {
249     EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
250 
251     auto startResult = mSession->startProgramListUpdates(filter);
252     if (startResult == Result::NOT_SUPPORTED) {
253         printSkipped("Program list not supported");
254         return std::nullopt;
255     }
256     EXPECT_EQ(Result::OK, startResult);
257     if (startResult != Result::OK) return std::nullopt;
258 
259     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
260 
261     auto stopResult = mSession->stopProgramListUpdates();
262     EXPECT_TRUE(stopResult.isOk());
263 
264     return mCallback->mProgramList;
265 }
266 
267 /**
268  * Test session opening.
269  *
270  * Verifies that:
271  *  - the method succeeds on a first and subsequent calls;
272  *  - the method succeeds when called for the second time without
273  *    closing previous session.
274  */
TEST_P(BroadcastRadioHalTest,OpenSession)275 TEST_P(BroadcastRadioHalTest, OpenSession) {
276     // simply open session for the first time
277     ASSERT_TRUE(openSession());
278 
279     // drop (without explicit close) and re-open the session
280     mSession.clear();
281     ASSERT_TRUE(openSession());
282 
283     // open the second session (the first one should be forcibly closed)
284     auto secondSession = mSession;
285     mSession.clear();
286     ASSERT_TRUE(openSession());
287 }
288 
isValidAmFmFreq(uint64_t freq)289 static bool isValidAmFmFreq(uint64_t freq) {
290     auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
291     return utils::isValid(id);
292 }
293 
validateRange(const AmFmBandRange & range)294 static void validateRange(const AmFmBandRange& range) {
295     EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
296     EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
297     EXPECT_LT(range.lowerBound, range.upperBound);
298     EXPECT_GT(range.spacing, 0u);
299     EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
300 }
301 
supportsFM(const AmFmRegionConfig & config)302 static bool supportsFM(const AmFmRegionConfig& config) {
303     for (auto&& range : config.ranges) {
304         if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
305     }
306     return false;
307 }
308 
309 /**
310  * Test fetching AM/FM regional configuration.
311  *
312  * Verifies that:
313  *  - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
314  *  - there is at least one AM/FM band configured;
315  *  - FM Deemphasis and RDS are correctly configured for FM-capable radio;
316  *  - all channel grids (frequency ranges and spacings) are valid;
317  *  - seek spacing is a multiple of the manual spacing value.
318  */
TEST_P(BroadcastRadioHalTest,GetAmFmRegionConfig)319 TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfig) {
320     AmFmRegionConfig config;
321     bool supported = getAmFmRegionConfig(false, &config);
322     if (!supported) {
323         printSkipped("AM/FM not supported");
324         return;
325     }
326 
327     EXPECT_GT(config.ranges.size(), 0u);
328     EXPECT_LE(popcountll(config.fmDeemphasis), 1);
329     EXPECT_LE(popcountll(config.fmRds), 1);
330 
331     for (auto&& range : config.ranges) {
332         validateRange(range);
333         EXPECT_EQ(0u, range.scanSpacing % range.spacing);
334         EXPECT_GE(range.scanSpacing, range.spacing);
335     }
336 
337     if (supportsFM(config)) {
338         EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
339     }
340 }
341 
342 /**
343  * Test fetching AM/FM regional capabilities.
344  *
345  * Verifies that:
346  *  - AM/FM regional capabilities are either available or not supported at all by the hardware;
347  *  - there is at least one AM/FM range supported;
348  *  - there is at least one de-emphasis filter mode supported for FM-capable radio;
349  *  - all channel grids (frequency ranges and spacings) are valid;
350  *  - seek spacing is not set.
351  */
TEST_P(BroadcastRadioHalTest,GetAmFmRegionConfigCapabilities)352 TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
353     AmFmRegionConfig config;
354     bool supported = getAmFmRegionConfig(true, &config);
355     if (!supported) {
356         printSkipped("AM/FM not supported");
357         return;
358     }
359 
360     EXPECT_GT(config.ranges.size(), 0u);
361 
362     for (auto&& range : config.ranges) {
363         validateRange(range);
364         EXPECT_EQ(0u, range.scanSpacing);
365     }
366 
367     if (supportsFM(config)) {
368         EXPECT_GE(popcountll(config.fmDeemphasis), 1);
369     }
370 }
371 
372 /**
373  * Test fetching DAB regional configuration.
374  *
375  * Verifies that:
376  *  - DAB regional configuration is either set at startup or not supported at all by the hardware;
377  *  - all channel labels match correct format;
378  *  - all channel frequencies are in correct range.
379  */
TEST_P(BroadcastRadioHalTest,GetDabRegionConfig)380 TEST_P(BroadcastRadioHalTest, GetDabRegionConfig) {
381     Result halResult;
382     hidl_vec<DabTableEntry> config;
383     auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
384         halResult = result;
385         config = configCb;
386     };
387     auto hidlResult = mModule->getDabRegionConfig(cb);
388     ASSERT_TRUE(hidlResult.isOk());
389 
390     if (halResult == Result::NOT_SUPPORTED) {
391         printSkipped("DAB not supported");
392         return;
393     }
394     ASSERT_EQ(Result::OK, halResult);
395 
396     std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
397     // double-check correctness of the test
398     ASSERT_TRUE(std::regex_match("5A", re));
399     ASSERT_FALSE(std::regex_match("5a", re));
400     ASSERT_FALSE(std::regex_match("1234ABCD", re));
401     ASSERT_TRUE(std::regex_match("CN 12D", re));
402     ASSERT_FALSE(std::regex_match(" 5A", re));
403 
404     for (auto&& entry : config) {
405         EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
406 
407         auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
408         EXPECT_TRUE(utils::isValid(id));
409     }
410 }
411 
412 /**
413  * Test tuning with FM selector.
414  *
415  * Verifies that:
416  *  - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
417  *  - if it is supported, the method succeeds;
418  *  - after a successful tune call, onCurrentProgramInfoChanged callback is
419  *    invoked carrying a proper selector;
420  *  - program changes exactly to what was requested.
421  */
TEST_P(BroadcastRadioHalTest,FmTune)422 TEST_P(BroadcastRadioHalTest, FmTune) {
423     ASSERT_TRUE(openSession());
424 
425     uint64_t freq = 90900;  // 90.9 FM
426     auto sel = make_selector_amfm(freq);
427 
428     /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
429      * callback setting infoCb, because egmock cannot distinguish calls with different matchers
430      * (there is one here and one in callback constructor).
431      *
432      * This sleep workaround will fix default implementation, but the real HW tests will still be
433      * flaky. We probably need to implement egmock alternative based on actions.
434      */
435     std::this_thread::sleep_for(gTuneWorkaround);
436 
437     // try tuning
438     ProgramInfo infoCb = {};
439     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
440                         InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
441             .Times(AnyNumber())
442             .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))))
443             .WillRepeatedly(testing::InvokeWithoutArgs([] { return Void(); }));
444     auto result = mSession->tune(sel);
445 
446     // expect a failure if it's not supported
447     if (!utils::isSupported(mProperties, sel)) {
448         EXPECT_EQ(Result::NOT_SUPPORTED, result);
449         return;
450     }
451 
452     // expect a callback if it succeeds
453     EXPECT_EQ(Result::OK, result);
454     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
455 
456     LOG(DEBUG) << "current program info: " << toString(infoCb);
457 
458     // it should tune exactly to what was requested
459     auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
460     EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
461 }
462 
463 /**
464  * Test tuning with invalid selectors.
465  *
466  * Verifies that:
467  *  - if the selector is not supported, it's ignored;
468  *  - if it is supported, an invalid value results with INVALID_ARGUMENTS;
469  */
TEST_P(BroadcastRadioHalTest,TuneFailsWithInvalid)470 TEST_P(BroadcastRadioHalTest, TuneFailsWithInvalid) {
471     ASSERT_TRUE(openSession());
472 
473     vector<ProgramIdentifier> invalid = {
474         make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
475         make_identifier(IdentifierType::RDS_PI, 0x10000),
476         make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
477         make_identifier(IdentifierType::DAB_SID_EXT, 0),
478         make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
479         make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
480     };
481 
482     for (auto&& id : invalid) {
483         ProgramSelector sel{id, {}};
484 
485         auto result = mSession->tune(sel);
486 
487         if (utils::isSupported(mProperties, sel)) {
488             EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
489         } else {
490             EXPECT_EQ(Result::NOT_SUPPORTED, result);
491         }
492     }
493 }
494 
495 /**
496  * Test tuning with DAB selector.
497  *
498  * Verifies that:
499  *  - if DAB selector is not supported, the method returns NOT_SUPPORTED;
500  *  - if it is supported, the method succeeds;
501  *  - after a successful tune call, onCurrentProgramInfoChanged callback is
502  *    invoked carrying a proper selector;
503  *  - program changes exactly to what was requested.
504  */
TEST_P(BroadcastRadioHalTest,DabTune)505 TEST_P(BroadcastRadioHalTest, DabTune) {
506     Result halResult;
507     hidl_vec<DabTableEntry> config;
508     auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
509         halResult = result;
510         config = configCb;
511     };
512     auto hidlResult = mModule->getDabRegionConfig(cb);
513     ASSERT_TRUE(hidlResult.isOk());
514 
515     if (halResult == Result::NOT_SUPPORTED) {
516         printSkipped("DAB not supported");
517         return;
518     }
519     ASSERT_EQ(Result::OK, halResult);
520     ASSERT_NE(config.size(), 0U);
521 
522     ASSERT_TRUE(openSession());
523 
524     ProgramSelector sel = {};
525     uint64_t freq = config[config.size() / 2].frequency;
526     sel.primaryId = make_identifier(IdentifierType::DAB_FREQUENCY,freq);
527 
528     std::this_thread::sleep_for(gTuneWorkaround);
529 
530     // try tuning
531     ProgramInfo infoCb = {};
532     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
533                         InfoHasId(utils::make_identifier(IdentifierType::DAB_FREQUENCY, freq)))
534         .Times(AnyNumber())
535         .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
536     auto result = mSession->tune(sel);
537 
538     // expect a failure if it's not supported
539     if (!utils::isSupported(mProperties, sel)) {
540         EXPECT_EQ(Result::NOT_SUPPORTED, result);
541         return;
542     }
543 
544     // expect a callback if it succeeds
545     EXPECT_EQ(Result::OK, result);
546     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
547 
548     LOG(DEBUG) << "current program info: " << toString(infoCb);
549 
550     // it should tune exactly to what was requested
551     auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY);
552     EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
553 }
554 
555 /**
556  * Test tuning with empty program selector.
557  *
558  * Verifies that:
559  *  - tune fails with NOT_SUPPORTED when program selector is not initialized.
560  */
TEST_P(BroadcastRadioHalTest,TuneFailsWithEmpty)561 TEST_P(BroadcastRadioHalTest, TuneFailsWithEmpty) {
562     ASSERT_TRUE(openSession());
563 
564     // Program type is 1-based, so 0 will always be invalid.
565     ProgramSelector sel = {};
566     auto result = mSession->tune(sel);
567     ASSERT_EQ(Result::NOT_SUPPORTED, result);
568 }
569 
570 /**
571  * Test seeking to next/prev station via ITunerSession::scan().
572  *
573  * Verifies that:
574  *  - the method succeeds;
575  *  - the program info is changed within timeout::tune;
576  *  - works both directions and with or without skipping sub-channel.
577  */
TEST_P(BroadcastRadioHalTest,Seek)578 TEST_P(BroadcastRadioHalTest, Seek) {
579     ASSERT_TRUE(openSession());
580 
581     // TODO(b/69958777): see FmTune workaround
582     std::this_thread::sleep_for(gTuneWorkaround);
583 
584     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
585     auto result = mSession->scan(true /* up */, true /* skip subchannel */);
586 
587     if (result == Result::NOT_SUPPORTED) {
588         printSkipped("seek not supported");
589         return;
590     }
591 
592     EXPECT_EQ(Result::OK, result);
593     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
594 
595     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
596     result = mSession->scan(false /* down */, false /* don't skip subchannel */);
597     EXPECT_EQ(Result::OK, result);
598     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
599 }
600 
601 /**
602  * Test step operation.
603  *
604  * Verifies that:
605  *  - the method succeeds or returns NOT_SUPPORTED;
606  *  - the program info is changed within timeout::tune if the method succeeded;
607  *  - works both directions.
608  */
TEST_P(BroadcastRadioHalTest,Step)609 TEST_P(BroadcastRadioHalTest, Step) {
610     ASSERT_TRUE(openSession());
611 
612     // TODO(b/69958777): see FmTune workaround
613     std::this_thread::sleep_for(gTuneWorkaround);
614 
615     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
616     auto result = mSession->step(true /* up */);
617     if (result == Result::NOT_SUPPORTED) {
618         printSkipped("step not supported");
619         return;
620     }
621     EXPECT_EQ(Result::OK, result);
622     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
623 
624     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
625     result = mSession->step(false /* down */);
626     EXPECT_EQ(Result::OK, result);
627     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
628 }
629 
630 /**
631  * Test tune cancellation.
632  *
633  * Verifies that:
634  *  - the method does not crash after being invoked multiple times.
635  */
TEST_P(BroadcastRadioHalTest,Cancel)636 TEST_P(BroadcastRadioHalTest, Cancel) {
637     ASSERT_TRUE(openSession());
638 
639     for (int i = 0; i < 10; i++) {
640         auto result = mSession->scan(true /* up */, true /* skip subchannel */);
641 
642         if (result == Result::NOT_SUPPORTED) {
643             printSkipped("cancel is skipped because of seek not supported");
644             return;
645         }
646 
647         ASSERT_EQ(Result::OK, result);
648 
649         auto cancelResult = mSession->cancel();
650         ASSERT_TRUE(cancelResult.isOk());
651     }
652 }
653 
654 /**
655  * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
656  *
657  * Verifies that:
658  *  - callback is called for empty parameters set.
659  */
TEST_P(BroadcastRadioHalTest,NoParameters)660 TEST_P(BroadcastRadioHalTest, NoParameters) {
661     ASSERT_TRUE(openSession());
662 
663     hidl_vec<VendorKeyValue> halResults = {};
664     bool wasCalled = false;
665     auto cb = [&](hidl_vec<VendorKeyValue> results) {
666         wasCalled = true;
667         halResults = results;
668     };
669 
670     auto hidlResult = mSession->setParameters({}, cb);
671     ASSERT_TRUE(hidlResult.isOk());
672     ASSERT_TRUE(wasCalled);
673     ASSERT_EQ(0u, halResults.size());
674 
675     wasCalled = false;
676     hidlResult = mSession->getParameters({}, cb);
677     ASSERT_TRUE(hidlResult.isOk());
678     ASSERT_TRUE(wasCalled);
679     ASSERT_EQ(0u, halResults.size());
680 }
681 
682 /**
683  * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
684  *
685  * Verifies that:
686  *  - unknown parameters are ignored;
687  *  - callback is called also for empty results set.
688  */
TEST_P(BroadcastRadioHalTest,UnknownParameters)689 TEST_P(BroadcastRadioHalTest, UnknownParameters) {
690     ASSERT_TRUE(openSession());
691 
692     hidl_vec<VendorKeyValue> halResults = {};
693     bool wasCalled = false;
694     auto cb = [&](hidl_vec<VendorKeyValue> results) {
695         wasCalled = true;
696         halResults = results;
697     };
698 
699     auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
700     ASSERT_TRUE(hidlResult.isOk());
701     ASSERT_TRUE(wasCalled);
702     ASSERT_EQ(0u, halResults.size());
703 
704     wasCalled = false;
705     hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
706     ASSERT_TRUE(hidlResult.isOk());
707     ASSERT_TRUE(wasCalled);
708     ASSERT_EQ(0u, halResults.size());
709 }
710 
711 /**
712  * Test session closing.
713  *
714  * Verifies that:
715  *  - the method does not crash after being invoked multiple times.
716  */
TEST_P(BroadcastRadioHalTest,Close)717 TEST_P(BroadcastRadioHalTest, Close) {
718     ASSERT_TRUE(openSession());
719 
720     for (int i = 0; i < 10; i++) {
721         auto cancelResult = mSession->close();
722         ASSERT_TRUE(cancelResult.isOk());
723     }
724 }
725 
726 /**
727  * Test geting image of invalid ID.
728  *
729  * Verifies that:
730  * - getImage call handles argument 0 gracefully.
731  */
TEST_P(BroadcastRadioHalTest,GetNoImage)732 TEST_P(BroadcastRadioHalTest, GetNoImage) {
733     size_t len = 0;
734     auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
735 
736     ASSERT_TRUE(result.isOk());
737     ASSERT_EQ(0u, len);
738 }
739 
740 /**
741  * Test getting config flags.
742  *
743  * Verifies that:
744  * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
745  * - call success or failure is consistent with setConfigFlag.
746  */
TEST_P(BroadcastRadioHalTest,FetchConfigFlags)747 TEST_P(BroadcastRadioHalTest, FetchConfigFlags) {
748     ASSERT_TRUE(openSession());
749 
750     for (auto flag : gConfigFlagValues) {
751         auto halResult = Result::UNKNOWN_ERROR;
752         auto cb = [&](Result result, bool) { halResult = result; };
753         auto hidlResult = mSession->isConfigFlagSet(flag, cb);
754         EXPECT_TRUE(hidlResult.isOk());
755 
756         if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
757             ASSERT_EQ(Result::OK, halResult);
758         }
759 
760         // set must fail or succeed the same way as get
761         auto setResult = mSession->setConfigFlag(flag, false);
762         EXPECT_EQ(halResult, setResult);
763         setResult = mSession->setConfigFlag(flag, true);
764         EXPECT_EQ(halResult, setResult);
765     }
766 }
767 
768 /**
769  * Test setting config flags.
770  *
771  * Verifies that:
772  * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
773  * - isConfigFlagSet reflects the state requested immediately after the set call.
774  */
TEST_P(BroadcastRadioHalTest,SetConfigFlags)775 TEST_P(BroadcastRadioHalTest, SetConfigFlags) {
776     ASSERT_TRUE(openSession());
777 
778     auto get = [&](ConfigFlag flag) {
779         auto halResult = Result::UNKNOWN_ERROR;
780         bool gotValue = false;
781         auto cb = [&](Result result, bool value) {
782             halResult = result;
783             gotValue = value;
784         };
785         auto hidlResult = mSession->isConfigFlagSet(flag, cb);
786         EXPECT_TRUE(hidlResult.isOk());
787         EXPECT_EQ(Result::OK, halResult);
788         return gotValue;
789     };
790 
791     for (auto flag : gConfigFlagValues) {
792         auto result = mSession->setConfigFlag(flag, false);
793         if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
794             // setting to true must result in the same error as false
795             auto secondResult = mSession->setConfigFlag(flag, true);
796             EXPECT_EQ(result, secondResult);
797             continue;
798         }
799         ASSERT_EQ(Result::OK, result);
800 
801         // verify false is set
802         auto value = get(flag);
803         EXPECT_FALSE(value);
804 
805         // try setting true this time
806         result = mSession->setConfigFlag(flag, true);
807         ASSERT_EQ(Result::OK, result);
808         value = get(flag);
809         EXPECT_TRUE(value);
810 
811         // false again
812         result = mSession->setConfigFlag(flag, false);
813         ASSERT_EQ(Result::OK, result);
814         value = get(flag);
815         EXPECT_FALSE(value);
816     }
817 }
818 
819 /**
820  * Test getting program list using empty program filter.
821  *
822  * Verifies that:
823  * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
824  * - the complete list is fetched within timeout::programListScan;
825  * - stopProgramListUpdates does not crash.
826  */
TEST_P(BroadcastRadioHalTest,GetProgramListFromEmptyFilter)827 TEST_P(BroadcastRadioHalTest, GetProgramListFromEmptyFilter) {
828     ASSERT_TRUE(openSession());
829 
830     getProgramList();
831 }
832 
833 /**
834  * Test getting program list using AMFM frequency program filter.
835  *
836  * Verifies that:
837  * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
838  * - the complete list is fetched within timeout::programListScan;
839  * - stopProgramListUpdates does not crash;
840  * - result for startProgramListUpdates using a filter with AMFM_FREQUENCY value of the first AMFM
841  *   program matches the expected result.
842  */
TEST_P(BroadcastRadioHalTest,GetProgramListFromAmFmFilter)843 TEST_P(BroadcastRadioHalTest, GetProgramListFromAmFmFilter) {
844     ASSERT_TRUE(openSession());
845 
846     auto completeList = getProgramList();
847     if (!completeList) return;
848 
849     ProgramFilter amfmFilter = {};
850     int expectedResultSize = 0;
851     uint64_t expectedFreq = 0;
852     for (auto&& program : *completeList) {
853         auto amfmIds = utils::getAllIds(program.selector, IdentifierType::AMFM_FREQUENCY);
854         EXPECT_LE(amfmIds.size(), 1u);
855         if (amfmIds.size() == 0) continue;
856 
857         if (expectedResultSize == 0) {
858             expectedFreq = amfmIds[0];
859             amfmFilter.identifiers = {
860                     make_identifier(IdentifierType::AMFM_FREQUENCY, expectedFreq)};
861             expectedResultSize = 1;
862         } else if (amfmIds[0] == expectedFreq) {
863             expectedResultSize++;
864         }
865     }
866 
867     if (expectedResultSize == 0) return;
868     auto amfmList = getProgramList(amfmFilter);
869     ASSERT_EQ(expectedResultSize, amfmList->size()) << "amfm filter result size is wrong";
870 }
871 
872 /**
873  * Test getting program list using DAB ensemble program filter.
874  *
875  * Verifies that:
876  * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
877  * - the complete list is fetched within timeout::programListScan;
878  * - stopProgramListUpdates does not crash;
879  * - result for startProgramListUpdates using a filter with DAB_ENSEMBLE value of the first DAB
880  *   program matches the expected result.
881  */
TEST_P(BroadcastRadioHalTest,GetProgramListFromDabFilter)882 TEST_P(BroadcastRadioHalTest, GetProgramListFromDabFilter) {
883     ASSERT_TRUE(openSession());
884 
885     auto completeList = getProgramList();
886     if (!completeList) return;
887 
888     ProgramFilter dabFilter = {};
889     int expectedResultSize = 0;
890     uint64_t expectedEnsemble = 0;
891     for (auto&& program : *completeList) {
892         auto dabEnsembles = utils::getAllIds(program.selector, IdentifierType::DAB_ENSEMBLE);
893         EXPECT_LE(dabEnsembles.size(), 1u);
894         if (dabEnsembles.size() == 0) continue;
895 
896         if (expectedResultSize == 0) {
897             expectedEnsemble = dabEnsembles[0];
898             dabFilter.identifiers = {
899                     make_identifier(IdentifierType::DAB_ENSEMBLE, expectedEnsemble)};
900             expectedResultSize = 1;
901         } else if (dabEnsembles[0] == expectedEnsemble) {
902             expectedResultSize++;
903         }
904     }
905 
906     if (expectedResultSize == 0) return;
907     auto dabList = getProgramList(dabFilter);
908     ASSERT_EQ(expectedResultSize, dabList->size()) << "dab filter result size is wrong";
909 }
910 
911 /**
912  * Test HD_STATION_NAME correctness.
913  *
914  * Verifies that if a program on the list contains HD_STATION_NAME identifier:
915  *  - the program provides station name in its metadata;
916  *  - the identifier matches the name;
917  *  - there is only one identifier of that type.
918  */
TEST_P(BroadcastRadioHalTest,HdRadioStationNameId)919 TEST_P(BroadcastRadioHalTest, HdRadioStationNameId) {
920     ASSERT_TRUE(openSession());
921 
922     auto list = getProgramList();
923     if (!list) return;
924 
925     for (auto&& program : *list) {
926         auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
927         EXPECT_LE(nameIds.size(), 1u);
928         if (nameIds.size() == 0) continue;
929 
930         auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
931         if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
932         ASSERT_TRUE(name.has_value());
933 
934         auto expectedId = utils::make_hdradio_station_name(*name);
935         EXPECT_EQ(expectedId.value, nameIds[0]);
936     }
937 }
938 
939 /**
940  * Test announcement listener registration.
941  *
942  * Verifies that:
943  *  - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
944  *  - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
945  *  - closing handle does not crash.
946  */
TEST_P(BroadcastRadioHalTest,AnnouncementListenerRegistration)947 TEST_P(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
948     sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
949 
950     Result halResult = Result::UNKNOWN_ERROR;
951     sp<ICloseHandle> closeHandle = nullptr;
952     auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
953         halResult = result;
954         closeHandle = closeHandle_;
955     };
956 
957     auto hidlResult =
958         mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
959     ASSERT_TRUE(hidlResult.isOk());
960 
961     if (halResult == Result::NOT_SUPPORTED) {
962         ASSERT_EQ(nullptr, closeHandle.get());
963         printSkipped("Announcements not supported");
964         return;
965     }
966 
967     ASSERT_EQ(Result::OK, halResult);
968     ASSERT_NE(nullptr, closeHandle.get());
969 
970     closeHandle->close();
971 }
972 
973 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
974 INSTANTIATE_TEST_SUITE_P(
975         PerInstance, BroadcastRadioHalTest,
976         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBroadcastRadio::descriptor)),
977         android::hardware::PrintInstanceNameToString);
978 
979 }  // namespace vts
980 }  // namespace V2_0
981 }  // namespace broadcastradio
982 }  // namespace hardware
983 }  // namespace android
984 
main(int argc,char ** argv)985 int main(int argc, char** argv) {
986     android::base::SetDefaultTag("BcRadio.vts");
987     android::base::SetMinimumLogSeverity(android::base::VERBOSE);
988     ::testing::InitGoogleTest(&argc, argv);
989     return RUN_ALL_TESTS();
990 }
991