blob: 023d906a81896ee1d21649a94d2df788225fa52f [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>
Zhuoyao Zhang190548f2018-02-08 20:40:23 -080029#include <broadcastradio-vts-utils/environment-utils.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080030#include <broadcastradio-vts-utils/mock-timeout.h>
31#include <broadcastradio-vts-utils/pointer-utils.h>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080032#include <cutils/bitops.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080033#include <gmock/gmock.h>
34
35#include <chrono>
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -080036#include <optional>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080037#include <regex>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080038
39namespace android {
40namespace hardware {
41namespace broadcastradio {
42namespace V2_0 {
43namespace vts {
44
45using namespace std::chrono_literals;
46
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080047using std::unordered_set;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080048using std::vector;
49using testing::_;
50using testing::AnyNumber;
51using testing::ByMove;
52using testing::DoAll;
53using testing::Invoke;
54using testing::SaveArg;
55
Zhuoyao Zhang190548f2018-02-08 20:40:23 -080056using broadcastradio::vts::BroadcastRadioHidlEnvironment;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080057using broadcastradio::vts::CallBarrier;
58using broadcastradio::vts::clearAndWait;
59using utils::make_identifier;
60using utils::make_selector_amfm;
61
62namespace timeout {
63
64static constexpr auto tune = 30s;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080065static constexpr auto programListScan = 5min;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080066
67} // namespace timeout
68
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080069static const ConfigFlag gConfigFlagValues[] = {
Tomasz Wasilczyk30240f62017-12-20 14:19:21 -080070 ConfigFlag::FORCE_MONO,
71 ConfigFlag::FORCE_ANALOG,
72 ConfigFlag::FORCE_DIGITAL,
73 ConfigFlag::RDS_AF,
74 ConfigFlag::RDS_REG,
75 ConfigFlag::DAB_DAB_LINKING,
76 ConfigFlag::DAB_FM_LINKING,
77 ConfigFlag::DAB_DAB_SOFT_LINKING,
78 ConfigFlag::DAB_FM_SOFT_LINKING,
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080079};
80
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080081class TunerCallbackMock : public ITunerCallback {
82 public:
83 TunerCallbackMock();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080084
85 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
86 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged, Return<void>(const ProgramInfo&));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080087 Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080088 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
89 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080090
91 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
92
93 std::mutex mLock;
94 utils::ProgramInfoSet mProgramList;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080095};
96
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -080097struct AnnouncementListenerMock : public IAnnouncementListener {
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -080098 MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
99};
100
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800101static BroadcastRadioHidlEnvironment<IBroadcastRadio>* gEnv = nullptr;
102
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800103class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
104 protected:
105 virtual void SetUp() override;
106 virtual void TearDown() override;
107
108 bool openSession();
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800109 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800110 std::optional<utils::ProgramInfoSet> getProgramList();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800111
112 sp<IBroadcastRadio> mModule;
113 Properties mProperties;
114 sp<ITunerSession> mSession;
115 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
116};
117
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800118static void printSkipped(std::string msg) {
119 std::cout << "[ SKIPPED ] " << msg << std::endl;
120}
121
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800122MATCHER_P(InfoHasId, id,
123 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
124 auto ids = utils::getAllIds(arg.selector, utils::getType(id));
125 return ids.end() != find(ids.begin(), ids.end(), id.value);
126}
127
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800128TunerCallbackMock::TunerCallbackMock() {
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800129 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged, _).Times(AnyNumber());
130
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800131 // we expect the antenna is connected through the whole test
132 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
133}
134
135Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
136 std::lock_guard<std::mutex> lk(mLock);
137
138 updateProgramList(mProgramList, chunk);
139
140 if (chunk.complete) onProgramListReady();
141
142 return {};
143}
144
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800145void BroadcastRadioHalTest::SetUp() {
146 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
147
148 // lookup HIDL service (radio module)
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800149 mModule = getService<IBroadcastRadio>(gEnv->getServiceName<IBroadcastRadio>());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800150 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
151
152 // get module properties
153 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
154 ASSERT_TRUE(propResult.isOk());
155
156 EXPECT_FALSE(mProperties.maker.empty());
157 EXPECT_FALSE(mProperties.product.empty());
158 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
159}
160
161void BroadcastRadioHalTest::TearDown() {
162 mSession.clear();
163 mModule.clear();
164 clearAndWait(mCallback, 1s);
165}
166
167bool BroadcastRadioHalTest::openSession() {
168 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
169
170 Result halResult = Result::UNKNOWN_ERROR;
171 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
172 halResult = result;
173 if (result != Result::OK) return;
174 mSession = session;
175 };
176 auto hidlResult = mModule->openSession(mCallback, openCb);
177
178 EXPECT_TRUE(hidlResult.isOk());
179 EXPECT_EQ(Result::OK, halResult);
180 EXPECT_NE(nullptr, mSession.get());
181
182 return nullptr != mSession.get();
183}
184
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800185bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
186 auto halResult = Result::UNKNOWN_ERROR;
187 auto cb = [&](Result result, AmFmRegionConfig configCb) {
188 halResult = result;
189 if (config) *config = configCb;
190 };
191
192 auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
193 EXPECT_TRUE(hidlResult.isOk());
194
195 if (halResult == Result::NOT_SUPPORTED) return false;
196
197 EXPECT_EQ(Result::OK, halResult);
198 return halResult == Result::OK;
199}
200
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800201std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
202 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
203
204 auto startResult = mSession->startProgramListUpdates({});
205 if (startResult == Result::NOT_SUPPORTED) {
206 printSkipped("Program list not supported");
207 return nullopt;
208 }
209 EXPECT_EQ(Result::OK, startResult);
210 if (startResult != Result::OK) return nullopt;
211
212 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
213
214 auto stopResult = mSession->stopProgramListUpdates();
215 EXPECT_TRUE(stopResult.isOk());
216
217 return mCallback->mProgramList;
218}
219
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800220/**
221 * Test session opening.
222 *
223 * Verifies that:
224 * - the method succeeds on a first and subsequent calls;
225 * - the method succeeds when called for the second time without
226 * closing previous session.
227 */
228TEST_F(BroadcastRadioHalTest, OpenSession) {
229 // simply open session for the first time
230 ASSERT_TRUE(openSession());
231
232 // drop (without explicit close) and re-open the session
233 mSession.clear();
234 ASSERT_TRUE(openSession());
235
236 // open the second session (the first one should be forcibly closed)
237 auto secondSession = mSession;
238 mSession.clear();
239 ASSERT_TRUE(openSession());
240}
241
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800242static bool isValidAmFmFreq(uint64_t freq) {
243 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
244 return utils::isValid(id);
245}
246
247static void validateRange(const AmFmBandRange& range) {
248 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
249 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
250 EXPECT_LT(range.lowerBound, range.upperBound);
251 EXPECT_GT(range.spacing, 0u);
252 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
253}
254
255static bool supportsFM(const AmFmRegionConfig& config) {
256 for (auto&& range : config.ranges) {
257 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
258 }
259 return false;
260}
261
262/**
263 * Test fetching AM/FM regional configuration.
264 *
265 * Verifies that:
266 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
267 * - there is at least one AM/FM band configured;
268 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
269 * - all channel grids (frequency ranges and spacings) are valid;
270 * - scan spacing is a multiply of manual spacing value.
271 */
272TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
273 AmFmRegionConfig config;
274 bool supported = getAmFmRegionConfig(false, &config);
275 if (!supported) {
276 printSkipped("AM/FM not supported");
277 return;
278 }
279
280 EXPECT_GT(config.ranges.size(), 0u);
281 EXPECT_LE(popcountll(config.fmDeemphasis), 1);
282 EXPECT_LE(popcountll(config.fmRds), 1);
283
284 for (auto&& range : config.ranges) {
285 validateRange(range);
286 EXPECT_EQ(0u, range.scanSpacing % range.spacing);
287 EXPECT_GE(range.scanSpacing, range.spacing);
288 }
289
290 if (supportsFM(config)) {
291 EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
292 }
293}
294
295/**
296 * Test fetching AM/FM regional capabilities.
297 *
298 * Verifies that:
299 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
300 * - there is at least one AM/FM range supported;
301 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
302 * - all channel grids (frequency ranges and spacings) are valid;
303 * - scan spacing is not set.
304 */
305TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
306 AmFmRegionConfig config;
307 bool supported = getAmFmRegionConfig(true, &config);
308 if (!supported) {
309 printSkipped("AM/FM not supported");
310 return;
311 }
312
313 EXPECT_GT(config.ranges.size(), 0u);
314
315 for (auto&& range : config.ranges) {
316 validateRange(range);
317 EXPECT_EQ(0u, range.scanSpacing);
318 }
319
320 if (supportsFM(config)) {
321 EXPECT_GE(popcountll(config.fmDeemphasis), 1);
322 }
323}
324
325/**
326 * Test fetching DAB regional configuration.
327 *
328 * Verifies that:
329 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
330 * - all channel labels match correct format;
331 * - all channel frequencies are in correct range.
332 */
333TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
334 Result halResult;
335 hidl_vec<DabTableEntry> config;
336 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
337 halResult = result;
338 config = configCb;
339 };
340 auto hidlResult = mModule->getDabRegionConfig(cb);
341 ASSERT_TRUE(hidlResult.isOk());
342
343 if (halResult == Result::NOT_SUPPORTED) {
344 printSkipped("DAB not supported");
345 return;
346 }
347 ASSERT_EQ(Result::OK, halResult);
348
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800349 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800350 // double-check correctness of the test
351 ASSERT_TRUE(std::regex_match("5A", re));
352 ASSERT_FALSE(std::regex_match("5a", re));
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800353 ASSERT_FALSE(std::regex_match("1234ABCD", re));
354 ASSERT_TRUE(std::regex_match("CN 12D", re));
355 ASSERT_FALSE(std::regex_match(" 5A", re));
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800356
357 for (auto&& entry : config) {
358 EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
359
360 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
361 EXPECT_TRUE(utils::isValid(id));
362 }
363}
364
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800365/**
366 * Test tuning with FM selector.
367 *
368 * Verifies that:
369 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
370 * - if it is supported, the method succeeds;
371 * - after a successful tune call, onCurrentProgramInfoChanged callback is
372 * invoked carrying a proper selector;
373 * - program changes exactly to what was requested.
374 */
375TEST_F(BroadcastRadioHalTest, FmTune) {
376 ASSERT_TRUE(openSession());
377
378 uint64_t freq = 100100; // 100.1 FM
379 auto sel = make_selector_amfm(freq);
380
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800381 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
382 * callback setting infoCb, because egmock cannot distinguish calls with different matchers
383 * (there is one here and one in callback constructor).
384 *
385 * This sleep workaround will fix default implementation, but the real HW tests will still be
386 * flaky. We probably need to implement egmock alternative based on actions.
387 */
388 std::this_thread::sleep_for(100ms);
389
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800390 // try tuning
391 ProgramInfo infoCb = {};
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800392 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged,
393 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800394 .Times(AnyNumber())
395 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
396 auto result = mSession->tune(sel);
397
398 // expect a failure if it's not supported
399 if (!utils::isSupported(mProperties, sel)) {
400 EXPECT_EQ(Result::NOT_SUPPORTED, result);
401 return;
402 }
403
404 // expect a callback if it succeeds
405 EXPECT_EQ(Result::OK, result);
406 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
407
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800408 ALOGD("current program info: %s", toString(infoCb).c_str());
409
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800410 // it should tune exactly to what was requested
411 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
412 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
413}
414
415/**
416 * Test tuning with invalid selectors.
417 *
418 * Verifies that:
419 * - if the selector is not supported, it's ignored;
420 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
421 */
422TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
423 ASSERT_TRUE(openSession());
424
425 vector<ProgramIdentifier> invalid = {
426 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
427 make_identifier(IdentifierType::RDS_PI, 0x10000),
428 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
429 make_identifier(IdentifierType::DAB_SID_EXT, 0),
430 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
431 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
432 };
433
434 for (auto&& id : invalid) {
435 ProgramSelector sel{id, {}};
436
437 auto result = mSession->tune(sel);
438
439 if (utils::isSupported(mProperties, sel)) {
440 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
441 } else {
442 EXPECT_EQ(Result::NOT_SUPPORTED, result);
443 }
444 }
445}
446
447/**
448 * Test tuning with empty program selector.
449 *
450 * Verifies that:
451 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
452 */
453TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
454 ASSERT_TRUE(openSession());
455
456 // Program type is 1-based, so 0 will always be invalid.
457 ProgramSelector sel = {};
458 auto result = mSession->tune(sel);
459 ASSERT_EQ(Result::NOT_SUPPORTED, result);
460}
461
462/**
463 * Test scanning to next/prev station.
464 *
465 * Verifies that:
466 * - the method succeeds;
467 * - the program info is changed within timeout::tune;
468 * - works both directions and with or without skipping sub-channel.
469 */
470TEST_F(BroadcastRadioHalTest, Scan) {
471 ASSERT_TRUE(openSession());
472
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800473 // TODO(b/69958777): see FmTune workaround
474 std::this_thread::sleep_for(100ms);
475
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800476 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
477 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
478 EXPECT_EQ(Result::OK, result);
479 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
480
481 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
482 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
483 EXPECT_EQ(Result::OK, result);
484 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
485}
486
487/**
488 * Test step operation.
489 *
490 * Verifies that:
491 * - the method succeeds or returns NOT_SUPPORTED;
492 * - the program info is changed within timeout::tune if the method succeeded;
493 * - works both directions.
494 */
495TEST_F(BroadcastRadioHalTest, Step) {
496 ASSERT_TRUE(openSession());
497
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800498 // TODO(b/69958777): see FmTune workaround
499 std::this_thread::sleep_for(100ms);
500
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800501 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber());
502 auto result = mSession->step(true /* up */);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800503 if (result == Result::NOT_SUPPORTED) {
504 printSkipped("step not supported");
505 return;
506 }
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800507 EXPECT_EQ(Result::OK, result);
508 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
509
510 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
511 result = mSession->step(false /* down */);
512 EXPECT_EQ(Result::OK, result);
513 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
514}
515
516/**
517 * Test tune cancellation.
518 *
519 * Verifies that:
520 * - the method does not crash after being invoked multiple times.
521 */
522TEST_F(BroadcastRadioHalTest, Cancel) {
523 ASSERT_TRUE(openSession());
524
525 for (int i = 0; i < 10; i++) {
526 auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
527 ASSERT_EQ(Result::OK, scanResult);
528
529 auto cancelResult = mSession->cancel();
530 ASSERT_TRUE(cancelResult.isOk());
531 }
532}
533
534/**
535 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
536 *
537 * Verifies that:
538 * - callback is called for empty parameters set.
539 */
540TEST_F(BroadcastRadioHalTest, NoParameters) {
541 ASSERT_TRUE(openSession());
542
543 hidl_vec<VendorKeyValue> halResults = {};
544 bool wasCalled = false;
545 auto cb = [&](hidl_vec<VendorKeyValue> results) {
546 wasCalled = true;
547 halResults = results;
548 };
549
550 auto hidlResult = mSession->setParameters({}, cb);
551 ASSERT_TRUE(hidlResult.isOk());
552 ASSERT_TRUE(wasCalled);
553 ASSERT_EQ(0u, halResults.size());
554
555 wasCalled = false;
556 hidlResult = mSession->getParameters({}, cb);
557 ASSERT_TRUE(hidlResult.isOk());
558 ASSERT_TRUE(wasCalled);
559 ASSERT_EQ(0u, halResults.size());
560}
561
562/**
563 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
564 *
565 * Verifies that:
566 * - unknown parameters are ignored;
567 * - callback is called also for empty results set.
568 */
569TEST_F(BroadcastRadioHalTest, UnknownParameters) {
570 ASSERT_TRUE(openSession());
571
572 hidl_vec<VendorKeyValue> halResults = {};
573 bool wasCalled = false;
574 auto cb = [&](hidl_vec<VendorKeyValue> results) {
575 wasCalled = true;
576 halResults = results;
577 };
578
579 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
580 ASSERT_TRUE(hidlResult.isOk());
581 ASSERT_TRUE(wasCalled);
582 ASSERT_EQ(0u, halResults.size());
583
584 wasCalled = false;
585 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
586 ASSERT_TRUE(hidlResult.isOk());
587 ASSERT_TRUE(wasCalled);
588 ASSERT_EQ(0u, halResults.size());
589}
590
591/**
592 * Test session closing.
593 *
594 * Verifies that:
595 * - the method does not crash after being invoked multiple times.
596 */
597TEST_F(BroadcastRadioHalTest, Close) {
598 ASSERT_TRUE(openSession());
599
600 for (int i = 0; i < 10; i++) {
601 auto cancelResult = mSession->close();
602 ASSERT_TRUE(cancelResult.isOk());
603 }
604}
605
606/**
607 * Test geting image of invalid ID.
608 *
609 * Verifies that:
610 * - getImage call handles argument 0 gracefully.
611 */
612TEST_F(BroadcastRadioHalTest, GetNoImage) {
613 size_t len = 0;
614 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
615
616 ASSERT_TRUE(result.isOk());
617 ASSERT_EQ(0u, len);
618}
619
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800620/**
621 * Test getting config flags.
622 *
623 * Verifies that:
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800624 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800625 * - call success or failure is consistent with setConfigFlag.
626 */
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800627TEST_F(BroadcastRadioHalTest, FetchConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800628 ASSERT_TRUE(openSession());
629
630 for (auto flag : gConfigFlagValues) {
631 auto halResult = Result::UNKNOWN_ERROR;
632 auto cb = [&](Result result, bool) { halResult = result; };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800633 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800634 EXPECT_TRUE(hidlResult.isOk());
635
636 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
637 ASSERT_EQ(Result::OK, halResult);
638 }
639
640 // set must fail or succeed the same way as get
641 auto setResult = mSession->setConfigFlag(flag, false);
642 EXPECT_EQ(halResult, setResult);
643 setResult = mSession->setConfigFlag(flag, true);
644 EXPECT_EQ(halResult, setResult);
645 }
646}
647
648/**
649 * Test setting config flags.
650 *
651 * Verifies that:
652 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800653 * - isConfigFlagSet reflects the state requested immediately after the set call.
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800654 */
655TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
656 ASSERT_TRUE(openSession());
657
658 auto get = [&](ConfigFlag flag) {
659 auto halResult = Result::UNKNOWN_ERROR;
660 bool gotValue = false;
661 auto cb = [&](Result result, bool value) {
662 halResult = result;
663 gotValue = value;
664 };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800665 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800666 EXPECT_TRUE(hidlResult.isOk());
667 EXPECT_EQ(Result::OK, halResult);
668 return gotValue;
669 };
670
671 for (auto flag : gConfigFlagValues) {
672 auto result = mSession->setConfigFlag(flag, false);
673 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
674 // setting to true must result in the same error as false
675 auto secondResult = mSession->setConfigFlag(flag, true);
676 EXPECT_EQ(result, secondResult);
677 continue;
678 }
679 ASSERT_EQ(Result::OK, result);
680
681 // verify false is set
682 auto value = get(flag);
683 EXPECT_FALSE(value);
684
685 // try setting true this time
686 result = mSession->setConfigFlag(flag, true);
687 ASSERT_EQ(Result::OK, result);
688 value = get(flag);
689 EXPECT_TRUE(value);
690
691 // false again
692 result = mSession->setConfigFlag(flag, false);
693 ASSERT_EQ(Result::OK, result);
694 value = get(flag);
695 EXPECT_FALSE(value);
696 }
697}
698
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800699/**
700 * Test getting program list.
701 *
702 * Verifies that:
703 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
704 * - the complete list is fetched within timeout::programListScan;
705 * - stopProgramListUpdates does not crash.
706 */
707TEST_F(BroadcastRadioHalTest, GetProgramList) {
708 ASSERT_TRUE(openSession());
709
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800710 getProgramList();
711}
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800712
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800713/**
714 * Test HD_STATION_NAME correctness.
715 *
716 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
717 * - the program provides station name in its metadata;
718 * - the identifier matches the name;
719 * - there is only one identifier of that type.
720 */
721TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
722 ASSERT_TRUE(openSession());
723
724 auto list = getProgramList();
725 if (!list) return;
726
727 for (auto&& program : *list) {
728 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
729 EXPECT_LE(nameIds.size(), 1u);
730 if (nameIds.size() == 0) continue;
731
732 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
733 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
734 ASSERT_TRUE(name.has_value());
735
736 auto expectedId = utils::make_hdradio_station_name(*name);
737 EXPECT_EQ(expectedId.value, nameIds[0]);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800738 }
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800739}
740
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800741/**
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800742 * Test announcement listener registration.
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800743 *
744 * Verifies that:
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800745 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800746 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
747 * - closing handle does not crash.
748 */
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800749TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
750 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800751
752 Result halResult = Result::UNKNOWN_ERROR;
753 sp<ICloseHandle> closeHandle = nullptr;
754 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
755 halResult = result;
756 closeHandle = closeHandle_;
757 };
758
759 auto hidlResult =
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800760 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800761 ASSERT_TRUE(hidlResult.isOk());
762
763 if (halResult == Result::NOT_SUPPORTED) {
764 ASSERT_EQ(nullptr, closeHandle.get());
765 printSkipped("Announcements not supported");
766 return;
767 }
768
769 ASSERT_EQ(Result::OK, halResult);
770 ASSERT_NE(nullptr, closeHandle.get());
771
772 closeHandle->close();
773}
774
Tomasz Wasilczyk4ce63822017-12-21 14:25:54 -0800775// TODO(b/70939328): test ProgramInfo's currentlyTunedId and
776// currentlyTunedChannel once the program list is implemented.
777
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800778} // namespace vts
779} // namespace V2_0
780} // namespace broadcastradio
781} // namespace hardware
782} // namespace android
783
784int main(int argc, char** argv) {
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800785 using android::hardware::broadcastradio::V2_0::vts::gEnv;
786 using android::hardware::broadcastradio::V2_0::IBroadcastRadio;
787 using android::hardware::broadcastradio::vts::BroadcastRadioHidlEnvironment;
788 gEnv = new BroadcastRadioHidlEnvironment<IBroadcastRadio>;
789 ::testing::AddGlobalTestEnvironment(gEnv);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800790 ::testing::InitGoogleTest(&argc, argv);
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800791 gEnv->init(&argc, argv);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800792 int status = RUN_ALL_TESTS();
793 ALOGI("Test result = %d", status);
794 return status;
795}