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