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