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