• 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 LOG_TAG "BcRadio.vts"
18 #define LOG_NDEBUG 0
19 #define EGMOCK_VERBOSE 1
20 
21 #include <VtsHalHidlTargetTestBase.h>
22 #include <android-base/logging.h>
23 #include <android-base/strings.h>
24 #include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
25 #include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
26 #include <android/hardware/broadcastradio/2.0/ITunerSession.h>
27 #include <android/hardware/broadcastradio/2.0/types.h>
28 #include <broadcastradio-utils-2x/Utils.h>
29 #include <broadcastradio-vts-utils/call-barrier.h>
30 #include <broadcastradio-vts-utils/environment-utils.h>
31 #include <broadcastradio-vts-utils/mock-timeout.h>
32 #include <broadcastradio-vts-utils/pointer-utils.h>
33 #include <cutils/bitops.h>
34 #include <gmock/gmock.h>
35 
36 #include <chrono>
37 #include <optional>
38 #include <regex>
39 
40 namespace android {
41 namespace hardware {
42 namespace broadcastradio {
43 namespace V2_0 {
44 namespace vts {
45 
46 using namespace std::chrono_literals;
47 
48 using std::unordered_set;
49 using std::vector;
50 using testing::_;
51 using testing::AnyNumber;
52 using testing::ByMove;
53 using testing::DoAll;
54 using testing::Invoke;
55 using testing::SaveArg;
56 
57 using broadcastradio::vts::BroadcastRadioHidlEnvironment;
58 using broadcastradio::vts::CallBarrier;
59 using broadcastradio::vts::clearAndWait;
60 using utils::make_identifier;
61 using utils::make_selector_amfm;
62 
63 namespace timeout {
64 
65 static constexpr auto tune = 30s;
66 static constexpr auto programListScan = 5min;
67 
68 }  // namespace timeout
69 
70 static constexpr auto gTuneWorkaround = 200ms;
71 
72 static const ConfigFlag gConfigFlagValues[] = {
73     ConfigFlag::FORCE_MONO,
74     ConfigFlag::FORCE_ANALOG,
75     ConfigFlag::FORCE_DIGITAL,
76     ConfigFlag::RDS_AF,
77     ConfigFlag::RDS_REG,
78     ConfigFlag::DAB_DAB_LINKING,
79     ConfigFlag::DAB_FM_LINKING,
80     ConfigFlag::DAB_DAB_SOFT_LINKING,
81     ConfigFlag::DAB_FM_SOFT_LINKING,
82 };
83 
84 class TunerCallbackMock : public ITunerCallback {
85    public:
86     TunerCallbackMock();
87 
88     MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
89     MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged_, Return<void>(const ProgramInfo&));
90     virtual Return<void> onCurrentProgramInfoChanged(const ProgramInfo& info);
91     Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
92     MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
93     MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
94 
95     MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
96 
97     std::mutex mLock;
98     utils::ProgramInfoSet mProgramList;
99 };
100 
101 struct AnnouncementListenerMock : public IAnnouncementListener {
102     MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
103 };
104 
105 static BroadcastRadioHidlEnvironment<IBroadcastRadio>* gEnv = nullptr;
106 
107 class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
108    protected:
109     virtual void SetUp() override;
110     virtual void TearDown() override;
111 
112     bool openSession();
113     bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
114     std::optional<utils::ProgramInfoSet> getProgramList();
115 
116     sp<IBroadcastRadio> mModule;
117     Properties mProperties;
118     sp<ITunerSession> mSession;
119     sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
120 };
121 
printSkipped(std::string msg)122 static void printSkipped(std::string msg) {
123     std::cout << "[  SKIPPED ] " << msg << std::endl;
124 }
125 
126 MATCHER_P(InfoHasId, id,
127           std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
128     auto ids = utils::getAllIds(arg.selector, utils::getType(id));
129     return ids.end() != find(ids.begin(), ids.end(), id.value);
130 }
131 
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 = getService<IBroadcastRadio>(gEnv->getServiceName<IBroadcastRadio>());
191     ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
192 
193     // get module properties
194     auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
195     ASSERT_TRUE(propResult.isOk());
196 
197     EXPECT_FALSE(mProperties.maker.empty());
198     EXPECT_FALSE(mProperties.product.empty());
199     EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
200 }
201 
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     EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
244 
245     auto startResult = mSession->startProgramListUpdates({});
246     if (startResult == Result::NOT_SUPPORTED) {
247         printSkipped("Program list not supported");
248         return nullopt;
249     }
250     EXPECT_EQ(Result::OK, startResult);
251     if (startResult != Result::OK) return nullopt;
252 
253     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
254 
255     auto stopResult = mSession->stopProgramListUpdates();
256     EXPECT_TRUE(stopResult.isOk());
257 
258     return mCallback->mProgramList;
259 }
260 
261 /**
262  * Test session opening.
263  *
264  * Verifies that:
265  *  - the method succeeds on a first and subsequent calls;
266  *  - the method succeeds when called for the second time without
267  *    closing previous session.
268  */
TEST_F(BroadcastRadioHalTest,OpenSession)269 TEST_F(BroadcastRadioHalTest, OpenSession) {
270     // simply open session for the first time
271     ASSERT_TRUE(openSession());
272 
273     // drop (without explicit close) and re-open the session
274     mSession.clear();
275     ASSERT_TRUE(openSession());
276 
277     // open the second session (the first one should be forcibly closed)
278     auto secondSession = mSession;
279     mSession.clear();
280     ASSERT_TRUE(openSession());
281 }
282 
isValidAmFmFreq(uint64_t freq)283 static bool isValidAmFmFreq(uint64_t freq) {
284     auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
285     return utils::isValid(id);
286 }
287 
validateRange(const AmFmBandRange & range)288 static void validateRange(const AmFmBandRange& range) {
289     EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
290     EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
291     EXPECT_LT(range.lowerBound, range.upperBound);
292     EXPECT_GT(range.spacing, 0u);
293     EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
294 }
295 
supportsFM(const AmFmRegionConfig & config)296 static bool supportsFM(const AmFmRegionConfig& config) {
297     for (auto&& range : config.ranges) {
298         if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
299     }
300     return false;
301 }
302 
303 /**
304  * Test fetching AM/FM regional configuration.
305  *
306  * Verifies that:
307  *  - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
308  *  - there is at least one AM/FM band configured;
309  *  - FM Deemphasis and RDS are correctly configured for FM-capable radio;
310  *  - all channel grids (frequency ranges and spacings) are valid;
311  *  - seek spacing is a multiple of the manual spacing value.
312  */
TEST_F(BroadcastRadioHalTest,GetAmFmRegionConfig)313 TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
314     AmFmRegionConfig config;
315     bool supported = getAmFmRegionConfig(false, &config);
316     if (!supported) {
317         printSkipped("AM/FM not supported");
318         return;
319     }
320 
321     EXPECT_GT(config.ranges.size(), 0u);
322     EXPECT_LE(popcountll(config.fmDeemphasis), 1);
323     EXPECT_LE(popcountll(config.fmRds), 1);
324 
325     for (auto&& range : config.ranges) {
326         validateRange(range);
327         EXPECT_EQ(0u, range.scanSpacing % range.spacing);
328         EXPECT_GE(range.scanSpacing, range.spacing);
329     }
330 
331     if (supportsFM(config)) {
332         EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
333     }
334 }
335 
336 /**
337  * Test fetching AM/FM regional capabilities.
338  *
339  * Verifies that:
340  *  - AM/FM regional capabilities are either available or not supported at all by the hardware;
341  *  - there is at least one AM/FM range supported;
342  *  - there is at least one de-emphasis filter mode supported for FM-capable radio;
343  *  - all channel grids (frequency ranges and spacings) are valid;
344  *  - seek spacing is not set.
345  */
TEST_F(BroadcastRadioHalTest,GetAmFmRegionConfigCapabilities)346 TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
347     AmFmRegionConfig config;
348     bool supported = getAmFmRegionConfig(true, &config);
349     if (!supported) {
350         printSkipped("AM/FM not supported");
351         return;
352     }
353 
354     EXPECT_GT(config.ranges.size(), 0u);
355 
356     for (auto&& range : config.ranges) {
357         validateRange(range);
358         EXPECT_EQ(0u, range.scanSpacing);
359     }
360 
361     if (supportsFM(config)) {
362         EXPECT_GE(popcountll(config.fmDeemphasis), 1);
363     }
364 }
365 
366 /**
367  * Test fetching DAB regional configuration.
368  *
369  * Verifies that:
370  *  - DAB regional configuration is either set at startup or not supported at all by the hardware;
371  *  - all channel labels match correct format;
372  *  - all channel frequencies are in correct range.
373  */
TEST_F(BroadcastRadioHalTest,GetDabRegionConfig)374 TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
375     Result halResult;
376     hidl_vec<DabTableEntry> config;
377     auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
378         halResult = result;
379         config = configCb;
380     };
381     auto hidlResult = mModule->getDabRegionConfig(cb);
382     ASSERT_TRUE(hidlResult.isOk());
383 
384     if (halResult == Result::NOT_SUPPORTED) {
385         printSkipped("DAB not supported");
386         return;
387     }
388     ASSERT_EQ(Result::OK, halResult);
389 
390     std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
391     // double-check correctness of the test
392     ASSERT_TRUE(std::regex_match("5A", re));
393     ASSERT_FALSE(std::regex_match("5a", re));
394     ASSERT_FALSE(std::regex_match("1234ABCD", re));
395     ASSERT_TRUE(std::regex_match("CN 12D", re));
396     ASSERT_FALSE(std::regex_match(" 5A", re));
397 
398     for (auto&& entry : config) {
399         EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
400 
401         auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
402         EXPECT_TRUE(utils::isValid(id));
403     }
404 }
405 
406 /**
407  * Test tuning with FM selector.
408  *
409  * Verifies that:
410  *  - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
411  *  - if it is supported, the method succeeds;
412  *  - after a successful tune call, onCurrentProgramInfoChanged callback is
413  *    invoked carrying a proper selector;
414  *  - program changes exactly to what was requested.
415  */
TEST_F(BroadcastRadioHalTest,FmTune)416 TEST_F(BroadcastRadioHalTest, FmTune) {
417     ASSERT_TRUE(openSession());
418 
419     uint64_t freq = 100100;  // 100.1 FM
420     auto sel = make_selector_amfm(freq);
421 
422     /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
423      * callback setting infoCb, because egmock cannot distinguish calls with different matchers
424      * (there is one here and one in callback constructor).
425      *
426      * This sleep workaround will fix default implementation, but the real HW tests will still be
427      * flaky. We probably need to implement egmock alternative based on actions.
428      */
429     std::this_thread::sleep_for(gTuneWorkaround);
430 
431     // try tuning
432     ProgramInfo infoCb = {};
433     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
434                         InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
435         .Times(AnyNumber())
436         .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
437     auto result = mSession->tune(sel);
438 
439     // expect a failure if it's not supported
440     if (!utils::isSupported(mProperties, sel)) {
441         EXPECT_EQ(Result::NOT_SUPPORTED, result);
442         return;
443     }
444 
445     // expect a callback if it succeeds
446     EXPECT_EQ(Result::OK, result);
447     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
448 
449     ALOGD("current program info: %s", toString(infoCb).c_str());
450 
451     // it should tune exactly to what was requested
452     auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
453     EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
454 }
455 
456 /**
457  * Test tuning with invalid selectors.
458  *
459  * Verifies that:
460  *  - if the selector is not supported, it's ignored;
461  *  - if it is supported, an invalid value results with INVALID_ARGUMENTS;
462  */
TEST_F(BroadcastRadioHalTest,TuneFailsWithInvalid)463 TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
464     ASSERT_TRUE(openSession());
465 
466     vector<ProgramIdentifier> invalid = {
467         make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
468         make_identifier(IdentifierType::RDS_PI, 0x10000),
469         make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
470         make_identifier(IdentifierType::DAB_SID_EXT, 0),
471         make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
472         make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
473     };
474 
475     for (auto&& id : invalid) {
476         ProgramSelector sel{id, {}};
477 
478         auto result = mSession->tune(sel);
479 
480         if (utils::isSupported(mProperties, sel)) {
481             EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
482         } else {
483             EXPECT_EQ(Result::NOT_SUPPORTED, result);
484         }
485     }
486 }
487 
488 /**
489  * Test tuning with empty program selector.
490  *
491  * Verifies that:
492  *  - tune fails with NOT_SUPPORTED when program selector is not initialized.
493  */
TEST_F(BroadcastRadioHalTest,TuneFailsWithEmpty)494 TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
495     ASSERT_TRUE(openSession());
496 
497     // Program type is 1-based, so 0 will always be invalid.
498     ProgramSelector sel = {};
499     auto result = mSession->tune(sel);
500     ASSERT_EQ(Result::NOT_SUPPORTED, result);
501 }
502 
503 /**
504  * Test seeking to next/prev station via ITunerSession::scan().
505  *
506  * Verifies that:
507  *  - the method succeeds;
508  *  - the program info is changed within timeout::tune;
509  *  - works both directions and with or without skipping sub-channel.
510  */
TEST_F(BroadcastRadioHalTest,Seek)511 TEST_F(BroadcastRadioHalTest, Seek) {
512     ASSERT_TRUE(openSession());
513 
514     // TODO(b/69958777): see FmTune workaround
515     std::this_thread::sleep_for(gTuneWorkaround);
516 
517     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
518     auto result = mSession->scan(true /* up */, true /* skip subchannel */);
519     EXPECT_EQ(Result::OK, result);
520     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
521 
522     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
523     result = mSession->scan(false /* down */, false /* don't skip subchannel */);
524     EXPECT_EQ(Result::OK, result);
525     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
526 }
527 
528 /**
529  * Test step operation.
530  *
531  * Verifies that:
532  *  - the method succeeds or returns NOT_SUPPORTED;
533  *  - the program info is changed within timeout::tune if the method succeeded;
534  *  - works both directions.
535  */
TEST_F(BroadcastRadioHalTest,Step)536 TEST_F(BroadcastRadioHalTest, Step) {
537     ASSERT_TRUE(openSession());
538 
539     // TODO(b/69958777): see FmTune workaround
540     std::this_thread::sleep_for(gTuneWorkaround);
541 
542     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
543     auto result = mSession->step(true /* up */);
544     if (result == Result::NOT_SUPPORTED) {
545         printSkipped("step not supported");
546         return;
547     }
548     EXPECT_EQ(Result::OK, result);
549     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
550 
551     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
552     result = mSession->step(false /* down */);
553     EXPECT_EQ(Result::OK, result);
554     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
555 }
556 
557 /**
558  * Test tune cancellation.
559  *
560  * Verifies that:
561  *  - the method does not crash after being invoked multiple times.
562  */
TEST_F(BroadcastRadioHalTest,Cancel)563 TEST_F(BroadcastRadioHalTest, Cancel) {
564     ASSERT_TRUE(openSession());
565 
566     for (int i = 0; i < 10; i++) {
567         auto result = mSession->scan(true /* up */, true /* skip subchannel */);
568         ASSERT_EQ(Result::OK, result);
569 
570         auto cancelResult = mSession->cancel();
571         ASSERT_TRUE(cancelResult.isOk());
572     }
573 }
574 
575 /**
576  * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
577  *
578  * Verifies that:
579  *  - callback is called for empty parameters set.
580  */
TEST_F(BroadcastRadioHalTest,NoParameters)581 TEST_F(BroadcastRadioHalTest, NoParameters) {
582     ASSERT_TRUE(openSession());
583 
584     hidl_vec<VendorKeyValue> halResults = {};
585     bool wasCalled = false;
586     auto cb = [&](hidl_vec<VendorKeyValue> results) {
587         wasCalled = true;
588         halResults = results;
589     };
590 
591     auto hidlResult = mSession->setParameters({}, cb);
592     ASSERT_TRUE(hidlResult.isOk());
593     ASSERT_TRUE(wasCalled);
594     ASSERT_EQ(0u, halResults.size());
595 
596     wasCalled = false;
597     hidlResult = mSession->getParameters({}, cb);
598     ASSERT_TRUE(hidlResult.isOk());
599     ASSERT_TRUE(wasCalled);
600     ASSERT_EQ(0u, halResults.size());
601 }
602 
603 /**
604  * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
605  *
606  * Verifies that:
607  *  - unknown parameters are ignored;
608  *  - callback is called also for empty results set.
609  */
TEST_F(BroadcastRadioHalTest,UnknownParameters)610 TEST_F(BroadcastRadioHalTest, UnknownParameters) {
611     ASSERT_TRUE(openSession());
612 
613     hidl_vec<VendorKeyValue> halResults = {};
614     bool wasCalled = false;
615     auto cb = [&](hidl_vec<VendorKeyValue> results) {
616         wasCalled = true;
617         halResults = results;
618     };
619 
620     auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
621     ASSERT_TRUE(hidlResult.isOk());
622     ASSERT_TRUE(wasCalled);
623     ASSERT_EQ(0u, halResults.size());
624 
625     wasCalled = false;
626     hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
627     ASSERT_TRUE(hidlResult.isOk());
628     ASSERT_TRUE(wasCalled);
629     ASSERT_EQ(0u, halResults.size());
630 }
631 
632 /**
633  * Test session closing.
634  *
635  * Verifies that:
636  *  - the method does not crash after being invoked multiple times.
637  */
TEST_F(BroadcastRadioHalTest,Close)638 TEST_F(BroadcastRadioHalTest, Close) {
639     ASSERT_TRUE(openSession());
640 
641     for (int i = 0; i < 10; i++) {
642         auto cancelResult = mSession->close();
643         ASSERT_TRUE(cancelResult.isOk());
644     }
645 }
646 
647 /**
648  * Test geting image of invalid ID.
649  *
650  * Verifies that:
651  * - getImage call handles argument 0 gracefully.
652  */
TEST_F(BroadcastRadioHalTest,GetNoImage)653 TEST_F(BroadcastRadioHalTest, GetNoImage) {
654     size_t len = 0;
655     auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
656 
657     ASSERT_TRUE(result.isOk());
658     ASSERT_EQ(0u, len);
659 }
660 
661 /**
662  * Test getting config flags.
663  *
664  * Verifies that:
665  * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
666  * - call success or failure is consistent with setConfigFlag.
667  */
TEST_F(BroadcastRadioHalTest,FetchConfigFlags)668 TEST_F(BroadcastRadioHalTest, FetchConfigFlags) {
669     ASSERT_TRUE(openSession());
670 
671     for (auto flag : gConfigFlagValues) {
672         auto halResult = Result::UNKNOWN_ERROR;
673         auto cb = [&](Result result, bool) { halResult = result; };
674         auto hidlResult = mSession->isConfigFlagSet(flag, cb);
675         EXPECT_TRUE(hidlResult.isOk());
676 
677         if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
678             ASSERT_EQ(Result::OK, halResult);
679         }
680 
681         // set must fail or succeed the same way as get
682         auto setResult = mSession->setConfigFlag(flag, false);
683         EXPECT_EQ(halResult, setResult);
684         setResult = mSession->setConfigFlag(flag, true);
685         EXPECT_EQ(halResult, setResult);
686     }
687 }
688 
689 /**
690  * Test setting config flags.
691  *
692  * Verifies that:
693  * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
694  * - isConfigFlagSet reflects the state requested immediately after the set call.
695  */
TEST_F(BroadcastRadioHalTest,SetConfigFlags)696 TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
697     ASSERT_TRUE(openSession());
698 
699     auto get = [&](ConfigFlag flag) {
700         auto halResult = Result::UNKNOWN_ERROR;
701         bool gotValue = false;
702         auto cb = [&](Result result, bool value) {
703             halResult = result;
704             gotValue = value;
705         };
706         auto hidlResult = mSession->isConfigFlagSet(flag, cb);
707         EXPECT_TRUE(hidlResult.isOk());
708         EXPECT_EQ(Result::OK, halResult);
709         return gotValue;
710     };
711 
712     for (auto flag : gConfigFlagValues) {
713         auto result = mSession->setConfigFlag(flag, false);
714         if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
715             // setting to true must result in the same error as false
716             auto secondResult = mSession->setConfigFlag(flag, true);
717             EXPECT_EQ(result, secondResult);
718             continue;
719         }
720         ASSERT_EQ(Result::OK, result);
721 
722         // verify false is set
723         auto value = get(flag);
724         EXPECT_FALSE(value);
725 
726         // try setting true this time
727         result = mSession->setConfigFlag(flag, true);
728         ASSERT_EQ(Result::OK, result);
729         value = get(flag);
730         EXPECT_TRUE(value);
731 
732         // false again
733         result = mSession->setConfigFlag(flag, false);
734         ASSERT_EQ(Result::OK, result);
735         value = get(flag);
736         EXPECT_FALSE(value);
737     }
738 }
739 
740 /**
741  * Test getting program list.
742  *
743  * Verifies that:
744  * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
745  * - the complete list is fetched within timeout::programListScan;
746  * - stopProgramListUpdates does not crash.
747  */
TEST_F(BroadcastRadioHalTest,GetProgramList)748 TEST_F(BroadcastRadioHalTest, GetProgramList) {
749     ASSERT_TRUE(openSession());
750 
751     getProgramList();
752 }
753 
754 /**
755  * Test HD_STATION_NAME correctness.
756  *
757  * Verifies that if a program on the list contains HD_STATION_NAME identifier:
758  *  - the program provides station name in its metadata;
759  *  - the identifier matches the name;
760  *  - there is only one identifier of that type.
761  */
TEST_F(BroadcastRadioHalTest,HdRadioStationNameId)762 TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
763     ASSERT_TRUE(openSession());
764 
765     auto list = getProgramList();
766     if (!list) return;
767 
768     for (auto&& program : *list) {
769         auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
770         EXPECT_LE(nameIds.size(), 1u);
771         if (nameIds.size() == 0) continue;
772 
773         auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
774         if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
775         ASSERT_TRUE(name.has_value());
776 
777         auto expectedId = utils::make_hdradio_station_name(*name);
778         EXPECT_EQ(expectedId.value, nameIds[0]);
779     }
780 }
781 
782 /**
783  * Test announcement listener registration.
784  *
785  * Verifies that:
786  *  - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
787  *  - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
788  *  - closing handle does not crash.
789  */
TEST_F(BroadcastRadioHalTest,AnnouncementListenerRegistration)790 TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
791     sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
792 
793     Result halResult = Result::UNKNOWN_ERROR;
794     sp<ICloseHandle> closeHandle = nullptr;
795     auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
796         halResult = result;
797         closeHandle = closeHandle_;
798     };
799 
800     auto hidlResult =
801         mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
802     ASSERT_TRUE(hidlResult.isOk());
803 
804     if (halResult == Result::NOT_SUPPORTED) {
805         ASSERT_EQ(nullptr, closeHandle.get());
806         printSkipped("Announcements not supported");
807         return;
808     }
809 
810     ASSERT_EQ(Result::OK, halResult);
811     ASSERT_NE(nullptr, closeHandle.get());
812 
813     closeHandle->close();
814 }
815 
816 }  // namespace vts
817 }  // namespace V2_0
818 }  // namespace broadcastradio
819 }  // namespace hardware
820 }  // namespace android
821 
main(int argc,char ** argv)822 int main(int argc, char** argv) {
823     using android::hardware::broadcastradio::V2_0::vts::gEnv;
824     using android::hardware::broadcastradio::V2_0::IBroadcastRadio;
825     using android::hardware::broadcastradio::vts::BroadcastRadioHidlEnvironment;
826     gEnv = new BroadcastRadioHidlEnvironment<IBroadcastRadio>;
827     ::testing::AddGlobalTestEnvironment(gEnv);
828     ::testing::InitGoogleTest(&argc, argv);
829     gEnv->init(&argc, argv);
830     int status = RUN_ALL_TESTS();
831     ALOGI("Test result = %d", status);
832     return status;
833 }
834