You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
737 lines
24 KiB
737 lines
24 KiB
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHidlTest);
|
|
INSTANTIATE_TEST_CASE_P(
|
|
PerInstance, BroadcastRadioHidlTest,
|
|
testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
|
|
IBroadcastRadioFactory::descriptor)),
|
|
::testing::Values("AM_FM", "SAT", "DT")),
|
|
android::hardware::PrintInstanceTupleNameToString<>); |