blob: 37095d43eff98e81b6abd960676767630e5f0394 [file] [log] [blame]
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "BcRadio.vts"
Tomasz Wasilczykdb902862018-01-14 17:22:03 -080018#define LOG_NDEBUG 0
19#define EGMOCK_VERBOSE 1
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080020
21#include <VtsHalHidlTargetTestBase.h>
22#include <android-base/logging.h>
23#include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
24#include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
25#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
26#include <android/hardware/broadcastradio/2.0/types.h>
27#include <broadcastradio-utils-2x/Utils.h>
28#include <broadcastradio-vts-utils/call-barrier.h>
29#include <broadcastradio-vts-utils/mock-timeout.h>
30#include <broadcastradio-vts-utils/pointer-utils.h>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080031#include <cutils/bitops.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080032#include <gmock/gmock.h>
33
34#include <chrono>
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -080035#include <optional>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080036#include <regex>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080037
38namespace android {
39namespace hardware {
40namespace broadcastradio {
41namespace V2_0 {
42namespace vts {
43
44using namespace std::chrono_literals;
45
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080046using std::unordered_set;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080047using std::vector;
48using testing::_;
49using testing::AnyNumber;
50using testing::ByMove;
51using testing::DoAll;
52using testing::Invoke;
53using testing::SaveArg;
54
55using broadcastradio::vts::CallBarrier;
56using broadcastradio::vts::clearAndWait;
57using utils::make_identifier;
58using utils::make_selector_amfm;
59
60namespace timeout {
61
62static constexpr auto tune = 30s;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080063static constexpr auto programListScan = 5min;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080064
65} // namespace timeout
66
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080067static const ConfigFlag gConfigFlagValues[] = {
Tomasz Wasilczyk30240f62017-12-20 14:19:21 -080068 ConfigFlag::FORCE_MONO,
69 ConfigFlag::FORCE_ANALOG,
70 ConfigFlag::FORCE_DIGITAL,
71 ConfigFlag::RDS_AF,
72 ConfigFlag::RDS_REG,
73 ConfigFlag::DAB_DAB_LINKING,
74 ConfigFlag::DAB_FM_LINKING,
75 ConfigFlag::DAB_DAB_SOFT_LINKING,
76 ConfigFlag::DAB_FM_SOFT_LINKING,
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080077};
78
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080079class TunerCallbackMock : public ITunerCallback {
80 public:
81 TunerCallbackMock();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080082
83 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
84 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged, Return<void>(const ProgramInfo&));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080085 Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080086 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
87 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080088
89 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
90
91 std::mutex mLock;
92 utils::ProgramInfoSet mProgramList;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080093};
94
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -080095struct AnnouncementListenerMock : public IAnnouncementListener {
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -080096 MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
97};
98
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080099class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
100 protected:
101 virtual void SetUp() override;
102 virtual void TearDown() override;
103
104 bool openSession();
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800105 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800106 std::optional<utils::ProgramInfoSet> getProgramList();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800107
108 sp<IBroadcastRadio> mModule;
109 Properties mProperties;
110 sp<ITunerSession> mSession;
111 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
112};
113
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800114static void printSkipped(std::string msg) {
115 std::cout << "[ SKIPPED ] " << msg << std::endl;
116}
117
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800118MATCHER_P(InfoHasId, id,
119 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
120 auto ids = utils::getAllIds(arg.selector, utils::getType(id));
121 return ids.end() != find(ids.begin(), ids.end(), id.value);
122}
123
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800124TunerCallbackMock::TunerCallbackMock() {
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800125 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged, _).Times(AnyNumber());
126
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800127 // we expect the antenna is connected through the whole test
128 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
129}
130
131Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
132 std::lock_guard<std::mutex> lk(mLock);
133
134 updateProgramList(mProgramList, chunk);
135
136 if (chunk.complete) onProgramListReady();
137
138 return {};
139}
140
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800141void BroadcastRadioHalTest::SetUp() {
142 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
143
144 // lookup HIDL service (radio module)
145 mModule = getService<IBroadcastRadio>();
146 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
147
148 // get module properties
149 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
150 ASSERT_TRUE(propResult.isOk());
151
152 EXPECT_FALSE(mProperties.maker.empty());
153 EXPECT_FALSE(mProperties.product.empty());
154 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
155}
156
157void BroadcastRadioHalTest::TearDown() {
158 mSession.clear();
159 mModule.clear();
160 clearAndWait(mCallback, 1s);
161}
162
163bool BroadcastRadioHalTest::openSession() {
164 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
165
166 Result halResult = Result::UNKNOWN_ERROR;
167 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
168 halResult = result;
169 if (result != Result::OK) return;
170 mSession = session;
171 };
172 auto hidlResult = mModule->openSession(mCallback, openCb);
173
174 EXPECT_TRUE(hidlResult.isOk());
175 EXPECT_EQ(Result::OK, halResult);
176 EXPECT_NE(nullptr, mSession.get());
177
178 return nullptr != mSession.get();
179}
180
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800181bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
182 auto halResult = Result::UNKNOWN_ERROR;
183 auto cb = [&](Result result, AmFmRegionConfig configCb) {
184 halResult = result;
185 if (config) *config = configCb;
186 };
187
188 auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
189 EXPECT_TRUE(hidlResult.isOk());
190
191 if (halResult == Result::NOT_SUPPORTED) return false;
192
193 EXPECT_EQ(Result::OK, halResult);
194 return halResult == Result::OK;
195}
196
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800197std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
198 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
199
200 auto startResult = mSession->startProgramListUpdates({});
201 if (startResult == Result::NOT_SUPPORTED) {
202 printSkipped("Program list not supported");
203 return nullopt;
204 }
205 EXPECT_EQ(Result::OK, startResult);
206 if (startResult != Result::OK) return nullopt;
207
208 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
209
210 auto stopResult = mSession->stopProgramListUpdates();
211 EXPECT_TRUE(stopResult.isOk());
212
213 return mCallback->mProgramList;
214}
215
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800216/**
217 * Test session opening.
218 *
219 * Verifies that:
220 * - the method succeeds on a first and subsequent calls;
221 * - the method succeeds when called for the second time without
222 * closing previous session.
223 */
224TEST_F(BroadcastRadioHalTest, OpenSession) {
225 // simply open session for the first time
226 ASSERT_TRUE(openSession());
227
228 // drop (without explicit close) and re-open the session
229 mSession.clear();
230 ASSERT_TRUE(openSession());
231
232 // open the second session (the first one should be forcibly closed)
233 auto secondSession = mSession;
234 mSession.clear();
235 ASSERT_TRUE(openSession());
236}
237
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800238static bool isValidAmFmFreq(uint64_t freq) {
239 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
240 return utils::isValid(id);
241}
242
243static void validateRange(const AmFmBandRange& range) {
244 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
245 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
246 EXPECT_LT(range.lowerBound, range.upperBound);
247 EXPECT_GT(range.spacing, 0u);
248 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
249}
250
251static bool supportsFM(const AmFmRegionConfig& config) {
252 for (auto&& range : config.ranges) {
253 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
254 }
255 return false;
256}
257
258/**
259 * Test fetching AM/FM regional configuration.
260 *
261 * Verifies that:
262 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
263 * - there is at least one AM/FM band configured;
264 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
265 * - all channel grids (frequency ranges and spacings) are valid;
266 * - scan spacing is a multiply of manual spacing value.
267 */
268TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
269 AmFmRegionConfig config;
270 bool supported = getAmFmRegionConfig(false, &config);
271 if (!supported) {
272 printSkipped("AM/FM not supported");
273 return;
274 }
275
276 EXPECT_GT(config.ranges.size(), 0u);
277 EXPECT_LE(popcountll(config.fmDeemphasis), 1);
278 EXPECT_LE(popcountll(config.fmRds), 1);
279
280 for (auto&& range : config.ranges) {
281 validateRange(range);
282 EXPECT_EQ(0u, range.scanSpacing % range.spacing);
283 EXPECT_GE(range.scanSpacing, range.spacing);
284 }
285
286 if (supportsFM(config)) {
287 EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
288 }
289}
290
291/**
292 * Test fetching AM/FM regional capabilities.
293 *
294 * Verifies that:
295 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
296 * - there is at least one AM/FM range supported;
297 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
298 * - all channel grids (frequency ranges and spacings) are valid;
299 * - scan spacing is not set.
300 */
301TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
302 AmFmRegionConfig config;
303 bool supported = getAmFmRegionConfig(true, &config);
304 if (!supported) {
305 printSkipped("AM/FM not supported");
306 return;
307 }
308
309 EXPECT_GT(config.ranges.size(), 0u);
310
311 for (auto&& range : config.ranges) {
312 validateRange(range);
313 EXPECT_EQ(0u, range.scanSpacing);
314 }
315
316 if (supportsFM(config)) {
317 EXPECT_GE(popcountll(config.fmDeemphasis), 1);
318 }
319}
320
321/**
322 * Test fetching DAB regional configuration.
323 *
324 * Verifies that:
325 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
326 * - all channel labels match correct format;
327 * - all channel frequencies are in correct range.
328 */
329TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
330 Result halResult;
331 hidl_vec<DabTableEntry> config;
332 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
333 halResult = result;
334 config = configCb;
335 };
336 auto hidlResult = mModule->getDabRegionConfig(cb);
337 ASSERT_TRUE(hidlResult.isOk());
338
339 if (halResult == Result::NOT_SUPPORTED) {
340 printSkipped("DAB not supported");
341 return;
342 }
343 ASSERT_EQ(Result::OK, halResult);
344
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800345 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800346 // double-check correctness of the test
347 ASSERT_TRUE(std::regex_match("5A", re));
348 ASSERT_FALSE(std::regex_match("5a", re));
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800349 ASSERT_FALSE(std::regex_match("1234ABCD", re));
350 ASSERT_TRUE(std::regex_match("CN 12D", re));
351 ASSERT_FALSE(std::regex_match(" 5A", re));
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800352
353 for (auto&& entry : config) {
354 EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
355
356 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
357 EXPECT_TRUE(utils::isValid(id));
358 }
359}
360
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800361/**
362 * Test tuning with FM selector.
363 *
364 * Verifies that:
365 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
366 * - if it is supported, the method succeeds;
367 * - after a successful tune call, onCurrentProgramInfoChanged callback is
368 * invoked carrying a proper selector;
369 * - program changes exactly to what was requested.
370 */
371TEST_F(BroadcastRadioHalTest, FmTune) {
372 ASSERT_TRUE(openSession());
373
374 uint64_t freq = 100100; // 100.1 FM
375 auto sel = make_selector_amfm(freq);
376
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800377 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
378 * callback setting infoCb, because egmock cannot distinguish calls with different matchers
379 * (there is one here and one in callback constructor).
380 *
381 * This sleep workaround will fix default implementation, but the real HW tests will still be
382 * flaky. We probably need to implement egmock alternative based on actions.
383 */
384 std::this_thread::sleep_for(100ms);
385
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800386 // try tuning
387 ProgramInfo infoCb = {};
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800388 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged,
389 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800390 .Times(AnyNumber())
391 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
392 auto result = mSession->tune(sel);
393
394 // expect a failure if it's not supported
395 if (!utils::isSupported(mProperties, sel)) {
396 EXPECT_EQ(Result::NOT_SUPPORTED, result);
397 return;
398 }
399
400 // expect a callback if it succeeds
401 EXPECT_EQ(Result::OK, result);
402 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
403
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800404 ALOGD("current program info: %s", toString(infoCb).c_str());
405
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800406 // it should tune exactly to what was requested
407 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
408 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
409}
410
411/**
412 * Test tuning with invalid selectors.
413 *
414 * Verifies that:
415 * - if the selector is not supported, it's ignored;
416 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
417 */
418TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
419 ASSERT_TRUE(openSession());
420
421 vector<ProgramIdentifier> invalid = {
422 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
423 make_identifier(IdentifierType::RDS_PI, 0x10000),
424 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
425 make_identifier(IdentifierType::DAB_SID_EXT, 0),
426 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
427 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
428 };
429
430 for (auto&& id : invalid) {
431 ProgramSelector sel{id, {}};
432
433 auto result = mSession->tune(sel);
434
435 if (utils::isSupported(mProperties, sel)) {
436 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
437 } else {
438 EXPECT_EQ(Result::NOT_SUPPORTED, result);
439 }
440 }
441}
442
443/**
444 * Test tuning with empty program selector.
445 *
446 * Verifies that:
447 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
448 */
449TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
450 ASSERT_TRUE(openSession());
451
452 // Program type is 1-based, so 0 will always be invalid.
453 ProgramSelector sel = {};
454 auto result = mSession->tune(sel);
455 ASSERT_EQ(Result::NOT_SUPPORTED, result);
456}
457
458/**
459 * Test scanning to next/prev station.
460 *
461 * Verifies that:
462 * - the method succeeds;
463 * - the program info is changed within timeout::tune;
464 * - works both directions and with or without skipping sub-channel.
465 */
466TEST_F(BroadcastRadioHalTest, Scan) {
467 ASSERT_TRUE(openSession());
468
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800469 // TODO(b/69958777): see FmTune workaround
470 std::this_thread::sleep_for(100ms);
471
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800472 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
473 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
474 EXPECT_EQ(Result::OK, result);
475 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
476
477 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
478 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
479 EXPECT_EQ(Result::OK, result);
480 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
481}
482
483/**
484 * Test step operation.
485 *
486 * Verifies that:
487 * - the method succeeds or returns NOT_SUPPORTED;
488 * - the program info is changed within timeout::tune if the method succeeded;
489 * - works both directions.
490 */
491TEST_F(BroadcastRadioHalTest, Step) {
492 ASSERT_TRUE(openSession());
493
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800494 // TODO(b/69958777): see FmTune workaround
495 std::this_thread::sleep_for(100ms);
496
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800497 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber());
498 auto result = mSession->step(true /* up */);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800499 if (result == Result::NOT_SUPPORTED) {
500 printSkipped("step not supported");
501 return;
502 }
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800503 EXPECT_EQ(Result::OK, result);
504 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
505
506 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
507 result = mSession->step(false /* down */);
508 EXPECT_EQ(Result::OK, result);
509 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
510}
511
512/**
513 * Test tune cancellation.
514 *
515 * Verifies that:
516 * - the method does not crash after being invoked multiple times.
517 */
518TEST_F(BroadcastRadioHalTest, Cancel) {
519 ASSERT_TRUE(openSession());
520
521 for (int i = 0; i < 10; i++) {
522 auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
523 ASSERT_EQ(Result::OK, scanResult);
524
525 auto cancelResult = mSession->cancel();
526 ASSERT_TRUE(cancelResult.isOk());
527 }
528}
529
530/**
531 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
532 *
533 * Verifies that:
534 * - callback is called for empty parameters set.
535 */
536TEST_F(BroadcastRadioHalTest, NoParameters) {
537 ASSERT_TRUE(openSession());
538
539 hidl_vec<VendorKeyValue> halResults = {};
540 bool wasCalled = false;
541 auto cb = [&](hidl_vec<VendorKeyValue> results) {
542 wasCalled = true;
543 halResults = results;
544 };
545
546 auto hidlResult = mSession->setParameters({}, cb);
547 ASSERT_TRUE(hidlResult.isOk());
548 ASSERT_TRUE(wasCalled);
549 ASSERT_EQ(0u, halResults.size());
550
551 wasCalled = false;
552 hidlResult = mSession->getParameters({}, cb);
553 ASSERT_TRUE(hidlResult.isOk());
554 ASSERT_TRUE(wasCalled);
555 ASSERT_EQ(0u, halResults.size());
556}
557
558/**
559 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
560 *
561 * Verifies that:
562 * - unknown parameters are ignored;
563 * - callback is called also for empty results set.
564 */
565TEST_F(BroadcastRadioHalTest, UnknownParameters) {
566 ASSERT_TRUE(openSession());
567
568 hidl_vec<VendorKeyValue> halResults = {};
569 bool wasCalled = false;
570 auto cb = [&](hidl_vec<VendorKeyValue> results) {
571 wasCalled = true;
572 halResults = results;
573 };
574
575 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
576 ASSERT_TRUE(hidlResult.isOk());
577 ASSERT_TRUE(wasCalled);
578 ASSERT_EQ(0u, halResults.size());
579
580 wasCalled = false;
581 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
582 ASSERT_TRUE(hidlResult.isOk());
583 ASSERT_TRUE(wasCalled);
584 ASSERT_EQ(0u, halResults.size());
585}
586
587/**
588 * Test session closing.
589 *
590 * Verifies that:
591 * - the method does not crash after being invoked multiple times.
592 */
593TEST_F(BroadcastRadioHalTest, Close) {
594 ASSERT_TRUE(openSession());
595
596 for (int i = 0; i < 10; i++) {
597 auto cancelResult = mSession->close();
598 ASSERT_TRUE(cancelResult.isOk());
599 }
600}
601
602/**
603 * Test geting image of invalid ID.
604 *
605 * Verifies that:
606 * - getImage call handles argument 0 gracefully.
607 */
608TEST_F(BroadcastRadioHalTest, GetNoImage) {
609 size_t len = 0;
610 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
611
612 ASSERT_TRUE(result.isOk());
613 ASSERT_EQ(0u, len);
614}
615
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800616/**
617 * Test getting config flags.
618 *
619 * Verifies that:
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800620 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800621 * - call success or failure is consistent with setConfigFlag.
622 */
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800623TEST_F(BroadcastRadioHalTest, FetchConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800624 ASSERT_TRUE(openSession());
625
626 for (auto flag : gConfigFlagValues) {
627 auto halResult = Result::UNKNOWN_ERROR;
628 auto cb = [&](Result result, bool) { halResult = result; };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800629 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800630 EXPECT_TRUE(hidlResult.isOk());
631
632 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
633 ASSERT_EQ(Result::OK, halResult);
634 }
635
636 // set must fail or succeed the same way as get
637 auto setResult = mSession->setConfigFlag(flag, false);
638 EXPECT_EQ(halResult, setResult);
639 setResult = mSession->setConfigFlag(flag, true);
640 EXPECT_EQ(halResult, setResult);
641 }
642}
643
644/**
645 * Test setting config flags.
646 *
647 * Verifies that:
648 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800649 * - isConfigFlagSet reflects the state requested immediately after the set call.
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800650 */
651TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
652 ASSERT_TRUE(openSession());
653
654 auto get = [&](ConfigFlag flag) {
655 auto halResult = Result::UNKNOWN_ERROR;
656 bool gotValue = false;
657 auto cb = [&](Result result, bool value) {
658 halResult = result;
659 gotValue = value;
660 };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800661 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800662 EXPECT_TRUE(hidlResult.isOk());
663 EXPECT_EQ(Result::OK, halResult);
664 return gotValue;
665 };
666
667 for (auto flag : gConfigFlagValues) {
668 auto result = mSession->setConfigFlag(flag, false);
669 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
670 // setting to true must result in the same error as false
671 auto secondResult = mSession->setConfigFlag(flag, true);
672 EXPECT_EQ(result, secondResult);
673 continue;
674 }
675 ASSERT_EQ(Result::OK, result);
676
677 // verify false is set
678 auto value = get(flag);
679 EXPECT_FALSE(value);
680
681 // try setting true this time
682 result = mSession->setConfigFlag(flag, true);
683 ASSERT_EQ(Result::OK, result);
684 value = get(flag);
685 EXPECT_TRUE(value);
686
687 // false again
688 result = mSession->setConfigFlag(flag, false);
689 ASSERT_EQ(Result::OK, result);
690 value = get(flag);
691 EXPECT_FALSE(value);
692 }
693}
694
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800695/**
696 * Test getting program list.
697 *
698 * Verifies that:
699 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
700 * - the complete list is fetched within timeout::programListScan;
701 * - stopProgramListUpdates does not crash.
702 */
703TEST_F(BroadcastRadioHalTest, GetProgramList) {
704 ASSERT_TRUE(openSession());
705
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800706 getProgramList();
707}
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800708
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800709/**
710 * Test HD_STATION_NAME correctness.
711 *
712 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
713 * - the program provides station name in its metadata;
714 * - the identifier matches the name;
715 * - there is only one identifier of that type.
716 */
717TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
718 ASSERT_TRUE(openSession());
719
720 auto list = getProgramList();
721 if (!list) return;
722
723 for (auto&& program : *list) {
724 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
725 EXPECT_LE(nameIds.size(), 1u);
726 if (nameIds.size() == 0) continue;
727
728 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
729 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
730 ASSERT_TRUE(name.has_value());
731
732 auto expectedId = utils::make_hdradio_station_name(*name);
733 EXPECT_EQ(expectedId.value, nameIds[0]);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800734 }
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800735}
736
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800737/**
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800738 * Test announcement listener registration.
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800739 *
740 * Verifies that:
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800741 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800742 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
743 * - closing handle does not crash.
744 */
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800745TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
746 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800747
748 Result halResult = Result::UNKNOWN_ERROR;
749 sp<ICloseHandle> closeHandle = nullptr;
750 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
751 halResult = result;
752 closeHandle = closeHandle_;
753 };
754
755 auto hidlResult =
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800756 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800757 ASSERT_TRUE(hidlResult.isOk());
758
759 if (halResult == Result::NOT_SUPPORTED) {
760 ASSERT_EQ(nullptr, closeHandle.get());
761 printSkipped("Announcements not supported");
762 return;
763 }
764
765 ASSERT_EQ(Result::OK, halResult);
766 ASSERT_NE(nullptr, closeHandle.get());
767
768 closeHandle->close();
769}
770
Tomasz Wasilczyk4ce63822017-12-21 14:25:54 -0800771// TODO(b/70939328): test ProgramInfo's currentlyTunedId and
772// currentlyTunedChannel once the program list is implemented.
773
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800774} // namespace vts
775} // namespace V2_0
776} // namespace broadcastradio
777} // namespace hardware
778} // namespace android
779
780int main(int argc, char** argv) {
781 ::testing::InitGoogleTest(&argc, argv);
782 int status = RUN_ALL_TESTS();
783 ALOGI("Test result = %d", status);
784 return status;
785}