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