blob: d170a6d469b6fd82bc61733b136dfe5bd8459a3b [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
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080019#include <android-base/logging.h>
Tomasz Wasilczyk8acabf22018-04-30 10:22:20 -070020#include <android-base/strings.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080021#include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
22#include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
23#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
24#include <android/hardware/broadcastradio/2.0/types.h>
25#include <broadcastradio-utils-2x/Utils.h>
26#include <broadcastradio-vts-utils/call-barrier.h>
27#include <broadcastradio-vts-utils/mock-timeout.h>
28#include <broadcastradio-vts-utils/pointer-utils.h>
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -080029#include <cutils/bitops.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080030#include <gmock/gmock.h>
Dan Shi27506e62019-12-12 10:54:49 -080031#include <gtest/gtest.h>
32#include <hidl/GtestPrinter.h>
33#include <hidl/ServiceManagement.h>
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080034
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
56using 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
Dan Shi27506e62019-12-12 10:54:49 -0800103class BroadcastRadioHalTest : public ::testing::TestWithParam<std::string> {
104 protected:
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800105 virtual void SetUp() override;
106 virtual void TearDown() override;
107
108 bool openSession();
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800109 bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800110 std::optional<utils::ProgramInfoSet> getProgramList();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800111
112 sp<IBroadcastRadio> mModule;
113 Properties mProperties;
114 sp<ITunerSession> mSession;
115 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
116};
117
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800118static void printSkipped(std::string msg) {
119 std::cout << "[ SKIPPED ] " << msg << std::endl;
120}
121
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800122MATCHER_P(InfoHasId, id,
123 std::string(negation ? "does not contain" : "contains") + " " + toString(id)) {
124 auto ids = utils::getAllIds(arg.selector, utils::getType(id));
125 return ids.end() != find(ids.begin(), ids.end(), id.value);
126}
127
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800128TunerCallbackMock::TunerCallbackMock() {
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800129 EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800130
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800131 // we expect the antenna is connected through the whole test
132 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
133}
134
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800135Return<void> TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700136 for (auto&& id : info.selector) {
137 EXPECT_NE(IdentifierType::INVALID, utils::getType(id));
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800138 }
139
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700140 auto logically = utils::getType(info.logicallyTunedTo);
141 /* This field is required for currently tuned program and should be INVALID
142 * for entries from the program list.
143 */
144 EXPECT_TRUE(
145 logically == IdentifierType::AMFM_FREQUENCY || logically == IdentifierType::RDS_PI ||
146 logically == IdentifierType::HD_STATION_ID_EXT ||
147 logically == IdentifierType::DAB_SID_EXT || logically == IdentifierType::DRMO_SERVICE_ID ||
148 logically == IdentifierType::SXM_SERVICE_ID ||
149 (logically >= IdentifierType::VENDOR_START && logically <= IdentifierType::VENDOR_END) ||
150 logically > IdentifierType::SXM_CHANNEL);
151
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800152 auto physically = utils::getType(info.physicallyTunedTo);
Tomasz Wasilczykcea64962018-05-23 09:56:58 -0700153 // ditto (see "logically" above)
154 EXPECT_TRUE(
155 physically == IdentifierType::AMFM_FREQUENCY ||
156 physically == IdentifierType::DAB_ENSEMBLE ||
157 physically == IdentifierType::DRMO_FREQUENCY || physically == IdentifierType::SXM_CHANNEL ||
158 (physically >= IdentifierType::VENDOR_START && physically <= IdentifierType::VENDOR_END) ||
159 physically > IdentifierType::SXM_CHANNEL);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800160
Tomasz Wasilczyk8acabf22018-04-30 10:22:20 -0700161 if (logically == IdentifierType::AMFM_FREQUENCY) {
162 auto ps = utils::getMetadataString(info, MetadataKey::RDS_PS);
163 if (ps.has_value()) {
164 EXPECT_NE("", android::base::Trim(*ps))
165 << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
166 }
167 }
168
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800169 return onCurrentProgramInfoChanged_(info);
170}
171
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800172Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
173 std::lock_guard<std::mutex> lk(mLock);
174
175 updateProgramList(mProgramList, chunk);
176
177 if (chunk.complete) onProgramListReady();
178
179 return {};
180}
181
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800182void BroadcastRadioHalTest::SetUp() {
183 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
184
185 // lookup HIDL service (radio module)
Dan Shi27506e62019-12-12 10:54:49 -0800186 mModule = IBroadcastRadio::getService(GetParam());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800187 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
188
189 // get module properties
190 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
191 ASSERT_TRUE(propResult.isOk());
192
193 EXPECT_FALSE(mProperties.maker.empty());
194 EXPECT_FALSE(mProperties.product.empty());
195 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
196}
197
198void BroadcastRadioHalTest::TearDown() {
199 mSession.clear();
200 mModule.clear();
201 clearAndWait(mCallback, 1s);
202}
203
204bool BroadcastRadioHalTest::openSession() {
205 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
206
207 Result halResult = Result::UNKNOWN_ERROR;
208 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
209 halResult = result;
210 if (result != Result::OK) return;
211 mSession = session;
212 };
213 auto hidlResult = mModule->openSession(mCallback, openCb);
214
215 EXPECT_TRUE(hidlResult.isOk());
216 EXPECT_EQ(Result::OK, halResult);
217 EXPECT_NE(nullptr, mSession.get());
218
219 return nullptr != mSession.get();
220}
221
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800222bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
223 auto halResult = Result::UNKNOWN_ERROR;
224 auto cb = [&](Result result, AmFmRegionConfig configCb) {
225 halResult = result;
226 if (config) *config = configCb;
227 };
228
229 auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
230 EXPECT_TRUE(hidlResult.isOk());
231
232 if (halResult == Result::NOT_SUPPORTED) return false;
233
234 EXPECT_EQ(Result::OK, halResult);
235 return halResult == Result::OK;
236}
237
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800238std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
239 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
240
241 auto startResult = mSession->startProgramListUpdates({});
242 if (startResult == Result::NOT_SUPPORTED) {
243 printSkipped("Program list not supported");
Dan Shi27506e62019-12-12 10:54:49 -0800244 return std::nullopt;
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800245 }
246 EXPECT_EQ(Result::OK, startResult);
Dan Shi27506e62019-12-12 10:54:49 -0800247 if (startResult != Result::OK) return std::nullopt;
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800248
249 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
250
251 auto stopResult = mSession->stopProgramListUpdates();
252 EXPECT_TRUE(stopResult.isOk());
253
254 return mCallback->mProgramList;
255}
256
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800257/**
258 * Test session opening.
259 *
260 * Verifies that:
261 * - the method succeeds on a first and subsequent calls;
262 * - the method succeeds when called for the second time without
263 * closing previous session.
264 */
Dan Shi27506e62019-12-12 10:54:49 -0800265TEST_P(BroadcastRadioHalTest, OpenSession) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800266 // simply open session for the first time
267 ASSERT_TRUE(openSession());
268
269 // drop (without explicit close) and re-open the session
270 mSession.clear();
271 ASSERT_TRUE(openSession());
272
273 // open the second session (the first one should be forcibly closed)
274 auto secondSession = mSession;
275 mSession.clear();
276 ASSERT_TRUE(openSession());
277}
278
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800279static bool isValidAmFmFreq(uint64_t freq) {
280 auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
281 return utils::isValid(id);
282}
283
284static void validateRange(const AmFmBandRange& range) {
285 EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
286 EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
287 EXPECT_LT(range.lowerBound, range.upperBound);
288 EXPECT_GT(range.spacing, 0u);
289 EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
290}
291
292static bool supportsFM(const AmFmRegionConfig& config) {
293 for (auto&& range : config.ranges) {
294 if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
295 }
296 return false;
297}
298
299/**
300 * Test fetching AM/FM regional configuration.
301 *
302 * Verifies that:
303 * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
304 * - there is at least one AM/FM band configured;
305 * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
306 * - all channel grids (frequency ranges and spacings) are valid;
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700307 * - seek spacing is a multiple of the manual spacing value.
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800308 */
Dan Shi27506e62019-12-12 10:54:49 -0800309TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfig) {
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800310 AmFmRegionConfig config;
311 bool supported = getAmFmRegionConfig(false, &config);
312 if (!supported) {
313 printSkipped("AM/FM not supported");
314 return;
315 }
316
317 EXPECT_GT(config.ranges.size(), 0u);
318 EXPECT_LE(popcountll(config.fmDeemphasis), 1);
319 EXPECT_LE(popcountll(config.fmRds), 1);
320
321 for (auto&& range : config.ranges) {
322 validateRange(range);
323 EXPECT_EQ(0u, range.scanSpacing % range.spacing);
324 EXPECT_GE(range.scanSpacing, range.spacing);
325 }
326
327 if (supportsFM(config)) {
328 EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
329 }
330}
331
332/**
333 * Test fetching AM/FM regional capabilities.
334 *
335 * Verifies that:
336 * - AM/FM regional capabilities are either available or not supported at all by the hardware;
337 * - there is at least one AM/FM range supported;
338 * - there is at least one de-emphasis filter mode supported for FM-capable radio;
339 * - all channel grids (frequency ranges and spacings) are valid;
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700340 * - seek spacing is not set.
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800341 */
Dan Shi27506e62019-12-12 10:54:49 -0800342TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800343 AmFmRegionConfig config;
344 bool supported = getAmFmRegionConfig(true, &config);
345 if (!supported) {
346 printSkipped("AM/FM not supported");
347 return;
348 }
349
350 EXPECT_GT(config.ranges.size(), 0u);
351
352 for (auto&& range : config.ranges) {
353 validateRange(range);
354 EXPECT_EQ(0u, range.scanSpacing);
355 }
356
357 if (supportsFM(config)) {
358 EXPECT_GE(popcountll(config.fmDeemphasis), 1);
359 }
360}
361
362/**
363 * Test fetching DAB regional configuration.
364 *
365 * Verifies that:
366 * - DAB regional configuration is either set at startup or not supported at all by the hardware;
367 * - all channel labels match correct format;
368 * - all channel frequencies are in correct range.
369 */
Dan Shi27506e62019-12-12 10:54:49 -0800370TEST_P(BroadcastRadioHalTest, GetDabRegionConfig) {
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800371 Result halResult;
372 hidl_vec<DabTableEntry> config;
373 auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
374 halResult = result;
375 config = configCb;
376 };
377 auto hidlResult = mModule->getDabRegionConfig(cb);
378 ASSERT_TRUE(hidlResult.isOk());
379
380 if (halResult == Result::NOT_SUPPORTED) {
381 printSkipped("DAB not supported");
382 return;
383 }
384 ASSERT_EQ(Result::OK, halResult);
385
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800386 std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800387 // double-check correctness of the test
388 ASSERT_TRUE(std::regex_match("5A", re));
389 ASSERT_FALSE(std::regex_match("5a", re));
Tomasz Wasilczyka425ded2018-01-12 15:15:59 -0800390 ASSERT_FALSE(std::regex_match("1234ABCD", re));
391 ASSERT_TRUE(std::regex_match("CN 12D", re));
392 ASSERT_FALSE(std::regex_match(" 5A", re));
Tomasz Wasilczyk8b70ee42017-12-21 11:51:29 -0800393
394 for (auto&& entry : config) {
395 EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
396
397 auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
398 EXPECT_TRUE(utils::isValid(id));
399 }
400}
401
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800402/**
403 * Test tuning with FM selector.
404 *
405 * Verifies that:
406 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
407 * - if it is supported, the method succeeds;
408 * - after a successful tune call, onCurrentProgramInfoChanged callback is
409 * invoked carrying a proper selector;
410 * - program changes exactly to what was requested.
411 */
Dan Shi27506e62019-12-12 10:54:49 -0800412TEST_P(BroadcastRadioHalTest, FmTune) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800413 ASSERT_TRUE(openSession());
414
415 uint64_t freq = 100100; // 100.1 FM
416 auto sel = make_selector_amfm(freq);
417
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800418 /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
419 * callback setting infoCb, because egmock cannot distinguish calls with different matchers
420 * (there is one here and one in callback constructor).
421 *
422 * This sleep workaround will fix default implementation, but the real HW tests will still be
423 * flaky. We probably need to implement egmock alternative based on actions.
424 */
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700425 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800426
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800427 // try tuning
428 ProgramInfo infoCb = {};
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800429 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800430 InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq)))
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800431 .Times(AnyNumber())
432 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
433 auto result = mSession->tune(sel);
434
435 // expect a failure if it's not supported
436 if (!utils::isSupported(mProperties, sel)) {
437 EXPECT_EQ(Result::NOT_SUPPORTED, result);
438 return;
439 }
440
441 // expect a callback if it succeeds
442 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800443 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800444
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800445 LOG(DEBUG) << "current program info: " << toString(infoCb);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800446
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800447 // it should tune exactly to what was requested
448 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
449 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
450}
451
452/**
453 * Test tuning with invalid selectors.
454 *
455 * Verifies that:
456 * - if the selector is not supported, it's ignored;
457 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
458 */
Dan Shi27506e62019-12-12 10:54:49 -0800459TEST_P(BroadcastRadioHalTest, TuneFailsWithInvalid) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800460 ASSERT_TRUE(openSession());
461
462 vector<ProgramIdentifier> invalid = {
463 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
464 make_identifier(IdentifierType::RDS_PI, 0x10000),
465 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
466 make_identifier(IdentifierType::DAB_SID_EXT, 0),
467 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
468 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
469 };
470
471 for (auto&& id : invalid) {
472 ProgramSelector sel{id, {}};
473
474 auto result = mSession->tune(sel);
475
476 if (utils::isSupported(mProperties, sel)) {
477 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
478 } else {
479 EXPECT_EQ(Result::NOT_SUPPORTED, result);
480 }
481 }
482}
483
484/**
485 * Test tuning with empty program selector.
486 *
487 * Verifies that:
488 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
489 */
Dan Shi27506e62019-12-12 10:54:49 -0800490TEST_P(BroadcastRadioHalTest, TuneFailsWithEmpty) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800491 ASSERT_TRUE(openSession());
492
493 // Program type is 1-based, so 0 will always be invalid.
494 ProgramSelector sel = {};
495 auto result = mSession->tune(sel);
496 ASSERT_EQ(Result::NOT_SUPPORTED, result);
497}
498
499/**
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700500 * Test seeking to next/prev station via ITunerSession::scan().
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800501 *
502 * Verifies that:
503 * - the method succeeds;
504 * - the program info is changed within timeout::tune;
505 * - works both directions and with or without skipping sub-channel.
506 */
Dan Shi27506e62019-12-12 10:54:49 -0800507TEST_P(BroadcastRadioHalTest, Seek) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800508 ASSERT_TRUE(openSession());
509
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800510 // TODO(b/69958777): see FmTune workaround
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700511 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800512
Tomasz Wasilczykaf898822018-12-03 13:15:50 -0800513 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800514 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
515 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800516 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800517
Tomasz Wasilczykaf898822018-12-03 13:15:50 -0800518 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800519 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
520 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800521 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800522}
523
524/**
525 * Test step operation.
526 *
527 * Verifies that:
528 * - the method succeeds or returns NOT_SUPPORTED;
529 * - the program info is changed within timeout::tune if the method succeeded;
530 * - works both directions.
531 */
Dan Shi27506e62019-12-12 10:54:49 -0800532TEST_P(BroadcastRadioHalTest, Step) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800533 ASSERT_TRUE(openSession());
534
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800535 // TODO(b/69958777): see FmTune workaround
Tomasz Wasilczyk55241f72018-04-30 08:53:24 -0700536 std::this_thread::sleep_for(gTuneWorkaround);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800537
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800538 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800539 auto result = mSession->step(true /* up */);
Tomasz Wasilczykdb902862018-01-14 17:22:03 -0800540 if (result == Result::NOT_SUPPORTED) {
541 printSkipped("step not supported");
542 return;
543 }
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800544 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800545 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800546
Tomasz Wasilczykaf898822018-12-03 13:15:50 -0800547 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800548 result = mSession->step(false /* down */);
549 EXPECT_EQ(Result::OK, result);
Tomasz Wasilczyk67360522018-02-10 14:05:18 -0800550 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800551}
552
553/**
554 * Test tune cancellation.
555 *
556 * Verifies that:
557 * - the method does not crash after being invoked multiple times.
558 */
Dan Shi27506e62019-12-12 10:54:49 -0800559TEST_P(BroadcastRadioHalTest, Cancel) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800560 ASSERT_TRUE(openSession());
561
562 for (int i = 0; i < 10; i++) {
Tomasz Wasilczykb557e0b2018-06-05 10:10:39 -0700563 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
564 ASSERT_EQ(Result::OK, result);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800565
566 auto cancelResult = mSession->cancel();
567 ASSERT_TRUE(cancelResult.isOk());
568 }
569}
570
571/**
572 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
573 *
574 * Verifies that:
575 * - callback is called for empty parameters set.
576 */
Dan Shi27506e62019-12-12 10:54:49 -0800577TEST_P(BroadcastRadioHalTest, NoParameters) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800578 ASSERT_TRUE(openSession());
579
580 hidl_vec<VendorKeyValue> halResults = {};
581 bool wasCalled = false;
582 auto cb = [&](hidl_vec<VendorKeyValue> results) {
583 wasCalled = true;
584 halResults = results;
585 };
586
587 auto hidlResult = mSession->setParameters({}, cb);
588 ASSERT_TRUE(hidlResult.isOk());
589 ASSERT_TRUE(wasCalled);
590 ASSERT_EQ(0u, halResults.size());
591
592 wasCalled = false;
593 hidlResult = mSession->getParameters({}, cb);
594 ASSERT_TRUE(hidlResult.isOk());
595 ASSERT_TRUE(wasCalled);
596 ASSERT_EQ(0u, halResults.size());
597}
598
599/**
600 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
601 *
602 * Verifies that:
603 * - unknown parameters are ignored;
604 * - callback is called also for empty results set.
605 */
Dan Shi27506e62019-12-12 10:54:49 -0800606TEST_P(BroadcastRadioHalTest, UnknownParameters) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800607 ASSERT_TRUE(openSession());
608
609 hidl_vec<VendorKeyValue> halResults = {};
610 bool wasCalled = false;
611 auto cb = [&](hidl_vec<VendorKeyValue> results) {
612 wasCalled = true;
613 halResults = results;
614 };
615
616 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
617 ASSERT_TRUE(hidlResult.isOk());
618 ASSERT_TRUE(wasCalled);
619 ASSERT_EQ(0u, halResults.size());
620
621 wasCalled = false;
622 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
623 ASSERT_TRUE(hidlResult.isOk());
624 ASSERT_TRUE(wasCalled);
625 ASSERT_EQ(0u, halResults.size());
626}
627
628/**
629 * Test session closing.
630 *
631 * Verifies that:
632 * - the method does not crash after being invoked multiple times.
633 */
Dan Shi27506e62019-12-12 10:54:49 -0800634TEST_P(BroadcastRadioHalTest, Close) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800635 ASSERT_TRUE(openSession());
636
637 for (int i = 0; i < 10; i++) {
638 auto cancelResult = mSession->close();
639 ASSERT_TRUE(cancelResult.isOk());
640 }
641}
642
643/**
644 * Test geting image of invalid ID.
645 *
646 * Verifies that:
647 * - getImage call handles argument 0 gracefully.
648 */
Dan Shi27506e62019-12-12 10:54:49 -0800649TEST_P(BroadcastRadioHalTest, GetNoImage) {
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800650 size_t len = 0;
651 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
652
653 ASSERT_TRUE(result.isOk());
654 ASSERT_EQ(0u, len);
655}
656
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800657/**
658 * Test getting config flags.
659 *
660 * Verifies that:
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800661 * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800662 * - call success or failure is consistent with setConfigFlag.
663 */
Dan Shi27506e62019-12-12 10:54:49 -0800664TEST_P(BroadcastRadioHalTest, FetchConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800665 ASSERT_TRUE(openSession());
666
667 for (auto flag : gConfigFlagValues) {
668 auto halResult = Result::UNKNOWN_ERROR;
669 auto cb = [&](Result result, bool) { halResult = result; };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800670 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800671 EXPECT_TRUE(hidlResult.isOk());
672
673 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
674 ASSERT_EQ(Result::OK, halResult);
675 }
676
677 // set must fail or succeed the same way as get
678 auto setResult = mSession->setConfigFlag(flag, false);
679 EXPECT_EQ(halResult, setResult);
680 setResult = mSession->setConfigFlag(flag, true);
681 EXPECT_EQ(halResult, setResult);
682 }
683}
684
685/**
686 * Test setting config flags.
687 *
688 * Verifies that:
689 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800690 * - isConfigFlagSet reflects the state requested immediately after the set call.
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800691 */
Dan Shi27506e62019-12-12 10:54:49 -0800692TEST_P(BroadcastRadioHalTest, SetConfigFlags) {
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800693 ASSERT_TRUE(openSession());
694
695 auto get = [&](ConfigFlag flag) {
696 auto halResult = Result::UNKNOWN_ERROR;
697 bool gotValue = false;
698 auto cb = [&](Result result, bool value) {
699 halResult = result;
700 gotValue = value;
701 };
Tomasz Wasilczyk3dd452a2018-01-12 14:57:45 -0800702 auto hidlResult = mSession->isConfigFlagSet(flag, cb);
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800703 EXPECT_TRUE(hidlResult.isOk());
704 EXPECT_EQ(Result::OK, halResult);
705 return gotValue;
706 };
707
708 for (auto flag : gConfigFlagValues) {
709 auto result = mSession->setConfigFlag(flag, false);
710 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
711 // setting to true must result in the same error as false
712 auto secondResult = mSession->setConfigFlag(flag, true);
713 EXPECT_EQ(result, secondResult);
714 continue;
715 }
716 ASSERT_EQ(Result::OK, result);
717
718 // verify false is set
719 auto value = get(flag);
720 EXPECT_FALSE(value);
721
722 // try setting true this time
723 result = mSession->setConfigFlag(flag, true);
724 ASSERT_EQ(Result::OK, result);
725 value = get(flag);
726 EXPECT_TRUE(value);
727
728 // false again
729 result = mSession->setConfigFlag(flag, false);
730 ASSERT_EQ(Result::OK, result);
731 value = get(flag);
732 EXPECT_FALSE(value);
733 }
734}
735
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800736/**
737 * Test getting program list.
738 *
739 * Verifies that:
740 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
741 * - the complete list is fetched within timeout::programListScan;
742 * - stopProgramListUpdates does not crash.
743 */
Dan Shi27506e62019-12-12 10:54:49 -0800744TEST_P(BroadcastRadioHalTest, GetProgramList) {
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800745 ASSERT_TRUE(openSession());
746
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800747 getProgramList();
748}
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800749
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800750/**
751 * Test HD_STATION_NAME correctness.
752 *
753 * Verifies that if a program on the list contains HD_STATION_NAME identifier:
754 * - the program provides station name in its metadata;
755 * - the identifier matches the name;
756 * - there is only one identifier of that type.
757 */
Dan Shi27506e62019-12-12 10:54:49 -0800758TEST_P(BroadcastRadioHalTest, HdRadioStationNameId) {
Tomasz Wasilczykc71624f2017-12-22 10:54:34 -0800759 ASSERT_TRUE(openSession());
760
761 auto list = getProgramList();
762 if (!list) return;
763
764 for (auto&& program : *list) {
765 auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
766 EXPECT_LE(nameIds.size(), 1u);
767 if (nameIds.size() == 0) continue;
768
769 auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
770 if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
771 ASSERT_TRUE(name.has_value());
772
773 auto expectedId = utils::make_hdradio_station_name(*name);
774 EXPECT_EQ(expectedId.value, nameIds[0]);
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800775 }
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800776}
777
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800778/**
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800779 * Test announcement listener registration.
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800780 *
781 * Verifies that:
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800782 * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800783 * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
784 * - closing handle does not crash.
785 */
Dan Shi27506e62019-12-12 10:54:49 -0800786TEST_P(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800787 sp<AnnouncementListenerMock> listener = new AnnouncementListenerMock();
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800788
789 Result halResult = Result::UNKNOWN_ERROR;
790 sp<ICloseHandle> closeHandle = nullptr;
791 auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
792 halResult = result;
793 closeHandle = closeHandle_;
794 };
795
796 auto hidlResult =
Tomasz Wasilczyk0d5ef5d2018-01-10 10:58:20 -0800797 mModule->registerAnnouncementListener({AnnouncementType::EMERGENCY}, listener, cb);
Tomasz Wasilczyk6a9f8562017-12-27 09:46:43 -0800798 ASSERT_TRUE(hidlResult.isOk());
799
800 if (halResult == Result::NOT_SUPPORTED) {
801 ASSERT_EQ(nullptr, closeHandle.get());
802 printSkipped("Announcements not supported");
803 return;
804 }
805
806 ASSERT_EQ(Result::OK, halResult);
807 ASSERT_NE(nullptr, closeHandle.get());
808
809 closeHandle->close();
810}
811
Dan Shi27506e62019-12-12 10:54:49 -0800812INSTANTIATE_TEST_SUITE_P(
813 PerInstance, BroadcastRadioHalTest,
814 testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBroadcastRadio::descriptor)),
815 android::hardware::PrintInstanceNameToString);
816
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800817} // namespace vts
818} // namespace V2_0
819} // namespace broadcastradio
820} // namespace hardware
821} // namespace android
822
823int main(int argc, char** argv) {
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800824 android::base::SetDefaultTag("BcRadio.vts");
825 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800826 ::testing::InitGoogleTest(&argc, argv);
Tomasz Wasilczyk84ec4e12018-11-13 11:26:23 -0800827 return RUN_ALL_TESTS();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800828}