blob: 46b3f19de684d9a042dcc79987607b128b154f89 [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"
18
19#include <VtsHalHidlTargetTestBase.h>
20#include <android-base/logging.h>
21#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>
29#include <gmock/gmock.h>
30
31#include <chrono>
32
33namespace android {
34namespace hardware {
35namespace broadcastradio {
36namespace V2_0 {
37namespace vts {
38
39using namespace std::chrono_literals;
40
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080041using std::unordered_set;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080042using std::vector;
43using testing::_;
44using testing::AnyNumber;
45using testing::ByMove;
46using testing::DoAll;
47using testing::Invoke;
48using testing::SaveArg;
49
50using broadcastradio::vts::CallBarrier;
51using broadcastradio::vts::clearAndWait;
52using utils::make_identifier;
53using utils::make_selector_amfm;
54
55namespace timeout {
56
57static constexpr auto tune = 30s;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080058static constexpr auto programListScan = 5min;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080059
60} // namespace timeout
61
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -080062static const ConfigFlag gConfigFlagValues[] = {
63 ConfigFlag::FORCE_MONO, ConfigFlag::FORCE_ANALOG, ConfigFlag::FORCE_DIGITAL,
64 ConfigFlag::RDS_AF, ConfigFlag::RDS_REG, ConfigFlag::DAB_IMPLICIT_LINKING,
65 ConfigFlag::DAB_HARD_LINKING, ConfigFlag::DAB_SOFT_LINKING,
66};
67
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080068class TunerCallbackMock : public ITunerCallback {
69 public:
70 TunerCallbackMock();
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080071
72 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
73 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged, Return<void>(const ProgramInfo&));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080074 Return<void> onProgramListUpdated(const ProgramListChunk& chunk);
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080075 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
76 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080077
78 MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
79
80 std::mutex mLock;
81 utils::ProgramInfoSet mProgramList;
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -080082};
83
84class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
85 protected:
86 virtual void SetUp() override;
87 virtual void TearDown() override;
88
89 bool openSession();
90
91 sp<IBroadcastRadio> mModule;
92 Properties mProperties;
93 sp<ITunerSession> mSession;
94 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
95};
96
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080097static void printSkipped(std::string msg) {
98 std::cout << "[ SKIPPED ] " << msg << std::endl;
99}
100
101TunerCallbackMock::TunerCallbackMock() {
102 // we expect the antenna is connected through the whole test
103 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
104}
105
106Return<void> TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
107 std::lock_guard<std::mutex> lk(mLock);
108
109 updateProgramList(mProgramList, chunk);
110
111 if (chunk.complete) onProgramListReady();
112
113 return {};
114}
115
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800116void BroadcastRadioHalTest::SetUp() {
117 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
118
119 // lookup HIDL service (radio module)
120 mModule = getService<IBroadcastRadio>();
121 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
122
123 // get module properties
124 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
125 ASSERT_TRUE(propResult.isOk());
126
127 EXPECT_FALSE(mProperties.maker.empty());
128 EXPECT_FALSE(mProperties.product.empty());
129 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
130}
131
132void BroadcastRadioHalTest::TearDown() {
133 mSession.clear();
134 mModule.clear();
135 clearAndWait(mCallback, 1s);
136}
137
138bool BroadcastRadioHalTest::openSession() {
139 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
140
141 Result halResult = Result::UNKNOWN_ERROR;
142 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
143 halResult = result;
144 if (result != Result::OK) return;
145 mSession = session;
146 };
147 auto hidlResult = mModule->openSession(mCallback, openCb);
148
149 EXPECT_TRUE(hidlResult.isOk());
150 EXPECT_EQ(Result::OK, halResult);
151 EXPECT_NE(nullptr, mSession.get());
152
153 return nullptr != mSession.get();
154}
155
156/**
157 * Test session opening.
158 *
159 * Verifies that:
160 * - the method succeeds on a first and subsequent calls;
161 * - the method succeeds when called for the second time without
162 * closing previous session.
163 */
164TEST_F(BroadcastRadioHalTest, OpenSession) {
165 // simply open session for the first time
166 ASSERT_TRUE(openSession());
167
168 // drop (without explicit close) and re-open the session
169 mSession.clear();
170 ASSERT_TRUE(openSession());
171
172 // open the second session (the first one should be forcibly closed)
173 auto secondSession = mSession;
174 mSession.clear();
175 ASSERT_TRUE(openSession());
176}
177
178/**
179 * Test tuning with FM selector.
180 *
181 * Verifies that:
182 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
183 * - if it is supported, the method succeeds;
184 * - after a successful tune call, onCurrentProgramInfoChanged callback is
185 * invoked carrying a proper selector;
186 * - program changes exactly to what was requested.
187 */
188TEST_F(BroadcastRadioHalTest, FmTune) {
189 ASSERT_TRUE(openSession());
190
191 uint64_t freq = 100100; // 100.1 FM
192 auto sel = make_selector_amfm(freq);
193
194 // try tuning
195 ProgramInfo infoCb = {};
196 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _)
197 .Times(AnyNumber())
198 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
199 auto result = mSession->tune(sel);
200
201 // expect a failure if it's not supported
202 if (!utils::isSupported(mProperties, sel)) {
203 EXPECT_EQ(Result::NOT_SUPPORTED, result);
204 return;
205 }
206
207 // expect a callback if it succeeds
208 EXPECT_EQ(Result::OK, result);
209 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
210
211 // it should tune exactly to what was requested
212 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
213 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
214}
215
216/**
217 * Test tuning with invalid selectors.
218 *
219 * Verifies that:
220 * - if the selector is not supported, it's ignored;
221 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
222 */
223TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
224 ASSERT_TRUE(openSession());
225
226 vector<ProgramIdentifier> invalid = {
227 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
228 make_identifier(IdentifierType::RDS_PI, 0x10000),
229 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
230 make_identifier(IdentifierType::DAB_SID_EXT, 0),
231 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
232 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
233 };
234
235 for (auto&& id : invalid) {
236 ProgramSelector sel{id, {}};
237
238 auto result = mSession->tune(sel);
239
240 if (utils::isSupported(mProperties, sel)) {
241 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
242 } else {
243 EXPECT_EQ(Result::NOT_SUPPORTED, result);
244 }
245 }
246}
247
248/**
249 * Test tuning with empty program selector.
250 *
251 * Verifies that:
252 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
253 */
254TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
255 ASSERT_TRUE(openSession());
256
257 // Program type is 1-based, so 0 will always be invalid.
258 ProgramSelector sel = {};
259 auto result = mSession->tune(sel);
260 ASSERT_EQ(Result::NOT_SUPPORTED, result);
261}
262
263/**
264 * Test scanning to next/prev station.
265 *
266 * Verifies that:
267 * - the method succeeds;
268 * - the program info is changed within timeout::tune;
269 * - works both directions and with or without skipping sub-channel.
270 */
271TEST_F(BroadcastRadioHalTest, Scan) {
272 ASSERT_TRUE(openSession());
273
274 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
275 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
276 EXPECT_EQ(Result::OK, result);
277 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
278
279 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
280 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
281 EXPECT_EQ(Result::OK, result);
282 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
283}
284
285/**
286 * Test step operation.
287 *
288 * Verifies that:
289 * - the method succeeds or returns NOT_SUPPORTED;
290 * - the program info is changed within timeout::tune if the method succeeded;
291 * - works both directions.
292 */
293TEST_F(BroadcastRadioHalTest, Step) {
294 ASSERT_TRUE(openSession());
295
296 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber());
297 auto result = mSession->step(true /* up */);
298 if (result == Result::NOT_SUPPORTED) return;
299 EXPECT_EQ(Result::OK, result);
300 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
301
302 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
303 result = mSession->step(false /* down */);
304 EXPECT_EQ(Result::OK, result);
305 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
306}
307
308/**
309 * Test tune cancellation.
310 *
311 * Verifies that:
312 * - the method does not crash after being invoked multiple times.
313 */
314TEST_F(BroadcastRadioHalTest, Cancel) {
315 ASSERT_TRUE(openSession());
316
317 for (int i = 0; i < 10; i++) {
318 auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
319 ASSERT_EQ(Result::OK, scanResult);
320
321 auto cancelResult = mSession->cancel();
322 ASSERT_TRUE(cancelResult.isOk());
323 }
324}
325
326/**
327 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
328 *
329 * Verifies that:
330 * - callback is called for empty parameters set.
331 */
332TEST_F(BroadcastRadioHalTest, NoParameters) {
333 ASSERT_TRUE(openSession());
334
335 hidl_vec<VendorKeyValue> halResults = {};
336 bool wasCalled = false;
337 auto cb = [&](hidl_vec<VendorKeyValue> results) {
338 wasCalled = true;
339 halResults = results;
340 };
341
342 auto hidlResult = mSession->setParameters({}, cb);
343 ASSERT_TRUE(hidlResult.isOk());
344 ASSERT_TRUE(wasCalled);
345 ASSERT_EQ(0u, halResults.size());
346
347 wasCalled = false;
348 hidlResult = mSession->getParameters({}, cb);
349 ASSERT_TRUE(hidlResult.isOk());
350 ASSERT_TRUE(wasCalled);
351 ASSERT_EQ(0u, halResults.size());
352}
353
354/**
355 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
356 *
357 * Verifies that:
358 * - unknown parameters are ignored;
359 * - callback is called also for empty results set.
360 */
361TEST_F(BroadcastRadioHalTest, UnknownParameters) {
362 ASSERT_TRUE(openSession());
363
364 hidl_vec<VendorKeyValue> halResults = {};
365 bool wasCalled = false;
366 auto cb = [&](hidl_vec<VendorKeyValue> results) {
367 wasCalled = true;
368 halResults = results;
369 };
370
371 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
372 ASSERT_TRUE(hidlResult.isOk());
373 ASSERT_TRUE(wasCalled);
374 ASSERT_EQ(0u, halResults.size());
375
376 wasCalled = false;
377 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
378 ASSERT_TRUE(hidlResult.isOk());
379 ASSERT_TRUE(wasCalled);
380 ASSERT_EQ(0u, halResults.size());
381}
382
383/**
384 * Test session closing.
385 *
386 * Verifies that:
387 * - the method does not crash after being invoked multiple times.
388 */
389TEST_F(BroadcastRadioHalTest, Close) {
390 ASSERT_TRUE(openSession());
391
392 for (int i = 0; i < 10; i++) {
393 auto cancelResult = mSession->close();
394 ASSERT_TRUE(cancelResult.isOk());
395 }
396}
397
398/**
399 * Test geting image of invalid ID.
400 *
401 * Verifies that:
402 * - getImage call handles argument 0 gracefully.
403 */
404TEST_F(BroadcastRadioHalTest, GetNoImage) {
405 size_t len = 0;
406 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
407
408 ASSERT_TRUE(result.isOk());
409 ASSERT_EQ(0u, len);
410}
411
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800412/**
413 * Test getting config flags.
414 *
415 * Verifies that:
416 * - getConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
417 * - call success or failure is consistent with setConfigFlag.
418 */
419TEST_F(BroadcastRadioHalTest, GetConfigFlags) {
420 ASSERT_TRUE(openSession());
421
422 for (auto flag : gConfigFlagValues) {
423 auto halResult = Result::UNKNOWN_ERROR;
424 auto cb = [&](Result result, bool) { halResult = result; };
425 auto hidlResult = mSession->getConfigFlag(flag, cb);
426 EXPECT_TRUE(hidlResult.isOk());
427
428 if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
429 ASSERT_EQ(Result::OK, halResult);
430 }
431
432 // set must fail or succeed the same way as get
433 auto setResult = mSession->setConfigFlag(flag, false);
434 EXPECT_EQ(halResult, setResult);
435 setResult = mSession->setConfigFlag(flag, true);
436 EXPECT_EQ(halResult, setResult);
437 }
438}
439
440/**
441 * Test setting config flags.
442 *
443 * Verifies that:
444 * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
445 * - getConfigFlag reflects the state requested immediately after the set call.
446 */
447TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
448 ASSERT_TRUE(openSession());
449
450 auto get = [&](ConfigFlag flag) {
451 auto halResult = Result::UNKNOWN_ERROR;
452 bool gotValue = false;
453 auto cb = [&](Result result, bool value) {
454 halResult = result;
455 gotValue = value;
456 };
457 auto hidlResult = mSession->getConfigFlag(flag, cb);
458 EXPECT_TRUE(hidlResult.isOk());
459 EXPECT_EQ(Result::OK, halResult);
460 return gotValue;
461 };
462
463 for (auto flag : gConfigFlagValues) {
464 auto result = mSession->setConfigFlag(flag, false);
465 if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
466 // setting to true must result in the same error as false
467 auto secondResult = mSession->setConfigFlag(flag, true);
468 EXPECT_EQ(result, secondResult);
469 continue;
470 }
471 ASSERT_EQ(Result::OK, result);
472
473 // verify false is set
474 auto value = get(flag);
475 EXPECT_FALSE(value);
476
477 // try setting true this time
478 result = mSession->setConfigFlag(flag, true);
479 ASSERT_EQ(Result::OK, result);
480 value = get(flag);
481 EXPECT_TRUE(value);
482
483 // false again
484 result = mSession->setConfigFlag(flag, false);
485 ASSERT_EQ(Result::OK, result);
486 value = get(flag);
487 EXPECT_FALSE(value);
488 }
489}
490
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800491/**
492 * Test getting program list.
493 *
494 * Verifies that:
495 * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
496 * - the complete list is fetched within timeout::programListScan;
497 * - stopProgramListUpdates does not crash.
498 */
499TEST_F(BroadcastRadioHalTest, GetProgramList) {
500 ASSERT_TRUE(openSession());
501
502 EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
503
504 auto startResult = mSession->startProgramListUpdates({});
505 if (startResult == Result::NOT_SUPPORTED) {
506 printSkipped("Program list not supported");
507 return;
508 }
509 ASSERT_EQ(Result::OK, startResult);
510
511 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
512
513 auto stopResult = mSession->stopProgramListUpdates();
514 EXPECT_TRUE(stopResult.isOk());
515}
516
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -0800517} // namespace vts
518} // namespace V2_0
519} // namespace broadcastradio
520} // namespace hardware
521} // namespace android
522
523int main(int argc, char** argv) {
524 ::testing::InitGoogleTest(&argc, argv);
525 int status = RUN_ALL_TESTS();
526 ALOGI("Test result = %d", status);
527 return status;
528}