blob: d20452bce558f0367afbddc04e2609f9a227882e [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 Wasilczyk213170b2017-02-07 17:38:21 -080020#include <android-base/logging.h>
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070021#include <call-barrier.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080022#include <cutils/native_handle.h>
23#include <cutils/properties.h>
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070024#include <gmock/gmock.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080025#include <hidl/HidlTransportSupport.h>
26#include <utils/threads.h>
27
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070028#include <chrono>
29
30#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080031#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080032#include <android/hardware/broadcastradio/1.1/ITuner.h>
33#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
34#include <android/hardware/broadcastradio/1.1/types.h>
35
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070036#include "mock-timeout.h"
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080037
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070038namespace android {
39namespace hardware {
40namespace broadcastradio {
41namespace V1_1 {
42namespace vts {
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080043
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070044using namespace std::chrono_literals;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080045
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070046using testing::_;
47using testing::AnyNumber;
48using testing::ByMove;
49using testing::DoAll;
Tomasz Wasilczyk24180092017-07-14 10:44:52 -070050using testing::Invoke;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070051using testing::SaveArg;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080052
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070053using broadcastradio::vts::CallBarrier;
54using V1_0::BandConfig;
55using V1_0::Class;
56using V1_0::MetaData;
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -070057using V1_0::MetadataKey;
58using V1_0::MetadataType;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080059
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070060static constexpr auto kConfigTimeout = 10s;
61static constexpr auto kConnectModuleTimeout = 1s;
62static constexpr auto kTuneTimeout = 30s;
63static constexpr auto kFullScanTimeout = 1min;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080064
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070065static void printSkipped(std::string msg) {
66 std::cout << "[ SKIPPED ] " << msg << std::endl;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -080067}
68
Tomasz Wasilczyk24180092017-07-14 10:44:52 -070069struct TunerCallbackMock : public ITunerCallback {
70 TunerCallbackMock() { EXPECT_CALL(*this, hardwareFailure()).Times(0); }
71
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070072 MOCK_METHOD0(hardwareFailure, Return<void>());
73 MOCK_TIMEOUT_METHOD2(configChange, Return<void>(Result, const BandConfig&));
74 MOCK_METHOD2(tuneComplete, Return<void>(Result, const V1_0::ProgramInfo&));
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -070075 MOCK_TIMEOUT_METHOD2(tuneComplete_1_1, Return<void>(Result, const ProgramSelector&));
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070076 MOCK_METHOD1(afSwitch, Return<void>(const V1_0::ProgramInfo&));
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -070077 MOCK_METHOD1(afSwitch_1_1, Return<void>(const ProgramSelector&));
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070078 MOCK_METHOD1(antennaStateChange, Return<void>(bool connected));
79 MOCK_METHOD1(trafficAnnouncement, Return<void>(bool active));
80 MOCK_METHOD1(emergencyAnnouncement, Return<void>(bool active));
81 MOCK_METHOD3(newMetadata, Return<void>(uint32_t ch, uint32_t subCh, const hidl_vec<MetaData>&));
82 MOCK_METHOD1(backgroundScanAvailable, Return<void>(bool));
83 MOCK_TIMEOUT_METHOD1(backgroundScanComplete, Return<void>(ProgramListResult));
84 MOCK_METHOD0(programListChanged, Return<void>());
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -070085 MOCK_METHOD0(programInfoChanged, Return<void>());
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -070086};
87
88class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase,
89 public ::testing::WithParamInterface<Class> {
90 protected:
91 virtual void SetUp() override;
92 virtual void TearDown() override;
93
94 // TODO(b/36864490): check all bands for good test conditions (ie. FM is more likely to have
95 // any stations on the list, so don't pick AM blindly).
96 bool openTuner(unsigned band);
97 const BandConfig& getBand(unsigned idx);
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:
108 hidl_vec<BandConfig> mBands;
109};
110
111void BroadcastRadioHalTest::SetUp() {
112 radioClass = GetParam();
113
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700114 // lookup HIDL service
115 auto factory = getService<IBroadcastRadioFactory>();
116 ASSERT_NE(nullptr, factory.get());
117
118 // connect radio module
119 Result connectResult;
120 CallBarrier onConnect;
121 factory->connectModule(radioClass, [&](Result ret, const sp<V1_0::IBroadcastRadio>& radio) {
122 connectResult = ret;
123 if (ret == Result::OK) mRadioModule = IBroadcastRadio::castFrom(radio);
124 onConnect.call();
125 });
126 ASSERT_TRUE(onConnect.waitForCall(kConnectModuleTimeout));
127
128 if (connectResult == Result::INVALID_ARGUMENTS) {
129 printSkipped("This device class is not supported.");
130 skipped = true;
131 return;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800132 }
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700133 ASSERT_EQ(connectResult, Result::OK);
134 ASSERT_NE(nullptr, mRadioModule.get());
135
136 // get module properties
137 Properties prop11;
138 auto& prop10 = prop11.base;
139 auto propResult =
140 mRadioModule->getProperties_1_1([&](const Properties& properties) { prop11 = properties; });
141
142 ASSERT_TRUE(propResult.isOk());
143 EXPECT_EQ(radioClass, prop10.classId);
144 EXPECT_GT(prop10.numTuners, 0u);
Tomasz Wasilczyke192c392017-07-16 15:14:34 -0700145 EXPECT_GT(prop11.supportedProgramTypes.size(), 0u);
146 EXPECT_GT(prop11.supportedIdentifierTypes.size(), 0u);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700147 if (radioClass == Class::AM_FM) {
148 EXPECT_GT(prop10.bands.size(), 0u);
149 }
150 mBands = prop10.bands;
151}
152
153void BroadcastRadioHalTest::TearDown() {
154 mTuner.clear();
155 mRadioModule.clear();
156 // TODO(b/36864490): wait (with timeout) until mCallback has only one reference
157}
158
159bool BroadcastRadioHalTest::openTuner(unsigned band) {
160 EXPECT_EQ(nullptr, mTuner.get());
161
162 if (radioClass == Class::AM_FM) {
163 EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _);
164 }
165
166 Result halResult = Result::NOT_INITIALIZED;
167 auto openCb = [&](Result result, const sp<V1_0::ITuner>& tuner) {
168 halResult = result;
169 if (result != Result::OK) return;
170 mTuner = ITuner::castFrom(tuner);
171 };
172 auto hidlResult = mRadioModule->openTuner(getBand(band), true, mCallback, openCb);
173
174 EXPECT_TRUE(hidlResult.isOk());
175 EXPECT_EQ(Result::OK, halResult);
176 EXPECT_NE(nullptr, mTuner.get());
177 if (radioClass == Class::AM_FM && mTuner != nullptr) {
178 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout);
179
180 BandConfig halConfig;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800181 Result halResult = Result::NOT_INITIALIZED;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700182 mTuner->getConfiguration([&](Result result, const BandConfig& config) {
183 halResult = result;
184 halConfig = config;
185 });
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800186 EXPECT_EQ(Result::OK, halResult);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700187 EXPECT_TRUE(halConfig.antennaConnected);
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800188 }
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700189
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800190 EXPECT_NE(nullptr, mTuner.get());
191 return nullptr != mTuner.get();
192}
193
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700194const BandConfig& BroadcastRadioHalTest::getBand(unsigned idx) {
195 static const BandConfig dummyBandConfig = {};
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800196
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700197 if (radioClass != Class::AM_FM) {
198 ALOGD("Not AM/FM radio, returning dummy band config");
199 return dummyBandConfig;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800200 }
201
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700202 EXPECT_GT(mBands.size(), idx);
203 if (mBands.size() <= idx) {
204 ALOGD("Band index out of bound, returning dummy band config");
205 return dummyBandConfig;
206 }
207
208 auto& band = mBands[idx];
209 ALOGD("Returning %s band", toString(band.type).c_str());
210 return band;
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800211}
212
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700213bool BroadcastRadioHalTest::getProgramList(
214 std::function<void(const hidl_vec<ProgramInfo>& list)> cb) {
215 ProgramListResult getListResult = ProgramListResult::NOT_INITIALIZED;
216 bool isListEmpty = true;
217 auto getListCb = [&](ProgramListResult result, const hidl_vec<ProgramInfo>& list) {
218 ALOGD("getListCb(%s, ProgramInfo[%zu])", toString(result).c_str(), list.size());
219 getListResult = result;
220 if (result != ProgramListResult::OK) return;
221 isListEmpty = (list.size() == 0);
222 if (!isListEmpty) cb(list);
223 };
224
225 // first try...
226 EXPECT_TIMEOUT_CALL(*mCallback, backgroundScanComplete, ProgramListResult::OK)
227 .Times(AnyNumber());
228 auto hidlResult = mTuner->getProgramList("", getListCb);
229 EXPECT_TRUE(hidlResult.isOk());
230 if (!hidlResult.isOk()) return false;
231
232 if (getListResult == ProgramListResult::NOT_STARTED) {
233 auto result = mTuner->startBackgroundScan();
234 EXPECT_EQ(ProgramListResult::OK, result);
235 getListResult = ProgramListResult::NOT_READY; // continue as in NOT_READY case
236 }
237 if (getListResult == ProgramListResult::NOT_READY) {
238 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, backgroundScanComplete, kFullScanTimeout);
239
240 // second (last) try...
241 hidlResult = mTuner->getProgramList("", getListCb);
242 EXPECT_TRUE(hidlResult.isOk());
243 if (!hidlResult.isOk()) return false;
244 EXPECT_EQ(ProgramListResult::OK, getListResult);
245 }
246
247 if (isListEmpty) {
248 printSkipped("Program list is empty.");
249 return false;
250 }
251 return true;
252}
253
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700254/**
255 * Test IBroadcastRadio::openTuner() method called twice.
256 *
257 * Verifies that:
258 * - the openTuner method succeeds when called for the second time without
259 * deleting previous ITuner instance.
260 *
261 * This is a more strict requirement than in 1.0, where a second openTuner
262 * might fail.
263 */
264TEST_P(BroadcastRadioHalTest, OpenTunerTwice) {
265 if (skipped) return;
266 ASSERT_TRUE(openTuner(0));
267
268 Result halResult = Result::NOT_INITIALIZED;
269 auto openCb = [&](Result result, const sp<V1_0::ITuner>&) { halResult = result; };
270 auto hidlResult = mRadioModule->openTuner(getBand(0), true, mCallback, openCb);
271 ASSERT_TRUE(hidlResult.isOk());
272 ASSERT_EQ(Result::OK, halResult);
273}
274
275/**
276 * Test tuning to program list entry.
277 *
278 * Verifies that:
279 * - getProgramList either succeeds or returns NOT_STARTED/NOT_READY status;
280 * - if the program list is NOT_STARTED, startBackgroundScan makes it completed
281 * within a full scan timeout and the next getProgramList call succeeds;
282 * - if the program list is not empty, tune_1_1 call succeeds.
283 */
284TEST_P(BroadcastRadioHalTest, TuneFromProgramList) {
285 if (skipped) return;
286 ASSERT_TRUE(openTuner(0));
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700287
288 ProgramInfo firstProgram;
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700289 auto getCb = [&](const hidl_vec<ProgramInfo>& list) {
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700290 // don't copy the whole list out, it might be heavy
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700291 firstProgram = list[0];
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700292 };
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700293 if (!getProgramList(getCb)) return;
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700294
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -0700295 ProgramSelector selCb;
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700296 EXPECT_CALL(*mCallback, tuneComplete(_, _));
297 EXPECT_TIMEOUT_CALL(*mCallback, tuneComplete_1_1, Result::OK, _)
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -0700298 .WillOnce(DoAll(SaveArg<1>(&selCb), testing::Return(ByMove(Void()))));
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700299 auto tuneResult = mTuner->tune_1_1(firstProgram.selector);
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700300 ASSERT_EQ(Result::OK, tuneResult);
301 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, tuneComplete_1_1, kTuneTimeout);
Tomasz Wasilczykf8866e72017-07-13 15:51:21 -0700302 EXPECT_EQ(firstProgram.selector.primaryId, selCb.primaryId);
Tomasz Wasilczyka02b6ef2017-07-05 11:23:30 -0700303}
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800304
Tomasz Wasilczyk24180092017-07-14 10:44:52 -0700305TEST_P(BroadcastRadioHalTest, CancelAnnouncement) {
306 if (skipped) return;
307 ASSERT_TRUE(openTuner(0));
308
309 auto hidlResult = mTuner->cancelAnnouncement();
310 EXPECT_EQ(Result::OK, hidlResult);
311}
312
Tomasz Wasilczykba3e2542017-07-17 13:59:21 -0700313/**
314 * Test getImage call with invalid image ID.
315 *
316 * Verifies that:
317 * - getImage call handles argument 0 gracefully
318 */
319TEST_P(BroadcastRadioHalTest, GetNoImage) {
320 if (skipped) return;
321
322 size_t len = 0;
323 auto hidlResult =
324 mRadioModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
325
326 ASSERT_TRUE(hidlResult.isOk());
327 ASSERT_EQ(0u, len);
328}
329
330/**
331 * Test proper image format in metadata.
332 *
333 * Verifies that:
334 * - all images in metadata are provided out-of-band (by id, not as a binary blob)
335 * - images are available for getImage call
336 */
337TEST_P(BroadcastRadioHalTest, OobImagesOnly) {
338 if (skipped) return;
339 ASSERT_TRUE(openTuner(0));
340
341 std::vector<int> imageIds;
342
343 ProgramInfo firstProgram;
344 auto getCb = [&](const hidl_vec<ProgramInfo>& list) {
345 for (auto&& program : list) {
346 for (auto&& entry : program.base.metadata) {
347 EXPECT_NE(MetadataType::RAW, entry.type);
348 if (entry.key != MetadataKey::ICON && entry.key != MetadataKey::ART) continue;
349 EXPECT_NE(0, entry.intValue);
350 EXPECT_EQ(0u, entry.rawValue.size());
351 if (entry.intValue != 0) imageIds.push_back(entry.intValue);
352 }
353 }
354 };
355 if (!getProgramList(getCb)) return;
356
357 if (imageIds.size() == 0) {
358 printSkipped("No images found");
359 return;
360 }
361
362 for (auto id : imageIds) {
363 ALOGD("Checking image %d", id);
364
365 size_t len = 0;
366 auto hidlResult =
367 mRadioModule->getImage(id, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
368
369 ASSERT_TRUE(hidlResult.isOk());
370 ASSERT_GT(len, 0u);
371 }
372}
373
Tomasz Wasilczykc9ba6462017-07-07 13:28:00 -0700374INSTANTIATE_TEST_CASE_P(BroadcastRadioHalTestCases, BroadcastRadioHalTest,
375 ::testing::Values(Class::AM_FM, Class::SAT, Class::DT));
376
377} // namespace vts
378} // namespace V1_1
379} // namespace broadcastradio
380} // namespace hardware
381} // namespace android
382
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800383int main(int argc, char** argv) {
Tomasz Wasilczyk213170b2017-02-07 17:38:21 -0800384 ::testing::InitGoogleTest(&argc, argv);
385 int status = RUN_ALL_TESTS();
386 ALOGI("Test result = %d", status);
387 return status;
388}