blob: 2a5ec8fee5eafabf39d91890cea5fc9bf4bc6e42 [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&));
Tomasz Wasilczyk67360522018-02-10 14:05:18 -080084 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged_, Return<void>(const ProgramInfo&));
85 virtual Return<void> onCurrentProgramInfoChanged(const ProgramInfo& info);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080086 Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080087 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
88 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080089
90 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
91
92 std::mutex mLock;
93 utils::ProgramInfoSet mProgramList;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080094};
95
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -080096struct AnnouncementListenerMock : public IAnnouncementListener {
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -080097 MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
98};
99
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800100class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
101 protected:
102 virtual void SetUp() override;
103 virtual void TearDown() override;
104
105 bool openSession();
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800106 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800107 std::optional<utils::ProgramInfoSet> getProgramList();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800108
109 sp<IBroadcastRadio> mModule;
110 Properties mProperties;
111 sp<ITunerSession> mSession;
112 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
113};
114
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800115static void printSkipped(std::string msg) {
116 std::cout << "[ SKIPPED ] " << msg << std::endl;
117}
118
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800119MATCHER_P(InfoHasId, id,
120 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
121 auto ids = utils::getAllIds(arg.selector, utils::getType(id));
122 return ids.end() != find(ids.begin(), ids.end(), id.value);
123}
124
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800125TunerCallbackMock::TunerCallbackMock() {
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800126 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800127
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800128 // we expect the antenna is connected through the whole test
129 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
130}
131
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800132Return<void> TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
133 auto logically = utils::getType(info.logicallyTunedTo);
134 if (logically != IdentifierType::INVALID) {
135 EXPECT_TRUE(logically == IdentifierType::AMFM_FREQUENCY ||
136 logically == IdentifierType::RDS_PI ||
137 logically == IdentifierType::HD_STATION_ID_EXT ||
138 logically == IdentifierType::DAB_SID_EXT ||
139 logically == IdentifierType::DRMO_SERVICE_ID ||
140 logically == IdentifierType::SXM_SERVICE_ID ||
141 (logically >= IdentifierType::VENDOR_START &&
142 logically <= IdentifierType::VENDOR_END) ||
143 logically > IdentifierType::SXM_CHANNEL);
144 }
145
146 auto physically = utils::getType(info.physicallyTunedTo);
147 if (physically != IdentifierType::INVALID) {
148 EXPECT_TRUE(physically == IdentifierType::AMFM_FREQUENCY ||
149 physically == IdentifierType::DAB_ENSEMBLE ||
150 physically == IdentifierType::DRMO_FREQUENCY ||
151 physically == IdentifierType::SXM_CHANNEL ||
152 (physically >= IdentifierType::VENDOR_START &&
153 physically <= IdentifierType::VENDOR_END) ||
154 physically > IdentifierType::SXM_CHANNEL);
155 }
156
157 return onCurrentProgramInfoChanged_(info);
158}
159
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800160Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
161 std::lock_guard<std::mutex> lk(mLock);
162
163 updateProgramList(mProgramList, chunk);
164
165 if (chunk.complete) onProgramListReady();
166
167 return {};
168}
169
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800170void BroadcastRadioHalTest::SetUp() {
171 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
172
173 // lookup HIDL service (radio module)
174 mModule = getService<IBroadcastRadio>();
175 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
176
177 // get module properties
178 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
179 ASSERT_TRUE(propResult.isOk());
180
181 EXPECT_FALSE(mProperties.maker.empty());
182 EXPECT_FALSE(mProperties.product.empty());
183 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
184}
185
186void BroadcastRadioHalTest::TearDown() {
187 mSession.clear();
188 mModule.clear();
189 clearAndWait(mCallback, 1s);
190}
191
192bool BroadcastRadioHalTest::openSession() {
193 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
194
195 Result halResult = Result::UNKNOWN_ERROR;
196 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
197 halResult = result;
198 if (result != Result::OK) return;
199 mSession = session;
200 };
201 auto hidlResult = mModule->openSession(mCallback, openCb);
202
203 EXPECT_TRUE(hidlResult.isOk());
204 EXPECT_EQ(Result::OK, halResult);
205 EXPECT_NE(nullptr, mSession.get());
206
207 return nullptr != mSession.get();
208}
209
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800210bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
211 auto halResult = Result::UNKNOWN_ERROR;
212 auto cb = [&](Result result, AmFmRegionConfig configCb) {
213 halResult = result;
214 if (config) *config = configCb;
215 };
216
217 auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
218 EXPECT_TRUE(hidlResult.isOk());
219
220 if (halResult == Result::NOT_SUPPORTED) return false;
221
222 EXPECT_EQ(Result::OK, halResult);
223 return halResult == Result::OK;
224}
225
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800226std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
227 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
228
229 auto startResult = mSession->startProgramListUpdates({});
230 if (startResult == Result::NOT_SUPPORTED) {
231 printSkipped("Program list not supported");
232 return nullopt;
233 }
234 EXPECT_EQ(Result::OK, startResult);
235 if (startResult != Result::OK) return nullopt;
236
237 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
238
239 auto stopResult = mSession->stopProgramListUpdates();
240 EXPECT_TRUE(stopResult.isOk());
241
242 return mCallback->mProgramList;
243}
244
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800245/**
246 * Test session opening.
247 *
248 * Verifies that:
249 * - the method succeeds on a first and subsequent calls;
250 * - the method succeeds when called for the second time without
251 * closing previous session.
252 */
253TEST_F(BroadcastRadioHalTest, OpenSession) {
254 // simply open session for the first time
255 ASSERT_TRUE(openSession());
256
257 // drop (without explicit close) and re-open the session
258 mSession.clear();
259 ASSERT_TRUE(openSession());
260
261 // open the second session (the first one should be forcibly closed)
262 auto secondSession = mSession;
263 mSession.clear();
264 ASSERT_TRUE(openSession());
265}
266
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800267static bool isValidAmFmFreq(uint64_t freq) {
268 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
269 return utils::isValid(id);
270}
271
272static void validateRange(const AmFmBandRange& range) {
273 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
274 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
275 EXPECT_LT(range.lowerBound, range.upperBound);
276 EXPECT_GT(range.spacing, 0u);
277 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
278}
279
280static bool supportsFM(const AmFmRegionConfig& config) {
281 for (auto&& range : config.ranges) {
282 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
283 }
284 return false;
285}
286
287/**
288 * Test fetching AM/FM regional configuration.
289 *
290 * Verifies that:
291 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
292 * - there is at least one AM/FM band configured;
293 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
294 * - all channel grids (frequency ranges and spacings) are valid;
295 * - scan spacing is a multiply of manual spacing value.
296 */
297TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
298 AmFmRegionConfig config;
299 bool supported = getAmFmRegionConfig(false, &config);
300 if (!supported) {
301 printSkipped("AM/FM not supported");
302 return;
303 }
304
305 EXPECT_GT(config.ranges.size(), 0u);
306 EXPECT_LE(popcountll(config.fmDeemphasis), 1);
307 EXPECT_LE(popcountll(config.fmRds), 1);
308
309 for (auto&& range : config.ranges) {
310 validateRange(range);
311 EXPECT_EQ(0u, range.scanSpacing % range.spacing);
312 EXPECT_GE(range.scanSpacing, range.spacing);
313 }
314
315 if (supportsFM(config)) {
316 EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
317 }
318}
319
320/**
321 * Test fetching AM/FM regional capabilities.
322 *
323 * Verifies that:
324 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
325 * - there is at least one AM/FM range supported;
326 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
327 * - all channel grids (frequency ranges and spacings) are valid;
328 * - scan spacing is not set.
329 */
330TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
331 AmFmRegionConfig config;
332 bool supported = getAmFmRegionConfig(true, &config);
333 if (!supported) {
334 printSkipped("AM/FM not supported");
335 return;
336 }
337
338 EXPECT_GT(config.ranges.size(), 0u);
339
340 for (auto&& range : config.ranges) {
341 validateRange(range);
342 EXPECT_EQ(0u, range.scanSpacing);
343 }
344
345 if (supportsFM(config)) {
346 EXPECT_GE(popcountll(config.fmDeemphasis), 1);
347 }
348}
349
350/**
351 * Test fetching DAB regional configuration.
352 *
353 * Verifies that:
354 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
355 * - all channel labels match correct format;
356 * - all channel frequencies are in correct range.
357 */
358TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
359 Result halResult;
360 hidl_vec<DabTableEntry> config;
361 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
362 halResult = result;
363 config = configCb;
364 };
365 auto hidlResult = mModule->getDabRegionConfig(cb);
366 ASSERT_TRUE(hidlResult.isOk());
367
368 if (halResult == Result::NOT_SUPPORTED) {
369 printSkipped("DAB not supported");
370 return;
371 }
372 ASSERT_EQ(Result::OK, halResult);
373
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800374 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800375 // double-check correctness of the test
376 ASSERT_TRUE(std::regex_match("5A", re));
377 ASSERT_FALSE(std::regex_match("5a", re));
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800378 ASSERT_FALSE(std::regex_match("1234ABCD", re));
379 ASSERT_TRUE(std::regex_match("CN 12D", re));
380 ASSERT_FALSE(std::regex_match(" 5A", re));
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800381
382 for (auto&& entry : config) {
383 EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
384
385 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
386 EXPECT_TRUE(utils::isValid(id));
387 }
388}
389
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800390/**
391 * Test tuning with FM selector.
392 *
393 * Verifies that:
394 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
395 * - if it is supported, the method succeeds;
396 * - after a successful tune call, onCurrentProgramInfoChanged callback is
397 * invoked carrying a proper selector;
398 * - program changes exactly to what was requested.
399 */
400TEST_F(BroadcastRadioHalTest, FmTune) {
401 ASSERT_TRUE(openSession());
402
403 uint64_t freq = 100100; // 100.1 FM
404 auto sel = make_selector_amfm(freq);
405
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800406 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
407 * callback setting infoCb, because egmock cannot distinguish calls with different matchers
408 * (there is one here and one in callback constructor).
409 *
410 * This sleep workaround will fix default implementation, but the real HW tests will still be
411 * flaky. We probably need to implement egmock alternative based on actions.
412 */
413 std::this_thread::sleep_for(100ms);
414
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800415 // try tuning
416 ProgramInfo infoCb = {};
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800417 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800418 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800419 .Times(AnyNumber())
420 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
421 auto result = mSession->tune(sel);
422
423 // expect a failure if it's not supported
424 if (!utils::isSupported(mProperties, sel)) {
425 EXPECT_EQ(Result::NOT_SUPPORTED, result);
426 return;
427 }
428
429 // expect a callback if it succeeds
430 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800431 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800432
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800433 ALOGD("current program info: %s", toString(infoCb).c_str());
434
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800435 // it should tune exactly to what was requested
436 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
437 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
438}
439
440/**
441 * Test tuning with invalid selectors.
442 *
443 * Verifies that:
444 * - if the selector is not supported, it's ignored;
445 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
446 */
447TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
448 ASSERT_TRUE(openSession());
449
450 vector<ProgramIdentifier> invalid = {
451 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
452 make_identifier(IdentifierType::RDS_PI, 0x10000),
453 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
454 make_identifier(IdentifierType::DAB_SID_EXT, 0),
455 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
456 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
457 };
458
459 for (auto&& id : invalid) {
460 ProgramSelector sel{id, {}};
461
462 auto result = mSession->tune(sel);
463
464 if (utils::isSupported(mProperties, sel)) {
465 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
466 } else {
467 EXPECT_EQ(Result::NOT_SUPPORTED, result);
468 }
469 }
470}
471
472/**
473 * Test tuning with empty program selector.
474 *
475 * Verifies that:
476 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
477 */
478TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
479 ASSERT_TRUE(openSession());
480
481 // Program type is 1-based, so 0 will always be invalid.
482 ProgramSelector sel = {};
483 auto result = mSession->tune(sel);
484 ASSERT_EQ(Result::NOT_SUPPORTED, result);
485}
486
487/**
488 * Test scanning to next/prev station.
489 *
490 * Verifies that:
491 * - the method succeeds;
492 * - the program info is changed within timeout::tune;
493 * - works both directions and with or without skipping sub-channel.
494 */
495TEST_F(BroadcastRadioHalTest, Scan) {
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 Wasilczyk67360522018-02-10 14:05:18 -0800501 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800502 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
503 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800504 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800505
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800506 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800507 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
508 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800509 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800510}
511
512/**
513 * Test step operation.
514 *
515 * Verifies that:
516 * - the method succeeds or returns NOT_SUPPORTED;
517 * - the program info is changed within timeout::tune if the method succeeded;
518 * - works both directions.
519 */
520TEST_F(BroadcastRadioHalTest, Step) {
521 ASSERT_TRUE(openSession());
522
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800523 // TODO(b/69958777): see FmTune workaround
524 std::this_thread::sleep_for(100ms);
525
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800526 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800527 auto result = mSession->step(true /* up */);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800528 if (result == Result::NOT_SUPPORTED) {
529 printSkipped("step not supported");
530 return;
531 }
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800532 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800533 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800534
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800535 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800536 result = mSession->step(false /* down */);
537 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800538 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800539}
540
541/**
542 * Test tune cancellation.
543 *
544 * Verifies that:
545 * - the method does not crash after being invoked multiple times.
546 */
547TEST_F(BroadcastRadioHalTest, Cancel) {
548 ASSERT_TRUE(openSession());
549
550 for (int i = 0; i < 10; i++) {
551 auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
552 ASSERT_EQ(Result::OK, scanResult);
553
554 auto cancelResult = mSession->cancel();
555 ASSERT_TRUE(cancelResult.isOk());
556 }
557}
558
559/**
560 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
561 *
562 * Verifies that:
563 * - callback is called for empty parameters set.
564 */
565TEST_F(BroadcastRadioHalTest, NoParameters) {
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({}, cb);
576 ASSERT_TRUE(hidlResult.isOk());
577 ASSERT_TRUE(wasCalled);
578 ASSERT_EQ(0u, halResults.size());
579
580 wasCalled = false;
581 hidlResult = mSession->getParameters({}, cb);
582 ASSERT_TRUE(hidlResult.isOk());
583 ASSERT_TRUE(wasCalled);
584 ASSERT_EQ(0u, halResults.size());
585}
586
587/**
588 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
589 *
590 * Verifies that:
591 * - unknown parameters are ignored;
592 * - callback is called also for empty results set.
593 */
594TEST_F(BroadcastRadioHalTest, UnknownParameters) {
595 ASSERT_TRUE(openSession());
596
597 hidl_vec<VendorKeyValue> halResults = {};
598 bool wasCalled = false;
599 auto cb = [&](hidl_vec<VendorKeyValue> results) {
600 wasCalled = true;
601 halResults = results;
602 };
603
604 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
605 ASSERT_TRUE(hidlResult.isOk());
606 ASSERT_TRUE(wasCalled);
607 ASSERT_EQ(0u, halResults.size());
608
609 wasCalled = false;
610 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
611 ASSERT_TRUE(hidlResult.isOk());
612 ASSERT_TRUE(wasCalled);
613 ASSERT_EQ(0u, halResults.size());
614}
615
616/**
617 * Test session closing.
618 *
619 * Verifies that:
620 * - the method does not crash after being invoked multiple times.
621 */
622TEST_F(BroadcastRadioHalTest, Close) {
623 ASSERT_TRUE(openSession());
624
625 for (int i = 0; i < 10; i++) {
626 auto cancelResult = mSession->close();
627 ASSERT_TRUE(cancelResult.isOk());
628 }
629}
630
631/**
632 * Test geting image of invalid ID.
633 *
634 * Verifies that:
635 * - getImage call handles argument 0 gracefully.
636 */
637TEST_F(BroadcastRadioHalTest, GetNoImage) {
638 size_t len = 0;
639 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
640
641 ASSERT_TRUE(result.isOk());
642 ASSERT_EQ(0u, len);
643}
644
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800645/**
646 * Test getting config flags.
647 *
648 * Verifies that:
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800649 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800650 * - call success or failure is consistent with setConfigFlag.
651 */
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800652TEST_F(BroadcastRadioHalTest, FetchConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800653 ASSERT_TRUE(openSession());
654
655 for (auto flag : gConfigFlagValues) {
656 auto halResult = Result::UNKNOWN_ERROR;
657 auto cb = [&](Result result, bool) { halResult = result; };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800658 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800659 EXPECT_TRUE(hidlResult.isOk());
660
661 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
662 ASSERT_EQ(Result::OK, halResult);
663 }
664
665 // set must fail or succeed the same way as get
666 auto setResult = mSession->setConfigFlag(flag, false);
667 EXPECT_EQ(halResult, setResult);
668 setResult = mSession->setConfigFlag(flag, true);
669 EXPECT_EQ(halResult, setResult);
670 }
671}
672
673/**
674 * Test setting config flags.
675 *
676 * Verifies that:
677 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800678 * - isConfigFlagSet reflects the state requested immediately after the set call.
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800679 */
680TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
681 ASSERT_TRUE(openSession());
682
683 auto get = [&](ConfigFlag flag) {
684 auto halResult = Result::UNKNOWN_ERROR;
685 bool gotValue = false;
686 auto cb = [&](Result result, bool value) {
687 halResult = result;
688 gotValue = value;
689 };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800690 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800691 EXPECT_TRUE(hidlResult.isOk());
692 EXPECT_EQ(Result::OK, halResult);
693 return gotValue;
694 };
695
696 for (auto flag : gConfigFlagValues) {
697 auto result = mSession->setConfigFlag(flag, false);
698 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
699 // setting to true must result in the same error as false
700 auto secondResult = mSession->setConfigFlag(flag, true);
701 EXPECT_EQ(result, secondResult);
702 continue;
703 }
704 ASSERT_EQ(Result::OK, result);
705
706 // verify false is set
707 auto value = get(flag);
708 EXPECT_FALSE(value);
709
710 // try setting true this time
711 result = mSession->setConfigFlag(flag, true);
712 ASSERT_EQ(Result::OK, result);
713 value = get(flag);
714 EXPECT_TRUE(value);
715
716 // false again
717 result = mSession->setConfigFlag(flag, false);
718 ASSERT_EQ(Result::OK, result);
719 value = get(flag);
720 EXPECT_FALSE(value);
721 }
722}
723
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800724/**
725 * Test getting program list.
726 *
727 * Verifies that:
728 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
729 * - the complete list is fetched within timeout::programListScan;
730 * - stopProgramListUpdates does not crash.
731 */
732TEST_F(BroadcastRadioHalTest, GetProgramList) {
733 ASSERT_TRUE(openSession());
734
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800735 getProgramList();
736}
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800737
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800738/**
739 * Test HD_STATION_NAME correctness.
740 *
741 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
742 * - the program provides station name in its metadata;
743 * - the identifier matches the name;
744 * - there is only one identifier of that type.
745 */
746TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
747 ASSERT_TRUE(openSession());
748
749 auto list = getProgramList();
750 if (!list) return;
751
752 for (auto&& program : *list) {
753 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
754 EXPECT_LE(nameIds.size(), 1u);
755 if (nameIds.size() == 0) continue;
756
757 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
758 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
759 ASSERT_TRUE(name.has_value());
760
761 auto expectedId = utils::make_hdradio_station_name(*name);
762 EXPECT_EQ(expectedId.value, nameIds[0]);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800763 }
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800764}
765
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800766/**
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800767 * Test announcement listener registration.
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800768 *
769 * Verifies that:
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800770 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800771 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
772 * - closing handle does not crash.
773 */
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800774TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
775 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800776
777 Result halResult = Result::UNKNOWN_ERROR;
778 sp<ICloseHandle> closeHandle = nullptr;
779 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
780 halResult = result;
781 closeHandle = closeHandle_;
782 };
783
784 auto hidlResult =
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800785 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800786 ASSERT_TRUE(hidlResult.isOk());
787
788 if (halResult == Result::NOT_SUPPORTED) {
789 ASSERT_EQ(nullptr, closeHandle.get());
790 printSkipped("Announcements not supported");
791 return;
792 }
793
794 ASSERT_EQ(Result::OK, halResult);
795 ASSERT_NE(nullptr, closeHandle.get());
796
797 closeHandle->close();
798}
799
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800800} // namespace vts
801} // namespace V2_0
802} // namespace broadcastradio
803} // namespace hardware
804} // namespace android
805
806int main(int argc, char** argv) {
807 ::testing::InitGoogleTest(&argc, argv);
808 int status = RUN_ALL_TESTS();
809 ALOGI("Test result = %d", status);
810 return status;
811}