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