blob: 356673f715c4ba3a04857b6df99a51f47ff9cfa1 [file] [log] [blame]
Weilin Xub23d0ea2022-05-09 18:26:23 +00001/*
2 * Copyright (C) 2022 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 <aidl/android/hardware/broadcastradio/BnAnnouncementListener.h>
20#include <aidl/android/hardware/broadcastradio/BnTunerCallback.h>
21#include <aidl/android/hardware/broadcastradio/ConfigFlag.h>
22#include <aidl/android/hardware/broadcastradio/IBroadcastRadio.h>
23#include <aidl/android/hardware/broadcastradio/ProgramListChunk.h>
24#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
25#include <aidl/android/hardware/broadcastradio/VendorKeyValue.h>
26#include <android-base/logging.h>
27#include <android-base/strings.h>
28#include <android-base/thread_annotations.h>
29#include <android/binder_manager.h>
30#include <android/binder_process.h>
31
32#include <aidl/Gtest.h>
33#include <aidl/Vintf.h>
34#include <broadcastradio-utils-aidl/Utils.h>
35#include <broadcastradio-vts-utils/mock-timeout.h>
36#include <cutils/bitops.h>
37#include <gmock/gmock.h>
38
39#include <chrono>
40#include <optional>
41#include <regex>
42
43namespace aidl::android::hardware::broadcastradio::vts {
44
45namespace {
46
47using ::aidl::android::hardware::broadcastradio::utils::makeIdentifier;
48using ::aidl::android::hardware::broadcastradio::utils::makeSelectorAmfm;
Weilin Xu0d4207d2022-12-09 00:37:44 +000049using ::aidl::android::hardware::broadcastradio::utils::makeSelectorDab;
Weilin Xub23d0ea2022-05-09 18:26:23 +000050using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
51using ::ndk::ScopedAStatus;
52using ::ndk::SharedRefBase;
53using ::std::string;
54using ::std::vector;
55using ::testing::_;
56using ::testing::AnyNumber;
57using ::testing::ByMove;
58using ::testing::DoAll;
59using ::testing::Invoke;
60using ::testing::SaveArg;
61
62namespace bcutils = ::aidl::android::hardware::broadcastradio::utils;
63
64inline constexpr std::chrono::seconds kTuneTimeoutSec =
65 std::chrono::seconds(IBroadcastRadio::TUNER_TIMEOUT_MS * 1000);
66inline constexpr std::chrono::seconds kProgramListScanTimeoutSec =
67 std::chrono::seconds(IBroadcastRadio::LIST_COMPLETE_TIMEOUT_MS * 1000);
68
Weilin Xu3277a212022-09-01 19:08:17 +000069const ConfigFlag kConfigFlagValues[] = {
70 ConfigFlag::FORCE_MONO,
71 ConfigFlag::FORCE_ANALOG,
72 ConfigFlag::FORCE_DIGITAL,
73 ConfigFlag::RDS_AF,
74 ConfigFlag::RDS_REG,
75 ConfigFlag::DAB_DAB_LINKING,
76 ConfigFlag::DAB_FM_LINKING,
77 ConfigFlag::DAB_DAB_SOFT_LINKING,
78 ConfigFlag::DAB_FM_SOFT_LINKING,
79};
80
Weilin Xub23d0ea2022-05-09 18:26:23 +000081void printSkipped(const string& msg) {
82 const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
83 LOG(INFO) << "[ SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
84 << " with message: " << msg;
85}
86
Weilin Xu3277a212022-09-01 19:08:17 +000087bool isValidAmFmFreq(int64_t freq) {
88 ProgramIdentifier id = bcutils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq);
89 return bcutils::isValid(id);
90}
91
92void validateRange(const AmFmBandRange& range) {
93 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
94 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
95 EXPECT_LT(range.lowerBound, range.upperBound);
96 EXPECT_GT(range.spacing, 0u);
97 EXPECT_EQ((range.upperBound - range.lowerBound) % range.spacing, 0u);
98}
99
100bool supportsFM(const AmFmRegionConfig& config) {
101 for (const auto& range : config.ranges) {
102 if (bcutils::getBand(range.lowerBound) == bcutils::FrequencyBand::FM) {
103 return true;
104 }
105 }
106 return false;
107}
108
Weilin Xub23d0ea2022-05-09 18:26:23 +0000109} // namespace
110
111class TunerCallbackMock : public BnTunerCallback {
112 public:
113 TunerCallbackMock();
Weilin Xu0d4207d2022-12-09 00:37:44 +0000114 ScopedAStatus onTuneFailed(Result result, const ProgramSelector& selector) override;
Weilin Xub23d0ea2022-05-09 18:26:23 +0000115 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChangedMock, ScopedAStatus(const ProgramInfo&));
116 ScopedAStatus onCurrentProgramInfoChanged(const ProgramInfo& info) override;
117 ScopedAStatus onProgramListUpdated(const ProgramListChunk& chunk) override;
118 MOCK_METHOD1(onAntennaStateChange, ScopedAStatus(bool connected));
119 MOCK_METHOD1(onParametersUpdated, ScopedAStatus(const vector<VendorKeyValue>& parameters));
120 MOCK_METHOD2(onConfigFlagUpdated, ScopedAStatus(ConfigFlag in_flag, bool in_value));
121 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
122
123 std::mutex mLock;
124 bcutils::ProgramInfoSet mProgramList GUARDED_BY(mLock);
125};
126
127struct AnnouncementListenerMock : public BnAnnouncementListener {
128 MOCK_METHOD1(onListUpdated, ScopedAStatus(const vector<Announcement>&));
129};
130
131class BroadcastRadioHalTest : public testing::TestWithParam<string> {
132 protected:
133 void SetUp() override;
134 void TearDown() override;
135
136 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
137 std::optional<bcutils::ProgramInfoSet> getProgramList();
138 std::optional<bcutils::ProgramInfoSet> getProgramList(const ProgramFilter& filter);
139
140 std::shared_ptr<IBroadcastRadio> mModule;
141 Properties mProperties;
142 std::shared_ptr<TunerCallbackMock> mCallback = SharedRefBase::make<TunerCallbackMock>();
143};
144
145MATCHER_P(InfoHasId, id, string(negation ? "does not contain" : "contains") + " " + id.toString()) {
146 vector<int> ids = bcutils::getAllIds(arg.selector, id.type);
147 return ids.end() != find(ids.begin(), ids.end(), id.value);
148}
149
150TunerCallbackMock::TunerCallbackMock() {
151 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
152
153 // we expect the antenna is connected through the whole test
154 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
155}
156
Weilin Xu0d4207d2022-12-09 00:37:44 +0000157ScopedAStatus TunerCallbackMock::onTuneFailed(Result result, const ProgramSelector& selector) {
158 LOG(DEBUG) << "Tune failed for selector" << selector.toString();
159 EXPECT_TRUE(result == Result::CANCELED);
160 return ndk::ScopedAStatus::ok();
161}
162
Weilin Xub23d0ea2022-05-09 18:26:23 +0000163ScopedAStatus TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
164 for (const auto& id : info.selector) {
165 EXPECT_NE(id.type, IdentifierType::INVALID);
166 }
167
168 IdentifierType logically = info.logicallyTunedTo.type;
169 // This field is required for currently tuned program and should be INVALID
170 // for entries from the program list.
171 EXPECT_TRUE(logically == IdentifierType::AMFM_FREQUENCY_KHZ ||
172 logically == IdentifierType::RDS_PI ||
173 logically == IdentifierType::HD_STATION_ID_EXT ||
174 logically == IdentifierType::DAB_SID_EXT ||
175 logically == IdentifierType::DRMO_SERVICE_ID ||
176 logically == IdentifierType::SXM_SERVICE_ID ||
177 (logically >= IdentifierType::VENDOR_START &&
178 logically <= IdentifierType::VENDOR_END) ||
179 logically > IdentifierType::SXM_CHANNEL);
180
181 IdentifierType physically = info.physicallyTunedTo.type;
182 // ditto (see "logically" above)
183 EXPECT_TRUE(physically == IdentifierType::AMFM_FREQUENCY_KHZ ||
Weilin Xu0d4207d2022-12-09 00:37:44 +0000184 physically == IdentifierType::DAB_FREQUENCY_KHZ ||
Weilin Xub23d0ea2022-05-09 18:26:23 +0000185 physically == IdentifierType::DRMO_FREQUENCY_KHZ ||
186 physically == IdentifierType::SXM_CHANNEL ||
187 (physically >= IdentifierType::VENDOR_START &&
188 physically <= IdentifierType::VENDOR_END) ||
189 physically > IdentifierType::SXM_CHANNEL);
190
191 if (logically == IdentifierType::AMFM_FREQUENCY_KHZ) {
192 std::optional<string> ps = bcutils::getMetadataString(info, Metadata::rdsPs);
193 if (ps.has_value()) {
194 EXPECT_NE(::android::base::Trim(*ps), "")
195 << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
196 }
197 }
198
199 return onCurrentProgramInfoChangedMock(info);
200}
201
202ScopedAStatus TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
203 std::lock_guard<std::mutex> lk(mLock);
204
205 updateProgramList(chunk, &mProgramList);
206
207 if (chunk.complete) {
208 onProgramListReady();
209 }
210
211 return ndk::ScopedAStatus::ok();
212}
213
214void BroadcastRadioHalTest::SetUp() {
215 EXPECT_EQ(mModule.get(), nullptr) << "Module is already open";
216
217 // lookup AIDL service (radio module)
218 AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
219 ASSERT_NE(binder, nullptr);
220 mModule = IBroadcastRadio::fromBinder(ndk::SpAIBinder(binder));
221 ASSERT_NE(mModule, nullptr) << "Couldn't find broadcast radio HAL implementation";
222
223 // get module properties
224 auto propResult = mModule->getProperties(&mProperties);
225
226 ASSERT_TRUE(propResult.isOk());
227 EXPECT_FALSE(mProperties.maker.empty());
228 EXPECT_FALSE(mProperties.product.empty());
229 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
230
231 // set callback
232 EXPECT_TRUE(mModule->setTunerCallback(mCallback).isOk());
233}
234
235void BroadcastRadioHalTest::TearDown() {
236 if (mModule) {
237 ASSERT_TRUE(mModule->unsetTunerCallback().isOk());
238 }
239}
240
241bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
242 auto halResult = mModule->getAmFmRegionConfig(full, config);
243
244 if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
245 return false;
246 }
247
248 EXPECT_TRUE(halResult.isOk());
249 return halResult.isOk();
250}
251
252std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
253 ProgramFilter emptyFilter = {};
254 return getProgramList(emptyFilter);
255}
256
257std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList(
258 const ProgramFilter& filter) {
259 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
260
261 auto startResult = mModule->startProgramListUpdates(filter);
262
263 if (startResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
264 printSkipped("Program list not supported");
265 return std::nullopt;
266 }
267 EXPECT_TRUE(startResult.isOk());
268 if (!startResult.isOk()) {
269 return std::nullopt;
270 }
271 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, kProgramListScanTimeoutSec);
272
273 auto stopResult = mModule->stopProgramListUpdates();
274
275 EXPECT_TRUE(stopResult.isOk());
276
277 return mCallback->mProgramList;
278}
279
280/**
281 * Test setting tuner callback to null.
282 *
283 * Verifies that:
284 * - Setting to a null tuner callback results with INVALID_ARGUMENTS.
285 */
286TEST_P(BroadcastRadioHalTest, TunerCallbackFailsWithNull) {
287 LOG(DEBUG) << "TunerCallbackFailsWithNull Test";
288
289 auto halResult = mModule->setTunerCallback(nullptr);
290
291 EXPECT_EQ(halResult.getServiceSpecificError(), resultToInt(Result::INVALID_ARGUMENTS));
292}
293
294/**
Weilin Xu3277a212022-09-01 19:08:17 +0000295 * Test fetching AM/FM regional configuration.
296 *
297 * Verifies that:
298 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
299 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
300 */
301TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfig) {
302 LOG(DEBUG) << "GetAmFmRegionConfig Test";
303
304 AmFmRegionConfig config;
305
306 bool supported = getAmFmRegionConfig(/* full= */ false, &config);
307
308 if (!supported) {
309 printSkipped("AM/FM not supported");
310 return;
311 }
312
313 EXPECT_LE(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
314 EXPECT_LE(popcountll(static_cast<unsigned long long>(config.fmRds)), 1);
315
316 if (supportsFM(config)) {
317 EXPECT_EQ(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
318 }
319}
320
321/**
322 * Test fetching ranges of AM/FM regional configuration.
323 *
324 * Verifies that:
325 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
326 * - there is at least one AM/FM band configured;
327 * - all channel grids (frequency ranges and spacings) are valid;
328 * - seek spacing is a multiple of the manual spacing value.
329 */
330TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigRanges) {
331 LOG(DEBUG) << "GetAmFmRegionConfigRanges Test";
332
333 AmFmRegionConfig config;
334
335 bool supported = getAmFmRegionConfig(/* full= */ false, &config);
336
337 if (!supported) {
338 printSkipped("AM/FM not supported");
339 return;
340 }
341
342 EXPECT_GT(config.ranges.size(), 0u);
343 for (const auto& range : config.ranges) {
344 validateRange(range);
345 EXPECT_EQ(range.seekSpacing % range.spacing, 0u);
346 EXPECT_GE(range.seekSpacing, range.spacing);
347 }
348}
349
350/**
351 * Test fetching FM regional capabilities.
352 *
353 * Verifies that:
354 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
355 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
356 */
357TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilitiesForFM) {
358 LOG(DEBUG) << "GetAmFmRegionConfigCapabilitiesForFM Test";
359
360 AmFmRegionConfig config;
361
362 bool supported = getAmFmRegionConfig(/* full= */ true, &config);
363
364 if (supported && supportsFM(config)) {
365 EXPECT_GE(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
366 } else {
367 printSkipped("FM not supported");
368 }
369}
370
371/**
372 * Test fetching the ranges of AM/FM regional capabilities.
373 *
374 * Verifies that:
375 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
376 * - there is at least one AM/FM range supported;
377 * - all channel grids (frequency ranges and spacings) are valid;
378 * - seek spacing is not set.
379 */
380TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilitiesRanges) {
381 LOG(DEBUG) << "GetAmFmRegionConfigCapabilitiesRanges Test";
382
383 AmFmRegionConfig config;
384
385 bool supported = getAmFmRegionConfig(/* full= */ true, &config);
386
387 if (!supported) {
388 printSkipped("AM/FM not supported");
389 return;
390 }
391
392 EXPECT_GT(config.ranges.size(), 0u);
393
394 for (const auto& range : config.ranges) {
395 validateRange(range);
396 EXPECT_EQ(range.seekSpacing, 0u);
397 }
398}
399
400/**
401 * Test fetching DAB regional configuration.
402 *
403 * Verifies that:
404 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
405 * - all channel labels match correct format;
406 * - all channel frequencies are in correct range.
407 */
408TEST_P(BroadcastRadioHalTest, GetDabRegionConfig) {
409 LOG(DEBUG) << "GetDabRegionConfig Test";
410 vector<DabTableEntry> config;
411
412 auto halResult = mModule->getDabRegionConfig(&config);
413
414 if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
415 printSkipped("DAB not supported");
416 return;
417 }
418 ASSERT_TRUE(halResult.isOk());
419
420 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
421
422 for (const auto& entry : config) {
423 EXPECT_TRUE(std::regex_match(string(entry.label), re));
424
425 ProgramIdentifier id =
426 bcutils::makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, entry.frequencyKhz);
427 EXPECT_TRUE(bcutils::isValid(id));
428 }
429}
430
431/**
Weilin Xub23d0ea2022-05-09 18:26:23 +0000432 * Test tuning without tuner callback set.
433 *
434 * Verifies that:
435 * - No tuner callback set results in INVALID_STATE, regardless of whether the selector is
436 * supported.
437 */
438TEST_P(BroadcastRadioHalTest, TuneFailsWithoutTunerCallback) {
439 LOG(DEBUG) << "TuneFailsWithoutTunerCallback Test";
440
441 mModule->unsetTunerCallback();
442 int64_t freq = 90900; // 90.9 FM
443 ProgramSelector sel = makeSelectorAmfm(freq);
444
445 auto result = mModule->tune(sel);
446
447 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
448}
449
450/**
451 * Test tuning with selectors that can be not supported.
452 *
453 * Verifies that:
454 * - if the selector is not supported, an invalid value results with NOT_SUPPORTED, regardless of
455 * whether it is valid;
456 * - if it is supported, the test is ignored;
457 */
458TEST_P(BroadcastRadioHalTest, TuneFailsWithNotSupported) {
459 LOG(DEBUG) << "TuneFailsWithInvalid Test";
460
461 vector<ProgramIdentifier> supportTestId = {
462 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0), // invalid
463 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 94900), // valid
464 makeIdentifier(IdentifierType::RDS_PI, 0x10000), // invalid
465 makeIdentifier(IdentifierType::RDS_PI, 0x1001), // valid
466 makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000), // invalid
467 makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x10000001), // valid
468 makeIdentifier(IdentifierType::DAB_SID_EXT, 0), // invalid
469 makeIdentifier(IdentifierType::DAB_SID_EXT, 0xA00001), // valid
470 makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000), // invalid
471 makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x10000001), // valid
472 makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000), // invalid
473 makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x10000001), // valid
474 };
475
476 auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
477 for (const auto& id : supportTestId) {
478 ProgramSelector sel{id, {}};
479
480 auto result = mModule->tune(sel);
481
482 if (!bcutils::isSupported(mProperties, sel)) {
483 EXPECT_EQ(result.getServiceSpecificError(), notSupportedError);
484 }
485 }
486}
487
488/**
489 * Test tuning with invalid selectors.
490 *
491 * Verifies that:
492 * - if the selector is not supported, it's ignored;
493 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
494 */
495TEST_P(BroadcastRadioHalTest, TuneFailsWithInvalid) {
496 LOG(DEBUG) << "TuneFailsWithInvalid Test";
497
498 vector<ProgramIdentifier> invalidId = {
499 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0),
500 makeIdentifier(IdentifierType::RDS_PI, 0x10000),
501 makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
502 makeIdentifier(IdentifierType::DAB_SID_EXT, 0),
503 makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
504 makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
505 };
506
507 auto invalidArgumentsError = resultToInt(Result::INVALID_ARGUMENTS);
508 for (const auto& id : invalidId) {
509 ProgramSelector sel{id, {}};
510
511 auto result = mModule->tune(sel);
512
513 if (bcutils::isSupported(mProperties, sel)) {
514 EXPECT_EQ(result.getServiceSpecificError(), invalidArgumentsError);
515 }
516 }
517}
518
519/**
520 * Test tuning with empty program selector.
521 *
522 * Verifies that:
523 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
524 */
525TEST_P(BroadcastRadioHalTest, TuneFailsWithEmpty) {
526 LOG(DEBUG) << "TuneFailsWithEmpty Test";
527
528 // Program type is 1-based, so 0 will always be invalid.
529 ProgramSelector sel = {};
530
531 auto result = mModule->tune(sel);
532
533 ASSERT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
534}
535
536/**
537 * Test tuning with FM selector.
538 *
539 * Verifies that:
540 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
541 * - if it is supported, the method succeeds;
542 * - after a successful tune call, onCurrentProgramInfoChanged callback is
543 * invoked carrying a proper selector;
544 * - program changes exactly to what was requested.
545 */
546TEST_P(BroadcastRadioHalTest, FmTune) {
547 LOG(DEBUG) << "FmTune Test";
548
549 int64_t freq = 90900; // 90.9 FM
550 ProgramSelector sel = makeSelectorAmfm(freq);
551 // try tuning
552 ProgramInfo infoCb = {};
553 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
554 InfoHasId(makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq)))
555 .Times(AnyNumber())
556 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))))
557 .WillRepeatedly(testing::InvokeWithoutArgs([] { return ndk::ScopedAStatus::ok(); }));
558
559 auto result = mModule->tune(sel);
560
561 // expect a failure if it's not supported
562 if (!bcutils::isSupported(mProperties, sel)) {
563 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
564 return;
565 }
566
567 // expect a callback if it succeeds
568 EXPECT_TRUE(result.isOk());
569 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
570
571 LOG(DEBUG) << "Current program info: " << infoCb.toString();
572
573 // it should tune exactly to what was requested
574 vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
575 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
576 << "FM freq " << freq << " kHz is not sent back by callback.";
577}
578
579/**
580 * Test tuning with DAB selector.
581 *
582 * Verifies that:
583 * - if DAB selector is not supported, the method returns NOT_SUPPORTED;
584 * - if it is supported, the method succeeds;
585 * - after a successful tune call, onCurrentProgramInfoChanged callback is
586 * invoked carrying a proper selector;
587 * - program changes exactly to what was requested.
588 */
589TEST_P(BroadcastRadioHalTest, DabTune) {
590 LOG(DEBUG) << "DabTune Test";
591 vector<DabTableEntry> config;
592
593 auto halResult = mModule->getDabRegionConfig(&config);
594
595 if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
596 printSkipped("DAB not supported");
597 return;
598 }
599 ASSERT_TRUE(halResult.isOk());
600 ASSERT_NE(config.size(), 0U);
601
Weilin Xu0d4207d2022-12-09 00:37:44 +0000602 auto programList = getProgramList();
603
604 if (!programList) {
605 printSkipped("Empty DAB station list, tune cannot be performed");
606 return;
607 }
608
Weilin Xub23d0ea2022-05-09 18:26:23 +0000609 ProgramSelector sel = {};
Weilin Xu0d4207d2022-12-09 00:37:44 +0000610 uint64_t freq = 0;
611 bool dabStationPresent = false;
612 for (auto&& programInfo : *programList) {
613 if (!utils::hasId(programInfo.selector, IdentifierType::DAB_FREQUENCY_KHZ)) {
614 continue;
615 }
616 for (auto&& config_entry : config) {
617 if (config_entry.frequencyKhz ==
618 utils::getId(programInfo.selector, IdentifierType::DAB_FREQUENCY_KHZ, 0)) {
619 freq = config_entry.frequencyKhz;
620 break;
621 }
622 }
623 // Do not trigger a tune request if the programList entry does not contain
624 // a valid DAB frequency.
625 if (freq == 0) {
626 continue;
627 }
628 int64_t dabSidExt = utils::getId(programInfo.selector, IdentifierType::DAB_SID_EXT, 0);
629 int64_t dabEns = utils::getId(programInfo.selector, IdentifierType::DAB_ENSEMBLE, 0);
630 sel = makeSelectorDab(dabSidExt, (int32_t)dabEns, freq);
631 dabStationPresent = true;
632 break;
633 }
634
635 if (!dabStationPresent) {
636 printSkipped("No DAB stations in the list, tune cannot be performed");
637 return;
638 }
Weilin Xub23d0ea2022-05-09 18:26:23 +0000639
640 // try tuning
641 ProgramInfo infoCb = {};
642 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
643 InfoHasId(makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq)))
644 .Times(AnyNumber())
645 .WillOnce(
646 DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))));
647
648 auto result = mModule->tune(sel);
649
650 // expect a failure if it's not supported
651 if (!bcutils::isSupported(mProperties, sel)) {
652 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
653 return;
654 }
655
656 // expect a callback if it succeeds
657 EXPECT_TRUE(result.isOk());
658 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
659 LOG(DEBUG) << "Current program info: " << infoCb.toString();
660
661 // it should tune exactly to what was requested
662 vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY_KHZ);
663 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
664 << "DAB freq " << freq << " kHz is not sent back by callback.";
Weilin Xub23d0ea2022-05-09 18:26:23 +0000665}
666
667/**
668 * Test seeking to next/prev station via IBroadcastRadio::seek().
669 *
670 * Verifies that:
671 * - the method succeeds;
672 * - the program info is changed within kTuneTimeoutSec;
673 * - works both directions and with or without skipping sub-channel.
674 */
675TEST_P(BroadcastRadioHalTest, Seek) {
676 LOG(DEBUG) << "Seek Test";
677
678 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
679
680 auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
681
682 if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
683 printSkipped("Seek not supported");
684 return;
685 }
686
687 EXPECT_TRUE(result.isOk());
688 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
689
690 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
691
692 result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
693
694 EXPECT_TRUE(result.isOk());
695 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
696}
697
698/**
699 * Test seeking without tuner callback set.
700 *
701 * Verifies that:
702 * - No tuner callback set results in INVALID_STATE.
703 */
704TEST_P(BroadcastRadioHalTest, SeekFailsWithoutTunerCallback) {
705 LOG(DEBUG) << "SeekFailsWithoutTunerCallback Test";
706
707 mModule->unsetTunerCallback();
708
709 auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
710
711 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
712
713 result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
714
715 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
716}
717
718/**
719 * Test step operation.
720 *
721 * Verifies that:
722 * - the method succeeds or returns NOT_SUPPORTED;
723 * - the program info is changed within kTuneTimeoutSec if the method succeeded;
724 * - works both directions.
725 */
726TEST_P(BroadcastRadioHalTest, Step) {
727 LOG(DEBUG) << "Step Test";
728
729 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
730
731 auto result = mModule->step(/* in_directionUp= */ true);
732
733 if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
734 printSkipped("Step not supported");
735 return;
736 }
737 EXPECT_TRUE(result.isOk());
738 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
739
740 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
741
742 result = mModule->step(/* in_directionUp= */ false);
743
744 EXPECT_TRUE(result.isOk());
745 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
746}
747
748/**
749 * Test step operation without tuner callback set.
750 *
751 * Verifies that:
752 * - No tuner callback set results in INVALID_STATE.
753 */
754TEST_P(BroadcastRadioHalTest, StepFailsWithoutTunerCallback) {
755 LOG(DEBUG) << "StepFailsWithoutTunerCallback Test";
756
757 mModule->unsetTunerCallback();
758
759 auto result = mModule->step(/* in_directionUp= */ true);
760
761 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
762
763 result = mModule->step(/* in_directionUp= */ false);
764
765 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
766}
767
768/**
769 * Test tune cancellation.
770 *
771 * Verifies that:
772 * - the method does not crash after being invoked multiple times.
773 *
774 * Since cancel() might be called after the HAL completes an operation (tune, seek, and step)
775 * and before the callback completions, the operation might not be actually canceled and the
776 * effect of cancel() is not deterministic to be tested here.
777 */
778TEST_P(BroadcastRadioHalTest, Cancel) {
779 LOG(DEBUG) << "Cancel Test";
780
781 auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
782 for (int i = 0; i < 10; i++) {
783 auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
784
785 if (result.getServiceSpecificError() == notSupportedError) {
786 printSkipped("Cancel is skipped because of seek not supported");
787 return;
788 }
789 EXPECT_TRUE(result.isOk());
790
791 auto cancelResult = mModule->cancel();
792
793 ASSERT_TRUE(cancelResult.isOk());
794 }
795}
796
797/**
Weilin Xu3277a212022-09-01 19:08:17 +0000798 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
799 *
800 * Verifies that:
801 * - callback is called for empty parameters set.
802 */
803TEST_P(BroadcastRadioHalTest, NoParameters) {
804 LOG(DEBUG) << "NoParameters Test";
805
806 vector<VendorKeyValue> parametersResults = {};
807
808 auto halResult = mModule->setParameters({}, &parametersResults);
809
810 ASSERT_TRUE(halResult.isOk());
811 ASSERT_EQ(parametersResults.size(), 0u);
812
813 parametersResults.clear();
814
815 halResult = mModule->getParameters({}, &parametersResults);
816
817 ASSERT_TRUE(halResult.isOk());
818 ASSERT_EQ(parametersResults.size(), 0u);
819}
820
821/**
822 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
823 *
824 * Verifies that:
825 * - unknown parameters are ignored;
826 * - callback is called also for empty results set.
827 */
828TEST_P(BroadcastRadioHalTest, UnknownParameters) {
829 LOG(DEBUG) << "UnknownParameters Test";
830
831 vector<VendorKeyValue> parametersResults = {};
832
833 auto halResult =
834 mModule->setParameters({{"com.android.unknown", "sample"}}, &parametersResults);
835
836 ASSERT_TRUE(halResult.isOk());
837 ASSERT_EQ(parametersResults.size(), 0u);
838
839 parametersResults.clear();
840
841 halResult = mModule->getParameters({"com.android.unknown*", "sample"}, &parametersResults);
842
843 ASSERT_TRUE(halResult.isOk());
844 ASSERT_EQ(parametersResults.size(), 0u);
845}
846
847/**
848 * Test geting image of invalid ID.
849 *
850 * Verifies that:
851 * - getImage call handles argument 0 gracefully.
852 */
853TEST_P(BroadcastRadioHalTest, GetNoImage) {
854 LOG(DEBUG) << "GetNoImage Test";
855 vector<uint8_t> rawImage;
856
857 auto result = mModule->getImage(IBroadcastRadio::INVALID_IMAGE, &rawImage);
858
859 ASSERT_TRUE(result.isOk());
860 ASSERT_EQ(rawImage.size(), 0u);
861}
862
863/**
864 * Test getting config flags.
865 *
866 * Verifies that:
867 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
868 * - call success or failure is consistent with setConfigFlag.
869 */
870TEST_P(BroadcastRadioHalTest, FetchConfigFlags) {
871 LOG(DEBUG) << "FetchConfigFlags Test";
872
873 for (const auto& flag : kConfigFlagValues) {
874 bool gotValue = false;
875
876 auto halResult = mModule->isConfigFlagSet(flag, &gotValue);
877
878 if (halResult.getServiceSpecificError() != resultToInt(Result::NOT_SUPPORTED) &&
879 halResult.getServiceSpecificError() != resultToInt(Result::INVALID_STATE)) {
880 ASSERT_TRUE(halResult.isOk());
881 }
882
883 // set must fail or succeed the same way as get
884 auto setResult = mModule->setConfigFlag(flag, /* value= */ false);
885
886 EXPECT_TRUE((halResult.isOk() && setResult.isOk()) ||
887 (halResult.getServiceSpecificError()) == setResult.getServiceSpecificError());
888
889 setResult = mModule->setConfigFlag(flag, /* value= */ true);
890
891 EXPECT_TRUE((halResult.isOk() && setResult.isOk()) ||
892 (halResult.getServiceSpecificError()) == setResult.getServiceSpecificError());
893 }
894}
895
896/**
897 * Test setting config flags.
898 *
899 * Verifies that:
900 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
901 * - isConfigFlagSet reflects the state requested immediately after the set call.
902 */
903TEST_P(BroadcastRadioHalTest, SetConfigFlags) {
904 LOG(DEBUG) << "SetConfigFlags Test";
905
906 auto get = [&](ConfigFlag flag) -> bool {
907 bool* gotValue = nullptr;
908
909 auto halResult = mModule->isConfigFlagSet(flag, gotValue);
910
911 EXPECT_FALSE(gotValue == nullptr);
912 EXPECT_TRUE(halResult.isOk());
913 return *gotValue;
914 };
915
916 auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
917 auto invalidStateError = resultToInt(Result::INVALID_STATE);
918 for (const auto& flag : kConfigFlagValues) {
919 auto result = mModule->setConfigFlag(flag, /* value= */ false);
920
921 if (result.getServiceSpecificError() == notSupportedError ||
922 result.getServiceSpecificError() == invalidStateError) {
923 // setting to true must result in the same error as false
924 auto secondResult = mModule->setConfigFlag(flag, /* value= */ true);
925
926 EXPECT_TRUE((result.isOk() && secondResult.isOk()) ||
927 result.getServiceSpecificError() == secondResult.getServiceSpecificError());
928 continue;
929 } else {
930 ASSERT_TRUE(result.isOk());
931 }
932
933 // verify false is set
934 bool value = get(flag);
935 EXPECT_FALSE(value);
936
937 // try setting true this time
938 result = mModule->setConfigFlag(flag, /* value= */ true);
939
940 ASSERT_TRUE(result.isOk());
941 value = get(flag);
942 EXPECT_TRUE(value);
943
944 // false again
945 result = mModule->setConfigFlag(flag, /* value= */ false);
946
947 ASSERT_TRUE(result.isOk());
948 value = get(flag);
949 EXPECT_FALSE(value);
950 }
951}
952
953/**
Weilin Xub23d0ea2022-05-09 18:26:23 +0000954 * Test getting program list using empty program filter.
955 *
956 * Verifies that:
957 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
958 * - the complete list is fetched within kProgramListScanTimeoutSec;
959 * - stopProgramListUpdates does not crash.
960 */
961TEST_P(BroadcastRadioHalTest, GetProgramListFromEmptyFilter) {
962 LOG(DEBUG) << "GetProgramListFromEmptyFilter Test";
963
964 getProgramList();
965}
966
967/**
968 * Test getting program list using AMFM frequency program filter.
969 *
970 * Verifies that:
971 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
972 * - the complete list is fetched within kProgramListScanTimeoutSec;
973 * - stopProgramListUpdates does not crash;
974 * - result for startProgramListUpdates using a filter with AMFM_FREQUENCY_KHZ value of the first
975 * AMFM program matches the expected result.
976 */
977TEST_P(BroadcastRadioHalTest, GetProgramListFromAmFmFilter) {
978 LOG(DEBUG) << "GetProgramListFromAmFmFilter Test";
979
980 std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
981 if (!completeList) {
982 printSkipped("No program list available");
983 return;
984 }
985
986 ProgramFilter amfmFilter = {};
987 int expectedResultSize = 0;
988 uint64_t expectedFreq = 0;
989 for (const auto& program : *completeList) {
990 vector<int> amfmIds =
991 bcutils::getAllIds(program.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
992 EXPECT_LE(amfmIds.size(), 1u);
993 if (amfmIds.size() == 0) {
994 continue;
995 }
996
997 if (expectedResultSize == 0) {
998 expectedFreq = amfmIds[0];
999 amfmFilter.identifiers = {
1000 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, expectedFreq)};
1001 expectedResultSize = 1;
1002 } else if (amfmIds[0] == expectedFreq) {
1003 expectedResultSize++;
1004 }
1005 }
1006
1007 if (expectedResultSize == 0) {
1008 printSkipped("No Am/FM programs available");
1009 return;
1010 }
1011 std::optional<bcutils::ProgramInfoSet> amfmList = getProgramList(amfmFilter);
1012 ASSERT_EQ(amfmList->size(), expectedResultSize) << "amfm filter result size is wrong";
1013}
1014
1015/**
1016 * Test getting program list using DAB ensemble program filter.
1017 *
1018 * Verifies that:
1019 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
1020 * - the complete list is fetched within kProgramListScanTimeoutSec;
1021 * - stopProgramListUpdates does not crash;
1022 * - result for startProgramListUpdates using a filter with DAB_ENSEMBLE value of the first DAB
1023 * program matches the expected result.
1024 */
1025TEST_P(BroadcastRadioHalTest, GetProgramListFromDabFilter) {
1026 LOG(DEBUG) << "GetProgramListFromDabFilter Test";
1027
1028 std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
1029 if (!completeList) {
1030 printSkipped("No program list available");
1031 return;
1032 }
1033
1034 ProgramFilter dabFilter = {};
1035 int expectedResultSize = 0;
1036 uint64_t expectedEnsemble = 0;
1037 for (const auto& program : *completeList) {
1038 auto dabEnsembles = bcutils::getAllIds(program.selector, IdentifierType::DAB_ENSEMBLE);
1039 EXPECT_LE(dabEnsembles.size(), 1u);
1040 if (dabEnsembles.size() == 0) {
1041 continue;
1042 }
1043
1044 if (expectedResultSize == 0) {
1045 expectedEnsemble = dabEnsembles[0];
1046 dabFilter.identifiers = {
1047 makeIdentifier(IdentifierType::DAB_ENSEMBLE, expectedEnsemble)};
1048 expectedResultSize = 1;
1049 } else if (dabEnsembles[0] == expectedEnsemble) {
1050 expectedResultSize++;
1051 }
1052 }
1053
1054 if (expectedResultSize == 0) {
1055 printSkipped("No DAB programs available");
1056 return;
1057 }
1058 std::optional<bcutils::ProgramInfoSet> dabList = getProgramList(dabFilter);
1059 ASSERT_EQ(dabList->size(), expectedResultSize) << "dab filter result size is wrong";
1060}
1061
Weilin Xu3277a212022-09-01 19:08:17 +00001062/**
1063 * Test HD_STATION_NAME correctness.
1064 *
1065 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
1066 * - the program provides station name in its metadata;
1067 * - the identifier matches the name;
1068 * - there is only one identifier of that type.
1069 */
1070TEST_P(BroadcastRadioHalTest, HdRadioStationNameId) {
1071 LOG(DEBUG) << "HdRadioStationNameId Test";
1072
1073 std::optional<bcutils::ProgramInfoSet> list = getProgramList();
1074 if (!list) {
1075 printSkipped("No program list");
1076 return;
1077 }
1078
1079 for (const auto& program : *list) {
1080 vector<int> nameIds = bcutils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
1081 EXPECT_LE(nameIds.size(), 1u);
1082 if (nameIds.size() == 0) {
1083 continue;
1084 }
1085
1086 std::optional<string> name = bcutils::getMetadataString(program, Metadata::programName);
1087 if (!name) {
1088 name = bcutils::getMetadataString(program, Metadata::rdsPs);
1089 }
1090 ASSERT_TRUE(name.has_value());
1091
1092 ProgramIdentifier expectedId = bcutils::makeHdRadioStationName(*name);
1093 EXPECT_EQ(nameIds[0], expectedId.value);
1094 }
1095}
1096
1097/**
1098 * Test announcement listener registration.
1099 *
1100 * Verifies that:
1101 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
1102 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
1103 * - closing handle does not crash.
1104 */
1105TEST_P(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
1106 LOG(DEBUG) << "AnnouncementListenerRegistration Test";
1107 std::shared_ptr<AnnouncementListenerMock> listener =
1108 SharedRefBase::make<AnnouncementListenerMock>();
1109 std::shared_ptr<ICloseHandle> closeHandle = nullptr;
1110
1111 auto halResult = mModule->registerAnnouncementListener(listener, {AnnouncementType::EMERGENCY},
1112 &closeHandle);
1113
1114 if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
1115 ASSERT_EQ(closeHandle.get(), nullptr);
1116 printSkipped("Announcements not supported");
1117 return;
1118 }
1119
1120 ASSERT_TRUE(halResult.isOk());
1121 ASSERT_NE(closeHandle.get(), nullptr);
1122
1123 closeHandle->close();
1124}
1125
Weilin Xub23d0ea2022-05-09 18:26:23 +00001126GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
1127INSTANTIATE_TEST_SUITE_P(
1128 PerInstance, BroadcastRadioHalTest,
1129 testing::ValuesIn(::android::getAidlHalInstanceNames(IBroadcastRadio::descriptor)),
1130 ::android::PrintInstanceNameToString);
1131
1132} // namespace aidl::android::hardware::broadcastradio::vts
1133
1134int main(int argc, char** argv) {
1135 android::base::SetDefaultTag("BcRadio.vts");
1136 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
1137 ::testing::InitGoogleTest(&argc, argv);
1138 ABinderProcess_setThreadPoolMaxThreadCount(4);
1139 ABinderProcess_startThreadPool();
1140 return RUN_ALL_TESTS();
1141}