| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "BroadcastRadioHidlHalTest" |
| #include <android-base/logging.h> |
| #include <cutils/native_handle.h> |
| #include <cutils/properties.h> |
| #include <gtest/gtest.h> |
| #include <hidl/GtestPrinter.h> |
| #include <hidl/HidlTransportSupport.h> |
| #include <hidl/ServiceManagement.h> |
| #include <utils/threads.h> |
| |
| #include <android/hardware/broadcastradio/1.0/IBroadcastRadio.h> |
| #include <android/hardware/broadcastradio/1.0/IBroadcastRadioFactory.h> |
| #include <android/hardware/broadcastradio/1.0/ITuner.h> |
| #include <android/hardware/broadcastradio/1.0/ITunerCallback.h> |
| #include <android/hardware/broadcastradio/1.0/types.h> |
| #include <broadcastradio-vts-utils/hal-1.x-enum-utils.h> |
| |
| using ::android::Condition; |
| using ::android::Mutex; |
| using ::android::sp; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::hardware::broadcastradio::V1_0::Band; |
| using ::android::hardware::broadcastradio::V1_0::BandConfig; |
| using ::android::hardware::broadcastradio::V1_0::Class; |
| using ::android::hardware::broadcastradio::V1_0::Direction; |
| using ::android::hardware::broadcastradio::V1_0::IBroadcastRadio; |
| using ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory; |
| using ::android::hardware::broadcastradio::V1_0::ITuner; |
| using ::android::hardware::broadcastradio::V1_0::ITunerCallback; |
| using ::android::hardware::broadcastradio::V1_0::MetaData; |
| using ::android::hardware::broadcastradio::V1_0::MetadataKey; |
| using ::android::hardware::broadcastradio::V1_0::MetadataType; |
| using ::android::hardware::broadcastradio::V1_0::ProgramInfo; |
| using ::android::hardware::broadcastradio::V1_0::Properties; |
| using ::android::hardware::broadcastradio::V1_0::Result; |
| using ::android::hardware::broadcastradio::V1_0::vts::RadioClassFromString; |
| |
| #define RETURN_IF_SKIPPED \ |
| if (skipped) { \ |
| std::cout << "[ SKIPPED ] This device class is not supported. " << std::endl; \ |
| return; \ |
| } |
| |
| // The main test class for Broadcast Radio HIDL HAL. |
| class BroadcastRadioHidlTest |
| : public ::testing::TestWithParam<std::tuple<std::string, std::string>> { |
| protected: |
| virtual void SetUp() override { |
| ASSERT_EQ(nullptr, mRadio.get()); |
| |
| radioClass = RadioClassFromString(std::get<1>(GetParam())); |
| |
| skipped = false; |
| |
| sp<IBroadcastRadioFactory> factory = |
| IBroadcastRadioFactory::getService(std::get<0>(GetParam())); |
| ASSERT_NE(nullptr, factory.get()); |
| |
| Result connectResult; |
| factory->connectModule(radioClass, [&](Result ret, const sp<IBroadcastRadio>& radio) { |
| connectResult = ret; |
| mRadio = radio; |
| onCallback_l(); |
| }); |
| EXPECT_EQ(true, waitForCallback(kConnectCallbacktimeoutNs)); |
| mCallbackCalled = false; |
| |
| if (connectResult == Result::INVALID_ARGUMENTS) { |
| skipped = true; |
| return; |
| } |
| ASSERT_EQ(connectResult, Result::OK); |
| |
| mTunerCallback = new MyCallback(this); |
| ASSERT_NE(nullptr, mRadio.get()); |
| ASSERT_NE(nullptr, mTunerCallback.get()); |
| } |
| |
| virtual void TearDown() override { |
| mTuner.clear(); |
| mRadio.clear(); |
| } |
| |
| class MyCallback : public ITunerCallback { |
| public: |
| |
| // ITunerCallback methods (see doc in ITunerCallback.hal) |
| virtual Return<void> hardwareFailure() { |
| ALOGI("%s", __FUNCTION__); |
| mParentTest->onHwFailureCallback(); |
| return Void(); |
| } |
| |
| virtual Return<void> configChange(Result result, const BandConfig& config) { |
| ALOGI("%s result %d", __FUNCTION__, result); |
| mParentTest->onConfigChangeCallback(result, config); |
| return Void(); |
| } |
| |
| virtual Return<void> tuneComplete(Result result, const ProgramInfo& info) { |
| ALOGI("%s result %d", __FUNCTION__, result); |
| mParentTest->onTuneCompleteCallback(result, info); |
| return Void(); |
| } |
| |
| virtual Return<void> afSwitch(const ProgramInfo& info __unused) { |
| return Void(); |
| } |
| |
| virtual Return<void> antennaStateChange(bool connected) { |
| ALOGI("%s connected %d", __FUNCTION__, connected); |
| return Void(); |
| } |
| |
| virtual Return<void> trafficAnnouncement(bool active) { |
| ALOGI("%s active %d", __FUNCTION__, active); |
| return Void(); |
| } |
| |
| virtual Return<void> emergencyAnnouncement(bool active) { |
| ALOGI("%s active %d", __FUNCTION__, active); |
| return Void(); |
| } |
| |
| virtual Return<void> newMetadata(uint32_t channel __unused, uint32_t subChannel __unused, |
| const ::android::hardware::hidl_vec<MetaData>& metadata __unused) { |
| ALOGI("%s", __FUNCTION__); |
| return Void(); |
| } |
| |
| MyCallback(BroadcastRadioHidlTest *parentTest) : mParentTest(parentTest) {} |
| |
| private: |
| // BroadcastRadioHidlTest instance to which callbacks will be notified. |
| BroadcastRadioHidlTest *mParentTest; |
| }; |
| |
| |
| /** |
| * Method called by MyCallback when a callback with no status or boolean value is received |
| */ |
| void onCallback() { |
| Mutex::Autolock _l(mLock); |
| onCallback_l(); |
| } |
| |
| /** |
| * Method called by MyCallback when hardwareFailure() callback is received |
| */ |
| void onHwFailureCallback() { |
| Mutex::Autolock _l(mLock); |
| mHwFailure = true; |
| onCallback_l(); |
| } |
| |
| /** |
| * Method called by MyCallback when configChange() callback is received. |
| */ |
| void onConfigChangeCallback(Result result, const BandConfig& config) { |
| Mutex::Autolock _l(mLock); |
| mResultCallbackData = result; |
| mBandConfigCallbackData = config; |
| onCallback_l(); |
| } |
| |
| /** |
| * Method called by MyCallback when tuneComplete() callback is received. |
| */ |
| void onTuneCompleteCallback(Result result, const ProgramInfo& info) { |
| Mutex::Autolock _l(mLock); |
| mResultCallbackData = result; |
| mProgramInfoCallbackData = info; |
| onCallback_l(); |
| } |
| |
| /** |
| * Method called by MyCallback when a boolean indication is received |
| */ |
| void onBoolCallback(bool result) { |
| Mutex::Autolock _l(mLock); |
| mBoolCallbackData = result; |
| onCallback_l(); |
| } |
| |
| |
| BroadcastRadioHidlTest() |
| : mCallbackCalled(false), mBoolCallbackData(false), mResultCallbackData(Result::OK), |
| mHwFailure(false) {} |
| |
| void onCallback_l() { |
| if (!mCallbackCalled) { |
| mCallbackCalled = true; |
| mCallbackCond.broadcast(); |
| } |
| } |
| |
| |
| bool waitForCallback(nsecs_t reltime = 0) { |
| Mutex::Autolock _l(mLock); |
| nsecs_t endTime = systemTime() + reltime; |
| while (!mCallbackCalled) { |
| if (reltime == 0) { |
| mCallbackCond.wait(mLock); |
| } else { |
| nsecs_t now = systemTime(); |
| if (now > endTime) { |
| return false; |
| } |
| mCallbackCond.waitRelative(mLock, endTime - now); |
| } |
| } |
| return true; |
| } |
| |
| bool getProperties(); |
| bool openTuner(); |
| bool checkAntenna(); |
| |
| /** |
| * Retrieves AM/FM band configuration from module properties. |
| * |
| * The configuration may not exist: if radio type is other than AM/FM |
| * or provided index is out of bounds. |
| * In such case, empty configuration is returned. |
| * |
| * @param idx Band index to retrieve. |
| * @return Band configuration reference. |
| */ |
| const BandConfig& getBand(unsigned idx); |
| |
| static const nsecs_t kConnectCallbacktimeoutNs = seconds_to_nanoseconds(1); |
| static const nsecs_t kConfigCallbacktimeoutNs = seconds_to_nanoseconds(10); |
| static const nsecs_t kTuneCallbacktimeoutNs = seconds_to_nanoseconds(30); |
| |
| Class radioClass; |
| bool skipped; |
| sp<IBroadcastRadio> mRadio; |
| Properties mHalProperties; |
| bool mHalPropertiesInitialized = false; |
| sp<ITuner> mTuner; |
| sp<MyCallback> mTunerCallback; |
| Mutex mLock; |
| Condition mCallbackCond; |
| bool mCallbackCalled; |
| bool mBoolCallbackData; |
| Result mResultCallbackData; |
| ProgramInfo mProgramInfoCallbackData; |
| BandConfig mBandConfigCallbackData; |
| bool mHwFailure; |
| }; |
| |
| namespace android { |
| namespace hardware { |
| namespace broadcastradio { |
| namespace V1_0 { |
| |
| /** |
| * Compares two BandConfig objects for testing purposes. |
| */ |
| static bool operator==(const BandConfig& l, const BandConfig& r) { |
| if (l.type != r.type) return false; |
| if (l.antennaConnected != r.antennaConnected) return false; |
| if (l.lowerLimit != r.lowerLimit) return false; |
| if (l.upperLimit != r.upperLimit) return false; |
| if (l.spacings != r.spacings) return false; |
| if (l.type == Band::AM || l.type == Band::AM_HD) { |
| return l.ext.am == r.ext.am; |
| } else if (l.type == Band::FM || l.type == Band::FM_HD) { |
| return l.ext.fm == r.ext.fm; |
| } else { |
| // unsupported type |
| return false; |
| } |
| } |
| |
| } // V1_0 |
| } // broadcastradio |
| } // hardware |
| } // android |
| |
| bool BroadcastRadioHidlTest::getProperties() |
| { |
| if (mHalPropertiesInitialized) return true; |
| |
| Result halResult = Result::NOT_INITIALIZED; |
| auto hidlReturn = mRadio->getProperties([&](Result result, const Properties& properties) { |
| halResult = result; |
| if (result == Result::OK) { |
| mHalProperties = properties; |
| } |
| }); |
| |
| EXPECT_TRUE(hidlReturn.isOk()); |
| EXPECT_EQ(Result::OK, halResult); |
| EXPECT_EQ(radioClass, mHalProperties.classId); |
| EXPECT_GT(mHalProperties.numTuners, 0u); |
| if (radioClass == Class::AM_FM) { |
| EXPECT_GT(mHalProperties.bands.size(), 0u); |
| } |
| |
| if (hidlReturn.isOk() && halResult == Result::OK) { |
| mHalPropertiesInitialized = true; |
| return true; |
| } |
| return false; |
| } |
| |
| bool BroadcastRadioHidlTest::openTuner() |
| { |
| if (!getProperties()) { |
| return false; |
| } |
| if (mTuner.get() == nullptr) { |
| Result halResult = Result::NOT_INITIALIZED; |
| auto openCb = [&](Result result, const sp<ITuner>& tuner) { |
| halResult = result; |
| if (result == Result::OK) { |
| mTuner = tuner; |
| } |
| }; |
| auto hidlReturn = mRadio->openTuner(getBand(0), true, mTunerCallback, openCb); |
| EXPECT_TRUE(hidlReturn.isOk()); |
| EXPECT_EQ(Result::OK, halResult); |
| if (radioClass == Class::AM_FM) { |
| EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs)); |
| } |
| } |
| EXPECT_NE(nullptr, mTuner.get()); |
| return nullptr != mTuner.get(); |
| } |
| |
| bool BroadcastRadioHidlTest::checkAntenna() |
| { |
| if (radioClass != Class::AM_FM) return true; |
| |
| BandConfig halConfig; |
| Result halResult = Result::NOT_INITIALIZED; |
| Return<void> hidlReturn = |
| mTuner->getConfiguration([&](Result result, const BandConfig& config) { |
| halResult = result; |
| if (result == Result::OK) { |
| halConfig = config; |
| } |
| }); |
| |
| return ((halResult == Result::OK) && (halConfig.antennaConnected == true)); |
| } |
| |
| const BandConfig& BroadcastRadioHidlTest::getBand(unsigned idx) { |
| static BandConfig dummyBandConfig = {}; |
| if (radioClass == Class::AM_FM) { |
| EXPECT_GT(mHalProperties.bands.size(), idx); |
| if (mHalProperties.bands.size() > idx) { |
| return mHalProperties.bands[idx]; |
| } else { |
| return dummyBandConfig; |
| } |
| } else { |
| return dummyBandConfig; |
| } |
| } |
| |
| /** |
| * Test IBroadcastRadio::getProperties() method |
| * |
| * Verifies that: |
| * - the HAL implements the method |
| * - the method returns 0 (no error) |
| * - the implementation class is radioClass |
| * - the implementation supports at least one tuner |
| * - the implementation supports at one band |
| */ |
| TEST_P(BroadcastRadioHidlTest, GetProperties) { |
| RETURN_IF_SKIPPED; |
| EXPECT_EQ(true, getProperties()); |
| } |
| |
| /** |
| * Test IBroadcastRadio::openTuner() method |
| * |
| * Verifies that: |
| * - the HAL implements the method |
| * - the method returns 0 (no error) and a valid ITuner interface |
| */ |
| TEST_P(BroadcastRadioHidlTest, OpenTuner) { |
| RETURN_IF_SKIPPED; |
| EXPECT_EQ(true, openTuner()); |
| } |
| |
| /** |
| * Test IBroadcastRadio::openTuner() after ITuner disposal. |
| * |
| * Verifies that: |
| * - ITuner destruction gets propagated through HAL |
| * - the openTuner method works well when called for the second time |
| */ |
| TEST_P(BroadcastRadioHidlTest, ReopenTuner) { |
| RETURN_IF_SKIPPED; |
| EXPECT_TRUE(openTuner()); |
| mTuner.clear(); |
| EXPECT_TRUE(openTuner()); |
| } |
| |
| /** |
| * Test IBroadcastRadio::openTuner() method called twice. |
| * |
| * Verifies that: |
| * - the openTuner method fails with INVALID_STATE or succeeds when called for the second time |
| * without deleting previous ITuner instance |
| */ |
| TEST_P(BroadcastRadioHidlTest, OpenTunerTwice) { |
| RETURN_IF_SKIPPED; |
| EXPECT_TRUE(openTuner()); |
| |
| Result halResult = Result::NOT_INITIALIZED; |
| auto openCb = [&](Result result, const sp<ITuner>&) { halResult = result; }; |
| auto hidlReturn = mRadio->openTuner(getBand(0), true, mTunerCallback, openCb); |
| EXPECT_TRUE(hidlReturn.isOk()); |
| if (halResult == Result::OK) { |
| if (radioClass == Class::AM_FM) { |
| EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs)); |
| } |
| } else { |
| EXPECT_EQ(Result::INVALID_STATE, halResult); |
| } |
| } |
| |
| /** |
| * Test ITuner::setConfiguration() and getConfiguration methods |
| * |
| * Verifies that: |
| * - the HAL implements both methods |
| * - the methods return 0 (no error) |
| * - the configuration callback is received within kConfigCallbacktimeoutNs ns |
| * - the configuration read back from HAl has the same class Id |
| * |
| * Skipped for other radio classes than AM/FM, because setConfiguration |
| * applies only for these bands. |
| */ |
| TEST_P(BroadcastRadioHidlTest, SetAndGetConfiguration) { |
| if (radioClass != Class::AM_FM) skipped = true; |
| RETURN_IF_SKIPPED; |
| ASSERT_EQ(true, openTuner()); |
| // test setConfiguration |
| mCallbackCalled = false; |
| Return<Result> hidlResult = mTuner->setConfiguration(getBand(1)); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs)); |
| EXPECT_EQ(Result::OK, mResultCallbackData); |
| EXPECT_EQ(getBand(1), mBandConfigCallbackData); |
| |
| // test getConfiguration |
| BandConfig halConfig; |
| Result halResult; |
| Return<void> hidlReturn = |
| mTuner->getConfiguration([&](Result result, const BandConfig& config) { |
| halResult = result; |
| if (result == Result::OK) { |
| halConfig = config; |
| } |
| }); |
| EXPECT_TRUE(hidlReturn.isOk()); |
| EXPECT_EQ(Result::OK, halResult); |
| EXPECT_EQ(getBand(1), halConfig); |
| } |
| |
| /** |
| * Test ITuner::setConfiguration() with invalid arguments. |
| * |
| * Verifies that: |
| * - the methods returns INVALID_ARGUMENTS on invalid arguments |
| * - the method recovers and succeeds after passing correct arguments |
| * |
| * Skipped for other radio classes than AM/FM, because setConfiguration |
| * applies only for these bands. |
| */ |
| TEST_P(BroadcastRadioHidlTest, SetConfigurationFails) { |
| if (radioClass != Class::AM_FM) skipped = true; |
| RETURN_IF_SKIPPED; |
| ASSERT_EQ(true, openTuner()); |
| |
| // Let's define a config that's bad for sure. |
| BandConfig badConfig = {}; |
| badConfig.type = Band::FM; |
| badConfig.lowerLimit = 0xFFFFFFFF; |
| badConfig.upperLimit = 0; |
| badConfig.spacings = (std::vector<uint32_t>){ 0 }; |
| |
| // Test setConfiguration failing on bad data. |
| mCallbackCalled = false; |
| auto setResult = mTuner->setConfiguration(badConfig); |
| EXPECT_TRUE(setResult.isOk()); |
| EXPECT_EQ(Result::INVALID_ARGUMENTS, setResult); |
| |
| // Test setConfiguration recovering after passing good data. |
| mCallbackCalled = false; |
| setResult = mTuner->setConfiguration(getBand(0)); |
| EXPECT_TRUE(setResult.isOk()); |
| EXPECT_EQ(Result::OK, setResult); |
| EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs)); |
| EXPECT_EQ(Result::OK, mResultCallbackData); |
| } |
| |
| /** |
| * Test ITuner::scan |
| * |
| * Verifies that: |
| * - the HAL implements the method |
| * - the method returns 0 (no error) |
| * - the tuned callback is received within kTuneCallbacktimeoutNs ns |
| * - skipping sub-channel or not does not fail the call |
| */ |
| TEST_P(BroadcastRadioHidlTest, Scan) { |
| RETURN_IF_SKIPPED; |
| ASSERT_EQ(true, openTuner()); |
| ASSERT_TRUE(checkAntenna()); |
| // test scan UP |
| mCallbackCalled = false; |
| Return<Result> hidlResult = mTuner->scan(Direction::UP, true); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs)); |
| |
| // test scan DOWN |
| mCallbackCalled = false; |
| hidlResult = mTuner->scan(Direction::DOWN, false); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs)); |
| } |
| |
| /** |
| * Test ITuner::step |
| * |
| * Verifies that: |
| * - the HAL implements the method |
| * - the method returns 0 (no error) |
| * - the tuned callback is received within kTuneCallbacktimeoutNs ns |
| * - skipping sub-channel or not does not fail the call |
| * |
| * Skipped for other radio classes than AM/FM, because step is not possible |
| * on DAB nor satellite. |
| */ |
| TEST_P(BroadcastRadioHidlTest, Step) { |
| if (radioClass != Class::AM_FM) skipped = true; |
| RETURN_IF_SKIPPED; |
| ASSERT_EQ(true, openTuner()); |
| ASSERT_TRUE(checkAntenna()); |
| // test step UP |
| mCallbackCalled = false; |
| Return<Result> hidlResult = mTuner->step(Direction::UP, false); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs)); |
| |
| // test step DOWN |
| mCallbackCalled = false; |
| hidlResult = mTuner->step(Direction::DOWN, true); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs)); |
| } |
| |
| /** |
| * Test ITuner::tune, getProgramInformation and cancel methods |
| * |
| * Verifies that: |
| * - the HAL implements the methods |
| * - the methods return 0 (no error) |
| * - the tuned callback is received within kTuneCallbacktimeoutNs ns after tune() |
| * |
| * Skipped for other radio classes than AM/FM, because tune to frequency |
| * is not possible on DAB nor satellite. |
| */ |
| TEST_P(BroadcastRadioHidlTest, TuneAndGetProgramInformationAndCancel) { |
| if (radioClass != Class::AM_FM) skipped = true; |
| RETURN_IF_SKIPPED; |
| ASSERT_EQ(true, openTuner()); |
| ASSERT_TRUE(checkAntenna()); |
| |
| auto& band = getBand(0); |
| |
| // test tune |
| ASSERT_GT(band.spacings.size(), 0u); |
| ASSERT_GT(band.upperLimit, band.lowerLimit); |
| |
| // test scan UP |
| uint32_t lowerLimit = band.lowerLimit; |
| uint32_t upperLimit = band.upperLimit; |
| uint32_t spacing = band.spacings[0]; |
| |
| uint32_t channel = |
| lowerLimit + (((upperLimit - lowerLimit) / 2 + spacing - 1) / spacing) * spacing; |
| mCallbackCalled = false; |
| mResultCallbackData = Result::NOT_INITIALIZED; |
| Return<Result> hidlResult = mTuner->tune(channel, 0); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs)); |
| EXPECT_EQ(channel, mProgramInfoCallbackData.channel); |
| |
| // test getProgramInformation |
| ProgramInfo halInfo; |
| Result halResult = Result::NOT_INITIALIZED; |
| Return<void> hidlReturn = mTuner->getProgramInformation( |
| [&](Result result, const ProgramInfo& info) { |
| halResult = result; |
| if (result == Result::OK) { |
| halInfo = info; |
| } |
| }); |
| EXPECT_TRUE(hidlReturn.isOk()); |
| EXPECT_EQ(Result::OK, halResult); |
| if (mResultCallbackData == Result::OK) { |
| EXPECT_LE(halInfo.channel, upperLimit); |
| EXPECT_GE(halInfo.channel, lowerLimit); |
| } |
| |
| // test cancel |
| mTuner->tune(lowerLimit, 0); |
| hidlResult = mTuner->cancel(); |
| EXPECT_TRUE(hidlResult.isOk()); |
| EXPECT_EQ(Result::OK, hidlResult); |
| } |
| |
| /** |
| * Test ITuner::tune failing when channel out of the range is provided. |
| * |
| * Verifies that: |
| * - the method returns INVALID_ARGUMENTS when applicable |
| * - the method recovers and succeeds after passing correct arguments |
| * |
| * Skipped for other radio classes than AM/FM, because tune to frequency |
| * is not possible on DAB nor satellite. |
| */ |
| TEST_P(BroadcastRadioHidlTest, TuneFailsOutOfBounds) { |
| if (radioClass != Class::AM_FM) skipped = true; |
| RETURN_IF_SKIPPED; |
| ASSERT_TRUE(openTuner()); |
| ASSERT_TRUE(checkAntenna()); |
| |
| // get current channel bounds |
| BandConfig halConfig; |
| Result halResult; |
| auto configResult = mTuner->getConfiguration([&](Result result, const BandConfig& config) { |
| halResult = result; |
| halConfig = config; |
| }); |
| ASSERT_TRUE(configResult.isOk()); |
| ASSERT_EQ(Result::OK, halResult); |
| |
| // try to tune slightly above the limit and expect to fail |
| auto badChannel = halConfig.upperLimit + halConfig.spacings[0]; |
| auto tuneResult = mTuner->tune(badChannel, 0); |
| EXPECT_TRUE(tuneResult.isOk()); |
| EXPECT_EQ(Result::INVALID_ARGUMENTS, tuneResult); |
| EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs)); |
| |
| // tuning exactly at the limit should succeed |
| auto goodChannel = halConfig.upperLimit; |
| tuneResult = mTuner->tune(goodChannel, 0); |
| EXPECT_TRUE(tuneResult.isOk()); |
| EXPECT_EQ(Result::OK, tuneResult); |
| EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs)); |
| } |
| |
| /** |
| * Test proper image format in metadata. |
| * |
| * Verifies that: |
| * - all images in metadata are provided in-band (as a binary blob, not by id) |
| * |
| * This is a counter-test for OobImagesOnly from 1.1 VTS. |
| */ |
| TEST_P(BroadcastRadioHidlTest, IbImagesOnly) { |
| RETURN_IF_SKIPPED; |
| ASSERT_TRUE(openTuner()); |
| ASSERT_TRUE(checkAntenna()); |
| |
| bool firstScan = true; |
| uint32_t firstChannel, prevChannel; |
| while (true) { |
| mCallbackCalled = false; |
| auto hidlResult = mTuner->scan(Direction::UP, true); |
| ASSERT_TRUE(hidlResult.isOk()); |
| if (hidlResult == Result::TIMEOUT) { |
| ALOGI("Got timeout on scan operation"); |
| break; |
| } |
| ASSERT_EQ(Result::OK, hidlResult); |
| ASSERT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs)); |
| |
| if (firstScan) { |
| firstScan = false; |
| firstChannel = mProgramInfoCallbackData.channel; |
| } else { |
| // scanned the whole band |
| if (mProgramInfoCallbackData.channel >= firstChannel && prevChannel <= firstChannel) { |
| break; |
| } |
| } |
| prevChannel = mProgramInfoCallbackData.channel; |
| |
| for (auto&& entry : mProgramInfoCallbackData.metadata) { |
| if (entry.key != MetadataKey::ICON && entry.key != MetadataKey::ART) continue; |
| EXPECT_EQ(MetadataType::RAW, entry.type); |
| EXPECT_EQ(0, entry.intValue); |
| EXPECT_GT(entry.rawValue.size(), 0u); |
| } |
| } |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| PerInstance, BroadcastRadioHidlTest, |
| testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames( |
| IBroadcastRadioFactory::descriptor)), |
| ::testing::Values("AM_FM", "SAT", "DT")), |
| android::hardware::PrintInstanceTupleNameToString<>); |