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