Refactor Broadcast Radio 1.1 VTS test.

Also apply some minor fixes and log tweaks to the default implementation.

Bug: b/36864490
Test: VTS
Change-Id: I16558d71c6cdee71854ecae9106b8a1c78032439
diff --git a/broadcastradio/1.1/default/BroadcastRadio.cpp b/broadcastradio/1.1/default/BroadcastRadio.cpp
index 3aac127..1119ffd 100644
--- a/broadcastradio/1.1/default/BroadcastRadio.cpp
+++ b/broadcastradio/1.1/default/BroadcastRadio.cpp
@@ -121,7 +121,7 @@
 Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused,
                                        const sp<V1_0::ITunerCallback>& callback,
                                        openTuner_cb _hidl_cb) {
-    ALOGV("%s", __func__);
+    ALOGV("%s(%s)", __func__, toString(config.type).c_str());
     lock_guard<mutex> lk(mMut);
 
     auto oldTuner = mTuner.promote();
diff --git a/broadcastradio/1.1/default/BroadcastRadioFactory.cpp b/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
index c2c1158..f57bc79 100644
--- a/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
+++ b/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
@@ -48,7 +48,7 @@
 }
 
 Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
-    ALOGV("%s", __func__);
+    ALOGV("%s(%s)", __func__, toString(classId).c_str());
 
     auto moduleIt = mRadioModules.find(classId);
     if (moduleIt == mRadioModules.end()) {
diff --git a/broadcastradio/1.1/default/Tuner.cpp b/broadcastradio/1.1/default/Tuner.cpp
index bb07f36..a36ec3f 100644
--- a/broadcastradio/1.1/default/Tuner.cpp
+++ b/broadcastradio/1.1/default/Tuner.cpp
@@ -105,9 +105,14 @@
     return info11;
 }
 
+bool Tuner::isFmLocked() {
+    if (!utils::isAmFm(utils::getType(mCurrentProgram))) return false;
+    return mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM;
+}
+
 void Tuner::tuneInternalLocked(const ProgramSelector& sel) {
     VirtualRadio* virtualRadio = nullptr;
-    if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+    if (isFmLocked()) {
         virtualRadio = &mVirtualFm;
     }
 
@@ -132,11 +137,11 @@
     lock_guard<mutex> lk(mMut);
     vector<VirtualProgram> list;
 
-    if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+    if (isFmLocked()) {
         list = mVirtualFm.getProgramList();
     }
 
-    if (list.size() == 0) {
+    if (list.empty()) {
         mIsTuneCompleted = false;
         auto task = [this, direction]() {
             ALOGI("Performing failed scan %s", toString(direction).c_str());
@@ -288,14 +293,17 @@
 
 Return<void> Tuner::getProgramList(const hidl_string& filter __unused, getProgramList_cb _hidl_cb) {
     ALOGV("%s", __func__);
+    lock_guard<mutex> lk(mMut);
 
     auto& virtualRadio = mVirtualFm;
-    if (mAmfmConfig.type != Band::FM_HD && mAmfmConfig.type != Band::FM) {
+    if (!isFmLocked()) {
+        ALOGI("bands other than FM are not supported yet");
         _hidl_cb(ProgramListResult::OK, {});
         return Void();
     }
 
     auto list = virtualRadio.getProgramList();
+    ALOGD("returning a list of %zu programs", list.size());
     _hidl_cb(ProgramListResult::OK, vector<ProgramInfo>(list.begin(), list.end()));
     return Void();
 }
diff --git a/broadcastradio/1.1/default/Tuner.h b/broadcastradio/1.1/default/Tuner.h
index 17ee99f..5c8a7e5 100644
--- a/broadcastradio/1.1/default/Tuner.h
+++ b/broadcastradio/1.1/default/Tuner.h
@@ -65,6 +65,7 @@
     ProgramInfo mCurrentProgramInfo = {};
 
     void tuneInternalLocked(const ProgramSelector& sel);
+    bool isFmLocked();  // TODO(b/36864090): make it generic, not FM only
 };
 
 }  // namespace implementation
diff --git a/broadcastradio/1.1/vts/functional/Android.bp b/broadcastradio/1.1/vts/functional/Android.bp
index a4c0849..c136019 100644
--- a/broadcastradio/1.1/vts/functional/Android.bp
+++ b/broadcastradio/1.1/vts/functional/Android.bp
@@ -29,9 +29,27 @@
         "android.hardware.broadcastradio@1.0",
         "android.hardware.broadcastradio@1.1",
     ],
-    static_libs: ["VtsHalHidlTargetTestBase"],
+    static_libs: [
+        "VtsHalHidlTargetTestBase",
+        "broadcastradio-vts-call-barrier",
+        "libgmock",
+    ],
     cflags: [
+        "-Wextra",
         "-O0",
         "-g",
     ],
 }
+
+cc_library_static {
+    name: "broadcastradio-vts-call-barrier",
+    srcs: [
+        "call-barrier.cpp",
+    ],
+    export_include_dirs: ["."],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
index 0de6fa5..6e6ab44 100644
--- a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
+++ b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
@@ -14,480 +14,236 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "BroadcastRadioHidlHalTest"
+#define LOG_TAG "broadcastradio.vts"
+
 #include <VtsHalHidlTargetTestBase.h>
 #include <android-base/logging.h>
+#include <call-barrier.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
+#include <gmock/gmock.h>
 #include <hidl/HidlTransportSupport.h>
 #include <utils/threads.h>
 
+#include <chrono>
+
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
-#include <android/hardware/broadcastradio/1.0/IBroadcastRadio.h>
 #include <android/hardware/broadcastradio/1.1/ITuner.h>
 #include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
 #include <android/hardware/broadcastradio/1.1/types.h>
 
+#include "mock-timeout.h"
 
-namespace V1_0 = ::android::hardware::broadcastradio::V1_0;
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace vts {
 
-using ::android::sp;
-using ::android::Mutex;
-using ::android::Condition;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-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::MetaData;
-using ::android::hardware::broadcastradio::V1_0::Properties;
-using ::android::hardware::broadcastradio::V1_1::IBroadcastRadioFactory;
-using ::android::hardware::broadcastradio::V1_1::ITuner;
-using ::android::hardware::broadcastradio::V1_1::ITunerCallback;
-using ::android::hardware::broadcastradio::V1_1::ProgramInfo;
-using ::android::hardware::broadcastradio::V1_1::Result;
-using ::android::hardware::broadcastradio::V1_1::ProgramListResult;
-using ::android::hardware::hidl_vec;
+using namespace std::chrono_literals;
 
-// The main test class for Broadcast Radio HIDL HAL.
+using testing::_;
+using testing::AnyNumber;
+using testing::ByMove;
+using testing::DoAll;
+using testing::SaveArg;
 
-class BroadcastRadioHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- protected:
-    virtual void SetUp() override {
-        auto factory = ::testing::VtsHalHidlTargetTestBase::getService<IBroadcastRadioFactory>();
-        ASSERT_NE(nullptr, factory.get());
-        Result halResult;
-        factory->connectModule(Class::AM_FM, [&](Result retval, const sp<IBroadcastRadio>& result) {
-            halResult = retval;
-            if (retval == Result::OK) {
-                mRadio = IBroadcastRadio::castFrom(result);
-            }
-        });
-        ASSERT_EQ(Result::OK, halResult);
-        mTunerCallback = new MyCallback(this);
-        ASSERT_NE(nullptr, mRadio.get());
-        ASSERT_NE(nullptr, mTunerCallback.get());
-    }
+using broadcastradio::vts::CallBarrier;
+using V1_0::BandConfig;
+using V1_0::Class;
+using V1_0::MetaData;
 
-    virtual void TearDown() override {
-        mTuner.clear();
-        mRadio.clear();
-    }
+static constexpr auto kConfigTimeout = 10s;
+static constexpr auto kConnectModuleTimeout = 1s;
+static constexpr auto kTuneTimeout = 30s;
+static constexpr auto kFullScanTimeout = 1min;
 
-    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 __unused) {
-            ALOGI("%s result %d", __FUNCTION__, result);
-            mParentTest->onResultCallback(result);
-            return Void();
-        }
-
-        virtual Return<void> tuneComplete(Result result __unused, const V1_0::ProgramInfo& info __unused) {
-            return Void();
-        }
-
-        virtual Return<void> tuneComplete_1_1(Result result, const ProgramInfo& info __unused) {
-            ALOGI("%s result %d", __FUNCTION__, result);
-            mParentTest->onResultCallback(result);
-            return Void();
-        }
-
-        virtual Return<void> afSwitch(const V1_0::ProgramInfo& info __unused) {
-            return Void();
-        }
-
-        virtual Return<void> afSwitch_1_1(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();
-        }
-
-        virtual Return<void> backgroundScanAvailable(bool isAvailable __unused) {
-            return Void();
-        }
-
-        virtual Return<void> backgroundScanComplete(ProgramListResult result) {
-            ALOGV("%s", __func__);
-            mParentTest->onProgramListResultCallback(result);
-            return Void();
-        }
-
-        virtual Return<void> programListChanged() {
-            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 a callback with status is received
-     */
-    void onResultCallback(Result result) {
-        Mutex::Autolock _l(mLock);
-        mResultCallbackData = result;
-        onCallback_l();
-    }
-
-    /**
-     * Method called by MyCallback when a callback with status is received
-     */
-    void onProgramListResultCallback(ProgramListResult result) {
-        Mutex::Autolock _l(mLock);
-        mProgramListResultCallbackData = result;
-        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();
-
-    static const nsecs_t kConfigCallbacktimeoutNs = seconds_to_nanoseconds(10);
-    static const nsecs_t kTuneCallbacktimeoutNs = seconds_to_nanoseconds(30);
-    static const nsecs_t kFullScanTimeoutNs = seconds_to_nanoseconds(60);
-
-    sp<IBroadcastRadio> mRadio;
-    Properties mHalProperties;
-    sp<ITuner> mTuner;
-    sp<MyCallback> mTunerCallback;
-    Mutex mLock;
-    Condition mCallbackCond;
-    bool mCallbackCalled;
-    bool mBoolCallbackData;
-    Result mResultCallbackData;
-    ProgramListResult mProgramListResultCallbackData;
-    bool mHwFailure;
-};
-
-// A class for test environment setup (kept since this file is a template).
-class BroadcastRadioHidlEnvironment : public ::testing::Environment {
- public:
-    virtual void SetUp() {}
-    virtual void TearDown() {}
-};
-
-bool BroadcastRadioHidlTest::getProperties()
-{
-    if (mHalProperties.bands.size() == 0) {
-        Result halResult = Result::NOT_INITIALIZED;
-        Return<void> 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(Class::AM_FM, mHalProperties.classId);
-        EXPECT_GT(mHalProperties.numTuners, 0u);
-        EXPECT_GT(mHalProperties.bands.size(), 0u);
-    }
-    return mHalProperties.bands.size() > 0;
+static void printSkipped(std::string msg) {
+    std::cout << "[  SKIPPED ] " << msg << std::endl;
 }
 
-bool BroadcastRadioHidlTest::openTuner()
-{
-    if (!getProperties()) {
-        return false;
+class TunerCallbackMock : public ITunerCallback {
+   public:
+    MOCK_METHOD0(hardwareFailure, Return<void>());
+    MOCK_TIMEOUT_METHOD2(configChange, Return<void>(Result, const BandConfig&));
+    MOCK_METHOD2(tuneComplete, Return<void>(Result, const V1_0::ProgramInfo&));
+    MOCK_TIMEOUT_METHOD2(tuneComplete_1_1, Return<void>(Result, const ProgramInfo&));
+    MOCK_METHOD1(afSwitch, Return<void>(const V1_0::ProgramInfo&));
+    MOCK_METHOD1(afSwitch_1_1, Return<void>(const ProgramInfo&));
+    MOCK_METHOD1(antennaStateChange, Return<void>(bool connected));
+    MOCK_METHOD1(trafficAnnouncement, Return<void>(bool active));
+    MOCK_METHOD1(emergencyAnnouncement, Return<void>(bool active));
+    MOCK_METHOD3(newMetadata, Return<void>(uint32_t ch, uint32_t subCh, const hidl_vec<MetaData>&));
+    MOCK_METHOD1(backgroundScanAvailable, Return<void>(bool));
+    MOCK_TIMEOUT_METHOD1(backgroundScanComplete, Return<void>(ProgramListResult));
+    MOCK_METHOD0(programListChanged, Return<void>());
+};
+
+class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase,
+                              public ::testing::WithParamInterface<Class> {
+   protected:
+    virtual void SetUp() override;
+    virtual void TearDown() override;
+
+    // TODO(b/36864490): check all bands for good test conditions (ie. FM is more likely to have
+    // any stations on the list, so don't pick AM blindly).
+    bool openTuner(unsigned band);
+    const BandConfig& getBand(unsigned idx);
+
+    Class radioClass;
+    bool skipped = false;
+
+    sp<IBroadcastRadio> mRadioModule;
+    sp<ITuner> mTuner;
+    sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
+
+   private:
+    hidl_vec<BandConfig> mBands;
+};
+
+void BroadcastRadioHalTest::SetUp() {
+    radioClass = GetParam();
+
+    // set general expectations for a callback
+    EXPECT_CALL(*mCallback, hardwareFailure()).Times(0);
+
+    // lookup HIDL service
+    auto factory = getService<IBroadcastRadioFactory>();
+    ASSERT_NE(nullptr, factory.get());
+
+    // connect radio module
+    Result connectResult;
+    CallBarrier onConnect;
+    factory->connectModule(radioClass, [&](Result ret, const sp<V1_0::IBroadcastRadio>& radio) {
+        connectResult = ret;
+        if (ret == Result::OK) mRadioModule = IBroadcastRadio::castFrom(radio);
+        onConnect.call();
+    });
+    ASSERT_TRUE(onConnect.waitForCall(kConnectModuleTimeout));
+
+    if (connectResult == Result::INVALID_ARGUMENTS) {
+        printSkipped("This device class is not supported.");
+        skipped = true;
+        return;
     }
-    if (mTuner.get() == nullptr) {
+    ASSERT_EQ(connectResult, Result::OK);
+    ASSERT_NE(nullptr, mRadioModule.get());
+
+    // get module properties
+    Properties prop11;
+    auto& prop10 = prop11.base;
+    auto propResult =
+        mRadioModule->getProperties_1_1([&](const Properties& properties) { prop11 = properties; });
+
+    ASSERT_TRUE(propResult.isOk());
+    EXPECT_EQ(radioClass, prop10.classId);
+    EXPECT_GT(prop10.numTuners, 0u);
+    if (radioClass == Class::AM_FM) {
+        EXPECT_GT(prop10.bands.size(), 0u);
+    }
+    mBands = prop10.bands;
+}
+
+void BroadcastRadioHalTest::TearDown() {
+    mTuner.clear();
+    mRadioModule.clear();
+    // TODO(b/36864490): wait (with timeout) until mCallback has only one reference
+}
+
+bool BroadcastRadioHalTest::openTuner(unsigned band) {
+    EXPECT_EQ(nullptr, mTuner.get());
+
+    if (radioClass == Class::AM_FM) {
+        EXPECT_TIMEOUT_CALL(*mCallback, configChange, Result::OK, _);
+    }
+
+    Result halResult = Result::NOT_INITIALIZED;
+    auto openCb = [&](Result result, const sp<V1_0::ITuner>& tuner) {
+        halResult = result;
+        if (result != Result::OK) return;
+        mTuner = ITuner::castFrom(tuner);
+    };
+    auto hidlResult = mRadioModule->openTuner(getBand(band), true, mCallback, openCb);
+
+    EXPECT_TRUE(hidlResult.isOk());
+    EXPECT_EQ(Result::OK, halResult);
+    EXPECT_NE(nullptr, mTuner.get());
+    if (radioClass == Class::AM_FM && mTuner != nullptr) {
+        EXPECT_TIMEOUT_CALL_WAIT(*mCallback, configChange, kConfigTimeout);
+
+        BandConfig halConfig;
         Result halResult = Result::NOT_INITIALIZED;
-        auto hidlReturn = mRadio->openTuner(mHalProperties.bands[0], true, mTunerCallback,
-                [&](Result result, const sp<V1_0::ITuner>& tuner) {
-                    halResult = result;
-                    if (result == Result::OK) {
-                        mTuner = ITuner::castFrom(tuner);
-                    }
-                });
-        EXPECT_TRUE(hidlReturn.isOk());
+        mTuner->getConfiguration([&](Result result, const BandConfig& config) {
+            halResult = result;
+            halConfig = config;
+        });
         EXPECT_EQ(Result::OK, halResult);
-        EXPECT_NE(nullptr, mTuner.get());
-        EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
+        EXPECT_TRUE(halConfig.antennaConnected);
     }
+
     EXPECT_NE(nullptr, mTuner.get());
     return nullptr != mTuner.get();
 }
 
-bool BroadcastRadioHidlTest::checkAntenna()
-{
-    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;
-                }
-            });
+const BandConfig& BroadcastRadioHalTest::getBand(unsigned idx) {
+    static const BandConfig dummyBandConfig = {};
 
-    return ((halResult == Result::OK) && (halConfig.antennaConnected == true));
-}
-
-
-/**
- * Test IBroadcastRadio::getProperties() method
- *
- * Verifies that:
- *  - the HAL implements the method
- *  - the method returns 0 (no error)
- *  - the implementation class is AM_FM
- *  - the implementation supports at least one tuner
- *  - the implementation supports at one band
- */
-TEST_F(BroadcastRadioHidlTest, GetProperties) {
-    EXPECT_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_F(BroadcastRadioHidlTest, OpenTuner) {
-    EXPECT_TRUE(openTuner());
-}
-
-/**
- * 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
- */
-TEST_F(BroadcastRadioHidlTest, SetAndGetConfiguration) {
-    ASSERT_TRUE(openTuner());
-    // test setConfiguration
-    mCallbackCalled = false;
-    Return<Result> hidlResult = mTuner->setConfiguration(mHalProperties.bands[0]);
-    EXPECT_TRUE(hidlResult.isOk());
-    EXPECT_EQ(Result::OK, hidlResult);
-    EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
-    EXPECT_EQ(Result::OK, mResultCallbackData);
-
-    // 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(mHalProperties.bands[0].type, halConfig.type);
-}
-
-/**
- * Test ITuner::scan
- *
- * Verifies that:
- *  - the HAL implements the method
- *  - the method returns 0 (no error)
- *  - the tuned callback is received within kTuneCallbacktimeoutNs ns
- */
-TEST_F(BroadcastRadioHidlTest, Scan) {
-    ASSERT_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_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
-
-    // test scan DOWN
-    mCallbackCalled = false;
-    hidlResult = mTuner->scan(Direction::DOWN, true);
-    EXPECT_TRUE(hidlResult.isOk());
-    EXPECT_EQ(Result::OK, hidlResult);
-    EXPECT_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
- */
-TEST_F(BroadcastRadioHidlTest, Step) {
-    ASSERT_TRUE(openTuner());
-    ASSERT_TRUE(checkAntenna());
-    // test step UP
-    mCallbackCalled = false;
-    Return<Result> hidlResult = mTuner->step(Direction::UP, true);
-    EXPECT_TRUE(hidlResult.isOk());
-    EXPECT_EQ(Result::OK, hidlResult);
-    EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
-
-    // test step DOWN
-    mCallbackCalled = false;
-    hidlResult = mTuner->step(Direction::DOWN, true);
-    EXPECT_TRUE(hidlResult.isOk());
-    EXPECT_EQ(Result::OK, hidlResult);
-    EXPECT_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()
- */
-TEST_F(BroadcastRadioHidlTest, TuneAndGetProgramInformationAndCancel) {
-    ASSERT_TRUE(openTuner());
-    ASSERT_TRUE(checkAntenna());
-
-    // test tune
-    ASSERT_GT(mHalProperties.bands[0].spacings.size(), 0u);
-    ASSERT_GT(mHalProperties.bands[0].upperLimit, mHalProperties.bands[0].lowerLimit);
-
-    // test scan UP
-    uint32_t lowerLimit = mHalProperties.bands[0].lowerLimit;
-    uint32_t upperLimit = mHalProperties.bands[0].upperLimit;
-    uint32_t spacing = mHalProperties.bands[0].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_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
-
-    // test getProgramInformation
-    ProgramInfo halInfo;
-    Result halResult = Result::NOT_INITIALIZED;
-    Return<void> hidlReturn = mTuner->getProgramInformation_1_1(
-        [&](Result result, const ProgramInfo& info) {
-            halResult = result;
-            if (result == Result::OK) {
-                halInfo = info;
-            }
-        });
-    EXPECT_TRUE(hidlReturn.isOk());
-    EXPECT_EQ(Result::OK, halResult);
-    auto &halInfo_1_1 = halInfo.base;
-    if (mResultCallbackData == Result::OK) {
-        EXPECT_LE(halInfo_1_1.channel, upperLimit);
-        EXPECT_GE(halInfo_1_1.channel, lowerLimit);
+    if (radioClass != Class::AM_FM) {
+        ALOGD("Not AM/FM radio, returning dummy band config");
+        return dummyBandConfig;
     }
 
-    // test cancel
-    mTuner->tune(lowerLimit, 0);
-    hidlResult = mTuner->cancel();
-    EXPECT_TRUE(hidlResult.isOk());
-    EXPECT_EQ(Result::OK, hidlResult);
+    EXPECT_GT(mBands.size(), idx);
+    if (mBands.size() <= idx) {
+        ALOGD("Band index out of bound, returning dummy band config");
+        return dummyBandConfig;
+    }
+
+    auto& band = mBands[idx];
+    ALOGD("Returning %s band", toString(band.type).c_str());
+    return band;
 }
 
-TEST_F(BroadcastRadioHidlTest, TuneFromProgramList) {
-    ASSERT_TRUE(openTuner());
-    ASSERT_TRUE(checkAntenna());
+/**
+ * Test IBroadcastRadio::openTuner() method called twice.
+ *
+ * Verifies that:
+ *  - the openTuner method succeeds when called for the second time without
+ *    deleting previous ITuner instance.
+ *
+ * This is a more strict requirement than in 1.0, where a second openTuner
+ * might fail.
+ */
+TEST_P(BroadcastRadioHalTest, OpenTunerTwice) {
+    if (skipped) return;
+    ASSERT_TRUE(openTuner(0));
+
+    Result halResult = Result::NOT_INITIALIZED;
+    auto openCb = [&](Result result, const sp<V1_0::ITuner>&) { halResult = result; };
+    auto hidlResult = mRadioModule->openTuner(getBand(0), true, mCallback, openCb);
+    ASSERT_TRUE(hidlResult.isOk());
+    ASSERT_EQ(Result::OK, halResult);
+}
+
+/**
+ * Test tuning to program list entry.
+ *
+ * Verifies that:
+ *  - getProgramList either succeeds or returns NOT_STARTED/NOT_READY status;
+ *  - if the program list is NOT_STARTED, startBackgroundScan makes it completed
+ *    within a full scan timeout and the next getProgramList call succeeds;
+ *  - if the program list is not empty, tune_1_1 call succeeds.
+ */
+TEST_P(BroadcastRadioHalTest, TuneFromProgramList) {
+    if (skipped) return;
+    ASSERT_TRUE(openTuner(0));
 
     ProgramInfo firstProgram;
     bool isListEmpty;
     ProgramListResult getListResult = ProgramListResult::NOT_INITIALIZED;
     auto getListCb = [&](ProgramListResult result, const hidl_vec<ProgramInfo>& list) {
+        ALOGD("getListCb(%s, ProgramInfo[%zu])", toString(result).c_str(), list.size());
         getListResult = result;
         if (result != ProgramListResult::OK) return;
         isListEmpty = (list.size() == 0);
@@ -496,40 +252,50 @@
     };
 
     // first try...
-    auto hidlReturn = mTuner->getProgramList("", getListCb);
-    ASSERT_TRUE(hidlReturn.isOk());
+    EXPECT_TIMEOUT_CALL(*mCallback, backgroundScanComplete, ProgramListResult::OK)
+        .Times(AnyNumber());
+    auto hidlResult = mTuner->getProgramList("", getListCb);
+    ASSERT_TRUE(hidlResult.isOk());
 
     if (getListResult == ProgramListResult::NOT_STARTED) {
         auto result = mTuner->startBackgroundScan();
-        ASSERT_TRUE(result.isOk());
         ASSERT_EQ(ProgramListResult::OK, result);
         getListResult = ProgramListResult::NOT_READY;  // continue as in NOT_READY case
     }
     if (getListResult == ProgramListResult::NOT_READY) {
-        ASSERT_TRUE(waitForCallback(kFullScanTimeoutNs));
-        ASSERT_EQ(ProgramListResult::OK, mProgramListResultCallbackData);
+        EXPECT_TIMEOUT_CALL_WAIT(*mCallback, backgroundScanComplete, kFullScanTimeout);
 
         // second (last) try...
-        hidlReturn = mTuner->getProgramList("", getListCb);
-        ASSERT_TRUE(hidlReturn.isOk());
+        hidlResult = mTuner->getProgramList("", getListCb);
+        ASSERT_TRUE(hidlResult.isOk());
         ASSERT_EQ(ProgramListResult::OK, getListResult);
     }
 
     if (isListEmpty) {
-        std::cout << "[  SKIPPED ] Program list is empty. " << std::endl;
+        printSkipped("Program list is empty.");
         return;
     }
 
+    ProgramInfo infoCb;
+    EXPECT_CALL(*mCallback, tuneComplete(_, _));
+    EXPECT_TIMEOUT_CALL(*mCallback, tuneComplete_1_1, Result::OK, _)
+        .WillOnce(DoAll(SaveArg<1>(&infoCb), testing::Return(ByMove(Void()))));
     auto tuneResult = mTuner->tune_1_1(firstProgram.selector);
-    ASSERT_TRUE(tuneResult.isOk());
-    EXPECT_EQ(Result::OK, tuneResult);
-    EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
-    // TODO(b/36864490): check this too, when mProgramInfoCallbackData is cherry-picked from 1.0
-    // EXPECT_EQ(firstProgram.selector.primaryId, mProgramInfoCallbackData.selector.primaryId);
+    ASSERT_EQ(Result::OK, tuneResult);
+    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, tuneComplete_1_1, kTuneTimeout);
+    EXPECT_EQ(firstProgram.selector.primaryId, infoCb.selector.primaryId);
 }
 
+INSTANTIATE_TEST_CASE_P(BroadcastRadioHalTestCases, BroadcastRadioHalTest,
+                        ::testing::Values(Class::AM_FM, Class::SAT, Class::DT));
+
+}  // namespace vts
+}  // namespace V1_1
+}  // namespace broadcastradio
+}  // namespace hardware
+}  // namespace android
+
 int main(int argc, char** argv) {
-  ::testing::AddGlobalTestEnvironment(new BroadcastRadioHidlEnvironment);
   ::testing::InitGoogleTest(&argc, argv);
   int status = RUN_ALL_TESTS();
   ALOGI("Test result = %d", status);
diff --git a/broadcastradio/1.1/vts/functional/call-barrier.cpp b/broadcastradio/1.1/vts/functional/call-barrier.cpp
new file mode 100644
index 0000000..fede297
--- /dev/null
+++ b/broadcastradio/1.1/vts/functional/call-barrier.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include "call-barrier.h"
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace vts {
+
+using std::lock_guard;
+using std::mutex;
+using std::unique_lock;
+
+void CallBarrier::call() {
+    lock_guard<mutex> lk(mMut);
+    mWasCalled = true;
+    mCond.notify_all();
+}
+
+bool CallBarrier::waitForCall(std::chrono::milliseconds timeout) {
+    unique_lock<mutex> lk(mMut);
+
+    if (mWasCalled) return true;
+
+    auto status = mCond.wait_for(lk, timeout);
+    return status == std::cv_status::no_timeout;
+}
+
+}  // namespace vts
+}  // namespace broadcastradio
+}  // namespace hardware
+}  // namespace android
diff --git a/broadcastradio/1.1/vts/functional/call-barrier.h b/broadcastradio/1.1/vts/functional/call-barrier.h
new file mode 100644
index 0000000..462396a
--- /dev/null
+++ b/broadcastradio/1.1/vts/functional/call-barrier.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_CALL_BARRIER
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_CALL_BARRIER
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace vts {
+
+/**
+ * A barrier for thread synchronization, where one should wait for another to
+ * reach a specific point in execution.
+ */
+class CallBarrier {
+   public:
+    /**
+     * Notify the other thread it may continue execution.
+     *
+     * This may be called before the other thread starts waiting on the barrier.
+     */
+    void call();
+
+    /**
+     * Wait for the other thread to reach call() execution point.
+     *
+     * @param timeout a maximum time to wait.
+     * @returns {@code false} if timed out, {@code true} otherwise.
+     */
+    bool waitForCall(std::chrono::milliseconds timeout);
+
+   private:
+    bool mWasCalled = false;
+    std::mutex mMut;
+    std::condition_variable mCond;
+};
+
+}  // namespace vts
+}  // namespace broadcastradio
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_CALL_BARRIER
diff --git a/broadcastradio/1.1/vts/functional/mock-timeout.h b/broadcastradio/1.1/vts/functional/mock-timeout.h
new file mode 100644
index 0000000..fa1114f
--- /dev/null
+++ b/broadcastradio/1.1/vts/functional/mock-timeout.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT
+
+#include <gmock/gmock.h>
+#include <thread>
+
+/**
+ * Common helper objects for gmock timeout extension.
+ *
+ * INTERNAL IMPLEMENTATION - don't use in user code.
+ */
+#define EGMOCK_TIMEOUT_METHOD_DEF_(Method, ...) \
+    std::atomic<bool> egmock_called_##Method;   \
+    std::mutex egmock_mut_##Method;             \
+    std::condition_variable egmock_cond_##Method;
+
+/**
+ * Common method body for gmock timeout extension.
+ *
+ * INTERNAL IMPLEMENTATION - don't use in user code.
+ */
+#define EGMOCK_TIMEOUT_METHOD_BODY_(Method, ...)             \
+    auto ret = egmock_##Method(__VA_ARGS__);                 \
+    {                                                        \
+        std::lock_guard<std::mutex> lk(egmock_mut_##Method); \
+        egmock_called_##Method = true;                       \
+        egmock_cond_##Method.notify_all();                   \
+    }                                                        \
+    return ret;
+
+/**
+ * Gmock MOCK_METHOD1 timeout-capable extension.
+ */
+#define MOCK_TIMEOUT_METHOD1(Method, ...)                                                 \
+    MOCK_METHOD1(egmock_##Method, __VA_ARGS__);                                           \
+    EGMOCK_TIMEOUT_METHOD_DEF_(Method);                                                   \
+    virtual GMOCK_RESULT_(, __VA_ARGS__) Method(GMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1) { \
+        EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1);                                   \
+    }
+
+/**
+ * Gmock MOCK_METHOD2 timeout-capable extension.
+ */
+#define MOCK_TIMEOUT_METHOD2(Method, ...)                                                        \
+    MOCK_METHOD2(egmock_##Method, __VA_ARGS__);                                                  \
+    EGMOCK_TIMEOUT_METHOD_DEF_(Method);                                                          \
+    virtual GMOCK_RESULT_(, __VA_ARGS__)                                                         \
+        Method(GMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1, GMOCK_ARG_(, 2, __VA_ARGS__) egmock_a2) { \
+        EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1, egmock_a2);                               \
+    }
+
+/**
+ * Gmock EXPECT_CALL timeout-capable extension.
+ *
+ * It has slightly different syntax from the original macro, to make method name accessible.
+ * So, instead of typing
+ *     EXPECT_CALL(account, charge(100, Currency::USD));
+ * you need to inline arguments
+ *     EXPECT_TIMEOUT_CALL(account, charge, 100, Currency::USD);
+ */
+#define EXPECT_TIMEOUT_CALL(obj, Method, ...) \
+    (obj).egmock_called_##Method = false;     \
+    EXPECT_CALL(obj, egmock_##Method(__VA_ARGS__))
+
+/**
+ * Waits for an earlier EXPECT_TIMEOUT_CALL to execute.
+ *
+ * It does not fully support special constraints of the EXPECT_CALL clause, just proceeds when the
+ * first call to a given method comes. For example, in the following code:
+ *     EXPECT_TIMEOUT_CALL(account, charge, 100, _);
+ *     account.charge(50, Currency::USD);
+ *     EXPECT_TIMEOUT_CALL_WAIT(account, charge, 500ms);
+ * the wait clause will just continue, as the charge method was called.
+ *
+ * @param obj object for a call
+ * @param Method the method to wait for
+ * @param timeout the maximum time for waiting
+ */
+#define EXPECT_TIMEOUT_CALL_WAIT(obj, Method, timeout)                      \
+    {                                                                       \
+        std::unique_lock<std::mutex> lk((obj).egmock_mut_##Method);         \
+        if (!(obj).egmock_called_##Method) {                                \
+            auto status = (obj).egmock_cond_##Method.wait_for(lk, timeout); \
+            EXPECT_EQ(std::cv_status::no_timeout, status);                  \
+        }                                                                   \
+    }
+
+#endif  // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT