1 /*
2  * Copyright (C) 2016 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 "BroadcastRadioHidlHalTest"
18 #include <android-base/logging.h>
19 #include <cutils/native_handle.h>
20 #include <cutils/properties.h>
21 #include <gtest/gtest.h>
22 #include <hidl/GtestPrinter.h>
23 #include <hidl/HidlTransportSupport.h>
24 #include <hidl/ServiceManagement.h>
25 #include <utils/threads.h>
26 
27 #include <android/hardware/broadcastradio/1.0/IBroadcastRadio.h>
28 #include <android/hardware/broadcastradio/1.0/IBroadcastRadioFactory.h>
29 #include <android/hardware/broadcastradio/1.0/ITuner.h>
30 #include <android/hardware/broadcastradio/1.0/ITunerCallback.h>
31 #include <android/hardware/broadcastradio/1.0/types.h>
32 #include <broadcastradio-vts-utils/hal-1.x-enum-utils.h>
33 
34 using ::android::Condition;
35 using ::android::Mutex;
36 using ::android::sp;
37 using ::android::hardware::Return;
38 using ::android::hardware::Void;
39 using ::android::hardware::broadcastradio::V1_0::Band;
40 using ::android::hardware::broadcastradio::V1_0::BandConfig;
41 using ::android::hardware::broadcastradio::V1_0::Class;
42 using ::android::hardware::broadcastradio::V1_0::Direction;
43 using ::android::hardware::broadcastradio::V1_0::IBroadcastRadio;
44 using ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory;
45 using ::android::hardware::broadcastradio::V1_0::ITuner;
46 using ::android::hardware::broadcastradio::V1_0::ITunerCallback;
47 using ::android::hardware::broadcastradio::V1_0::MetaData;
48 using ::android::hardware::broadcastradio::V1_0::MetadataKey;
49 using ::android::hardware::broadcastradio::V1_0::MetadataType;
50 using ::android::hardware::broadcastradio::V1_0::ProgramInfo;
51 using ::android::hardware::broadcastradio::V1_0::Properties;
52 using ::android::hardware::broadcastradio::V1_0::Result;
53 using ::android::hardware::broadcastradio::V1_0::vts::RadioClassFromString;
54 
55 #define RETURN_IF_SKIPPED \
56     if (skipped) { \
57         std::cout << "[  SKIPPED ] This device class is not supported. " << std::endl; \
58         return; \
59     }
60 
61 // The main test class for Broadcast Radio HIDL HAL.
62 class BroadcastRadioHidlTest
63     : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
64   protected:
SetUp()65     virtual void SetUp() override {
66         ASSERT_EQ(nullptr, mRadio.get());
67 
68         radioClass = RadioClassFromString(std::get<1>(GetParam()));
69 
70         skipped = false;
71 
72         sp<IBroadcastRadioFactory> factory =
73                 IBroadcastRadioFactory::getService(std::get<0>(GetParam()));
74         ASSERT_NE(nullptr, factory.get());
75 
76         Result connectResult;
77         factory->connectModule(radioClass, [&](Result ret, const sp<IBroadcastRadio>& radio) {
78             connectResult = ret;
79             mRadio = radio;
80             onCallback_l();
81         });
82         EXPECT_EQ(true, waitForCallback(kConnectCallbacktimeoutNs));
83         mCallbackCalled = false;
84 
85         if (connectResult == Result::INVALID_ARGUMENTS) {
86             skipped = true;
87             return;
88         }
89         ASSERT_EQ(connectResult, Result::OK);
90 
91         mTunerCallback = new MyCallback(this);
92         ASSERT_NE(nullptr, mRadio.get());
93         ASSERT_NE(nullptr, mTunerCallback.get());
94     }
95 
TearDown()96     virtual void TearDown() override {
97         mTuner.clear();
98         mRadio.clear();
99     }
100 
101     class MyCallback : public ITunerCallback {
102      public:
103 
104         // ITunerCallback methods (see doc in ITunerCallback.hal)
hardwareFailure()105         virtual Return<void> hardwareFailure() {
106             ALOGI("%s", __FUNCTION__);
107             mParentTest->onHwFailureCallback();
108             return Void();
109         }
110 
configChange(Result result,const BandConfig & config)111         virtual Return<void> configChange(Result result, const BandConfig& config) {
112             ALOGI("%s result %d", __FUNCTION__, result);
113             mParentTest->onConfigChangeCallback(result, config);
114             return Void();
115         }
116 
tuneComplete(Result result,const ProgramInfo & info)117         virtual Return<void> tuneComplete(Result result, const ProgramInfo& info) {
118             ALOGI("%s result %d", __FUNCTION__, result);
119             mParentTest->onTuneCompleteCallback(result, info);
120             return Void();
121         }
122 
afSwitch(const ProgramInfo & info __unused)123         virtual Return<void> afSwitch(const ProgramInfo& info __unused) {
124             return Void();
125         }
126 
antennaStateChange(bool connected)127         virtual Return<void> antennaStateChange(bool connected) {
128             ALOGI("%s connected %d", __FUNCTION__, connected);
129             return Void();
130         }
131 
trafficAnnouncement(bool active)132         virtual Return<void> trafficAnnouncement(bool active) {
133             ALOGI("%s active %d", __FUNCTION__, active);
134             return Void();
135         }
136 
emergencyAnnouncement(bool active)137         virtual Return<void> emergencyAnnouncement(bool active) {
138             ALOGI("%s active %d", __FUNCTION__, active);
139             return Void();
140         }
141 
newMetadata(uint32_t channel __unused,uint32_t subChannel __unused,const::android::hardware::hidl_vec<MetaData> & metadata __unused)142         virtual Return<void> newMetadata(uint32_t channel __unused, uint32_t subChannel __unused,
143                            const ::android::hardware::hidl_vec<MetaData>& metadata __unused) {
144             ALOGI("%s", __FUNCTION__);
145             return Void();
146         }
147 
MyCallback(BroadcastRadioHidlTest * parentTest)148                 MyCallback(BroadcastRadioHidlTest *parentTest) : mParentTest(parentTest) {}
149 
150      private:
151         // BroadcastRadioHidlTest instance to which callbacks will be notified.
152         BroadcastRadioHidlTest *mParentTest;
153     };
154 
155 
156     /**
157      * Method called by MyCallback when a callback with no status or boolean value is received
158      */
onCallback()159     void onCallback() {
160         Mutex::Autolock _l(mLock);
161         onCallback_l();
162     }
163 
164     /**
165      * Method called by MyCallback when hardwareFailure() callback is received
166      */
onHwFailureCallback()167     void onHwFailureCallback() {
168         Mutex::Autolock _l(mLock);
169         mHwFailure = true;
170         onCallback_l();
171     }
172 
173     /**
174      * Method called by MyCallback when configChange() callback is received.
175      */
onConfigChangeCallback(Result result,const BandConfig & config)176     void onConfigChangeCallback(Result result, const BandConfig& config) {
177         Mutex::Autolock _l(mLock);
178         mResultCallbackData = result;
179         mBandConfigCallbackData = config;
180         onCallback_l();
181     }
182 
183     /**
184      * Method called by MyCallback when tuneComplete() callback is received.
185      */
onTuneCompleteCallback(Result result,const ProgramInfo & info)186     void onTuneCompleteCallback(Result result, const ProgramInfo& info) {
187         Mutex::Autolock _l(mLock);
188         mResultCallbackData = result;
189         mProgramInfoCallbackData = info;
190         onCallback_l();
191     }
192 
193     /**
194      * Method called by MyCallback when a boolean indication is received
195      */
onBoolCallback(bool result)196     void onBoolCallback(bool result) {
197         Mutex::Autolock _l(mLock);
198         mBoolCallbackData = result;
199         onCallback_l();
200     }
201 
202 
BroadcastRadioHidlTest()203     BroadcastRadioHidlTest()
204         : mCallbackCalled(false), mBoolCallbackData(false), mResultCallbackData(Result::OK),
205         mHwFailure(false) {}
206 
onCallback_l()207     void onCallback_l() {
208         if (!mCallbackCalled) {
209             mCallbackCalled = true;
210             mCallbackCond.broadcast();
211         }
212     }
213 
214 
waitForCallback(nsecs_t reltime=0)215     bool waitForCallback(nsecs_t reltime = 0) {
216         Mutex::Autolock _l(mLock);
217         nsecs_t endTime = systemTime() + reltime;
218         while (!mCallbackCalled) {
219             if (reltime == 0) {
220                 mCallbackCond.wait(mLock);
221             } else {
222                 nsecs_t now = systemTime();
223                 if (now > endTime) {
224                     return false;
225                 }
226                 mCallbackCond.waitRelative(mLock, endTime - now);
227             }
228         }
229         return true;
230     }
231 
232     bool getProperties();
233     bool openTuner();
234     bool checkAntenna();
235 
236     /**
237      * Retrieves AM/FM band configuration from module properties.
238      *
239      * The configuration may not exist: if radio type is other than AM/FM
240      * or provided index is out of bounds.
241      * In such case, empty configuration is returned.
242      *
243      * @param idx Band index to retrieve.
244      * @return Band configuration reference.
245      */
246     const BandConfig& getBand(unsigned idx);
247 
248     static const nsecs_t kConnectCallbacktimeoutNs = seconds_to_nanoseconds(1);
249     static const nsecs_t kConfigCallbacktimeoutNs = seconds_to_nanoseconds(10);
250     static const nsecs_t kTuneCallbacktimeoutNs = seconds_to_nanoseconds(30);
251 
252     Class radioClass;
253     bool skipped;
254     sp<IBroadcastRadio> mRadio;
255     Properties mHalProperties;
256     bool mHalPropertiesInitialized = false;
257     sp<ITuner> mTuner;
258     sp<MyCallback> mTunerCallback;
259     Mutex mLock;
260     Condition mCallbackCond;
261     bool mCallbackCalled;
262     bool mBoolCallbackData;
263     Result mResultCallbackData;
264     ProgramInfo mProgramInfoCallbackData;
265     BandConfig mBandConfigCallbackData;
266     bool mHwFailure;
267 };
268 
269 namespace android {
270 namespace hardware {
271 namespace broadcastradio {
272 namespace V1_0 {
273 
274 /**
275  * Compares two BandConfig objects for testing purposes.
276  */
operator ==(const BandConfig & l,const BandConfig & r)277 static bool operator==(const BandConfig& l, const BandConfig& r) {
278     if (l.type != r.type) return false;
279     if (l.antennaConnected != r.antennaConnected) return false;
280     if (l.lowerLimit != r.lowerLimit) return false;
281     if (l.upperLimit != r.upperLimit) return false;
282     if (l.spacings != r.spacings) return false;
283     if (l.type == Band::AM || l.type == Band::AM_HD) {
284         return l.ext.am == r.ext.am;
285     } else if (l.type == Band::FM || l.type == Band::FM_HD) {
286         return l.ext.fm == r.ext.fm;
287     } else {
288         // unsupported type
289         return false;
290     }
291 }
292 
293 }  // V1_0
294 }  // broadcastradio
295 }  // hardware
296 }  // android
297 
getProperties()298 bool BroadcastRadioHidlTest::getProperties()
299 {
300     if (mHalPropertiesInitialized) return true;
301 
302     Result halResult = Result::NOT_INITIALIZED;
303     auto hidlReturn = mRadio->getProperties([&](Result result, const Properties& properties) {
304         halResult = result;
305         if (result == Result::OK) {
306             mHalProperties = properties;
307         }
308     });
309 
310     EXPECT_TRUE(hidlReturn.isOk());
311     EXPECT_EQ(Result::OK, halResult);
312     EXPECT_EQ(radioClass, mHalProperties.classId);
313     EXPECT_GT(mHalProperties.numTuners, 0u);
314     if (radioClass == Class::AM_FM) {
315         EXPECT_GT(mHalProperties.bands.size(), 0u);
316     }
317 
318     if (hidlReturn.isOk() && halResult == Result::OK) {
319         mHalPropertiesInitialized = true;
320         return true;
321     }
322     return false;
323 }
324 
openTuner()325 bool BroadcastRadioHidlTest::openTuner()
326 {
327     if (!getProperties()) {
328         return false;
329     }
330     if (mTuner.get() == nullptr) {
331         Result halResult = Result::NOT_INITIALIZED;
332         auto openCb = [&](Result result, const sp<ITuner>& tuner) {
333             halResult = result;
334             if (result == Result::OK) {
335                 mTuner = tuner;
336             }
337         };
338         auto hidlReturn = mRadio->openTuner(getBand(0), true, mTunerCallback, openCb);
339         EXPECT_TRUE(hidlReturn.isOk());
340         EXPECT_EQ(Result::OK, halResult);
341         if (radioClass == Class::AM_FM) {
342             EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
343         }
344     }
345     EXPECT_NE(nullptr, mTuner.get());
346     return nullptr != mTuner.get();
347 }
348 
checkAntenna()349 bool BroadcastRadioHidlTest::checkAntenna()
350 {
351     if (radioClass != Class::AM_FM) return true;
352 
353     BandConfig halConfig;
354     Result halResult = Result::NOT_INITIALIZED;
355     Return<void> hidlReturn =
356             mTuner->getConfiguration([&](Result result, const BandConfig& config) {
357                 halResult = result;
358                 if (result == Result::OK) {
359                     halConfig = config;
360                 }
361             });
362 
363     return ((halResult == Result::OK) && (halConfig.antennaConnected == true));
364 }
365 
getBand(unsigned idx)366 const BandConfig& BroadcastRadioHidlTest::getBand(unsigned idx) {
367     static BandConfig dummyBandConfig = {};
368     if (radioClass == Class::AM_FM) {
369         EXPECT_GT(mHalProperties.bands.size(), idx);
370         if (mHalProperties.bands.size() > idx) {
371             return mHalProperties.bands[idx];
372         } else {
373             return dummyBandConfig;
374         }
375     } else {
376         return dummyBandConfig;
377     }
378 }
379 
380 /**
381  * Test IBroadcastRadio::getProperties() method
382  *
383  * Verifies that:
384  *  - the HAL implements the method
385  *  - the method returns 0 (no error)
386  *  - the implementation class is radioClass
387  *  - the implementation supports at least one tuner
388  *  - the implementation supports at one band
389  */
TEST_P(BroadcastRadioHidlTest,GetProperties)390 TEST_P(BroadcastRadioHidlTest, GetProperties) {
391     RETURN_IF_SKIPPED;
392     EXPECT_EQ(true, getProperties());
393 }
394 
395 /**
396  * Test IBroadcastRadio::openTuner() method
397  *
398  * Verifies that:
399  *  - the HAL implements the method
400  *  - the method returns 0 (no error) and a valid ITuner interface
401  */
TEST_P(BroadcastRadioHidlTest,OpenTuner)402 TEST_P(BroadcastRadioHidlTest, OpenTuner) {
403     RETURN_IF_SKIPPED;
404     EXPECT_EQ(true, openTuner());
405 }
406 
407 /**
408  * Test IBroadcastRadio::openTuner() after ITuner disposal.
409  *
410  * Verifies that:
411  *  - ITuner destruction gets propagated through HAL
412  *  - the openTuner method works well when called for the second time
413  */
TEST_P(BroadcastRadioHidlTest,ReopenTuner)414 TEST_P(BroadcastRadioHidlTest, ReopenTuner) {
415     RETURN_IF_SKIPPED;
416     EXPECT_TRUE(openTuner());
417     mTuner.clear();
418     EXPECT_TRUE(openTuner());
419 }
420 
421 /**
422  * Test IBroadcastRadio::openTuner() method called twice.
423  *
424  * Verifies that:
425  *  - the openTuner method fails with INVALID_STATE or succeeds when called for the second time
426  *    without deleting previous ITuner instance
427  */
TEST_P(BroadcastRadioHidlTest,OpenTunerTwice)428 TEST_P(BroadcastRadioHidlTest, OpenTunerTwice) {
429     RETURN_IF_SKIPPED;
430     EXPECT_TRUE(openTuner());
431 
432     Result halResult = Result::NOT_INITIALIZED;
433     auto openCb = [&](Result result, const sp<ITuner>&) { halResult = result; };
434     auto hidlReturn = mRadio->openTuner(getBand(0), true, mTunerCallback, openCb);
435     EXPECT_TRUE(hidlReturn.isOk());
436     if (halResult == Result::OK) {
437         if (radioClass == Class::AM_FM) {
438             EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
439         }
440     } else {
441         EXPECT_EQ(Result::INVALID_STATE, halResult);
442     }
443 }
444 
445 /**
446  * Test ITuner::setConfiguration() and getConfiguration methods
447  *
448  * Verifies that:
449  *  - the HAL implements both methods
450  *  - the methods return 0 (no error)
451  *  - the configuration callback is received within kConfigCallbacktimeoutNs ns
452  *  - the configuration read back from HAl has the same class Id
453  *
454  * Skipped for other radio classes than AM/FM, because setConfiguration
455  * applies only for these bands.
456  */
TEST_P(BroadcastRadioHidlTest,SetAndGetConfiguration)457 TEST_P(BroadcastRadioHidlTest, SetAndGetConfiguration) {
458     if (radioClass != Class::AM_FM) skipped = true;
459     RETURN_IF_SKIPPED;
460     ASSERT_EQ(true, openTuner());
461     // test setConfiguration
462     mCallbackCalled = false;
463     Return<Result> hidlResult = mTuner->setConfiguration(getBand(1));
464     EXPECT_TRUE(hidlResult.isOk());
465     EXPECT_EQ(Result::OK, hidlResult);
466     EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
467     EXPECT_EQ(Result::OK, mResultCallbackData);
468     EXPECT_EQ(getBand(1), mBandConfigCallbackData);
469 
470     // test getConfiguration
471     BandConfig halConfig;
472     Result halResult;
473     Return<void> hidlReturn =
474             mTuner->getConfiguration([&](Result result, const BandConfig& config) {
475                 halResult = result;
476                 if (result == Result::OK) {
477                     halConfig = config;
478                 }
479             });
480     EXPECT_TRUE(hidlReturn.isOk());
481     EXPECT_EQ(Result::OK, halResult);
482     EXPECT_EQ(getBand(1), halConfig);
483 }
484 
485 /**
486  * Test ITuner::setConfiguration() with invalid arguments.
487  *
488  * Verifies that:
489  *  - the methods returns INVALID_ARGUMENTS on invalid arguments
490  *  - the method recovers and succeeds after passing correct arguments
491  *
492  * Skipped for other radio classes than AM/FM, because setConfiguration
493  * applies only for these bands.
494  */
TEST_P(BroadcastRadioHidlTest,SetConfigurationFails)495 TEST_P(BroadcastRadioHidlTest, SetConfigurationFails) {
496     if (radioClass != Class::AM_FM) skipped = true;
497     RETURN_IF_SKIPPED;
498     ASSERT_EQ(true, openTuner());
499 
500     // Let's define a config that's bad for sure.
501     BandConfig badConfig = {};
502     badConfig.type = Band::FM;
503     badConfig.lowerLimit = 0xFFFFFFFF;
504     badConfig.upperLimit = 0;
505     badConfig.spacings = (std::vector<uint32_t>){ 0 };
506 
507     // Test setConfiguration failing on bad data.
508     mCallbackCalled = false;
509     auto setResult = mTuner->setConfiguration(badConfig);
510     EXPECT_TRUE(setResult.isOk());
511     EXPECT_EQ(Result::INVALID_ARGUMENTS, setResult);
512 
513     // Test setConfiguration recovering after passing good data.
514     mCallbackCalled = false;
515     setResult = mTuner->setConfiguration(getBand(0));
516     EXPECT_TRUE(setResult.isOk());
517     EXPECT_EQ(Result::OK, setResult);
518     EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
519     EXPECT_EQ(Result::OK, mResultCallbackData);
520 }
521 
522 /**
523  * Test ITuner::scan
524  *
525  * Verifies that:
526  *  - the HAL implements the method
527  *  - the method returns 0 (no error)
528  *  - the tuned callback is received within kTuneCallbacktimeoutNs ns
529  *  - skipping sub-channel or not does not fail the call
530  */
TEST_P(BroadcastRadioHidlTest,Scan)531 TEST_P(BroadcastRadioHidlTest, Scan) {
532     RETURN_IF_SKIPPED;
533     ASSERT_EQ(true, openTuner());
534     ASSERT_TRUE(checkAntenna());
535     // test scan UP
536     mCallbackCalled = false;
537     Return<Result> hidlResult = mTuner->scan(Direction::UP, true);
538     EXPECT_TRUE(hidlResult.isOk());
539     EXPECT_EQ(Result::OK, hidlResult);
540     EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
541 
542     // test scan DOWN
543     mCallbackCalled = false;
544     hidlResult = mTuner->scan(Direction::DOWN, false);
545     EXPECT_TRUE(hidlResult.isOk());
546     EXPECT_EQ(Result::OK, hidlResult);
547     EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
548 }
549 
550 /**
551  * Test ITuner::step
552  *
553  * Verifies that:
554  *  - the HAL implements the method
555  *  - the method returns 0 (no error)
556  *  - the tuned callback is received within kTuneCallbacktimeoutNs ns
557  *  - skipping sub-channel or not does not fail the call
558  *
559  * Skipped for other radio classes than AM/FM, because step is not possible
560  * on DAB nor satellite.
561  */
TEST_P(BroadcastRadioHidlTest,Step)562 TEST_P(BroadcastRadioHidlTest, Step) {
563     if (radioClass != Class::AM_FM) skipped = true;
564     RETURN_IF_SKIPPED;
565     ASSERT_EQ(true, openTuner());
566     ASSERT_TRUE(checkAntenna());
567     // test step UP
568     mCallbackCalled = false;
569     Return<Result> hidlResult = mTuner->step(Direction::UP, false);
570     EXPECT_TRUE(hidlResult.isOk());
571     EXPECT_EQ(Result::OK, hidlResult);
572     EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
573 
574     // test step DOWN
575     mCallbackCalled = false;
576     hidlResult = mTuner->step(Direction::DOWN, true);
577     EXPECT_TRUE(hidlResult.isOk());
578     EXPECT_EQ(Result::OK, hidlResult);
579     EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
580 }
581 
582 /**
583  * Test ITuner::tune,  getProgramInformation and cancel methods
584  *
585  * Verifies that:
586  *  - the HAL implements the methods
587  *  - the methods return 0 (no error)
588  *  - the tuned callback is received within kTuneCallbacktimeoutNs ns after tune()
589  *
590  * Skipped for other radio classes than AM/FM, because tune to frequency
591  * is not possible on DAB nor satellite.
592  */
TEST_P(BroadcastRadioHidlTest,TuneAndGetProgramInformationAndCancel)593 TEST_P(BroadcastRadioHidlTest, TuneAndGetProgramInformationAndCancel) {
594     if (radioClass != Class::AM_FM) skipped = true;
595     RETURN_IF_SKIPPED;
596     ASSERT_EQ(true, openTuner());
597     ASSERT_TRUE(checkAntenna());
598 
599     auto& band = getBand(0);
600 
601     // test tune
602     ASSERT_GT(band.spacings.size(), 0u);
603     ASSERT_GT(band.upperLimit, band.lowerLimit);
604 
605     // test scan UP
606     uint32_t lowerLimit = band.lowerLimit;
607     uint32_t upperLimit = band.upperLimit;
608     uint32_t spacing = band.spacings[0];
609 
610     uint32_t channel =
611             lowerLimit + (((upperLimit - lowerLimit) / 2 + spacing - 1) / spacing) * spacing;
612     mCallbackCalled = false;
613     mResultCallbackData = Result::NOT_INITIALIZED;
614     Return<Result> hidlResult = mTuner->tune(channel, 0);
615     EXPECT_TRUE(hidlResult.isOk());
616     EXPECT_EQ(Result::OK, hidlResult);
617     EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
618     EXPECT_EQ(channel, mProgramInfoCallbackData.channel);
619 
620     // test getProgramInformation
621     ProgramInfo halInfo;
622     Result halResult = Result::NOT_INITIALIZED;
623     Return<void> hidlReturn = mTuner->getProgramInformation(
624         [&](Result result, const ProgramInfo& info) {
625             halResult = result;
626             if (result == Result::OK) {
627                 halInfo = info;
628             }
629         });
630     EXPECT_TRUE(hidlReturn.isOk());
631     EXPECT_EQ(Result::OK, halResult);
632     if (mResultCallbackData == Result::OK) {
633         EXPECT_LE(halInfo.channel, upperLimit);
634         EXPECT_GE(halInfo.channel, lowerLimit);
635     }
636 
637     // test cancel
638     mTuner->tune(lowerLimit, 0);
639     hidlResult = mTuner->cancel();
640     EXPECT_TRUE(hidlResult.isOk());
641     EXPECT_EQ(Result::OK, hidlResult);
642 }
643 
644 /**
645  * Test ITuner::tune failing when channel out of the range is provided.
646  *
647  * Verifies that:
648  *  - the method returns INVALID_ARGUMENTS when applicable
649  *  - the method recovers and succeeds after passing correct arguments
650  *
651  * Skipped for other radio classes than AM/FM, because tune to frequency
652  * is not possible on DAB nor satellite.
653  */
TEST_P(BroadcastRadioHidlTest,TuneFailsOutOfBounds)654 TEST_P(BroadcastRadioHidlTest, TuneFailsOutOfBounds) {
655     if (radioClass != Class::AM_FM) skipped = true;
656     RETURN_IF_SKIPPED;
657     ASSERT_TRUE(openTuner());
658     ASSERT_TRUE(checkAntenna());
659 
660     // get current channel bounds
661     BandConfig halConfig;
662     Result halResult;
663     auto configResult = mTuner->getConfiguration([&](Result result, const BandConfig& config) {
664         halResult = result;
665         halConfig = config;
666     });
667     ASSERT_TRUE(configResult.isOk());
668     ASSERT_EQ(Result::OK, halResult);
669 
670     // try to tune slightly above the limit and expect to fail
671     auto badChannel = halConfig.upperLimit + halConfig.spacings[0];
672     auto tuneResult = mTuner->tune(badChannel, 0);
673     EXPECT_TRUE(tuneResult.isOk());
674     EXPECT_EQ(Result::INVALID_ARGUMENTS, tuneResult);
675     EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
676 
677     // tuning exactly at the limit should succeed
678     auto goodChannel = halConfig.upperLimit;
679     tuneResult = mTuner->tune(goodChannel, 0);
680     EXPECT_TRUE(tuneResult.isOk());
681     EXPECT_EQ(Result::OK, tuneResult);
682     EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
683 }
684 
685 /**
686  * Test proper image format in metadata.
687  *
688  * Verifies that:
689  * - all images in metadata are provided in-band (as a binary blob, not by id)
690  *
691  * This is a counter-test for OobImagesOnly from 1.1 VTS.
692  */
TEST_P(BroadcastRadioHidlTest,IbImagesOnly)693 TEST_P(BroadcastRadioHidlTest, IbImagesOnly) {
694     RETURN_IF_SKIPPED;
695     ASSERT_TRUE(openTuner());
696     ASSERT_TRUE(checkAntenna());
697 
698     bool firstScan = true;
699     uint32_t firstChannel, prevChannel;
700     while (true) {
701         mCallbackCalled = false;
702         auto hidlResult = mTuner->scan(Direction::UP, true);
703         ASSERT_TRUE(hidlResult.isOk());
704         if (hidlResult == Result::TIMEOUT) {
705             ALOGI("Got timeout on scan operation");
706             break;
707         }
708         ASSERT_EQ(Result::OK, hidlResult);
709         ASSERT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
710 
711         if (firstScan) {
712             firstScan = false;
713             firstChannel = mProgramInfoCallbackData.channel;
714         } else {
715             // scanned the whole band
716             if (mProgramInfoCallbackData.channel >= firstChannel && prevChannel <= firstChannel) {
717                 break;
718             }
719         }
720         prevChannel = mProgramInfoCallbackData.channel;
721 
722         for (auto&& entry : mProgramInfoCallbackData.metadata) {
723             if (entry.key != MetadataKey::ICON && entry.key != MetadataKey::ART) continue;
724             EXPECT_EQ(MetadataType::RAW, entry.type);
725             EXPECT_EQ(0, entry.intValue);
726             EXPECT_GT(entry.rawValue.size(), 0u);
727         }
728     }
729 }
730 
731 INSTANTIATE_TEST_CASE_P(
732         PerInstance, BroadcastRadioHidlTest,
733         testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
734                                  IBroadcastRadioFactory::descriptor)),
735                          ::testing::Values("AM_FM", "SAT", "DT")),
736         android::hardware::PrintInstanceTupleNameToString<>);