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