blob: c6bc344a8d615c3b203495140d787452911e2050 [file] [log] [blame]
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -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 Wasilczykc9ba6462017-07-07 13:28:00 -070017#define LOG_TAG "broadcastradio.vts"
18
Yuexi Maed2bb4e2017-03-10 00:44:45 -080019#include <VtsHalHidlTargetTestBase.h>
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -070020#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
21#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
22#include <android/hardware/broadcastradio/1.1/ITuner.h>
23#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
24#include <android/hardware/broadcastradio/1.1/types.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080025#include <android-base/logging.h>
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -070026#include <broadcastradio-utils/Utils.h>
27#include <broadcastradio-vts-utils/call-barrier.h>
28#include <broadcastradio-vts-utils/mock-timeout.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080029#include <cutils/native_handle.h>
30#include <cutils/properties.h>
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070031#include <gmock/gmock.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080032#include <hidl/HidlTransportSupport.h>
33#include <utils/threads.h>
34
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070035#include <chrono>
36
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070037namespace android {
38namespace hardware {
39namespace broadcastradio {
40namespace V1_1 {
41namespace vts {
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080042
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070043using namespace std::chrono_literals;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080044
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070045using testing::_;
46using testing::AnyNumber;
47using testing::ByMove;
48using testing::DoAll;
Tomasz Wasilczyk24180092017-07-14 10:44:52 -070049using testing::Invoke;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070050using testing::SaveArg;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080051
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070052using broadcastradio::vts::CallBarrier;
53using V1_0::BandConfig;
54using V1_0::Class;
55using V1_0::MetaData;
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -070056using V1_0::MetadataKey;
57using V1_0::MetadataType;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080058
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -070059using std::chrono::steady_clock;
60using std::this_thread::sleep_for;
61
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070062static constexpr auto kConfigTimeout = 10s;
63static constexpr auto kConnectModuleTimeout = 1s;
64static constexpr auto kTuneTimeout = 30s;
65static constexpr auto kFullScanTimeout = 1min;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080066
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070067static void printSkipped(std::string msg) {
68 std::cout << "[ SKIPPED ] " << msg << std::endl;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080069}
70
Tomasz Wasilczyk24180092017-07-14 10:44:52 -070071struct TunerCallbackMock : public ITunerCallback {
72 TunerCallbackMock() { EXPECT_CALL(*this, hardwareFailure()).Times(0); }
73
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070074 MOCK_METHOD0(hardwareFailure, Return<void>());
75 MOCK_TIMEOUT_METHOD2(configChange, Return<void>(Result, const BandConfig&));
76 MOCK_METHOD2(tuneComplete, Return<void>(Result, const V1_0::ProgramInfo&));
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -070077 MOCK_TIMEOUT_METHOD2(tuneComplete_1_1, Return<void>(Result, const ProgramSelector&));
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070078 MOCK_METHOD1(afSwitch, Return<void>(const V1_0::ProgramInfo&));
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -070079 MOCK_METHOD1(afSwitch_1_1, Return<void>(const ProgramSelector&));
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070080 MOCK_METHOD1(antennaStateChange, Return<void>(bool connected));
81 MOCK_METHOD1(trafficAnnouncement, Return<void>(bool active));
82 MOCK_METHOD1(emergencyAnnouncement, Return<void>(bool active));
83 MOCK_METHOD3(newMetadata, Return<void>(uint32_t ch, uint32_t subCh, const hidl_vec<MetaData>&));
84 MOCK_METHOD1(backgroundScanAvailable, Return<void>(bool));
85 MOCK_TIMEOUT_METHOD1(backgroundScanComplete, Return<void>(ProgramListResult));
86 MOCK_METHOD0(programListChanged, Return<void>());
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -070087 MOCK_METHOD0(programInfoChanged, Return<void>());
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070088};
89
90class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase,
91 public ::testing::WithParamInterface<Class> {
92 protected:
93 virtual void SetUp() override;
94 virtual void TearDown() override;
95
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -070096 bool openTuner();
97 bool nextBand();
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -070098 bool getProgramList(std::function<void(const hidl_vec<ProgramInfo>& list)> cb);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070099
100 Class radioClass;
101 bool skipped = false;
102
103 sp<IBroadcastRadio> mRadioModule;
104 sp<ITuner> mTuner;
105 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
106
107 private:
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700108 const BandConfig& getBand(unsigned idx);
109
110 unsigned currentBandIndex = 0;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700111 hidl_vec<BandConfig> mBands;
112};
113
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700114/**
115 * Clears strong pointer and waits until the object gets destroyed.
116 *
117 * @param ptr The pointer to get cleared.
118 * @param timeout Time to wait for other references.
119 */
120template <typename T>
121static void clearAndWait(sp<T>& ptr, std::chrono::milliseconds timeout) {
122 wp<T> wptr = ptr;
123 ptr.clear();
124 auto limit = steady_clock::now() + timeout;
125 while (wptr.promote() != nullptr) {
126 constexpr auto step = 10ms;
127 if (steady_clock::now() + step > limit) {
128 FAIL() << "Pointer was not released within timeout";
129 break;
130 }
131 sleep_for(step);
132 }
133}
134
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700135void BroadcastRadioHalTest::SetUp() {
136 radioClass = GetParam();
137
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700138 // lookup HIDL service
139 auto factory = getService<IBroadcastRadioFactory>();
140 ASSERT_NE(nullptr, factory.get());
141
142 // connect radio module
143 Result connectResult;
144 CallBarrier onConnect;
145 factory->connectModule(radioClass, [&](Result ret, const sp<V1_0::IBroadcastRadio>& radio) {
146 connectResult = ret;
147 if (ret == Result::OK) mRadioModule = IBroadcastRadio::castFrom(radio);
148 onConnect.call();
149 });
150 ASSERT_TRUE(onConnect.waitForCall(kConnectModuleTimeout));
151
152 if (connectResult == Result::INVALID_ARGUMENTS) {
153 printSkipped("This device class is not supported.");
154 skipped = true;
155 return;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800156 }
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700157 ASSERT_EQ(connectResult, Result::OK);
158 ASSERT_NE(nullptr, mRadioModule.get());
159
160 // get module properties
161 Properties prop11;
162 auto& prop10 = prop11.base;
163 auto propResult =
164 mRadioModule->getProperties_1_1([&](const Properties& properties) { prop11 = properties; });
165
166 ASSERT_TRUE(propResult.isOk());
167 EXPECT_EQ(radioClass, prop10.classId);
168 EXPECT_GT(prop10.numTuners, 0u);
Tomasz Wasilczyke192c392017-07-16 15:14:34 -0700169 EXPECT_GT(prop11.supportedProgramTypes.size(), 0u);
170 EXPECT_GT(prop11.supportedIdentifierTypes.size(), 0u);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700171 if (radioClass == Class::AM_FM) {
172 EXPECT_GT(prop10.bands.size(), 0u);
173 }
174 mBands = prop10.bands;
175}
176
177void BroadcastRadioHalTest::TearDown() {
178 mTuner.clear();
179 mRadioModule.clear();
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700180 clearAndWait(mCallback, 1s);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700181}
182
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700183bool BroadcastRadioHalTest::openTuner() {
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700184 EXPECT_EQ(nullptr, mTuner.get());
185
186 if (radioClass == Class::AM_FM) {
187 EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _);
188 }
189
190 Result halResult = Result::NOT_INITIALIZED;
191 auto openCb = [&](Result result, const sp<V1_0::ITuner>& tuner) {
192 halResult = result;
193 if (result != Result::OK) return;
194 mTuner = ITuner::castFrom(tuner);
195 };
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700196 currentBandIndex = 0;
197 auto hidlResult = mRadioModule->openTuner(getBand(0), true, mCallback, openCb);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700198
199 EXPECT_TRUE(hidlResult.isOk());
200 EXPECT_EQ(Result::OK, halResult);
201 EXPECT_NE(nullptr, mTuner.get());
202 if (radioClass == Class::AM_FM && mTuner != nullptr) {
203 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout);
204
205 BandConfig halConfig;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800206 Result halResult = Result::NOT_INITIALIZED;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700207 mTuner->getConfiguration([&](Result result, const BandConfig& config) {
208 halResult = result;
209 halConfig = config;
210 });
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800211 EXPECT_EQ(Result::OK, halResult);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700212 EXPECT_TRUE(halConfig.antennaConnected);
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800213 }
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700214
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800215 EXPECT_NE(nullptr, mTuner.get());
216 return nullptr != mTuner.get();
217}
218
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700219const BandConfig& BroadcastRadioHalTest::getBand(unsigned idx) {
220 static const BandConfig dummyBandConfig = {};
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800221
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700222 if (radioClass != Class::AM_FM) {
223 ALOGD("Not AM/FM radio, returning dummy band config");
224 return dummyBandConfig;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800225 }
226
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700227 EXPECT_GT(mBands.size(), idx);
228 if (mBands.size() <= idx) {
229 ALOGD("Band index out of bound, returning dummy band config");
230 return dummyBandConfig;
231 }
232
233 auto& band = mBands[idx];
234 ALOGD("Returning %s band", toString(band.type).c_str());
235 return band;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800236}
237
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700238bool BroadcastRadioHalTest::nextBand() {
239 if (currentBandIndex + 1 >= mBands.size()) return false;
240 currentBandIndex++;
241
242 BandConfig bandCb;
243 EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _)
244 .WillOnce(DoAll(SaveArg<1>(&bandCb), testing::Return(ByMove(Void()))));
245 auto hidlResult = mTuner->setConfiguration(getBand(currentBandIndex));
246 EXPECT_EQ(Result::OK, hidlResult);
247 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout);
248 EXPECT_EQ(getBand(currentBandIndex), bandCb);
249
250 return true;
251}
252
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700253bool BroadcastRadioHalTest::getProgramList(
254 std::function<void(const hidl_vec<ProgramInfo>& list)> cb) {
255 ProgramListResult getListResult = ProgramListResult::NOT_INITIALIZED;
256 bool isListEmpty = true;
257 auto getListCb = [&](ProgramListResult result, const hidl_vec<ProgramInfo>& list) {
258 ALOGD("getListCb(%s, ProgramInfo[%zu])", toString(result).c_str(), list.size());
259 getListResult = result;
260 if (result != ProgramListResult::OK) return;
261 isListEmpty = (list.size() == 0);
262 if (!isListEmpty) cb(list);
263 };
264
265 // first try...
266 EXPECT_TIMEOUT_CALL(*mCallback, backgroundScanComplete, ProgramListResult::OK)
267 .Times(AnyNumber());
268 auto hidlResult = mTuner->getProgramList("", getListCb);
269 EXPECT_TRUE(hidlResult.isOk());
270 if (!hidlResult.isOk()) return false;
271
272 if (getListResult == ProgramListResult::NOT_STARTED) {
273 auto result = mTuner->startBackgroundScan();
274 EXPECT_EQ(ProgramListResult::OK, result);
275 getListResult = ProgramListResult::NOT_READY; // continue as in NOT_READY case
276 }
277 if (getListResult == ProgramListResult::NOT_READY) {
278 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, backgroundScanComplete, kFullScanTimeout);
279
280 // second (last) try...
281 hidlResult = mTuner->getProgramList("", getListCb);
282 EXPECT_TRUE(hidlResult.isOk());
283 if (!hidlResult.isOk()) return false;
284 EXPECT_EQ(ProgramListResult::OK, getListResult);
285 }
286
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700287 return !isListEmpty;
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700288}
289
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700290/**
291 * Test IBroadcastRadio::openTuner() method called twice.
292 *
293 * Verifies that:
294 * - the openTuner method succeeds when called for the second time without
295 * deleting previous ITuner instance.
296 *
297 * This is a more strict requirement than in 1.0, where a second openTuner
298 * might fail.
299 */
300TEST_P(BroadcastRadioHalTest, OpenTunerTwice) {
301 if (skipped) return;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700302
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700303 ASSERT_TRUE(openTuner());
304
305 auto secondTuner = mTuner;
306 mTuner.clear();
307
308 ASSERT_TRUE(openTuner());
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700309}
310
311/**
312 * Test tuning to program list entry.
313 *
314 * Verifies that:
315 * - getProgramList either succeeds or returns NOT_STARTED/NOT_READY status;
316 * - if the program list is NOT_STARTED, startBackgroundScan makes it completed
317 * within a full scan timeout and the next getProgramList call succeeds;
318 * - if the program list is not empty, tune_1_1 call succeeds.
319 */
320TEST_P(BroadcastRadioHalTest, TuneFromProgramList) {
321 if (skipped) return;
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700322 ASSERT_TRUE(openTuner());
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700323
324 ProgramInfo firstProgram;
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700325 bool foundAny = false;
326 do {
327 auto getCb = [&](const hidl_vec<ProgramInfo>& list) {
328 // don't copy the whole list out, it might be heavy
329 firstProgram = list[0];
330 };
331 if (getProgramList(getCb)) foundAny = true;
332 } while (nextBand());
333 if (HasFailure()) return;
334 if (!foundAny) {
335 printSkipped("Program list is empty.");
336 return;
337 }
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700338
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -0700339 ProgramSelector selCb;
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700340 EXPECT_CALL(*mCallback, tuneComplete(_, _)).Times(0);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700341 EXPECT_TIMEOUT_CALL(*mCallback, tuneComplete_1_1, Result::OK, _)
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -0700342 .WillOnce(DoAll(SaveArg<1>(&selCb), testing::Return(ByMove(Void()))));
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700343 auto tuneResult = mTuner->tune_1_1(firstProgram.selector);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700344 ASSERT_EQ(Result::OK, tuneResult);
345 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, tuneComplete_1_1, kTuneTimeout);
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -0700346 EXPECT_EQ(firstProgram.selector.primaryId, selCb.primaryId);
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700347}
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800348
Tomasz Wasilczyk24180092017-07-14 10:44:52 -0700349TEST_P(BroadcastRadioHalTest, CancelAnnouncement) {
350 if (skipped) return;
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700351 ASSERT_TRUE(openTuner());
Tomasz Wasilczyk24180092017-07-14 10:44:52 -0700352
353 auto hidlResult = mTuner->cancelAnnouncement();
354 EXPECT_EQ(Result::OK, hidlResult);
355}
356
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700357/**
358 * Test getImage call with invalid image ID.
359 *
360 * Verifies that:
361 * - getImage call handles argument 0 gracefully
362 */
363TEST_P(BroadcastRadioHalTest, GetNoImage) {
364 if (skipped) return;
365
366 size_t len = 0;
367 auto hidlResult =
368 mRadioModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
369
370 ASSERT_TRUE(hidlResult.isOk());
371 ASSERT_EQ(0u, len);
372}
373
374/**
375 * Test proper image format in metadata.
376 *
377 * Verifies that:
378 * - all images in metadata are provided out-of-band (by id, not as a binary blob)
379 * - images are available for getImage call
380 */
381TEST_P(BroadcastRadioHalTest, OobImagesOnly) {
382 if (skipped) return;
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700383 ASSERT_TRUE(openTuner());
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700384
385 std::vector<int> imageIds;
386
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700387 do {
388 auto getCb = [&](const hidl_vec<ProgramInfo>& list) {
389 for (auto&& program : list) {
390 for (auto&& entry : program.base.metadata) {
391 EXPECT_NE(MetadataType::RAW, entry.type);
392 if (entry.key != MetadataKey::ICON && entry.key != MetadataKey::ART) continue;
393 EXPECT_NE(0, entry.intValue);
394 EXPECT_EQ(0u, entry.rawValue.size());
395 if (entry.intValue != 0) imageIds.push_back(entry.intValue);
396 }
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700397 }
Tomasz Wasilczykc1763a62017-07-25 10:01:17 -0700398 };
399 getProgramList(getCb);
400 } while (nextBand());
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700401
402 if (imageIds.size() == 0) {
403 printSkipped("No images found");
404 return;
405 }
406
407 for (auto id : imageIds) {
408 ALOGD("Checking image %d", id);
409
410 size_t len = 0;
411 auto hidlResult =
412 mRadioModule->getImage(id, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
413
414 ASSERT_TRUE(hidlResult.isOk());
415 ASSERT_GT(len, 0u);
416 }
417}
418
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700419INSTANTIATE_TEST_CASE_P(BroadcastRadioHalTestCases, BroadcastRadioHalTest,
420 ::testing::Values(Class::AM_FM, Class::SAT, Class::DT));
421
422} // namespace vts
423} // namespace V1_1
424} // namespace broadcastradio
425} // namespace hardware
426} // namespace android
427
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800428int main(int argc, char** argv) {
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800429 ::testing::InitGoogleTest(&argc, argv);
430 int status = RUN_ALL_TESTS();
431 ALOGI("Test result = %d", status);
432 return status;
433}