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