blob: 02c9356f0c032c913037a02e275fff47ad68aaf4 [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
68void printSkipped(const string& msg) {
69 const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
70 LOG(INFO) << "[ SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
71 << " with message: " << msg;
72}
73
74} // namespace
75
76class TunerCallbackMock : public BnTunerCallback {
77 public:
78 TunerCallbackMock();
79
80 MOCK_METHOD2(onTuneFailed, ScopedAStatus(Result, const ProgramSelector&));
81 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChangedMock, ScopedAStatus(const ProgramInfo&));
82 ScopedAStatus onCurrentProgramInfoChanged(const ProgramInfo& info) override;
83 ScopedAStatus onProgramListUpdated(const ProgramListChunk& chunk) override;
84 MOCK_METHOD1(onAntennaStateChange, ScopedAStatus(bool connected));
85 MOCK_METHOD1(onParametersUpdated, ScopedAStatus(const vector<VendorKeyValue>& parameters));
86 MOCK_METHOD2(onConfigFlagUpdated, ScopedAStatus(ConfigFlag in_flag, bool in_value));
87 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
88
89 std::mutex mLock;
90 bcutils::ProgramInfoSet mProgramList GUARDED_BY(mLock);
91};
92
93struct AnnouncementListenerMock : public BnAnnouncementListener {
94 MOCK_METHOD1(onListUpdated, ScopedAStatus(const vector<Announcement>&));
95};
96
97class BroadcastRadioHalTest : public testing::TestWithParam<string> {
98 protected:
99 void SetUp() override;
100 void TearDown() override;
101
102 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
103 std::optional<bcutils::ProgramInfoSet> getProgramList();
104 std::optional<bcutils::ProgramInfoSet> getProgramList(const ProgramFilter& filter);
105
106 std::shared_ptr<IBroadcastRadio> mModule;
107 Properties mProperties;
108 std::shared_ptr<TunerCallbackMock> mCallback = SharedRefBase::make<TunerCallbackMock>();
109};
110
111MATCHER_P(InfoHasId, id, string(negation ? "does not contain" : "contains") + " " + id.toString()) {
112 vector<int> ids = bcutils::getAllIds(arg.selector, id.type);
113 return ids.end() != find(ids.begin(), ids.end(), id.value);
114}
115
116TunerCallbackMock::TunerCallbackMock() {
117 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
118
119 // we expect the antenna is connected through the whole test
120 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
121}
122
123ScopedAStatus TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
124 for (const auto& id : info.selector) {
125 EXPECT_NE(id.type, IdentifierType::INVALID);
126 }
127
128 IdentifierType logically = info.logicallyTunedTo.type;
129 // This field is required for currently tuned program and should be INVALID
130 // for entries from the program list.
131 EXPECT_TRUE(logically == IdentifierType::AMFM_FREQUENCY_KHZ ||
132 logically == IdentifierType::RDS_PI ||
133 logically == IdentifierType::HD_STATION_ID_EXT ||
134 logically == IdentifierType::DAB_SID_EXT ||
135 logically == IdentifierType::DRMO_SERVICE_ID ||
136 logically == IdentifierType::SXM_SERVICE_ID ||
137 (logically >= IdentifierType::VENDOR_START &&
138 logically <= IdentifierType::VENDOR_END) ||
139 logically > IdentifierType::SXM_CHANNEL);
140
141 IdentifierType physically = info.physicallyTunedTo.type;
142 // ditto (see "logically" above)
143 EXPECT_TRUE(physically == IdentifierType::AMFM_FREQUENCY_KHZ ||
144 physically == IdentifierType::DAB_ENSEMBLE ||
145 physically == IdentifierType::DRMO_FREQUENCY_KHZ ||
146 physically == IdentifierType::SXM_CHANNEL ||
147 (physically >= IdentifierType::VENDOR_START &&
148 physically <= IdentifierType::VENDOR_END) ||
149 physically > IdentifierType::SXM_CHANNEL);
150
151 if (logically == IdentifierType::AMFM_FREQUENCY_KHZ) {
152 std::optional<string> ps = bcutils::getMetadataString(info, Metadata::rdsPs);
153 if (ps.has_value()) {
154 EXPECT_NE(::android::base::Trim(*ps), "")
155 << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
156 }
157 }
158
159 return onCurrentProgramInfoChangedMock(info);
160}
161
162ScopedAStatus TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
163 std::lock_guard<std::mutex> lk(mLock);
164
165 updateProgramList(chunk, &mProgramList);
166
167 if (chunk.complete) {
168 onProgramListReady();
169 }
170
171 return ndk::ScopedAStatus::ok();
172}
173
174void BroadcastRadioHalTest::SetUp() {
175 EXPECT_EQ(mModule.get(), nullptr) << "Module is already open";
176
177 // lookup AIDL service (radio module)
178 AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
179 ASSERT_NE(binder, nullptr);
180 mModule = IBroadcastRadio::fromBinder(ndk::SpAIBinder(binder));
181 ASSERT_NE(mModule, nullptr) << "Couldn't find broadcast radio HAL implementation";
182
183 // get module properties
184 auto propResult = mModule->getProperties(&mProperties);
185
186 ASSERT_TRUE(propResult.isOk());
187 EXPECT_FALSE(mProperties.maker.empty());
188 EXPECT_FALSE(mProperties.product.empty());
189 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
190
191 // set callback
192 EXPECT_TRUE(mModule->setTunerCallback(mCallback).isOk());
193}
194
195void BroadcastRadioHalTest::TearDown() {
196 if (mModule) {
197 ASSERT_TRUE(mModule->unsetTunerCallback().isOk());
198 }
199}
200
201bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
202 auto halResult = mModule->getAmFmRegionConfig(full, config);
203
204 if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
205 return false;
206 }
207
208 EXPECT_TRUE(halResult.isOk());
209 return halResult.isOk();
210}
211
212std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
213 ProgramFilter emptyFilter = {};
214 return getProgramList(emptyFilter);
215}
216
217std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList(
218 const ProgramFilter& filter) {
219 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
220
221 auto startResult = mModule->startProgramListUpdates(filter);
222
223 if (startResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
224 printSkipped("Program list not supported");
225 return std::nullopt;
226 }
227 EXPECT_TRUE(startResult.isOk());
228 if (!startResult.isOk()) {
229 return std::nullopt;
230 }
231 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, kProgramListScanTimeoutSec);
232
233 auto stopResult = mModule->stopProgramListUpdates();
234
235 EXPECT_TRUE(stopResult.isOk());
236
237 return mCallback->mProgramList;
238}
239
240/**
241 * Test setting tuner callback to null.
242 *
243 * Verifies that:
244 * - Setting to a null tuner callback results with INVALID_ARGUMENTS.
245 */
246TEST_P(BroadcastRadioHalTest, TunerCallbackFailsWithNull) {
247 LOG(DEBUG) << "TunerCallbackFailsWithNull Test";
248
249 auto halResult = mModule->setTunerCallback(nullptr);
250
251 EXPECT_EQ(halResult.getServiceSpecificError(), resultToInt(Result::INVALID_ARGUMENTS));
252}
253
254/**
255 * Test tuning without tuner callback set.
256 *
257 * Verifies that:
258 * - No tuner callback set results in INVALID_STATE, regardless of whether the selector is
259 * supported.
260 */
261TEST_P(BroadcastRadioHalTest, TuneFailsWithoutTunerCallback) {
262 LOG(DEBUG) << "TuneFailsWithoutTunerCallback Test";
263
264 mModule->unsetTunerCallback();
265 int64_t freq = 90900; // 90.9 FM
266 ProgramSelector sel = makeSelectorAmfm(freq);
267
268 auto result = mModule->tune(sel);
269
270 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
271}
272
273/**
274 * Test tuning with selectors that can be not supported.
275 *
276 * Verifies that:
277 * - if the selector is not supported, an invalid value results with NOT_SUPPORTED, regardless of
278 * whether it is valid;
279 * - if it is supported, the test is ignored;
280 */
281TEST_P(BroadcastRadioHalTest, TuneFailsWithNotSupported) {
282 LOG(DEBUG) << "TuneFailsWithInvalid Test";
283
284 vector<ProgramIdentifier> supportTestId = {
285 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0), // invalid
286 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 94900), // valid
287 makeIdentifier(IdentifierType::RDS_PI, 0x10000), // invalid
288 makeIdentifier(IdentifierType::RDS_PI, 0x1001), // valid
289 makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000), // invalid
290 makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x10000001), // valid
291 makeIdentifier(IdentifierType::DAB_SID_EXT, 0), // invalid
292 makeIdentifier(IdentifierType::DAB_SID_EXT, 0xA00001), // valid
293 makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000), // invalid
294 makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x10000001), // valid
295 makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000), // invalid
296 makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x10000001), // valid
297 };
298
299 auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
300 for (const auto& id : supportTestId) {
301 ProgramSelector sel{id, {}};
302
303 auto result = mModule->tune(sel);
304
305 if (!bcutils::isSupported(mProperties, sel)) {
306 EXPECT_EQ(result.getServiceSpecificError(), notSupportedError);
307 }
308 }
309}
310
311/**
312 * Test tuning with invalid selectors.
313 *
314 * Verifies that:
315 * - if the selector is not supported, it's ignored;
316 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
317 */
318TEST_P(BroadcastRadioHalTest, TuneFailsWithInvalid) {
319 LOG(DEBUG) << "TuneFailsWithInvalid Test";
320
321 vector<ProgramIdentifier> invalidId = {
322 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0),
323 makeIdentifier(IdentifierType::RDS_PI, 0x10000),
324 makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
325 makeIdentifier(IdentifierType::DAB_SID_EXT, 0),
326 makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
327 makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
328 };
329
330 auto invalidArgumentsError = resultToInt(Result::INVALID_ARGUMENTS);
331 for (const auto& id : invalidId) {
332 ProgramSelector sel{id, {}};
333
334 auto result = mModule->tune(sel);
335
336 if (bcutils::isSupported(mProperties, sel)) {
337 EXPECT_EQ(result.getServiceSpecificError(), invalidArgumentsError);
338 }
339 }
340}
341
342/**
343 * Test tuning with empty program selector.
344 *
345 * Verifies that:
346 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
347 */
348TEST_P(BroadcastRadioHalTest, TuneFailsWithEmpty) {
349 LOG(DEBUG) << "TuneFailsWithEmpty Test";
350
351 // Program type is 1-based, so 0 will always be invalid.
352 ProgramSelector sel = {};
353
354 auto result = mModule->tune(sel);
355
356 ASSERT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
357}
358
359/**
360 * Test tuning with FM selector.
361 *
362 * Verifies that:
363 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
364 * - if it is supported, the method succeeds;
365 * - after a successful tune call, onCurrentProgramInfoChanged callback is
366 * invoked carrying a proper selector;
367 * - program changes exactly to what was requested.
368 */
369TEST_P(BroadcastRadioHalTest, FmTune) {
370 LOG(DEBUG) << "FmTune Test";
371
372 int64_t freq = 90900; // 90.9 FM
373 ProgramSelector sel = makeSelectorAmfm(freq);
374 // try tuning
375 ProgramInfo infoCb = {};
376 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
377 InfoHasId(makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq)))
378 .Times(AnyNumber())
379 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))))
380 .WillRepeatedly(testing::InvokeWithoutArgs([] { return ndk::ScopedAStatus::ok(); }));
381
382 auto result = mModule->tune(sel);
383
384 // expect a failure if it's not supported
385 if (!bcutils::isSupported(mProperties, sel)) {
386 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
387 return;
388 }
389
390 // expect a callback if it succeeds
391 EXPECT_TRUE(result.isOk());
392 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
393
394 LOG(DEBUG) << "Current program info: " << infoCb.toString();
395
396 // it should tune exactly to what was requested
397 vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
398 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
399 << "FM freq " << freq << " kHz is not sent back by callback.";
400}
401
402/**
403 * Test tuning with DAB selector.
404 *
405 * Verifies that:
406 * - if DAB selector is not supported, the method returns NOT_SUPPORTED;
407 * - if it is supported, the method succeeds;
408 * - after a successful tune call, onCurrentProgramInfoChanged callback is
409 * invoked carrying a proper selector;
410 * - program changes exactly to what was requested.
411 */
412TEST_P(BroadcastRadioHalTest, DabTune) {
413 LOG(DEBUG) << "DabTune Test";
414 vector<DabTableEntry> config;
415
416 auto halResult = mModule->getDabRegionConfig(&config);
417
418 if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
419 printSkipped("DAB not supported");
420 return;
421 }
422 ASSERT_TRUE(halResult.isOk());
423 ASSERT_NE(config.size(), 0U);
424
425 // TODO(245787803): use a DAB frequency that can actually be tuned to.
426 ProgramSelector sel = {};
427 int64_t freq = config[config.size() / 2].frequencyKhz;
428 sel.primaryId = makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq);
429
430 // try tuning
431 ProgramInfo infoCb = {};
432 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
433 InfoHasId(makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq)))
434 .Times(AnyNumber())
435 .WillOnce(
436 DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))));
437
438 auto result = mModule->tune(sel);
439
440 // expect a failure if it's not supported
441 if (!bcutils::isSupported(mProperties, sel)) {
442 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
443 return;
444 }
445
446 // expect a callback if it succeeds
447 EXPECT_TRUE(result.isOk());
448 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
449 LOG(DEBUG) << "Current program info: " << infoCb.toString();
450
451 // it should tune exactly to what was requested
452 vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY_KHZ);
453 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
454 << "DAB freq " << freq << " kHz is not sent back by callback.";
455 ;
456}
457
458/**
459 * Test seeking to next/prev station via IBroadcastRadio::seek().
460 *
461 * Verifies that:
462 * - the method succeeds;
463 * - the program info is changed within kTuneTimeoutSec;
464 * - works both directions and with or without skipping sub-channel.
465 */
466TEST_P(BroadcastRadioHalTest, Seek) {
467 LOG(DEBUG) << "Seek Test";
468
469 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
470
471 auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
472
473 if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
474 printSkipped("Seek not supported");
475 return;
476 }
477
478 EXPECT_TRUE(result.isOk());
479 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
480
481 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
482
483 result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
484
485 EXPECT_TRUE(result.isOk());
486 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
487}
488
489/**
490 * Test seeking without tuner callback set.
491 *
492 * Verifies that:
493 * - No tuner callback set results in INVALID_STATE.
494 */
495TEST_P(BroadcastRadioHalTest, SeekFailsWithoutTunerCallback) {
496 LOG(DEBUG) << "SeekFailsWithoutTunerCallback Test";
497
498 mModule->unsetTunerCallback();
499
500 auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
501
502 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
503
504 result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
505
506 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
507}
508
509/**
510 * Test step operation.
511 *
512 * Verifies that:
513 * - the method succeeds or returns NOT_SUPPORTED;
514 * - the program info is changed within kTuneTimeoutSec if the method succeeded;
515 * - works both directions.
516 */
517TEST_P(BroadcastRadioHalTest, Step) {
518 LOG(DEBUG) << "Step Test";
519
520 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
521
522 auto result = mModule->step(/* in_directionUp= */ true);
523
524 if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
525 printSkipped("Step not supported");
526 return;
527 }
528 EXPECT_TRUE(result.isOk());
529 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
530
531 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
532
533 result = mModule->step(/* in_directionUp= */ false);
534
535 EXPECT_TRUE(result.isOk());
536 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
537}
538
539/**
540 * Test step operation without tuner callback set.
541 *
542 * Verifies that:
543 * - No tuner callback set results in INVALID_STATE.
544 */
545TEST_P(BroadcastRadioHalTest, StepFailsWithoutTunerCallback) {
546 LOG(DEBUG) << "StepFailsWithoutTunerCallback Test";
547
548 mModule->unsetTunerCallback();
549
550 auto result = mModule->step(/* in_directionUp= */ true);
551
552 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
553
554 result = mModule->step(/* in_directionUp= */ false);
555
556 EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
557}
558
559/**
560 * Test tune cancellation.
561 *
562 * Verifies that:
563 * - the method does not crash after being invoked multiple times.
564 *
565 * Since cancel() might be called after the HAL completes an operation (tune, seek, and step)
566 * and before the callback completions, the operation might not be actually canceled and the
567 * effect of cancel() is not deterministic to be tested here.
568 */
569TEST_P(BroadcastRadioHalTest, Cancel) {
570 LOG(DEBUG) << "Cancel Test";
571
572 auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
573 for (int i = 0; i < 10; i++) {
574 auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
575
576 if (result.getServiceSpecificError() == notSupportedError) {
577 printSkipped("Cancel is skipped because of seek not supported");
578 return;
579 }
580 EXPECT_TRUE(result.isOk());
581
582 auto cancelResult = mModule->cancel();
583
584 ASSERT_TRUE(cancelResult.isOk());
585 }
586}
587
588/**
589 * Test getting program list using empty program filter.
590 *
591 * Verifies that:
592 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
593 * - the complete list is fetched within kProgramListScanTimeoutSec;
594 * - stopProgramListUpdates does not crash.
595 */
596TEST_P(BroadcastRadioHalTest, GetProgramListFromEmptyFilter) {
597 LOG(DEBUG) << "GetProgramListFromEmptyFilter Test";
598
599 getProgramList();
600}
601
602/**
603 * Test getting program list using AMFM frequency program filter.
604 *
605 * Verifies that:
606 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
607 * - the complete list is fetched within kProgramListScanTimeoutSec;
608 * - stopProgramListUpdates does not crash;
609 * - result for startProgramListUpdates using a filter with AMFM_FREQUENCY_KHZ value of the first
610 * AMFM program matches the expected result.
611 */
612TEST_P(BroadcastRadioHalTest, GetProgramListFromAmFmFilter) {
613 LOG(DEBUG) << "GetProgramListFromAmFmFilter Test";
614
615 std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
616 if (!completeList) {
617 printSkipped("No program list available");
618 return;
619 }
620
621 ProgramFilter amfmFilter = {};
622 int expectedResultSize = 0;
623 uint64_t expectedFreq = 0;
624 for (const auto& program : *completeList) {
625 vector<int> amfmIds =
626 bcutils::getAllIds(program.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
627 EXPECT_LE(amfmIds.size(), 1u);
628 if (amfmIds.size() == 0) {
629 continue;
630 }
631
632 if (expectedResultSize == 0) {
633 expectedFreq = amfmIds[0];
634 amfmFilter.identifiers = {
635 makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, expectedFreq)};
636 expectedResultSize = 1;
637 } else if (amfmIds[0] == expectedFreq) {
638 expectedResultSize++;
639 }
640 }
641
642 if (expectedResultSize == 0) {
643 printSkipped("No Am/FM programs available");
644 return;
645 }
646 std::optional<bcutils::ProgramInfoSet> amfmList = getProgramList(amfmFilter);
647 ASSERT_EQ(amfmList->size(), expectedResultSize) << "amfm filter result size is wrong";
648}
649
650/**
651 * Test getting program list using DAB ensemble program filter.
652 *
653 * Verifies that:
654 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
655 * - the complete list is fetched within kProgramListScanTimeoutSec;
656 * - stopProgramListUpdates does not crash;
657 * - result for startProgramListUpdates using a filter with DAB_ENSEMBLE value of the first DAB
658 * program matches the expected result.
659 */
660TEST_P(BroadcastRadioHalTest, GetProgramListFromDabFilter) {
661 LOG(DEBUG) << "GetProgramListFromDabFilter Test";
662
663 std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
664 if (!completeList) {
665 printSkipped("No program list available");
666 return;
667 }
668
669 ProgramFilter dabFilter = {};
670 int expectedResultSize = 0;
671 uint64_t expectedEnsemble = 0;
672 for (const auto& program : *completeList) {
673 auto dabEnsembles = bcutils::getAllIds(program.selector, IdentifierType::DAB_ENSEMBLE);
674 EXPECT_LE(dabEnsembles.size(), 1u);
675 if (dabEnsembles.size() == 0) {
676 continue;
677 }
678
679 if (expectedResultSize == 0) {
680 expectedEnsemble = dabEnsembles[0];
681 dabFilter.identifiers = {
682 makeIdentifier(IdentifierType::DAB_ENSEMBLE, expectedEnsemble)};
683 expectedResultSize = 1;
684 } else if (dabEnsembles[0] == expectedEnsemble) {
685 expectedResultSize++;
686 }
687 }
688
689 if (expectedResultSize == 0) {
690 printSkipped("No DAB programs available");
691 return;
692 }
693 std::optional<bcutils::ProgramInfoSet> dabList = getProgramList(dabFilter);
694 ASSERT_EQ(dabList->size(), expectedResultSize) << "dab filter result size is wrong";
695}
696
697GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
698INSTANTIATE_TEST_SUITE_P(
699 PerInstance, BroadcastRadioHalTest,
700 testing::ValuesIn(::android::getAidlHalInstanceNames(IBroadcastRadio::descriptor)),
701 ::android::PrintInstanceNameToString);
702
703} // namespace aidl::android::hardware::broadcastradio::vts
704
705int main(int argc, char** argv) {
706 android::base::SetDefaultTag("BcRadio.vts");
707 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
708 ::testing::InitGoogleTest(&argc, argv);
709 ABinderProcess_setThreadPoolMaxThreadCount(4);
710 ABinderProcess_startThreadPool();
711 return RUN_ALL_TESTS();
712}