blob: 571b80c793d90cc8ad94af51693c78e06b6fa2ac [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>
Tomasz Wasilczyk8acabf22018-04-30 10:22:20 -070023#include <android-base/strings.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080024#include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
25#include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
26#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
27#include <android/hardware/broadcastradio/2.0/types.h>
28#include <broadcastradio-utils-2x/Utils.h>
29#include <broadcastradio-vts-utils/call-barrier.h>
Zhuoyao Zhang190548f2018-02-08 20:40:23 -080030#include <broadcastradio-vts-utils/environment-utils.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080031#include <broadcastradio-vts-utils/mock-timeout.h>
32#include <broadcastradio-vts-utils/pointer-utils.h>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080033#include <cutils/bitops.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080034#include <gmock/gmock.h>
35
36#include <chrono>
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -080037#include <optional>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080038#include <regex>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080039
40namespace android {
41namespace hardware {
42namespace broadcastradio {
43namespace V2_0 {
44namespace vts {
45
46using namespace std::chrono_literals;
47
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080048using std::unordered_set;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080049using std::vector;
50using testing::_;
51using testing::AnyNumber;
52using testing::ByMove;
53using testing::DoAll;
54using testing::Invoke;
55using testing::SaveArg;
56
Zhuoyao Zhang190548f2018-02-08 20:40:23 -080057using broadcastradio::vts::BroadcastRadioHidlEnvironment;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080058using broadcastradio::vts::CallBarrier;
59using broadcastradio::vts::clearAndWait;
60using utils::make_identifier;
61using utils::make_selector_amfm;
62
63namespace timeout {
64
65static constexpr auto tune = 30s;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080066static constexpr auto programListScan = 5min;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080067
68} // namespace timeout
69
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -070070static constexpr auto gTuneWorkaround = 200ms;
71
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080072static const ConfigFlag gConfigFlagValues[] = {
Tomasz Wasilczyk30240f62017-12-20 14:19:21 -080073 ConfigFlag::FORCE_MONO,
74 ConfigFlag::FORCE_ANALOG,
75 ConfigFlag::FORCE_DIGITAL,
76 ConfigFlag::RDS_AF,
77 ConfigFlag::RDS_REG,
78 ConfigFlag::DAB_DAB_LINKING,
79 ConfigFlag::DAB_FM_LINKING,
80 ConfigFlag::DAB_DAB_SOFT_LINKING,
81 ConfigFlag::DAB_FM_SOFT_LINKING,
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080082};
83
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080084class TunerCallbackMock : public ITunerCallback {
85 public:
86 TunerCallbackMock();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080087
88 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
Tomasz Wasilczyk67360522018-02-10 14:05:18 -080089 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged_, Return<void>(const ProgramInfo&));
90 virtual Return<void> onCurrentProgramInfoChanged(const ProgramInfo& info);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080091 Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080092 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
93 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080094
95 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
96
97 std::mutex mLock;
98 utils::ProgramInfoSet mProgramList;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080099};
100
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800101struct AnnouncementListenerMock : public IAnnouncementListener {
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800102 MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
103};
104
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800105static BroadcastRadioHidlEnvironment<IBroadcastRadio>* gEnv = nullptr;
106
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800107class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
108 protected:
109 virtual void SetUp() override;
110 virtual void TearDown() override;
111
112 bool openSession();
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800113 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800114 std::optional<utils::ProgramInfoSet> getProgramList();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800115
116 sp<IBroadcastRadio> mModule;
117 Properties mProperties;
118 sp<ITunerSession> mSession;
119 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
120};
121
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800122static void printSkipped(std::string msg) {
123 std::cout << "[ SKIPPED ] " << msg << std::endl;
124}
125
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800126MATCHER_P(InfoHasId, id,
127 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
128 auto ids = utils::getAllIds(arg.selector, utils::getType(id));
129 return ids.end() != find(ids.begin(), ids.end(), id.value);
130}
131
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800132TunerCallbackMock::TunerCallbackMock() {
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800133 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800134
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800135 // we expect the antenna is connected through the whole test
136 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
137}
138
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800139Return<void> TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
140 auto logically = utils::getType(info.logicallyTunedTo);
141 if (logically != IdentifierType::INVALID) {
142 EXPECT_TRUE(logically == IdentifierType::AMFM_FREQUENCY ||
143 logically == IdentifierType::RDS_PI ||
144 logically == IdentifierType::HD_STATION_ID_EXT ||
145 logically == IdentifierType::DAB_SID_EXT ||
146 logically == IdentifierType::DRMO_SERVICE_ID ||
147 logically == IdentifierType::SXM_SERVICE_ID ||
148 (logically >= IdentifierType::VENDOR_START &&
149 logically <= IdentifierType::VENDOR_END) ||
150 logically > IdentifierType::SXM_CHANNEL);
151 }
152
153 auto physically = utils::getType(info.physicallyTunedTo);
154 if (physically != IdentifierType::INVALID) {
155 EXPECT_TRUE(physically == IdentifierType::AMFM_FREQUENCY ||
156 physically == IdentifierType::DAB_ENSEMBLE ||
157 physically == IdentifierType::DRMO_FREQUENCY ||
158 physically == IdentifierType::SXM_CHANNEL ||
159 (physically >= IdentifierType::VENDOR_START &&
160 physically <= IdentifierType::VENDOR_END) ||
161 physically > IdentifierType::SXM_CHANNEL);
162 }
163
Tomasz Wasilczyk8acabf22018-04-30 10:22:20 -0700164 if (logically == IdentifierType::AMFM_FREQUENCY) {
165 auto ps = utils::getMetadataString(info, MetadataKey::RDS_PS);
166 if (ps.has_value()) {
167 EXPECT_NE("", android::base::Trim(*ps))
168 << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
169 }
170 }
171
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800172 return onCurrentProgramInfoChanged_(info);
173}
174
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800175Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
176 std::lock_guard<std::mutex> lk(mLock);
177
178 updateProgramList(mProgramList, chunk);
179
180 if (chunk.complete) onProgramListReady();
181
182 return {};
183}
184
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800185void BroadcastRadioHalTest::SetUp() {
186 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
187
188 // lookup HIDL service (radio module)
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800189 mModule = getService<IBroadcastRadio>(gEnv->getServiceName<IBroadcastRadio>());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800190 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
191
192 // get module properties
193 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
194 ASSERT_TRUE(propResult.isOk());
195
196 EXPECT_FALSE(mProperties.maker.empty());
197 EXPECT_FALSE(mProperties.product.empty());
198 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
199}
200
201void BroadcastRadioHalTest::TearDown() {
202 mSession.clear();
203 mModule.clear();
204 clearAndWait(mCallback, 1s);
205}
206
207bool BroadcastRadioHalTest::openSession() {
208 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
209
210 Result halResult = Result::UNKNOWN_ERROR;
211 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
212 halResult = result;
213 if (result != Result::OK) return;
214 mSession = session;
215 };
216 auto hidlResult = mModule->openSession(mCallback, openCb);
217
218 EXPECT_TRUE(hidlResult.isOk());
219 EXPECT_EQ(Result::OK, halResult);
220 EXPECT_NE(nullptr, mSession.get());
221
222 return nullptr != mSession.get();
223}
224
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800225bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
226 auto halResult = Result::UNKNOWN_ERROR;
227 auto cb = [&](Result result, AmFmRegionConfig configCb) {
228 halResult = result;
229 if (config) *config = configCb;
230 };
231
232 auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
233 EXPECT_TRUE(hidlResult.isOk());
234
235 if (halResult == Result::NOT_SUPPORTED) return false;
236
237 EXPECT_EQ(Result::OK, halResult);
238 return halResult == Result::OK;
239}
240
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800241std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
242 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
243
244 auto startResult = mSession->startProgramListUpdates({});
245 if (startResult == Result::NOT_SUPPORTED) {
246 printSkipped("Program list not supported");
247 return nullopt;
248 }
249 EXPECT_EQ(Result::OK, startResult);
250 if (startResult != Result::OK) return nullopt;
251
252 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
253
254 auto stopResult = mSession->stopProgramListUpdates();
255 EXPECT_TRUE(stopResult.isOk());
256
257 return mCallback->mProgramList;
258}
259
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800260/**
261 * Test session opening.
262 *
263 * Verifies that:
264 * - the method succeeds on a first and subsequent calls;
265 * - the method succeeds when called for the second time without
266 * closing previous session.
267 */
268TEST_F(BroadcastRadioHalTest, OpenSession) {
269 // simply open session for the first time
270 ASSERT_TRUE(openSession());
271
272 // drop (without explicit close) and re-open the session
273 mSession.clear();
274 ASSERT_TRUE(openSession());
275
276 // open the second session (the first one should be forcibly closed)
277 auto secondSession = mSession;
278 mSession.clear();
279 ASSERT_TRUE(openSession());
280}
281
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800282static bool isValidAmFmFreq(uint64_t freq) {
283 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
284 return utils::isValid(id);
285}
286
287static void validateRange(const AmFmBandRange& range) {
288 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
289 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
290 EXPECT_LT(range.lowerBound, range.upperBound);
291 EXPECT_GT(range.spacing, 0u);
292 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
293}
294
295static bool supportsFM(const AmFmRegionConfig& config) {
296 for (auto&& range : config.ranges) {
297 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
298 }
299 return false;
300}
301
302/**
303 * Test fetching AM/FM regional configuration.
304 *
305 * Verifies that:
306 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
307 * - there is at least one AM/FM band configured;
308 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
309 * - all channel grids (frequency ranges and spacings) are valid;
310 * - scan spacing is a multiply of manual spacing value.
311 */
312TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
313 AmFmRegionConfig config;
314 bool supported = getAmFmRegionConfig(false, &config);
315 if (!supported) {
316 printSkipped("AM/FM not supported");
317 return;
318 }
319
320 EXPECT_GT(config.ranges.size(), 0u);
321 EXPECT_LE(popcountll(config.fmDeemphasis), 1);
322 EXPECT_LE(popcountll(config.fmRds), 1);
323
324 for (auto&& range : config.ranges) {
325 validateRange(range);
326 EXPECT_EQ(0u, range.scanSpacing % range.spacing);
327 EXPECT_GE(range.scanSpacing, range.spacing);
328 }
329
330 if (supportsFM(config)) {
331 EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
332 }
333}
334
335/**
336 * Test fetching AM/FM regional capabilities.
337 *
338 * Verifies that:
339 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
340 * - there is at least one AM/FM range supported;
341 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
342 * - all channel grids (frequency ranges and spacings) are valid;
343 * - scan spacing is not set.
344 */
345TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
346 AmFmRegionConfig config;
347 bool supported = getAmFmRegionConfig(true, &config);
348 if (!supported) {
349 printSkipped("AM/FM not supported");
350 return;
351 }
352
353 EXPECT_GT(config.ranges.size(), 0u);
354
355 for (auto&& range : config.ranges) {
356 validateRange(range);
357 EXPECT_EQ(0u, range.scanSpacing);
358 }
359
360 if (supportsFM(config)) {
361 EXPECT_GE(popcountll(config.fmDeemphasis), 1);
362 }
363}
364
365/**
366 * Test fetching DAB regional configuration.
367 *
368 * Verifies that:
369 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
370 * - all channel labels match correct format;
371 * - all channel frequencies are in correct range.
372 */
373TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
374 Result halResult;
375 hidl_vec<DabTableEntry> config;
376 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
377 halResult = result;
378 config = configCb;
379 };
380 auto hidlResult = mModule->getDabRegionConfig(cb);
381 ASSERT_TRUE(hidlResult.isOk());
382
383 if (halResult == Result::NOT_SUPPORTED) {
384 printSkipped("DAB not supported");
385 return;
386 }
387 ASSERT_EQ(Result::OK, halResult);
388
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800389 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800390 // double-check correctness of the test
391 ASSERT_TRUE(std::regex_match("5A", re));
392 ASSERT_FALSE(std::regex_match("5a", re));
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800393 ASSERT_FALSE(std::regex_match("1234ABCD", re));
394 ASSERT_TRUE(std::regex_match("CN 12D", re));
395 ASSERT_FALSE(std::regex_match(" 5A", re));
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800396
397 for (auto&& entry : config) {
398 EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
399
400 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
401 EXPECT_TRUE(utils::isValid(id));
402 }
403}
404
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800405/**
406 * Test tuning with FM selector.
407 *
408 * Verifies that:
409 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
410 * - if it is supported, the method succeeds;
411 * - after a successful tune call, onCurrentProgramInfoChanged callback is
412 * invoked carrying a proper selector;
413 * - program changes exactly to what was requested.
414 */
415TEST_F(BroadcastRadioHalTest, FmTune) {
416 ASSERT_TRUE(openSession());
417
418 uint64_t freq = 100100; // 100.1 FM
419 auto sel = make_selector_amfm(freq);
420
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800421 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
422 * callback setting infoCb, because egmock cannot distinguish calls with different matchers
423 * (there is one here and one in callback constructor).
424 *
425 * This sleep workaround will fix default implementation, but the real HW tests will still be
426 * flaky. We probably need to implement egmock alternative based on actions.
427 */
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700428 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800429
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800430 // try tuning
431 ProgramInfo infoCb = {};
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800432 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800433 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800434 .Times(AnyNumber())
435 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
436 auto result = mSession->tune(sel);
437
438 // expect a failure if it's not supported
439 if (!utils::isSupported(mProperties, sel)) {
440 EXPECT_EQ(Result::NOT_SUPPORTED, result);
441 return;
442 }
443
444 // expect a callback if it succeeds
445 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800446 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800447
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800448 ALOGD("current program info: %s", toString(infoCb).c_str());
449
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800450 // it should tune exactly to what was requested
451 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
452 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
453}
454
455/**
456 * Test tuning with invalid selectors.
457 *
458 * Verifies that:
459 * - if the selector is not supported, it's ignored;
460 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
461 */
462TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
463 ASSERT_TRUE(openSession());
464
465 vector<ProgramIdentifier> invalid = {
466 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
467 make_identifier(IdentifierType::RDS_PI, 0x10000),
468 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
469 make_identifier(IdentifierType::DAB_SID_EXT, 0),
470 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
471 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
472 };
473
474 for (auto&& id : invalid) {
475 ProgramSelector sel{id, {}};
476
477 auto result = mSession->tune(sel);
478
479 if (utils::isSupported(mProperties, sel)) {
480 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
481 } else {
482 EXPECT_EQ(Result::NOT_SUPPORTED, result);
483 }
484 }
485}
486
487/**
488 * Test tuning with empty program selector.
489 *
490 * Verifies that:
491 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
492 */
493TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
494 ASSERT_TRUE(openSession());
495
496 // Program type is 1-based, so 0 will always be invalid.
497 ProgramSelector sel = {};
498 auto result = mSession->tune(sel);
499 ASSERT_EQ(Result::NOT_SUPPORTED, result);
500}
501
502/**
503 * Test scanning to next/prev station.
504 *
505 * Verifies that:
506 * - the method succeeds;
507 * - the program info is changed within timeout::tune;
508 * - works both directions and with or without skipping sub-channel.
509 */
510TEST_F(BroadcastRadioHalTest, Scan) {
511 ASSERT_TRUE(openSession());
512
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800513 // TODO(b/69958777): see FmTune workaround
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700514 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800515
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800516 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800517 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
518 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800519 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800520
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800521 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800522 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
523 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800524 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800525}
526
527/**
528 * Test step operation.
529 *
530 * Verifies that:
531 * - the method succeeds or returns NOT_SUPPORTED;
532 * - the program info is changed within timeout::tune if the method succeeded;
533 * - works both directions.
534 */
535TEST_F(BroadcastRadioHalTest, Step) {
536 ASSERT_TRUE(openSession());
537
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800538 // TODO(b/69958777): see FmTune workaround
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700539 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800540
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800541 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800542 auto result = mSession->step(true /* up */);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800543 if (result == Result::NOT_SUPPORTED) {
544 printSkipped("step not supported");
545 return;
546 }
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800547 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800548 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800549
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800550 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800551 result = mSession->step(false /* down */);
552 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800553 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800554}
555
556/**
557 * Test tune cancellation.
558 *
559 * Verifies that:
560 * - the method does not crash after being invoked multiple times.
561 */
562TEST_F(BroadcastRadioHalTest, Cancel) {
563 ASSERT_TRUE(openSession());
564
565 for (int i = 0; i < 10; i++) {
566 auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
567 ASSERT_EQ(Result::OK, scanResult);
568
569 auto cancelResult = mSession->cancel();
570 ASSERT_TRUE(cancelResult.isOk());
571 }
572}
573
574/**
575 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
576 *
577 * Verifies that:
578 * - callback is called for empty parameters set.
579 */
580TEST_F(BroadcastRadioHalTest, NoParameters) {
581 ASSERT_TRUE(openSession());
582
583 hidl_vec<VendorKeyValue> halResults = {};
584 bool wasCalled = false;
585 auto cb = [&](hidl_vec<VendorKeyValue> results) {
586 wasCalled = true;
587 halResults = results;
588 };
589
590 auto hidlResult = mSession->setParameters({}, cb);
591 ASSERT_TRUE(hidlResult.isOk());
592 ASSERT_TRUE(wasCalled);
593 ASSERT_EQ(0u, halResults.size());
594
595 wasCalled = false;
596 hidlResult = mSession->getParameters({}, cb);
597 ASSERT_TRUE(hidlResult.isOk());
598 ASSERT_TRUE(wasCalled);
599 ASSERT_EQ(0u, halResults.size());
600}
601
602/**
603 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
604 *
605 * Verifies that:
606 * - unknown parameters are ignored;
607 * - callback is called also for empty results set.
608 */
609TEST_F(BroadcastRadioHalTest, UnknownParameters) {
610 ASSERT_TRUE(openSession());
611
612 hidl_vec<VendorKeyValue> halResults = {};
613 bool wasCalled = false;
614 auto cb = [&](hidl_vec<VendorKeyValue> results) {
615 wasCalled = true;
616 halResults = results;
617 };
618
619 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
620 ASSERT_TRUE(hidlResult.isOk());
621 ASSERT_TRUE(wasCalled);
622 ASSERT_EQ(0u, halResults.size());
623
624 wasCalled = false;
625 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
626 ASSERT_TRUE(hidlResult.isOk());
627 ASSERT_TRUE(wasCalled);
628 ASSERT_EQ(0u, halResults.size());
629}
630
631/**
632 * Test session closing.
633 *
634 * Verifies that:
635 * - the method does not crash after being invoked multiple times.
636 */
637TEST_F(BroadcastRadioHalTest, Close) {
638 ASSERT_TRUE(openSession());
639
640 for (int i = 0; i < 10; i++) {
641 auto cancelResult = mSession->close();
642 ASSERT_TRUE(cancelResult.isOk());
643 }
644}
645
646/**
647 * Test geting image of invalid ID.
648 *
649 * Verifies that:
650 * - getImage call handles argument 0 gracefully.
651 */
652TEST_F(BroadcastRadioHalTest, GetNoImage) {
653 size_t len = 0;
654 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
655
656 ASSERT_TRUE(result.isOk());
657 ASSERT_EQ(0u, len);
658}
659
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800660/**
661 * Test getting config flags.
662 *
663 * Verifies that:
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800664 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800665 * - call success or failure is consistent with setConfigFlag.
666 */
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800667TEST_F(BroadcastRadioHalTest, FetchConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800668 ASSERT_TRUE(openSession());
669
670 for (auto flag : gConfigFlagValues) {
671 auto halResult = Result::UNKNOWN_ERROR;
672 auto cb = [&](Result result, bool) { halResult = result; };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800673 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800674 EXPECT_TRUE(hidlResult.isOk());
675
676 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
677 ASSERT_EQ(Result::OK, halResult);
678 }
679
680 // set must fail or succeed the same way as get
681 auto setResult = mSession->setConfigFlag(flag, false);
682 EXPECT_EQ(halResult, setResult);
683 setResult = mSession->setConfigFlag(flag, true);
684 EXPECT_EQ(halResult, setResult);
685 }
686}
687
688/**
689 * Test setting config flags.
690 *
691 * Verifies that:
692 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800693 * - isConfigFlagSet reflects the state requested immediately after the set call.
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800694 */
695TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
696 ASSERT_TRUE(openSession());
697
698 auto get = [&](ConfigFlag flag) {
699 auto halResult = Result::UNKNOWN_ERROR;
700 bool gotValue = false;
701 auto cb = [&](Result result, bool value) {
702 halResult = result;
703 gotValue = value;
704 };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800705 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800706 EXPECT_TRUE(hidlResult.isOk());
707 EXPECT_EQ(Result::OK, halResult);
708 return gotValue;
709 };
710
711 for (auto flag : gConfigFlagValues) {
712 auto result = mSession->setConfigFlag(flag, false);
713 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
714 // setting to true must result in the same error as false
715 auto secondResult = mSession->setConfigFlag(flag, true);
716 EXPECT_EQ(result, secondResult);
717 continue;
718 }
719 ASSERT_EQ(Result::OK, result);
720
721 // verify false is set
722 auto value = get(flag);
723 EXPECT_FALSE(value);
724
725 // try setting true this time
726 result = mSession->setConfigFlag(flag, true);
727 ASSERT_EQ(Result::OK, result);
728 value = get(flag);
729 EXPECT_TRUE(value);
730
731 // false again
732 result = mSession->setConfigFlag(flag, false);
733 ASSERT_EQ(Result::OK, result);
734 value = get(flag);
735 EXPECT_FALSE(value);
736 }
737}
738
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800739/**
740 * Test getting program list.
741 *
742 * Verifies that:
743 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
744 * - the complete list is fetched within timeout::programListScan;
745 * - stopProgramListUpdates does not crash.
746 */
747TEST_F(BroadcastRadioHalTest, GetProgramList) {
748 ASSERT_TRUE(openSession());
749
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800750 getProgramList();
751}
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800752
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800753/**
754 * Test HD_STATION_NAME correctness.
755 *
756 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
757 * - the program provides station name in its metadata;
758 * - the identifier matches the name;
759 * - there is only one identifier of that type.
760 */
761TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
762 ASSERT_TRUE(openSession());
763
764 auto list = getProgramList();
765 if (!list) return;
766
767 for (auto&& program : *list) {
768 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
769 EXPECT_LE(nameIds.size(), 1u);
770 if (nameIds.size() == 0) continue;
771
772 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
773 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
774 ASSERT_TRUE(name.has_value());
775
776 auto expectedId = utils::make_hdradio_station_name(*name);
777 EXPECT_EQ(expectedId.value, nameIds[0]);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800778 }
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800779}
780
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800781/**
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800782 * Test announcement listener registration.
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800783 *
784 * Verifies that:
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800785 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800786 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
787 * - closing handle does not crash.
788 */
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800789TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
790 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800791
792 Result halResult = Result::UNKNOWN_ERROR;
793 sp<ICloseHandle> closeHandle = nullptr;
794 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
795 halResult = result;
796 closeHandle = closeHandle_;
797 };
798
799 auto hidlResult =
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800800 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800801 ASSERT_TRUE(hidlResult.isOk());
802
803 if (halResult == Result::NOT_SUPPORTED) {
804 ASSERT_EQ(nullptr, closeHandle.get());
805 printSkipped("Announcements not supported");
806 return;
807 }
808
809 ASSERT_EQ(Result::OK, halResult);
810 ASSERT_NE(nullptr, closeHandle.get());
811
812 closeHandle->close();
813}
814
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800815} // namespace vts
816} // namespace V2_0
817} // namespace broadcastradio
818} // namespace hardware
819} // namespace android
820
821int main(int argc, char** argv) {
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800822 using android::hardware::broadcastradio::V2_0::vts::gEnv;
823 using android::hardware::broadcastradio::V2_0::IBroadcastRadio;
824 using android::hardware::broadcastradio::vts::BroadcastRadioHidlEnvironment;
825 gEnv = new BroadcastRadioHidlEnvironment<IBroadcastRadio>;
826 ::testing::AddGlobalTestEnvironment(gEnv);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800827 ::testing::InitGoogleTest(&argc, argv);
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800828 gEnv->init(&argc, argv);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800829 int status = RUN_ALL_TESTS();
830 ALOGI("Test result = %d", status);
831 return status;
832}