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