Merge "Extend configChange and tuneComplete callbacks with checking the carried value."
diff --git a/audio/2.0/vts/functional/Android.bp b/audio/2.0/vts/functional/Android.bp
new file mode 100644
index 0000000..05b1817
--- /dev/null
+++ b/audio/2.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalAudioV2_0TargetTest",
+    gtest: true,
+    srcs: ["AudioPrimaryHidlHalTest.cpp"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libhidlbase",
+        "libutils",
+        "libcutils",
+        "android.hardware.audio@2.0",
+        "android.hardware.audio.common@2.0",
+    ],
+    static_libs: ["libgtest"],
+    cflags: [
+        "-O0",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..cc20456
--- /dev/null
+++ b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,708 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VtsHalAudioV2_0TargetTest"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdio>
+#include <limits>
+#include <list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <android/hardware/audio/2.0/types.h>
+#include <android/hardware/audio/common/2.0/types.h>
+
+#include "utility/ReturnIn.h"
+#include "utility/AssertOk.h"
+#include "utility/PrettyPrintAudioTypes.h"
+
+using std::string;
+using std::to_string;
+using std::vector;
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::audio::V2_0::DeviceAddress;
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::audio::V2_0::IDevicesFactory;
+using ::android::hardware::audio::V2_0::IStream;
+using ::android::hardware::audio::V2_0::IStreamIn;
+using ::android::hardware::audio::V2_0::IStreamOut;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::audio::common::V2_0::AudioChannelMask;
+using ::android::hardware::audio::common::V2_0::AudioConfig;
+using ::android::hardware::audio::common::V2_0::AudioDevice;
+using ::android::hardware::audio::common::V2_0::AudioFormat;
+using ::android::hardware::audio::common::V2_0::AudioHandleConsts;
+using ::android::hardware::audio::common::V2_0::AudioInputFlag;
+using ::android::hardware::audio::common::V2_0::AudioIoHandle;
+using ::android::hardware::audio::common::V2_0::AudioOffloadInfo;
+using ::android::hardware::audio::common::V2_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V2_0::AudioSource;
+
+using utility::returnIn;
+
+namespace doc {
+/** Document the current test case.
+ * Eg: calling `doc::test("Dump the state of the hal")` in the "debugDump" test will output:
+ *   <testcase name="debugDump" status="run" time="6" classname="AudioPrimaryHidlTest"
+            description="Dump the state of the hal." />
+ * see https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#logging-additional-information
+ */
+void test(const std::string& testCaseDocumentation) {
+    ::testing::Test::RecordProperty("description", testCaseDocumentation);
+}
+
+/** Document why a test was not fully run. Usually due to an optional feature not implemented. */
+void partialTest(const std::string& reason) {
+    ::testing::Test::RecordProperty("partialyRunTest", reason);
+}
+}
+
+// Register callback for static object destruction
+// Avoid destroying static objects after main return.
+// Post main return destruction leads to incorrect gtest timing measurements as well as harder
+// debuging if anything goes wrong during destruction.
+class Environment : public ::testing::Environment {
+public:
+    using TearDownFunc = std::function<void ()>;
+     void registerTearDown(TearDownFunc&& tearDown) {
+         tearDowns.push_back(std::move(tearDown));
+    }
+
+private:
+    void TearDown() override {
+        // Call the tear downs in reverse order of insertion
+        for (auto& tearDown : tearDowns) {
+            tearDown();
+        }
+    }
+    std::list<TearDownFunc> tearDowns;
+};
+// Instance to register global tearDown
+static Environment* environment;
+
+class HidlTest : public ::testing::Test {
+protected:
+    // Convenient member to store results
+    Result res;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////// getService audio_devices_factory //////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// Test all audio devices
+class AudioHidlTest : public HidlTest {
+public:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base
+
+        if (devicesFactory == nullptr) {
+            environment->registerTearDown([]{ devicesFactory.clear(); });
+            devicesFactory = IDevicesFactory::getService();
+        }
+        ASSERT_TRUE(devicesFactory != nullptr);
+    }
+
+protected:
+    // Cache the devicesFactory retrieval to speed up each test by ~0.5s
+    static sp<IDevicesFactory> devicesFactory;
+};
+sp<IDevicesFactory> AudioHidlTest::devicesFactory;
+
+TEST_F(AudioHidlTest, GetAudioDevicesFactoryService) {
+    doc::test("test the getService (called in SetUp)");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// openDevice primary ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// Test the primary device
+class AudioPrimaryHidlTest : public AudioHidlTest {
+public:
+    /** Primary HAL test are NOT thread safe. */
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base
+
+        if (device == nullptr) {
+            environment->registerTearDown([]{ device.clear(); });
+            IDevicesFactory::Result result;
+            ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY,
+                                                 returnIn(result, device)));
+        }
+        ASSERT_TRUE(device != nullptr);
+    }
+
+protected:
+    // Cache the device opening to speed up each test by ~0.5s
+    static sp<IDevice> device;
+};
+sp<IDevice> AudioPrimaryHidlTest::device;
+
+TEST_F(AudioPrimaryHidlTest, OpenPrimaryDevice) {
+    doc::test("Test the openDevice (called in SetUp)");
+}
+
+TEST_F(AudioPrimaryHidlTest, Init) {
+    doc::test("Test that the audio primary hal initialized correctly");
+    ASSERT_OK(device->initCheck());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {set,get}MasterVolume ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class MasterVolumeTest : public AudioPrimaryHidlTest {
+protected:
+    void testSetGetConsistency(float volume, Result expectedSetResult, float expectedGetVolume) {
+        SCOPED_TRACE("Test set/get consistency for " + to_string(volume));
+        auto ret = device->setMasterVolume(volume);
+        ASSERT_TRUE(ret.isOk());
+        ASSERT_EQ(expectedSetResult, ret);
+
+        float observedVolume;
+        ASSERT_OK(device->getMasterVolume(returnIn(res, observedVolume)));
+        ASSERT_OK(res);
+
+        // Check that `get` returned the expected value
+        EXPECT_EQ(expectedGetVolume, observedVolume);
+    }
+};
+
+TEST_F(MasterVolumeTest, MasterVolumeTest) {
+    doc::test("Test the master volume if supported");
+    {
+        SCOPED_TRACE("Check for master volume support");
+        auto ret = device->setMasterVolume(1);
+        ASSERT_TRUE(ret.isOk());
+        if (ret == Result::NOT_SUPPORTED) {
+            doc::partialTest("Master volume is not supported");
+            return;
+        }
+    }
+    // NOTE: this code has never been tested on a platform supporting MasterVolume
+    float lastValidVolumeSet;
+    using Volumes = float[];
+    SCOPED_TRACE("As set/get master volume are supported...");
+    {
+        SCOPED_TRACE("...test them with valid values");
+        for (float validVolume : Volumes{0, 0.5, 1}) {
+            ASSERT_NO_FATAL_FAILURE(testSetGetConsistency(validVolume, Result::OK, validVolume));
+            lastValidVolumeSet = validVolume;
+        }
+    }{
+        SCOPED_TRACE("...test them with tricky values");
+        for (float outOfBoundVolume :Volumes{-0.1, 1.1, NAN, INFINITY, -INFINITY,
+                                             1 + std::numeric_limits<float>::epsilon()}) {
+        ASSERT_NO_FATAL_FAILURE(testSetGetConsistency(outOfBoundVolume,
+                                                      Result::INVALID_ARGUMENTS,
+                                                      lastValidVolumeSet));
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// {set,get}{Master,Mic}Mute /////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class BoolAccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
+protected:
+    template <class Getter, class Setter>
+    void testBoolAccessors(const string& propertyName, const vector<bool>& valuesToTest,
+                           Setter setter, Getter getter) {
+        for (bool setState : valuesToTest) {
+            SCOPED_TRACE("Test " + propertyName + " state: " + to_string(setState));
+            ASSERT_OK((device.get()->*setter)(setState));
+            bool getState;
+            ASSERT_OK((device.get()->*getter)(returnIn(res, getState)));
+            ASSERT_OK(res);
+            ASSERT_EQ(setState, getState);
+        }
+    }
+};
+
+TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) {
+    doc::test("Check that the mic can be muted and unmuted");
+    testBoolAccessors("mic mute", {true, false, true}, &IDevice::setMicMute, &IDevice::getMicMute);
+    // TODO: check that the mic is really muted (all sample are 0)
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, MasterMuteTest) {
+    doc::test("If master mute is supported, try to mute and unmute the master output");
+    {
+        SCOPED_TRACE("Check for master mute support");
+        auto ret = device->setMasterMute(false);
+        ASSERT_TRUE(ret.isOk());
+        if (ret == Result::NOT_SUPPORTED) {
+            doc::partialTest("Master mute is not supported");
+            return;
+        }
+    }
+    // NOTE: this code has never been tested on a platform supporting MasterMute
+    testBoolAccessors("master mute", {true, false, true},
+                      &IDevice::setMasterMute, &IDevice::getMasterMute);
+    // TODO: check that the master volume is really muted
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////// Required and recommended audio format support ///////////////
+// From: https://source.android.com/compatibility/android-cdd.html#5_4_audio_recording
+// From: https://source.android.com/compatibility/android-cdd.html#5_5_audio_playback
+/////////// TODO: move to the beginning of the file for easier update ////////
+//////////////////////////////////////////////////////////////////////////////
+
+class AudioConfigPrimaryTest : public AudioPrimaryHidlTest {
+public:
+    // Cache result ?
+    static const vector<AudioConfig> getRequiredSupportPlaybackAudioConfig() {
+        return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
+                                  {8000, 11025, 16000, 22050, 32000, 44100},
+                                  {AudioFormat::PCM_16_BIT});
+    }
+
+    static const vector<AudioConfig> getRecommendedSupportPlaybackAudioConfig() {
+        return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
+                                  {24000, 48000},
+                                  {AudioFormat::PCM_16_BIT});
+    }
+
+    static const vector<AudioConfig> getSupportedPlaybackAudioConfig() {
+        // TODO: retrieve audio config supported by the platform
+        // as declared in the policy configuration
+        return {};
+    }
+
+    static const vector<AudioConfig> getRequiredSupportCaptureAudioConfig() {
+        return combineAudioConfig({AudioChannelMask::IN_MONO},
+                                  {8000, 11025, 16000, 44100},
+                                  {AudioFormat::PCM_16_BIT});
+    }
+    static const vector<AudioConfig> getRecommendedSupportCaptureAudioConfig() {
+        return combineAudioConfig({AudioChannelMask::IN_STEREO},
+                                  {22050, 48000},
+                                  {AudioFormat::PCM_16_BIT});
+    }
+    static const vector<AudioConfig> getSupportedCaptureAudioConfig() {
+        // TODO: retrieve audio config supported by the platform
+        // as declared in the policy configuration
+        return {};
+    }
+private:
+    static const vector<AudioConfig> combineAudioConfig(
+            vector<AudioChannelMask> channelMasks,
+            vector<uint32_t> sampleRates,
+            vector<AudioFormat> formats) {
+        vector<AudioConfig> configs;
+        for (auto channelMask: channelMasks) {
+            for (auto sampleRate : sampleRates) {
+                for (auto format : formats) {
+                    AudioConfig config{};
+                    // leave offloadInfo to 0
+                    config.channelMask = channelMask;
+                    config.sampleRateHz = sampleRate;
+                    config.format = format;
+                    // FIXME: leave frameCount to 0 ?
+                    configs.push_back(config);
+                }
+            }
+        }
+        return configs;
+    }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////// getInputBufferSize /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// FIXME: execute input test only if platform declares android.hardware.microphone
+//        how to get this value ? is it a property ???
+
+class AudioCaptureConfigPrimaryTest : public AudioConfigPrimaryTest,
+                                      public ::testing::WithParamInterface<AudioConfig> {
+protected:
+    void inputBufferSizeTest(const AudioConfig& audioConfig, bool supportRequired) {
+        uint64_t bufferSize;
+        ASSERT_OK(device->getInputBufferSize(audioConfig, returnIn(res, bufferSize)));
+
+        switch (res) {
+            case Result::INVALID_ARGUMENTS:
+                EXPECT_FALSE(supportRequired);
+                break;
+            case Result::OK:
+                // Check that the buffer is of a sane size
+                // For now only that it is > 0
+                EXPECT_GT(bufferSize, uint64_t(0));
+                break;
+            default:
+                FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+        }
+    }
+};
+
+// Test that the required capture config and those declared in the policy are indeed supported
+class RequiredInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {};
+TEST_P(RequiredInputBufferSizeTest, RequiredInputBufferSizeTest) {
+    doc::test("Input buffer size must be retrievable for a format with required support.");
+    inputBufferSizeTest(GetParam(), true);
+}
+INSTANTIATE_TEST_CASE_P(
+        RequiredInputBufferSize, RequiredInputBufferSizeTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+        SupportedInputBufferSize, RequiredInputBufferSizeTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()));
+
+// Test that the recommended capture config are supported or lead to a INVALID_ARGUMENTS return
+class OptionalInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {};
+TEST_P(OptionalInputBufferSizeTest, OptionalInputBufferSizeTest) {
+    doc::test("Input buffer size should be retrievable for a format with recommended support.");
+    inputBufferSizeTest(GetParam(), false);
+}
+INSTANTIATE_TEST_CASE_P(
+        RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()));
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// setScreenState ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, setScreenState) {
+    doc::test("Check that the hal can receive the screen state");
+    for (bool turnedOn : {false, true, true, false, false}) {
+        auto ret = device->setScreenState(turnedOn);
+        ASSERT_TRUE(ret.isOk());
+        Result result = ret;
+        ASSERT_TRUE(result == Result::OK || result == Result::NOT_SUPPORTED) << toString(result);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {get,set}Parameters /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, getParameters) {
+    doc::test("Check that the hal can set and get parameters");
+    hidl_vec<hidl_string> keys;
+    hidl_vec<ParameterValue> values;
+    ASSERT_OK(device->getParameters(keys, returnIn(res, values)));
+    ASSERT_OK(device->setParameters(values));
+    values.resize(0);
+    ASSERT_OK(device->setParameters(values));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// debugDebug //////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, debugDump) {
+    doc::test("Check that the hal can dump its state without error");
+    FILE* file = tmpfile();
+    ASSERT_NE(nullptr, file) << errno;
+
+    auto* nativeHandle = native_handle_create(1, 0);
+    ASSERT_NE(nullptr, nativeHandle);
+    nativeHandle->data[0] = fileno(file);
+
+    hidl_handle handle;
+    handle.setTo(nativeHandle, true /*take ownership*/);
+
+    auto ret = device->debugDump(handle);
+    ASSERT_TRUE(ret.isOk());
+
+    // FIXME: debugDump does not return a Result.
+    // This mean that the hal can not report that it not implementing the function.
+    // As the default hal does not implement debugDump, the function can not be tested.
+    /*
+    res = ret;
+    if (res == Result::NOT_SUPPORTED) {
+         doc::partialTest("debugDump is not implemented");
+         return;
+    }
+    ASSERT_OK(res);
+    rewind(file);
+
+    // Check that at least one bit was written by the hal
+    char buff;
+    ASSERT_EQ(1, fread(&buff, sizeof(buff), 1, file));
+    */
+    EXPECT_EQ(0, fclose(file)) << errno;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// open{Output,Input}Stream //////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+template <class Stream>
+class OpenStreamTest : public AudioConfigPrimaryTest,
+                       public ::testing::WithParamInterface<AudioConfig> {
+protected:
+    template <class Open>
+    void testOpen(Open openStream, const AudioConfig& config) {
+        // FIXME: Open a stream without an IOHandle
+        //        This is not required to be accepted by hal implementations
+        AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
+        AudioConfig suggestedConfig{};
+        ASSERT_OK(openStream(ioHandle, config, returnIn(res, stream, suggestedConfig)));
+
+        // TODO: only allow failure for RecommendedPlaybackAudioConfig
+        switch (res) {
+            case Result::OK:
+                ASSERT_TRUE(stream != nullptr);
+                audioConfig = config;
+                break;
+            case Result::INVALID_ARGUMENTS:
+                ASSERT_TRUE(stream == nullptr);
+                AudioConfig suggestedConfigRetry;
+                // Could not open stream with config, try again with the suggested one
+                ASSERT_OK(openStream(ioHandle, suggestedConfig,
+                                     returnIn(res, stream, suggestedConfigRetry)));
+                // This time it must succeed
+                ASSERT_OK(res);
+                ASSERT_TRUE(stream == nullptr);
+                audioConfig = suggestedConfig;
+                break;
+            default:
+                FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+        }
+        open = true;
+    }
+
+private:
+    void TearDown() override {
+        if (open) {
+            ASSERT_OK(stream->close());
+        }
+    }
+
+protected:
+
+    AudioConfig audioConfig;
+    sp<Stream> stream;
+    bool open = false;
+};
+
+////////////////////////////// openOutputStream //////////////////////////////
+
+class OutputStreamTest : public OpenStreamTest<IStreamOut> {
+    virtual void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
+
+        const AudioConfig& config = GetParam();
+        DeviceAddress deviceAddr{};  // Ignored by primary hal
+        AudioOutputFlag flags = AudioOutputFlag::NONE; // TODO: test all flag combination
+        testOpen([&](AudioIoHandle handle, AudioConfig config, auto cb)
+                 { return device->openOutputStream(handle, deviceAddr, config, flags, cb); },
+                 config);
+    }
+};
+TEST_P(OutputStreamTest, OpenOutputStreamTest) {
+    doc::test("Check that output streams can be open with the required and recommended config");
+    // Open done in SetUp
+}
+INSTANTIATE_TEST_CASE_P(
+        RequiredOutputStreamConfigSupport, OutputStreamTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportPlaybackAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+        SupportedOutputStreamConfig, OutputStreamTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedPlaybackAudioConfig()));
+
+INSTANTIATE_TEST_CASE_P(
+        RecommendedOutputStreamConfigSupport, OutputStreamTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportPlaybackAudioConfig()));
+
+////////////////////////////// openInputStream //////////////////////////////
+
+class InputStreamTest : public OpenStreamTest<IStreamIn> {
+
+    virtual void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
+
+        const AudioConfig& config = GetParam();
+        DeviceAddress deviceAddr{}; // TODO: test all devices
+        AudioInputFlag flags = AudioInputFlag::NONE; // TODO: test all flag combination
+        AudioSource source = AudioSource::DEFAULT; // TODO: test all flag combination
+        testOpen([&](AudioIoHandle handle, AudioConfig config, auto cb)
+                 { return device->openInputStream(handle, deviceAddr, config, flags, source, cb); },
+                 config);
+    }
+};
+
+TEST_P(InputStreamTest, OpenInputStreamTest) {
+    doc::test("Check that input streams can be open with the required and recommended config");
+    // Open done in setup
+}
+INSTANTIATE_TEST_CASE_P(
+        RequiredInputStreamConfigSupport, InputStreamTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+        SupportedInputStreamConfig, InputStreamTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()));
+
+INSTANTIATE_TEST_CASE_P(
+        RecommendedInputStreamConfigSupport, InputStreamTest,
+        ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()));
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////// IStream getters ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/** Unpack the provided result.
+ * If the result is not OK, register a failure and return an undefined value. */
+template <class R>
+static R extract(Return<R> ret) {
+    if (!ret.isOk()) {
+        ADD_FAILURE();
+        return R{};
+    }
+    return ret;
+}
+
+template <class Property, class CapabilityGetter, class Getter, class Setter>
+static void testCapabilityGetter(const string& name,IStream* stream, Property currentValue,
+                                 CapabilityGetter capablityGetter, Getter getter, Setter setter) {
+    hidl_vec<Property> capabilities;
+    ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities)));
+    if (capabilities.size() == 0) {
+        // The default hal should probably return a NOT_SUPPORTED if the hal does not expose
+        // capability retrieval. For now it returns an empty list if not implemented
+        doc::partialTest(name + " is not supported");
+        return;
+    };
+    // TODO: This code has never been tested on a hal that supports getSupportedSampleRates
+    EXPECT_NE(std::find(capabilities.begin(), capabilities.end(), currentValue),
+             capabilities.end())
+        << "current " << name << " is not in the list of the supported ones "
+        << toString(capabilities);
+
+    // Check that all declared supported values are indeed supported
+    for (auto capability : capabilities) {
+        ASSERT_OK((stream->*setter)(capability));
+        ASSERT_EQ(capability, extract((stream->*getter)()));
+    }
+}
+
+static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) {
+    uint32_t sampleRateHz;
+    AudioChannelMask mask;
+    AudioFormat format;
+
+    stream->getAudioProperties(returnIn(sampleRateHz, mask, format));
+
+    // FIXME: the qcom hal it does not currently negotiate the sampleRate & channel mask
+    // EXPECT_EQ(expectedConfig.sampleRateHz, sampleRateHz);
+    // EXPECT_EQ(expectedConfig.channelMask, mask);
+    EXPECT_EQ(expectedConfig.format, format);
+}
+
+static void testAccessors(IStream* stream, AudioConfig audioConfig) {
+    doc::test("Test IStream getters and setters that can be called in the stop state");
+
+    auto frameCount = extract(stream->getFrameCount());
+    ASSERT_EQ(audioConfig.frameCount, frameCount);
+
+    auto sampleRate = extract(stream->getSampleRate());
+    // FIXME: the qcom hal it does not currently negotiate the sampleRate
+    // ASSERT_EQ(audioConfig.sampleRateHz, sampleRate);
+
+    auto channelMask = extract(stream->getChannelMask());
+    // FIXME: the qcom hal it does not currently negotiate the channelMask
+    // ASSERT_EQ(audioConfig.channelMask, channelMask);
+
+    auto frameSize = extract(stream->getFrameSize());
+    ASSERT_GE(frameSize, 0U);
+
+    auto bufferSize = extract(stream->getBufferSize());
+    ASSERT_GE(bufferSize, frameSize);
+
+    testCapabilityGetter("getSupportedsampleRate", stream, sampleRate,
+                         &IStream::getSupportedSampleRates,
+                         &IStream::getSampleRate, &IStream::setSampleRate);
+
+    testCapabilityGetter("getSupportedChannelMask", stream, channelMask,
+                         &IStream::getSupportedChannelMasks,
+                         &IStream::getChannelMask, &IStream::setChannelMask);
+
+    AudioFormat format = extract(stream->getFormat());
+    ASSERT_EQ(audioConfig.format, format);
+
+    testCapabilityGetter("getSupportedFormats", stream, format,
+                         &IStream::getSupportedFormats, &IStream::getFormat, &IStream::setFormat);
+
+    testGetAudioProperties(stream, audioConfig);
+
+    // FIXME: Stream wrapper does not implement getDevice properly.
+    // It needs to call getProperty({"routing"}).
+    // The current implementation segfault with the default hal
+    /*
+     * auto ret = stream->getDevice();
+     * ASSERT_TRUE(ret.isOk());
+     * AudioDevice device = ret;
+     * ASSERT_EQ(AudioDevice::OUT_ALL, device);
+     */
+}
+
+TEST_P(InputStreamTest, GettersTest) {
+    testAccessors(stream.get(), audioConfig);
+}
+TEST_P(OutputStreamTest, GettersTest) {
+    testAccessors(stream.get(), audioConfig);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// AudioPatches ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+
+TEST_F(AudioPrimaryHidlTest, AudioPatches) {
+    doc::test("Test if audio patches are supported");
+    auto result = device->supportsAudioPatches();
+    ASSERT_TRUE(result.isOk());
+    bool supportsAudioPatch = result;
+    if (!supportsAudioPatch) {
+        doc::partialTest("Audio patches are not supported");
+        return;
+    }
+    // TODO: test audio patches
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////// Clean caches on global tear down ////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char** argv) {
+    environment = new Environment;
+    ::testing::AddGlobalTestEnvironment(environment);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
diff --git a/audio/2.0/vts/functional/utility/AssertOk.h b/audio/2.0/vts/functional/utility/AssertOk.h
new file mode 100644
index 0000000..5397436
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/AssertOk.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <hidl/Status.h>
+
+namespace detail {
+
+inline void assertOk(::android::hardware::Return<void> ret) {
+    ASSERT_TRUE(ret.isOk());
+}
+
+inline void assertOk(::android::hardware::audio::V2_0::Result result) {
+    ASSERT_EQ(decltype(result)::OK, result);
+}
+
+inline void assertOk(::android::hardware::Return<::android::hardware::audio::V2_0::Result> ret) {
+    ASSERT_TRUE(ret.isOk());
+    ::android::hardware::audio::V2_0::Result result = ret;
+    assertOk(result);
+}
+
+}
+
+// Test anything provided is and contains only OK
+#define ASSERT_OK(ret) ASSERT_NO_FATAL_FAILURE(detail::assertOk(ret))
+#define EXPECT_OK(ret) EXPECT_NO_FATAL_FAILURE(detail::assertOk(ret))
diff --git a/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h b/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h
new file mode 100644
index 0000000..8dfcb29
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+// Use HIDL generated toString methods to pretty print gtest errors
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V2_0 {
+inline void PrintTo(const Result& result, ::std::ostream* os) {
+    *os << toString(result);
+}
+} // namespace V2_0
+namespace common {
+namespace V2_0 {
+inline void PrintTo(const AudioConfig& config, ::std::ostream* os) {
+    *os << toString(config);
+}
+} // namespace V2_0
+} // namespace common
+}
+}
+}
diff --git a/audio/2.0/vts/functional/utility/ReturnIn.h b/audio/2.0/vts/functional/utility/ReturnIn.h
new file mode 100644
index 0000000..bb2389a
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/ReturnIn.h
@@ -0,0 +1,58 @@
+/*
+ * 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 <tuple>
+
+namespace utility {
+
+namespace detail {
+// Helper class to generate the HIDL synchronous callback
+template <class... ResultStore>
+class ReturnIn {
+ public:
+    // Provide to the constructor the variables where the output parameters must be copied
+    // TODO: take pointers to match google output parameter style ?
+    ReturnIn(ResultStore&... ts) : results(ts...) {}
+    // Synchronous callback
+    template <class... Results>
+    void operator() (Results&&...results) {
+        set(std::forward<Results>(results)...);
+    }
+ private:
+    // Recursively set all output parameters
+    template <class Head, class... Tail>
+    void set(Head&& head, Tail&&... tail) {
+        std::get<sizeof...(ResultStore) - sizeof...(Tail) - 1>(results)
+                  = std::forward<Head>(head);
+        set(tail...);
+    }
+    // Trivial case
+    void set() {}
+
+    // All variables to set are stored here
+    std::tuple<ResultStore&...> results;
+};
+} // namespace detail
+
+// Generate the HIDL synchronous callback with a copy policy
+// Input: the variables (lvalue reference) where to save the return values
+// Output: the callback to provide to a HIDL call with a synchronous callback
+// The output parameters *will be copied* do not use this function if you have
+// a zero copy policy
+template <class... ResultStore>
+detail::ReturnIn<ResultStore...> returnIn(ResultStore&... ts) { return {ts...};}
+
+}
diff --git a/audio/Android.bp b/audio/Android.bp
index 3121a36..8a1e892 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -1,6 +1,7 @@
 // This is an autogenerated file, do not edit.
 subdirs = [
     "2.0",
+    "2.0/vts/functional",
     "common/2.0",
     "effect/2.0",
     "effect/2.0/vts/functional",
diff --git a/bluetooth/1.0/default/async_fd_watcher.cc b/bluetooth/1.0/default/async_fd_watcher.cc
index 287d007..2f23a69 100644
--- a/bluetooth/1.0/default/async_fd_watcher.cc
+++ b/bluetooth/1.0/default/async_fd_watcher.cc
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <condition_variable>
+#include <map>
 #include <mutex>
 #include <thread>
 #include <vector>
@@ -26,6 +27,8 @@
 #include "sys/select.h"
 #include "unistd.h"
 
+static const int INVALID_FD = -1;
+
 namespace android {
 namespace hardware {
 namespace bluetooth {
@@ -36,8 +39,7 @@
   // Add file descriptor and callback
   {
     std::unique_lock<std::mutex> guard(internal_mutex_);
-    read_fd_ = file_descriptor;
-    cb_ = on_read_fd_ready_callback;
+    watched_fds_[file_descriptor] = on_read_fd_ready_callback;
   }
 
   // Start the thread if not started yet
@@ -58,7 +60,7 @@
   return 0;
 }
 
-void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
+void AsyncFdWatcher::StopWatchingFileDescriptors() { stopThread(); }
 
 AsyncFdWatcher::~AsyncFdWatcher() {}
 
@@ -90,8 +92,7 @@
 
   {
     std::unique_lock<std::mutex> guard(internal_mutex_);
-    cb_ = nullptr;
-    read_fd_ = -1;
+    watched_fds_.clear();
   }
 
   {
@@ -115,7 +116,11 @@
     fd_set read_fds;
     FD_ZERO(&read_fds);
     FD_SET(notification_listen_fd_, &read_fds);
-    FD_SET(read_fd_, &read_fds);
+    int max_read_fd = INVALID_FD;
+    for (auto& it : watched_fds_) {
+      FD_SET(it.first, &read_fds);
+      max_read_fd = std::max(max_read_fd, it.first);
+    }
 
     struct timeval timeout;
     struct timeval* timeout_ptr = NULL;
@@ -126,7 +131,7 @@
     }
 
     // Wait until there is data available to read on some FD.
-    int nfds = std::max(notification_listen_fd_, read_fd_);
+    int nfds = std::max(notification_listen_fd_, max_read_fd);
     int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
 
     // There was some error.
@@ -153,10 +158,21 @@
       continue;
     }
 
-    // Invoke the data ready callback if appropriate.
-    if (FD_ISSET(read_fd_, &read_fds)) {
+    // Invoke the data ready callbacks if appropriate.
+    std::vector<decltype(watched_fds_)::value_type> saved_callbacks;
+    {
       std::unique_lock<std::mutex> guard(internal_mutex_);
-      if (cb_) cb_(read_fd_);
+      for (auto& it : watched_fds_) {
+        if (FD_ISSET(it.first, &read_fds)) {
+          saved_callbacks.push_back(it);
+        }
+      }
+    }
+
+    for (auto& it : saved_callbacks) {
+      if (it.second) {
+        it.second(it.first);
+      }
     }
   }
 }
diff --git a/bluetooth/1.0/default/async_fd_watcher.h b/bluetooth/1.0/default/async_fd_watcher.h
index 3f7ff54..358cbc3 100644
--- a/bluetooth/1.0/default/async_fd_watcher.h
+++ b/bluetooth/1.0/default/async_fd_watcher.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <map>
 #include <mutex>
 #include <thread>
 
@@ -36,7 +37,7 @@
                                  const ReadCallback& on_read_fd_ready_callback);
   int ConfigureTimeout(const std::chrono::milliseconds timeout,
                        const TimeoutCallback& on_timeout_callback);
-  void StopWatchingFileDescriptor();
+  void StopWatchingFileDescriptors();
 
  private:
   AsyncFdWatcher(const AsyncFdWatcher&) = delete;
@@ -52,10 +53,9 @@
   std::mutex internal_mutex_;
   std::mutex timeout_mutex_;
 
-  int read_fd_;
+  std::map<int, ReadCallback> watched_fds_;
   int notification_listen_fd_;
   int notification_write_fd_;
-  ReadCallback cb_;
   TimeoutCallback timeout_cb_;
   std::chrono::milliseconds timeout_ms_;
 };
diff --git a/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc b/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
index a7f5bda..b0c533c 100644
--- a/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
+++ b/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#define LOG_TAG "async_fd_watcher_unittest"
+
 #include "async_fd_watcher.h"
 #include <gtest/gtest.h>
 #include <cstdint>
@@ -122,8 +124,8 @@
   }
 
   void CleanUpServer() {
-    async_fd_watcher_.StopWatchingFileDescriptor();
-    conn_watcher_.StopWatchingFileDescriptor();
+    async_fd_watcher_.StopWatchingFileDescriptors();
+    conn_watcher_.StopWatchingFileDescriptors();
     close(socket_fd_);
   }
 
@@ -211,7 +213,7 @@
   });
 
   ConnectClient();
-  conn_watcher.StopWatchingFileDescriptor();
+  conn_watcher.StopWatchingFileDescriptors();
   close(socket_fd);
 }
 
@@ -233,7 +235,7 @@
   EXPECT_FALSE(timed_out);
   sleep(1);
   EXPECT_TRUE(timed_out);
-  conn_watcher.StopWatchingFileDescriptor();
+  conn_watcher.StopWatchingFileDescriptors();
   close(socket_fd);
 }
 
@@ -265,10 +267,64 @@
   sleep(1);
   EXPECT_TRUE(timed_out);
   EXPECT_TRUE(timed_out2);
-  conn_watcher.StopWatchingFileDescriptor();
+  conn_watcher.StopWatchingFileDescriptors();
   close(socket_fd);
 }
 
+// Use a single AsyncFdWatcher to watch two file descriptors.
+TEST_F(AsyncFdWatcherSocketTest, WatchTwoFileDescriptors) {
+  int sockfd[2];
+  socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+  bool cb1_called = false;
+  bool* cb1_called_ptr = &cb1_called;
+  bool cb2_called = false;
+  bool* cb2_called_ptr = &cb2_called;
+
+  AsyncFdWatcher watcher;
+  watcher.WatchFdForNonBlockingReads(sockfd[0], [cb1_called_ptr](int fd) {
+    char read_buf[1] = {0};
+    int n = TEMP_FAILURE_RETRY(read(fd, read_buf, sizeof(read_buf)));
+    ASSERT_TRUE(n == sizeof(read_buf));
+    ASSERT_TRUE(read_buf[0] == '1');
+    *cb1_called_ptr = true;
+  });
+
+  watcher.WatchFdForNonBlockingReads(sockfd[1], [cb2_called_ptr](int fd) {
+    char read_buf[1] = {0};
+    int n = TEMP_FAILURE_RETRY(read(fd, read_buf, sizeof(read_buf)));
+    ASSERT_TRUE(n == sizeof(read_buf));
+    ASSERT_TRUE(read_buf[0] == '2');
+    *cb2_called_ptr = true;
+  });
+
+  // Fail if the test doesn't pass within 3 seconds
+  watcher.ConfigureTimeout(std::chrono::seconds(3), [this]() {
+    bool connection_timeout = true;
+    ASSERT_FALSE(connection_timeout);
+  });
+
+  EXPECT_FALSE(cb1_called);
+  EXPECT_FALSE(cb2_called);
+
+  char one_buf[1] = {'1'};
+  TEMP_FAILURE_RETRY(write(sockfd[1], one_buf, sizeof(one_buf)));
+
+  sleep(1);
+
+  EXPECT_TRUE(cb1_called);
+  EXPECT_FALSE(cb2_called);
+
+  char two_buf[1] = {'2'};
+  TEMP_FAILURE_RETRY(write(sockfd[0], two_buf, sizeof(two_buf)));
+
+  sleep(1);
+
+  EXPECT_TRUE(cb1_called);
+  EXPECT_TRUE(cb2_called);
+
+  watcher.StopWatchingFileDescriptors();
+}
+
 // Use two AsyncFdWatchers to set up a server socket.
 TEST_F(AsyncFdWatcherSocketTest, ClientServer) {
   ConfigureServer();
diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc
index 3878129..2c55d1c 100644
--- a/bluetooth/1.0/default/vendor_interface.cc
+++ b/bluetooth/1.0/default/vendor_interface.cc
@@ -274,7 +274,7 @@
 }
 
 void VendorInterface::Close() {
-  fd_watcher_.StopWatchingFileDescriptor();
+  fd_watcher_.StopWatchingFileDescriptors();
 
   if (lib_interface_ != nullptr) {
     bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE;
diff --git a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
index 5b6089d..4aa6d7e 100644
--- a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
+++ b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
@@ -325,18 +325,42 @@
 }
 
 /*
+ * PowerCycleAfterClose:
+ * Calls powerCycle() after close()
+ * Checks status
+ */
+TEST_F(NfcHidlTest, PowerCycleAfterClose) {
+  EXPECT_EQ(NfcStatus::OK, nfc_->close());
+  // Wait for CLOSE_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+
+  EXPECT_EQ(NfcStatus::FAILED, nfc_->powerCycle());
+
+  EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+  // Wait for OPEN_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+/*
  * CoreInitialized:
- * Calls coreInitialized()
+ * Calls coreInitialized() with different data
  * Waits for NfcEvent.POST_INIT_CPLT
  */
 TEST_F(NfcHidlTest, CoreInitialized) {
   NfcData data;
   data.resize(1);
-  data[0] = 0;
-  EXPECT_EQ(NfcStatus::OK, nfc_->coreInitialized(data));
-  // Wait for NfcEvent.POST_INIT_CPLT
-  EXPECT_EQ(std::cv_status::no_timeout, wait());
-  EXPECT_EQ(NfcEvent::POST_INIT_CPLT, last_event_);
+  for (int i = 0; i <= 6; i++)
+  {
+    data[0] = i;
+    EXPECT_EQ(NfcStatus::OK, nfc_->coreInitialized(data));
+    // Wait for NfcEvent.POST_INIT_CPLT
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(NfcEvent::POST_INIT_CPLT, last_event_);
+  }
 }
 
 /*
@@ -348,6 +372,27 @@
   EXPECT_EQ(NfcStatus::OK, nfc_->controlGranted());
 }
 
+/*
+ * ControlGrantedAfterClose:
+ * Call controlGranted() after close
+ * Checks the return value
+ */
+TEST_F(NfcHidlTest, ControlGrantedAfterClose) {
+  EXPECT_EQ(NfcStatus::OK, nfc_->close());
+  // Wait for CLOSE_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+
+  EXPECT_EQ(NfcStatus::OK, nfc_->controlGranted());
+
+  EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+  // Wait for OPEN_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
 /* PreDiscover:
  * Calls prediscover()
  * Checks the return value
@@ -356,6 +401,59 @@
   EXPECT_EQ(NfcStatus::OK, nfc_->prediscover());
 }
 
+/*
+ * PreDiscoverAfterClose:
+ * Call prediscover() after close
+ * Checks the return value
+ */
+TEST_F(NfcHidlTest, PreDiscoverAfterClose) {
+  EXPECT_EQ(NfcStatus::OK, nfc_->close());
+  // Wait for CLOSE_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+
+  EXPECT_EQ(NfcStatus::OK, nfc_->prediscover());
+
+  EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+  // Wait for OPEN_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+/*
+ * CloseAfterClose:
+ * Calls close() multiple times
+ * Checks status
+ */
+TEST_F(NfcHidlTest, CloseAfterClose) {
+  EXPECT_EQ(NfcStatus::OK, nfc_->close());
+  // Wait for CLOSE_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+
+  EXPECT_EQ(NfcStatus::FAILED, nfc_->close());
+
+  EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+  // Wait for OPEN_CPLT event
+  EXPECT_EQ(std::cv_status::no_timeout, wait());
+  EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+  EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+
+/*
+ * OpenAfterOpen:
+ * Calls open() multiple times
+ * Checks status
+ */
+TEST_F(NfcHidlTest, OpenAfterOpen) {
+  EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+  EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+}
+
 int main(int argc, char** argv) {
   ::testing::AddGlobalTestEnvironment(new NfcHidlEnvironment);
   ::testing::InitGoogleTest(&argc, argv);