blob: 5409fe13dbcce01519f1a66196ae929ab47323ba [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
Tomasz Wasilczykdb902862018-01-14 17:22:03 -080017#define EGMOCK_VERBOSE 1
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080018
19#include <VtsHalHidlTargetTestBase.h>
20#include <android-base/logging.h>
Tomasz Wasilczyk8acabf22018-04-30 10:22:20 -070021#include <android-base/strings.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080022#include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
23#include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
24#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
25#include <android/hardware/broadcastradio/2.0/types.h>
26#include <broadcastradio-utils-2x/Utils.h>
27#include <broadcastradio-vts-utils/call-barrier.h>
Zhuoyao Zhang190548f2018-02-08 20:40:23 -080028#include <broadcastradio-vts-utils/environment-utils.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080029#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
Zhuoyao Zhang190548f2018-02-08 20:40:23 -080055using broadcastradio::vts::BroadcastRadioHidlEnvironment;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080056using broadcastradio::vts::CallBarrier;
57using broadcastradio::vts::clearAndWait;
58using utils::make_identifier;
59using utils::make_selector_amfm;
60
61namespace timeout {
62
63static constexpr auto tune = 30s;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080064static constexpr auto programListScan = 5min;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080065
66} // namespace timeout
67
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -070068static constexpr auto gTuneWorkaround = 200ms;
69
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080070static const ConfigFlag gConfigFlagValues[] = {
Tomasz Wasilczyk30240f62017-12-20 14:19:21 -080071 ConfigFlag::FORCE_MONO,
72 ConfigFlag::FORCE_ANALOG,
73 ConfigFlag::FORCE_DIGITAL,
74 ConfigFlag::RDS_AF,
75 ConfigFlag::RDS_REG,
76 ConfigFlag::DAB_DAB_LINKING,
77 ConfigFlag::DAB_FM_LINKING,
78 ConfigFlag::DAB_DAB_SOFT_LINKING,
79 ConfigFlag::DAB_FM_SOFT_LINKING,
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080080};
81
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080082class TunerCallbackMock : public ITunerCallback {
83 public:
84 TunerCallbackMock();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080085
86 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
Tomasz Wasilczyk67360522018-02-10 14:05:18 -080087 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged_, Return<void>(const ProgramInfo&));
88 virtual Return<void> onCurrentProgramInfoChanged(const ProgramInfo& info);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080089 Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080090 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
91 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080092
93 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
94
95 std::mutex mLock;
96 utils::ProgramInfoSet mProgramList;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080097};
98
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -080099struct AnnouncementListenerMock : public IAnnouncementListener {
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800100 MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
101};
102
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800103static BroadcastRadioHidlEnvironment<IBroadcastRadio>* gEnv = nullptr;
104
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800105class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
106 protected:
107 virtual void SetUp() override;
108 virtual void TearDown() override;
109
110 bool openSession();
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800111 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800112 std::optional<utils::ProgramInfoSet> getProgramList();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800113
114 sp<IBroadcastRadio> mModule;
115 Properties mProperties;
116 sp<ITunerSession> mSession;
117 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
118};
119
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800120static void printSkipped(std::string msg) {
121 std::cout << "[ SKIPPED ] " << msg << std::endl;
122}
123
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800124MATCHER_P(InfoHasId, id,
125 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
126 auto ids = utils::getAllIds(arg.selector, utils::getType(id));
127 return ids.end() != find(ids.begin(), ids.end(), id.value);
128}
129
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800130TunerCallbackMock::TunerCallbackMock() {
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800131 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800132
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800133 // we expect the antenna is connected through the whole test
134 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
135}
136
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800137Return<void> TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700138 for (auto&& id : info.selector) {
139 EXPECT_NE(IdentifierType::INVALID, utils::getType(id));
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800140 }
141
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700142 auto logically = utils::getType(info.logicallyTunedTo);
143 /* This field is required for currently tuned program and should be INVALID
144 * for entries from the program list.
145 */
146 EXPECT_TRUE(
147 logically == IdentifierType::AMFM_FREQUENCY || logically == IdentifierType::RDS_PI ||
148 logically == IdentifierType::HD_STATION_ID_EXT ||
149 logically == IdentifierType::DAB_SID_EXT || logically == IdentifierType::DRMO_SERVICE_ID ||
150 logically == IdentifierType::SXM_SERVICE_ID ||
151 (logically >= IdentifierType::VENDOR_START && logically <= IdentifierType::VENDOR_END) ||
152 logically > IdentifierType::SXM_CHANNEL);
153
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800154 auto physically = utils::getType(info.physicallyTunedTo);
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700155 // ditto (see "logically" above)
156 EXPECT_TRUE(
157 physically == IdentifierType::AMFM_FREQUENCY ||
158 physically == IdentifierType::DAB_ENSEMBLE ||
159 physically == IdentifierType::DRMO_FREQUENCY || physically == IdentifierType::SXM_CHANNEL ||
160 (physically >= IdentifierType::VENDOR_START && physically <= IdentifierType::VENDOR_END) ||
161 physically > IdentifierType::SXM_CHANNEL);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800162
Tomasz Wasilczyk8acabf22018-04-30 10:22:20 -0700163 if (logically == IdentifierType::AMFM_FREQUENCY) {
164 auto ps = utils::getMetadataString(info, MetadataKey::RDS_PS);
165 if (ps.has_value()) {
166 EXPECT_NE("", android::base::Trim(*ps))
167 << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
168 }
169 }
170
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800171 return onCurrentProgramInfoChanged_(info);
172}
173
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800174Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
175 std::lock_guard<std::mutex> lk(mLock);
176
177 updateProgramList(mProgramList, chunk);
178
179 if (chunk.complete) onProgramListReady();
180
181 return {};
182}
183
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800184void BroadcastRadioHalTest::SetUp() {
185 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
186
187 // lookup HIDL service (radio module)
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800188 mModule = getService<IBroadcastRadio>(gEnv->getServiceName<IBroadcastRadio>());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800189 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
190
191 // get module properties
192 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
193 ASSERT_TRUE(propResult.isOk());
194
195 EXPECT_FALSE(mProperties.maker.empty());
196 EXPECT_FALSE(mProperties.product.empty());
197 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
198}
199
200void BroadcastRadioHalTest::TearDown() {
201 mSession.clear();
202 mModule.clear();
203 clearAndWait(mCallback, 1s);
204}
205
206bool BroadcastRadioHalTest::openSession() {
207 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
208
209 Result halResult = Result::UNKNOWN_ERROR;
210 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
211 halResult = result;
212 if (result != Result::OK) return;
213 mSession = session;
214 };
215 auto hidlResult = mModule->openSession(mCallback, openCb);
216
217 EXPECT_TRUE(hidlResult.isOk());
218 EXPECT_EQ(Result::OK, halResult);
219 EXPECT_NE(nullptr, mSession.get());
220
221 return nullptr != mSession.get();
222}
223
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800224bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
225 auto halResult = Result::UNKNOWN_ERROR;
226 auto cb = [&](Result result, AmFmRegionConfig configCb) {
227 halResult = result;
228 if (config) *config = configCb;
229 };
230
231 auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
232 EXPECT_TRUE(hidlResult.isOk());
233
234 if (halResult == Result::NOT_SUPPORTED) return false;
235
236 EXPECT_EQ(Result::OK, halResult);
237 return halResult == Result::OK;
238}
239
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800240std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
241 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
242
243 auto startResult = mSession->startProgramListUpdates({});
244 if (startResult == Result::NOT_SUPPORTED) {
245 printSkipped("Program list not supported");
246 return nullopt;
247 }
248 EXPECT_EQ(Result::OK, startResult);
249 if (startResult != Result::OK) return nullopt;
250
251 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
252
253 auto stopResult = mSession->stopProgramListUpdates();
254 EXPECT_TRUE(stopResult.isOk());
255
256 return mCallback->mProgramList;
257}
258
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800259/**
260 * Test session opening.
261 *
262 * Verifies that:
263 * - the method succeeds on a first and subsequent calls;
264 * - the method succeeds when called for the second time without
265 * closing previous session.
266 */
267TEST_F(BroadcastRadioHalTest, OpenSession) {
268 // simply open session for the first time
269 ASSERT_TRUE(openSession());
270
271 // drop (without explicit close) and re-open the session
272 mSession.clear();
273 ASSERT_TRUE(openSession());
274
275 // open the second session (the first one should be forcibly closed)
276 auto secondSession = mSession;
277 mSession.clear();
278 ASSERT_TRUE(openSession());
279}
280
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800281static bool isValidAmFmFreq(uint64_t freq) {
282 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
283 return utils::isValid(id);
284}
285
286static void validateRange(const AmFmBandRange& range) {
287 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
288 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
289 EXPECT_LT(range.lowerBound, range.upperBound);
290 EXPECT_GT(range.spacing, 0u);
291 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
292}
293
294static bool supportsFM(const AmFmRegionConfig& config) {
295 for (auto&& range : config.ranges) {
296 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
297 }
298 return false;
299}
300
301/**
302 * Test fetching AM/FM regional configuration.
303 *
304 * Verifies that:
305 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
306 * - there is at least one AM/FM band configured;
307 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
308 * - all channel grids (frequency ranges and spacings) are valid;
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700309 * - seek spacing is a multiple of the manual spacing value.
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800310 */
311TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
312 AmFmRegionConfig config;
313 bool supported = getAmFmRegionConfig(false, &config);
314 if (!supported) {
315 printSkipped("AM/FM not supported");
316 return;
317 }
318
319 EXPECT_GT(config.ranges.size(), 0u);
320 EXPECT_LE(popcountll(config.fmDeemphasis), 1);
321 EXPECT_LE(popcountll(config.fmRds), 1);
322
323 for (auto&& range : config.ranges) {
324 validateRange(range);
325 EXPECT_EQ(0u, range.scanSpacing % range.spacing);
326 EXPECT_GE(range.scanSpacing, range.spacing);
327 }
328
329 if (supportsFM(config)) {
330 EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
331 }
332}
333
334/**
335 * Test fetching AM/FM regional capabilities.
336 *
337 * Verifies that:
338 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
339 * - there is at least one AM/FM range supported;
340 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
341 * - all channel grids (frequency ranges and spacings) are valid;
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700342 * - seek spacing is not set.
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800343 */
344TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
345 AmFmRegionConfig config;
346 bool supported = getAmFmRegionConfig(true, &config);
347 if (!supported) {
348 printSkipped("AM/FM not supported");
349 return;
350 }
351
352 EXPECT_GT(config.ranges.size(), 0u);
353
354 for (auto&& range : config.ranges) {
355 validateRange(range);
356 EXPECT_EQ(0u, range.scanSpacing);
357 }
358
359 if (supportsFM(config)) {
360 EXPECT_GE(popcountll(config.fmDeemphasis), 1);
361 }
362}
363
364/**
365 * Test fetching DAB regional configuration.
366 *
367 * Verifies that:
368 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
369 * - all channel labels match correct format;
370 * - all channel frequencies are in correct range.
371 */
372TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
373 Result halResult;
374 hidl_vec<DabTableEntry> config;
375 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
376 halResult = result;
377 config = configCb;
378 };
379 auto hidlResult = mModule->getDabRegionConfig(cb);
380 ASSERT_TRUE(hidlResult.isOk());
381
382 if (halResult == Result::NOT_SUPPORTED) {
383 printSkipped("DAB not supported");
384 return;
385 }
386 ASSERT_EQ(Result::OK, halResult);
387
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800388 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800389 // double-check correctness of the test
390 ASSERT_TRUE(std::regex_match("5A", re));
391 ASSERT_FALSE(std::regex_match("5a", re));
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800392 ASSERT_FALSE(std::regex_match("1234ABCD", re));
393 ASSERT_TRUE(std::regex_match("CN 12D", re));
394 ASSERT_FALSE(std::regex_match(" 5A", re));
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800395
396 for (auto&& entry : config) {
397 EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
398
399 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
400 EXPECT_TRUE(utils::isValid(id));
401 }
402}
403
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800404/**
405 * Test tuning with FM selector.
406 *
407 * Verifies that:
408 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
409 * - if it is supported, the method succeeds;
410 * - after a successful tune call, onCurrentProgramInfoChanged callback is
411 * invoked carrying a proper selector;
412 * - program changes exactly to what was requested.
413 */
414TEST_F(BroadcastRadioHalTest, FmTune) {
415 ASSERT_TRUE(openSession());
416
Jan Kowal03e3f602021-01-21 12:44:44 +0100417 uint64_t freq = 90900; // 90.9 FM
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800418 auto sel = make_selector_amfm(freq);
419
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800420 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
421 * callback setting infoCb, because egmock cannot distinguish calls with different matchers
422 * (there is one here and one in callback constructor).
423 *
424 * This sleep workaround will fix default implementation, but the real HW tests will still be
425 * flaky. We probably need to implement egmock alternative based on actions.
426 */
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700427 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800428
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800429 // try tuning
430 ProgramInfo infoCb = {};
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800431 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800432 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800433 .Times(AnyNumber())
434 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
435 auto result = mSession->tune(sel);
436
437 // expect a failure if it's not supported
438 if (!utils::isSupported(mProperties, sel)) {
439 EXPECT_EQ(Result::NOT_SUPPORTED, result);
440 return;
441 }
442
443 // expect a callback if it succeeds
444 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800445 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800446
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800447 LOG(DEBUG) << "current program info: " << toString(infoCb);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800448
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800449 // it should tune exactly to what was requested
450 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
451 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
452}
453
454/**
455 * Test tuning with invalid selectors.
456 *
457 * Verifies that:
458 * - if the selector is not supported, it's ignored;
459 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
460 */
461TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
462 ASSERT_TRUE(openSession());
463
464 vector<ProgramIdentifier> invalid = {
465 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
466 make_identifier(IdentifierType::RDS_PI, 0x10000),
467 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
468 make_identifier(IdentifierType::DAB_SID_EXT, 0),
469 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
470 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
471 };
472
473 for (auto&& id : invalid) {
474 ProgramSelector sel{id, {}};
475
476 auto result = mSession->tune(sel);
477
478 if (utils::isSupported(mProperties, sel)) {
479 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
480 } else {
481 EXPECT_EQ(Result::NOT_SUPPORTED, result);
482 }
483 }
484}
485
486/**
hyewon.eum20eb5752020-12-04 12:09:17 +0900487 * Test tuning with DAB selector.
488 *
489 * Verifies that:
490 * - if DAB selector is not supported, the method returns NOT_SUPPORTED;
491 * - if it is supported, the method succeeds;
492 * - after a successful tune call, onCurrentProgramInfoChanged callback is
493 * invoked carrying a proper selector;
494 * - program changes exactly to what was requested.
495 */
496TEST_F(BroadcastRadioHalTest, DabTune) {
497 ASSERT_TRUE(openSession());
498
499 ProgramSelector sel = {};
500 uint64_t freq = 178352;
501 sel.primaryId = make_identifier(IdentifierType::DAB_FREQUENCY,freq);
502
503 std::this_thread::sleep_for(gTuneWorkaround);
504
505 // try tuning
506 ProgramInfo infoCb = {};
507 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
508 InfoHasId(utils::make_identifier(IdentifierType::DAB_FREQUENCY, freq)))
509 .Times(AnyNumber())
510 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
511 auto result = mSession->tune(sel);
512
513 // expect a failure if it's not supported
514 if (!utils::isSupported(mProperties, sel)) {
515 EXPECT_EQ(Result::NOT_SUPPORTED, result);
516 return;
517 }
518
519 // expect a callback if it succeeds
520 EXPECT_EQ(Result::OK, result);
521 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
522
523 LOG(DEBUG) << "current program info: " << toString(infoCb);
524
525 // it should tune exactly to what was requested
526 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY);
527 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
528}
529
530/**
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800531 * Test tuning with empty program selector.
532 *
533 * Verifies that:
534 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
535 */
536TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
537 ASSERT_TRUE(openSession());
538
539 // Program type is 1-based, so 0 will always be invalid.
540 ProgramSelector sel = {};
541 auto result = mSession->tune(sel);
542 ASSERT_EQ(Result::NOT_SUPPORTED, result);
543}
544
545/**
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700546 * Test seeking to next/prev station via ITunerSession::scan().
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800547 *
548 * Verifies that:
549 * - the method succeeds;
550 * - the program info is changed within timeout::tune;
551 * - works both directions and with or without skipping sub-channel.
552 */
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700553TEST_F(BroadcastRadioHalTest, Seek) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800554 ASSERT_TRUE(openSession());
555
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800556 // TODO(b/69958777): see FmTune workaround
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700557 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800558
Tomasz Wasilczykaf898822018-12-03 13:15:50 -0800559 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800560 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
hyewon.eum20eb5752020-12-04 12:09:17 +0900561
562 if (result == Result::NOT_SUPPORTED) {
563 printSkipped("seek not supported");
564 return;
565 }
566
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800567 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800568 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800569
Tomasz Wasilczykaf898822018-12-03 13:15:50 -0800570 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800571 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
572 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800573 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800574}
575
576/**
577 * Test step operation.
578 *
579 * Verifies that:
580 * - the method succeeds or returns NOT_SUPPORTED;
581 * - the program info is changed within timeout::tune if the method succeeded;
582 * - works both directions.
583 */
584TEST_F(BroadcastRadioHalTest, Step) {
585 ASSERT_TRUE(openSession());
586
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800587 // TODO(b/69958777): see FmTune workaround
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700588 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800589
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800590 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800591 auto result = mSession->step(true /* up */);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800592 if (result == Result::NOT_SUPPORTED) {
593 printSkipped("step not supported");
594 return;
595 }
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800596 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800597 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800598
Tomasz Wasilczykaf898822018-12-03 13:15:50 -0800599 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800600 result = mSession->step(false /* down */);
601 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800602 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800603}
604
605/**
606 * Test tune cancellation.
607 *
608 * Verifies that:
609 * - the method does not crash after being invoked multiple times.
610 */
611TEST_F(BroadcastRadioHalTest, Cancel) {
612 ASSERT_TRUE(openSession());
613
614 for (int i = 0; i < 10; i++) {
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700615 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
hyewon.eum20eb5752020-12-04 12:09:17 +0900616
617 if (result == Result::NOT_SUPPORTED) {
618 printSkipped("cancel is skipped because of seek not supported");
619 return;
620 }
621
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700622 ASSERT_EQ(Result::OK, result);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800623
624 auto cancelResult = mSession->cancel();
625 ASSERT_TRUE(cancelResult.isOk());
626 }
627}
628
629/**
630 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
631 *
632 * Verifies that:
633 * - callback is called for empty parameters set.
634 */
635TEST_F(BroadcastRadioHalTest, NoParameters) {
636 ASSERT_TRUE(openSession());
637
638 hidl_vec<VendorKeyValue> halResults = {};
639 bool wasCalled = false;
640 auto cb = [&](hidl_vec<VendorKeyValue> results) {
641 wasCalled = true;
642 halResults = results;
643 };
644
645 auto hidlResult = mSession->setParameters({}, cb);
646 ASSERT_TRUE(hidlResult.isOk());
647 ASSERT_TRUE(wasCalled);
648 ASSERT_EQ(0u, halResults.size());
649
650 wasCalled = false;
651 hidlResult = mSession->getParameters({}, cb);
652 ASSERT_TRUE(hidlResult.isOk());
653 ASSERT_TRUE(wasCalled);
654 ASSERT_EQ(0u, halResults.size());
655}
656
657/**
658 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
659 *
660 * Verifies that:
661 * - unknown parameters are ignored;
662 * - callback is called also for empty results set.
663 */
664TEST_F(BroadcastRadioHalTest, UnknownParameters) {
665 ASSERT_TRUE(openSession());
666
667 hidl_vec<VendorKeyValue> halResults = {};
668 bool wasCalled = false;
669 auto cb = [&](hidl_vec<VendorKeyValue> results) {
670 wasCalled = true;
671 halResults = results;
672 };
673
674 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
675 ASSERT_TRUE(hidlResult.isOk());
676 ASSERT_TRUE(wasCalled);
677 ASSERT_EQ(0u, halResults.size());
678
679 wasCalled = false;
680 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
681 ASSERT_TRUE(hidlResult.isOk());
682 ASSERT_TRUE(wasCalled);
683 ASSERT_EQ(0u, halResults.size());
684}
685
686/**
687 * Test session closing.
688 *
689 * Verifies that:
690 * - the method does not crash after being invoked multiple times.
691 */
692TEST_F(BroadcastRadioHalTest, Close) {
693 ASSERT_TRUE(openSession());
694
695 for (int i = 0; i < 10; i++) {
696 auto cancelResult = mSession->close();
697 ASSERT_TRUE(cancelResult.isOk());
698 }
699}
700
701/**
702 * Test geting image of invalid ID.
703 *
704 * Verifies that:
705 * - getImage call handles argument 0 gracefully.
706 */
707TEST_F(BroadcastRadioHalTest, GetNoImage) {
708 size_t len = 0;
709 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
710
711 ASSERT_TRUE(result.isOk());
712 ASSERT_EQ(0u, len);
713}
714
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800715/**
716 * Test getting config flags.
717 *
718 * Verifies that:
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800719 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800720 * - call success or failure is consistent with setConfigFlag.
721 */
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800722TEST_F(BroadcastRadioHalTest, FetchConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800723 ASSERT_TRUE(openSession());
724
725 for (auto flag : gConfigFlagValues) {
726 auto halResult = Result::UNKNOWN_ERROR;
727 auto cb = [&](Result result, bool) { halResult = result; };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800728 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800729 EXPECT_TRUE(hidlResult.isOk());
730
731 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
732 ASSERT_EQ(Result::OK, halResult);
733 }
734
735 // set must fail or succeed the same way as get
736 auto setResult = mSession->setConfigFlag(flag, false);
737 EXPECT_EQ(halResult, setResult);
738 setResult = mSession->setConfigFlag(flag, true);
739 EXPECT_EQ(halResult, setResult);
740 }
741}
742
743/**
744 * Test setting config flags.
745 *
746 * Verifies that:
747 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800748 * - isConfigFlagSet reflects the state requested immediately after the set call.
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800749 */
750TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
751 ASSERT_TRUE(openSession());
752
753 auto get = [&](ConfigFlag flag) {
754 auto halResult = Result::UNKNOWN_ERROR;
755 bool gotValue = false;
756 auto cb = [&](Result result, bool value) {
757 halResult = result;
758 gotValue = value;
759 };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800760 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800761 EXPECT_TRUE(hidlResult.isOk());
762 EXPECT_EQ(Result::OK, halResult);
763 return gotValue;
764 };
765
766 for (auto flag : gConfigFlagValues) {
767 auto result = mSession->setConfigFlag(flag, false);
768 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
769 // setting to true must result in the same error as false
770 auto secondResult = mSession->setConfigFlag(flag, true);
771 EXPECT_EQ(result, secondResult);
772 continue;
773 }
774 ASSERT_EQ(Result::OK, result);
775
776 // verify false is set
777 auto value = get(flag);
778 EXPECT_FALSE(value);
779
780 // try setting true this time
781 result = mSession->setConfigFlag(flag, true);
782 ASSERT_EQ(Result::OK, result);
783 value = get(flag);
784 EXPECT_TRUE(value);
785
786 // false again
787 result = mSession->setConfigFlag(flag, false);
788 ASSERT_EQ(Result::OK, result);
789 value = get(flag);
790 EXPECT_FALSE(value);
791 }
792}
793
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800794/**
795 * Test getting program list.
796 *
797 * Verifies that:
798 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
799 * - the complete list is fetched within timeout::programListScan;
800 * - stopProgramListUpdates does not crash.
801 */
802TEST_F(BroadcastRadioHalTest, GetProgramList) {
803 ASSERT_TRUE(openSession());
804
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800805 getProgramList();
806}
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800807
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800808/**
809 * Test HD_STATION_NAME correctness.
810 *
811 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
812 * - the program provides station name in its metadata;
813 * - the identifier matches the name;
814 * - there is only one identifier of that type.
815 */
816TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
817 ASSERT_TRUE(openSession());
818
819 auto list = getProgramList();
820 if (!list) return;
821
822 for (auto&& program : *list) {
823 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
824 EXPECT_LE(nameIds.size(), 1u);
825 if (nameIds.size() == 0) continue;
826
827 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
828 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
829 ASSERT_TRUE(name.has_value());
830
831 auto expectedId = utils::make_hdradio_station_name(*name);
832 EXPECT_EQ(expectedId.value, nameIds[0]);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800833 }
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800834}
835
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800836/**
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800837 * Test announcement listener registration.
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800838 *
839 * Verifies that:
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800840 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800841 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
842 * - closing handle does not crash.
843 */
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800844TEST_F(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
845 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800846
847 Result halResult = Result::UNKNOWN_ERROR;
848 sp<ICloseHandle> closeHandle = nullptr;
849 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
850 halResult = result;
851 closeHandle = closeHandle_;
852 };
853
854 auto hidlResult =
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800855 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800856 ASSERT_TRUE(hidlResult.isOk());
857
858 if (halResult == Result::NOT_SUPPORTED) {
859 ASSERT_EQ(nullptr, closeHandle.get());
860 printSkipped("Announcements not supported");
861 return;
862 }
863
864 ASSERT_EQ(Result::OK, halResult);
865 ASSERT_NE(nullptr, closeHandle.get());
866
867 closeHandle->close();
868}
869
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800870} // namespace vts
871} // namespace V2_0
872} // namespace broadcastradio
873} // namespace hardware
874} // namespace android
875
876int main(int argc, char** argv) {
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800877 using android::hardware::broadcastradio::V2_0::vts::gEnv;
878 using android::hardware::broadcastradio::V2_0::IBroadcastRadio;
879 using android::hardware::broadcastradio::vts::BroadcastRadioHidlEnvironment;
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800880 android::base::SetDefaultTag("BcRadio.vts");
881 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800882 gEnv = new BroadcastRadioHidlEnvironment<IBroadcastRadio>;
883 ::testing::AddGlobalTestEnvironment(gEnv);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800884 ::testing::InitGoogleTest(&argc, argv);
Zhuoyao Zhang190548f2018-02-08 20:40:23 -0800885 gEnv->init(&argc, argv);
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800886 return RUN_ALL_TESTS();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800887}