Add VTS tests for the primary audio hal

Test: run the vts test on target
Test: vts-tradefed r vts-hal-hidl -m VtsHalAudioV2_0Target

Bug: 34170075
Change-Id: I4bd6cb0aa5b7cc628537cd7c024542c4db8b592d
Signed-off-by: Kevin Rocard <krocard@google.com>
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",