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