Merge "Camera2: Add distortion correction support" into pi-dev
diff --git a/audio/4.0/config/audio_policy_configuration.xsd b/audio/4.0/config/audio_policy_configuration.xsd
index e5b4449..34c2b11 100644
--- a/audio/4.0/config/audio_policy_configuration.xsd
+++ b/audio/4.0/config/audio_policy_configuration.xsd
@@ -104,7 +104,7 @@
                         <xs:element name="devicePorts" type="devicePorts" minOccurs="0"/>
                         <xs:element name="routes" type="routes" minOccurs="0"/>
                     </xs:sequence>
-                    <xs:attribute name="name" type="xsd:string" use="required"/>
+                    <xs:attribute name="name" type="xs:string" use="required"/>
                     <xs:attribute name="halVersion" type="halVersion" use="required"/>
                 </xs:complexType>
                 <xs:unique name="mixPortNameUniqueness">
diff --git a/audio/common/4.0/types.hal b/audio/common/4.0/types.hal
index dfc350e..e55491d 100644
--- a/audio/common/4.0/types.hal
+++ b/audio/common/4.0/types.hal
@@ -373,21 +373,21 @@
     OUT_TOP_BACK_LEFT         = 0x8000,
     OUT_TOP_BACK_CENTER       = 0x10000,
     OUT_TOP_BACK_RIGHT        = 0x20000,
-    OUT_TOP_CENTER_LEFT       = 0x40000,
-    OUT_TOP_CENTER_RIGHT      = 0x80000,
+    OUT_TOP_SIDE_LEFT         = 0x40000,
+    OUT_TOP_SIDE_RIGHT        = 0x80000,
 
     OUT_MONO     = OUT_FRONT_LEFT,
     OUT_STEREO   = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
     OUT_2POINT1  = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
     OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT |
-                         OUT_TOP_CENTER_LEFT | OUT_TOP_CENTER_RIGHT),
+                         OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
     OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT |
-                         OUT_TOP_CENTER_LEFT | OUT_TOP_CENTER_RIGHT |
+                         OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT |
                          OUT_LOW_FREQUENCY),
     OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_CENTER | OUT_FRONT_RIGHT |
-                         OUT_TOP_CENTER_LEFT | OUT_TOP_CENTER_RIGHT),
+                         OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
     OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_CENTER | OUT_FRONT_RIGHT |
-                        OUT_TOP_CENTER_LEFT | OUT_TOP_CENTER_RIGHT |
+                        OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT |
                         OUT_LOW_FREQUENCY),
     OUT_QUAD     = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT |
             OUT_BACK_LEFT | OUT_BACK_RIGHT),
@@ -406,6 +406,10 @@
     OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT |
             OUT_FRONT_CENTER | OUT_LOW_FREQUENCY |
             OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+    OUT_5POINT1POINT2  = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+    OUT_5POINT1POINT4  = (OUT_5POINT1 |
+            OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT |
+            OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
     OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT |
             OUT_FRONT_CENTER | OUT_LOW_FREQUENCY |
             OUT_BACK_LEFT | OUT_BACK_RIGHT |
@@ -415,6 +419,10 @@
             OUT_FRONT_CENTER | OUT_LOW_FREQUENCY |
             OUT_BACK_LEFT | OUT_BACK_RIGHT |
             OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+    OUT_7POINT1POINT2  = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+    OUT_7POINT1POINT4  = (OUT_7POINT1 |
+            OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT |
+            OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
     // Note that the 2.0 OUT_ALL* have been moved to helper functions
 
     /* These are bits only, not complete values */
diff --git a/audio/common/all-versions/test/utility/include/utility/ReturnIn.h b/audio/common/all-versions/test/utility/include/utility/ReturnIn.h
index 2b92a21..7fd0d4a 100644
--- a/audio/common/all-versions/test/utility/include/utility/ReturnIn.h
+++ b/audio/common/all-versions/test/utility/include/utility/ReturnIn.h
@@ -45,7 +45,7 @@
     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...);
+        set(std::forward<Tail>(tail)...);
     }
     // Trivial case
     void set() {}
@@ -56,7 +56,7 @@
 }  // namespace detail
 
 // Generate the HIDL synchronous callback with a copy policy
-// Input: the variables (lvalue reference) where to save the return values
+// Input: the variables (lvalue references) where to copy 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
diff --git a/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h b/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h
index a998b06..70c3d56 100644
--- a/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h
+++ b/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h
@@ -52,6 +52,12 @@
     return EnumConverter<Source, Destination>{source};
 }
 
+/** Allows converting an enum to its bitfield or itself. */
+template <class Enum>
+EnumConverter<Enum> mkBitfield(Enum value) {
+    return EnumConverter<Enum>{value};
+}
+
 }  // namespace utils
 }  // namespace common
 }  // namespace audio
diff --git a/audio/core/4.0/vts/OWNERS b/audio/core/4.0/vts/OWNERS
new file mode 100644
index 0000000..8711a9f
--- /dev/null
+++ b/audio/core/4.0/vts/OWNERS
@@ -0,0 +1,5 @@
+elaurent@google.com
+krocard@google.com
+mnaganov@google.com
+yim@google.com
+zhuoyao@google.com
\ No newline at end of file
diff --git a/audio/core/4.0/vts/functional/Android.bp b/audio/core/4.0/vts/functional/Android.bp
new file mode 100644
index 0000000..dde3e7b
--- /dev/null
+++ b/audio/core/4.0/vts/functional/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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: "VtsHalAudioV4_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "AudioPrimaryHidlHalTest.cpp",
+        "ValidateAudioConfiguration.cpp"
+    ],
+    static_libs: [
+        "android.hardware.audio.common.test.utility",
+        "android.hardware.audio@4.0",
+        "android.hardware.audio.common@4.0",
+        "libxml2",
+    ],
+    shared_libs: [
+        "libicuuc",
+    ],
+    header_libs: [
+        "android.hardware.audio.common.util@all-versions",
+    ],
+}
diff --git a/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..a568a3c
--- /dev/null
+++ b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,1324 @@
+/*
+ * 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 "VtsHalAudioV4_0TargetTest"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdio>
+#include <initializer_list>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include <android-base/logging.h>
+
+#include <android/hardware/audio/4.0/IDevice.h>
+#include <android/hardware/audio/4.0/IDevicesFactory.h>
+#include <android/hardware/audio/4.0/IPrimaryDevice.h>
+#include <android/hardware/audio/4.0/types.h>
+#include <android/hardware/audio/common/4.0/types.h>
+
+#include <common/all-versions/VersionUtils.h>
+
+#include "utility/AssertOk.h"
+#include "utility/Documentation.h"
+#include "utility/EnvironmentTearDown.h"
+#define AUDIO_HAL_VERSION V4_0
+#include "utility/PrettyPrintAudioTypes.h"
+#include "utility/ReturnIn.h"
+
+using std::initializer_list;
+using std::string;
+using std::to_string;
+using std::vector;
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::audio::V4_0::AudioDrain;
+using ::android::hardware::audio::V4_0::DeviceAddress;
+using ::android::hardware::audio::V4_0::IDevice;
+using ::android::hardware::audio::V4_0::IPrimaryDevice;
+using TtyMode = ::android::hardware::audio::V4_0::IPrimaryDevice::TtyMode;
+using ::android::hardware::audio::V4_0::IDevicesFactory;
+using ::android::hardware::audio::V4_0::IStream;
+using ::android::hardware::audio::V4_0::IStreamIn;
+using ::android::hardware::audio::V4_0::TimeSpec;
+using ReadParameters = ::android::hardware::audio::V4_0::IStreamIn::ReadParameters;
+using ReadStatus = ::android::hardware::audio::V4_0::IStreamIn::ReadStatus;
+using ::android::hardware::audio::V4_0::IStreamOut;
+using ::android::hardware::audio::V4_0::IStreamOutCallback;
+using ::android::hardware::audio::V4_0::MmapBufferInfo;
+using ::android::hardware::audio::V4_0::MmapPosition;
+using ::android::hardware::audio::V4_0::ParameterValue;
+using ::android::hardware::audio::V4_0::Result;
+using ::android::hardware::audio::V4_0::SourceMetadata;
+using ::android::hardware::audio::V4_0::SinkMetadata;
+using ::android::hardware::audio::common::V4_0::AudioChannelMask;
+using ::android::hardware::audio::common::V4_0::AudioConfig;
+using ::android::hardware::audio::common::V4_0::AudioDevice;
+using ::android::hardware::audio::common::V4_0::AudioFormat;
+using ::android::hardware::audio::common::V4_0::AudioHandleConsts;
+using ::android::hardware::audio::common::V4_0::AudioHwSync;
+using ::android::hardware::audio::common::V4_0::AudioInputFlag;
+using ::android::hardware::audio::common::V4_0::AudioIoHandle;
+using ::android::hardware::audio::common::V4_0::AudioMode;
+using ::android::hardware::audio::common::V4_0::AudioOffloadInfo;
+using ::android::hardware::audio::common::V4_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V4_0::AudioSource;
+using ::android::hardware::audio::common::V4_0::ThreadInfo;
+using ::android::hardware::audio::common::utils::mkBitfield;
+
+using namespace ::android::hardware::audio::common::test::utility;
+
+class AudioHidlTestEnvironment : public ::Environment {
+   public:
+    virtual void registerTestServices() override { registerTestService<IDevicesFactory>(); }
+};
+
+// Instance to register global tearDown
+static AudioHidlTestEnvironment* environment;
+
+class HidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   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 = ::testing::VtsHalHidlTargetTestBase::getService<IDevicesFactory>(
+                environment->getServiceName<IDevicesFactory>("default"));
+        }
+        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)");
+}
+
+TEST_F(AudioHidlTest, OpenDeviceInvalidParameter) {
+    doc::test("test passing an invalid parameter to openDevice");
+    Result result;
+    sp<IDevice> device;
+    ASSERT_OK(devicesFactory->openDevice("Non existing device", returnIn(result, device)));
+    ASSERT_EQ(Result::INVALID_ARGUMENTS, result);
+    ASSERT_TRUE(device == nullptr);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// 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) {
+            Result result;
+            sp<IDevice> baseDevice;
+            ASSERT_OK(devicesFactory->openDevice("primary", returnIn(result, baseDevice)));
+            ASSERT_OK(result);
+            ASSERT_TRUE(baseDevice != nullptr);
+
+            environment->registerTearDown([] { device.clear(); });
+            device = IPrimaryDevice::castFrom(baseDevice);
+            ASSERT_TRUE(device != nullptr);
+        }
+    }
+
+   protected:
+    // Cache the device opening to speed up each test by ~0.5s
+    static sp<IPrimaryDevice> device;
+};
+sp<IPrimaryDevice> 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}{Master,Mic}{Mute,Volume} /////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+template <class Property>
+class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
+   protected:
+    /** Test a property getter and setter. */
+    template <class Getter, class Setter>
+    void testAccessors(const string& propertyName, const vector<Property>& valuesToTest,
+                       Setter setter, Getter getter, const vector<Property>& invalidValues = {}) {
+        Property initialValue;  // Save initial value to restore it at the end
+                                // of the test
+        ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue)));
+        ASSERT_OK(res);
+
+        for (Property setValue : valuesToTest) {
+            SCOPED_TRACE("Test " + propertyName + " getter and setter for " +
+                         testing::PrintToString(setValue));
+            ASSERT_OK((device.get()->*setter)(setValue));
+            Property getValue;
+            // Make sure the getter returns the same value just set
+            ASSERT_OK((device.get()->*getter)(returnIn(res, getValue)));
+            ASSERT_OK(res);
+            EXPECT_EQ(setValue, getValue);
+        }
+
+        for (Property invalidValue : invalidValues) {
+            SCOPED_TRACE("Try to set " + propertyName + " with the invalid value " +
+                         testing::PrintToString(invalidValue));
+            EXPECT_RESULT(Result::INVALID_ARGUMENTS, (device.get()->*setter)(invalidValue));
+        }
+
+        ASSERT_OK((device.get()->*setter)(initialValue));  // restore initial value
+    }
+
+    /** Test the getter and setter of an optional feature. */
+    template <class Getter, class Setter>
+    void testOptionalAccessors(const string& propertyName, const vector<Property>& valuesToTest,
+                               Setter setter, Getter getter,
+                               const vector<Property>& invalidValues = {}) {
+        doc::test("Test the optional " + propertyName + " getters and setter");
+        {
+            SCOPED_TRACE("Test feature support by calling the getter");
+            Property initialValue;
+            ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue)));
+            if (res == Result::NOT_SUPPORTED) {
+                doc::partialTest(propertyName + " getter is not supported");
+                return;
+            }
+            ASSERT_OK(res);  // If it is supported it must succeed
+        }
+        // The feature is supported, test it
+        testAccessors(propertyName, valuesToTest, setter, getter, invalidValues);
+    }
+};
+
+using BoolAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<bool>;
+
+TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) {
+    doc::test("Check that the mic can be muted and unmuted");
+    testAccessors("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");
+    testOptionalAccessors("master mute", {true, false, true}, &IDevice::setMasterMute,
+                          &IDevice::getMasterMute);
+    // TODO: check that the master volume is really muted
+}
+
+using FloatAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<float>;
+TEST_F(FloatAccessorPrimaryHidlTest, MasterVolumeTest) {
+    doc::test("Test the master volume if supported");
+    testOptionalAccessors(
+        "master volume", {0, 0.5, 1}, &IDevice::setMasterVolume, &IDevice::getMasterVolume,
+        {-0.1, 1.1, NAN, INFINITY, -INFINITY, 1 + std::numeric_limits<float>::epsilon()});
+    // TODO: check that the master volume is really changed
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// AudioPatches ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class AudioPatchPrimaryHidlTest : public AudioPrimaryHidlTest {
+   protected:
+    bool areAudioPatchesSupported() {
+        auto result = device->supportsAudioPatches();
+        EXPECT_IS_OK(result);
+        return result;
+    }
+};
+
+TEST_F(AudioPatchPrimaryHidlTest, AudioPatches) {
+    doc::test("Test if audio patches are supported");
+    if (!areAudioPatchesSupported()) {
+        doc::partialTest("Audio patches are not supported");
+        return;
+    }
+    // TODO: test audio patches
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////// 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 AudioPatchPrimaryHidlTest {
+   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 = mkBitfield(channelMask);
+                    config.sampleRateHz = sampleRate;
+                    config.format = format;
+                    // FIXME: leave frameCount to 0 ?
+                    configs.push_back(config);
+                }
+            }
+        }
+        return configs;
+    }
+};
+
+/** Generate a test name based on an audio config.
+ *
+ * As the only parameter changing are channel mask and sample rate,
+ * only print those ones in the test name.
+ */
+static string generateTestName(const testing::TestParamInfo<AudioConfig>& info) {
+    const AudioConfig& config = info.param;
+    return to_string(info.index) + "__" + to_string(config.sampleRateHz) + "_" +
+           // "MONO" is more clear than "FRONT_LEFT"
+           ((config.channelMask == mkBitfield(AudioChannelMask::OUT_MONO) ||
+             config.channelMask == mkBitfield(AudioChannelMask::IN_MONO))
+                ? "MONO"
+                : ::testing::PrintToString(config.channelMask));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////// 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()),
+    &generateTestName);
+INSTANTIATE_TEST_CASE_P(
+    SupportedInputBufferSize, RequiredInputBufferSizeTest,
+    ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()),
+    &generateTestName);
+
+// 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()),
+    &generateTestName);
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// 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_IS_OK(ret);
+        Result result = ret;
+        auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED};
+        ASSERT_RESULT(okOrNotSupported, result);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {get,set}Parameters /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, getParameters) {
+    doc::test("Check that the hal can set and get parameters");
+    hidl_vec<ParameterValue> context;
+    hidl_vec<hidl_string> keys;
+    hidl_vec<ParameterValue> values;
+    ASSERT_OK(device->getParameters(context, keys, returnIn(res, values)));
+    ASSERT_OK(device->setParameters(context, values));
+    values.resize(0);
+    ASSERT_OK(device->setParameters(context, values));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// debugDebug //////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+template <class DebugDump>
+static void testDebugDump(DebugDump debugDump) {
+    // File descriptors to our pipe. fds[0] corresponds to the read end and
+    // fds[1] to the write end.
+    int fds[2];
+    ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+    // Make sure that the pipe is at least 1 MB in size. The test process runs
+    // in su domain, so it should be safe to make this call.
+    fcntl(fds[0], F_SETPIPE_SZ, 1 << 20);
+
+    // Wrap the temporary file file descriptor in a native handle
+    auto* nativeHandle = native_handle_create(1, 0);
+    ASSERT_NE(nullptr, nativeHandle);
+    nativeHandle->data[0] = fds[1];
+
+    // Wrap this native handle in a hidl handle
+    hidl_handle handle;
+    handle.setTo(nativeHandle, false /*take ownership*/);
+
+    ASSERT_OK(debugDump(handle));
+
+    // Check that at least one bit was written by the hal
+    // TODO: debugDump does not return a Result.
+    // This mean that the hal can not report that it not implementing the
+    // function.
+    char buff;
+    if (read(fds[0], &buff, 1) != 1) {
+        doc::note("debugDump does not seem implemented");
+    }
+    EXPECT_EQ(0, close(fds[0])) << errno;
+    EXPECT_EQ(0, close(fds[1])) << errno;
+}
+
+TEST_F(AudioPrimaryHidlTest, DebugDump) {
+    doc::test("Check that the hal can dump its state without error");
+    testDebugDump([](const auto& handle) { return device->debug(handle, {/* options */}); });
+}
+
+TEST_F(AudioPrimaryHidlTest, DebugDumpInvalidArguments) {
+    doc::test("Check that the hal dump doesn't crash on invalid arguments");
+    ASSERT_OK(device->debug(hidl_handle(), {/* options */}));
+}
+
+TEST_F(AudioPrimaryHidlTest, SetConnectedState) {
+    doc::test("Check that the HAL can be notified of device connection and deconnection");
+    using AD = AudioDevice;
+    for (auto deviceType : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) {
+        SCOPED_TRACE("device=" + ::testing::PrintToString(deviceType));
+        for (bool state : {true, false}) {
+            SCOPED_TRACE("state=" + ::testing::PrintToString(state));
+            DeviceAddress address = {};
+            address.device = deviceType;
+            auto ret = device->setConnectedState(address, state);
+            ASSERT_TRUE(ret.isOk());
+            if (res == Result::NOT_SUPPORTED) {
+                doc::partialTest("setConnectedState is not supported");
+                return;
+            }
+            ASSERT_OK(res);
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// 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;
+    }
+
+    Return<Result> closeStream() {
+        open = false;
+        return stream->close();
+    }
+
+   private:
+    void TearDown() override {
+        if (open) {
+            ASSERT_OK(stream->close());
+        }
+    }
+
+   protected:
+    AudioConfig audioConfig;
+    DeviceAddress address = {};
+    sp<Stream> stream;
+    bool open = false;
+};
+
+////////////////////////////// openOutputStream //////////////////////////////
+
+class OutputStreamTest : public OpenStreamTest<IStreamOut> {
+    virtual void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp());  // setup base
+        address.device = AudioDevice::OUT_DEFAULT;
+        const AudioConfig& config = GetParam();
+        // TODO: test all flag combination
+        auto flags = hidl_bitfield<AudioOutputFlag>(AudioOutputFlag::NONE);
+        SourceMetadata metadata = {{{}}};  // create on track metadata
+        testOpen(
+            [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+                return device->openOutputStream(handle, address, config, flags, metadata, 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()),
+    &generateTestName);
+INSTANTIATE_TEST_CASE_P(
+    SupportedOutputStreamConfig, OutputStreamTest,
+    ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedPlaybackAudioConfig()),
+    &generateTestName);
+
+INSTANTIATE_TEST_CASE_P(
+    RecommendedOutputStreamConfigSupport, OutputStreamTest,
+    ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportPlaybackAudioConfig()),
+    &generateTestName);
+
+////////////////////////////// openInputStream //////////////////////////////
+
+class InputStreamTest : public OpenStreamTest<IStreamIn> {
+    virtual void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp());  // setup base
+        address.device = AudioDevice::IN_DEFAULT;
+        const AudioConfig& config = GetParam();
+        // TODO: test all supported flags and source
+        auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
+        SinkMetadata metadata = {{{AudioSource::DEFAULT, 1}}};
+        testOpen(
+            [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+                return device->openInputStream(handle, address, config, flags, metadata, 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()),
+    &generateTestName);
+INSTANTIATE_TEST_CASE_P(
+    SupportedInputStreamConfig, InputStreamTest,
+    ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()),
+    &generateTestName);
+
+INSTANTIATE_TEST_CASE_P(
+    RecommendedInputStreamConfigSupport, InputStreamTest,
+    ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()),
+    &generateTestName);
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////// 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()) {
+        EXPECT_IS_OK(ret);
+        return R{};
+    }
+    return ret;
+}
+
+/* Could not find a way to write a test for two parametrized class fixure
+ * thus use this macro do duplicate tests for Input and Output stream */
+#define TEST_IO_STREAM(test_name, documentation, code) \
+    TEST_P(InputStreamTest, test_name) {               \
+        doc::test(documentation);                      \
+        code;                                          \
+    }                                                  \
+    TEST_P(OutputStreamTest, test_name) {              \
+        doc::test(documentation);                      \
+        code;                                          \
+    }
+
+TEST_IO_STREAM(GetFrameCount, "Check that the stream frame count == the one it was opened with",
+               ASSERT_EQ(audioConfig.frameCount, extract(stream->getFrameCount())))
+
+TEST_IO_STREAM(GetSampleRate, "Check that the stream sample rate == the one it was opened with",
+               ASSERT_EQ(audioConfig.sampleRateHz, extract(stream->getSampleRate())))
+
+TEST_IO_STREAM(GetChannelMask, "Check that the stream channel mask == the one it was opened with",
+               ASSERT_EQ(audioConfig.channelMask, extract(stream->getChannelMask())))
+
+TEST_IO_STREAM(GetFormat, "Check that the stream format == the one it was opened with",
+               ASSERT_EQ(audioConfig.format, extract(stream->getFormat())))
+
+// TODO: for now only check that the framesize is not incoherent
+TEST_IO_STREAM(GetFrameSize, "Check that the stream frame size == the one it was opened with",
+               ASSERT_GT(extract(stream->getFrameSize()), 0U))
+
+TEST_IO_STREAM(GetBufferSize, "Check that the stream buffer size== the one it was opened with",
+               ASSERT_GE(extract(stream->getBufferSize()), extract(stream->getFrameSize())));
+
+template <class Property, class CapablityGetter>
+static void testCapabilityGetter(const string& name, IStream* stream,
+                                 CapablityGetter capablityGetter,
+                                 Return<Property> (IStream::*getter)(),
+                                 Return<Result> (IStream::*setter)(Property),
+                                 bool currentMustBeSupported = true) {
+    hidl_vec<Property> capabilities;
+    auto ret = capablityGetter(stream, capabilities);
+    if (ret == Result::NOT_SUPPORTED) {
+        doc::partialTest(name + " is not supported");
+        return;
+    };
+    ASSERT_OK(ret);
+
+    if (currentMustBeSupported) {
+        Property currentValue = extract((stream->*getter)());
+        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) {
+        auto ret = (stream->*setter)(capability);
+        ASSERT_TRUE(ret.isOk());
+        if (ret == Result::NOT_SUPPORTED) {
+            doc::partialTest("Setter is not supported");
+            return;
+        }
+        ASSERT_OK(ret);
+        ASSERT_EQ(capability, extract((stream->*getter)()));
+    }
+}
+
+Result getSupportedSampleRates(IStream* stream, hidl_vec<uint32_t>& rates) {
+    Result res;
+    EXPECT_OK(stream->getSupportedSampleRates(extract(stream->getFormat()), returnIn(res, rates)));
+    return res;
+}
+
+Result getSupportedChannelMasks(IStream* stream,
+                                hidl_vec<hidl_bitfield<AudioChannelMask>>& channels) {
+    Result res;
+    EXPECT_OK(
+        stream->getSupportedSampleRates(extract(stream->getFormat()), returnIn(res, channels)));
+    return res;
+}
+
+Result getSupportedFormats(IStream* stream, hidl_vec<AudioFormat>& capabilities) {
+    EXPECT_OK(stream->getSupportedFormats(returnIn(capabilities)));
+    // TODO: this should be an optional function
+    return Result::OK;
+}
+
+TEST_IO_STREAM(SupportedSampleRate, "Check that the stream sample rate is declared as supported",
+               testCapabilityGetter("getSupportedSampleRate", stream.get(),
+                                    &getSupportedSampleRates, &IStream::getSampleRate,
+                                    &IStream::setSampleRate,
+                                    // getSupportedSampleRate returns the native sampling rates,
+                                    // (the sampling rates that can be played without resampling)
+                                    // but other sampling rates can be supported by the HAL.
+                                    false))
+
+TEST_IO_STREAM(SupportedChannelMask, "Check that the stream channel mask is declared as supported",
+               testCapabilityGetter("getSupportedChannelMask", stream.get(),
+                                    &getSupportedChannelMasks, &IStream::getChannelMask,
+                                    &IStream::setChannelMask))
+
+TEST_IO_STREAM(SupportedFormat, "Check that the stream format is declared as supported",
+               testCapabilityGetter("getSupportedFormat", stream.get(), &getSupportedFormats,
+                                    &IStream::getFormat, &IStream::setFormat))
+
+static void testGetDevice(IStream* stream, AudioDevice expectedDevice) {
+    hidl_vec<DeviceAddress> devices;
+    Result res;
+    ASSERT_OK(stream->getDevices(returnIn(res, devices)));
+    if (res == Result::NOT_SUPPORTED) {
+        return doc::partialTest("GetDevices is not supported");
+    }
+    // The stream was constructed with one device, thus getDevices must only return one
+    ASSERT_EQ(1U, devices.size());
+    AudioDevice device = devices[0].device;
+    ASSERT_TRUE(device == expectedDevice)
+        << "Expected: " << ::testing::PrintToString(expectedDevice)
+        << "\n  Actual: " << ::testing::PrintToString(device);
+}
+
+TEST_IO_STREAM(GetDevice, "Check that the stream device == the one it was opened with",
+               areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported")
+                                          : testGetDevice(stream.get(), address.device))
+
+static void testSetDevice(IStream* stream, const DeviceAddress& address) {
+    DeviceAddress otherAddress = address;
+    otherAddress.device = (address.device & AudioDevice::BIT_IN) == 0 ? AudioDevice::OUT_SPEAKER
+                                                                      : AudioDevice::IN_BUILTIN_MIC;
+    EXPECT_OK(stream->setDevices({otherAddress}));
+
+    ASSERT_OK(stream->setDevices({address}));  // Go back to the original value
+}
+
+TEST_IO_STREAM(SetDevice, "Check that the stream can be rerouted to SPEAKER or BUILTIN_MIC",
+               areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported")
+                                          : testSetDevice(stream.get(), address))
+
+static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) {
+    uint32_t sampleRateHz;
+    hidl_bitfield<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);
+}
+
+TEST_IO_STREAM(GetAudioProperties,
+               "Check that the stream audio properties == the ones it was opened with",
+               testGetAudioProperties(stream.get(), audioConfig))
+
+static auto invalidArgsOrNotSupportedOrOK = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED,
+                                             Result::OK};
+TEST_IO_STREAM(SetHwAvSync, "Try to set hardware sync to an invalid value",
+               ASSERT_RESULT(invalidArgsOrNotSupportedOrOK, stream->setHwAvSync(666)))
+
+static void checkGetHwAVSync(IDevice* device) {
+    Result res;
+    AudioHwSync sync;
+    ASSERT_OK(device->getHwAvSync(returnIn(res, sync)));
+    if (res == Result::NOT_SUPPORTED) {
+        return doc::partialTest("getHwAvSync is not supported");
+    }
+    ASSERT_OK(res);
+}
+TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", checkGetHwAVSync(device.get()));
+
+static void checkGetNoParameter(IStream* stream, hidl_vec<hidl_string> keys,
+                                initializer_list<Result> expectedResults) {
+    hidl_vec<ParameterValue> context;
+    hidl_vec<ParameterValue> parameters;
+    Result res;
+    ASSERT_OK(stream->getParameters(context, keys, returnIn(res, parameters)));
+    ASSERT_RESULT(expectedResults, res);
+    if (res == Result::OK) {
+        for (auto& parameter : parameters) {
+            ASSERT_EQ(0U, parameter.value.size()) << toString(parameter);
+        }
+    }
+}
+
+/* Get/Set parameter is intended to be an opaque channel between vendors app and
+ * their HALs.
+ * Thus can not be meaningfully tested.
+ */
+TEST_IO_STREAM(getEmptySetParameter, "Retrieve the values of an empty set",
+               checkGetNoParameter(stream.get(), {} /* keys */, {Result::OK}))
+
+TEST_IO_STREAM(getNonExistingParameter, "Retrieve the values of an non existing parameter",
+               checkGetNoParameter(stream.get(), {"Non existing key"} /* keys */,
+                                   {Result::NOT_SUPPORTED}))
+
+TEST_IO_STREAM(setEmptySetParameter, "Set the values of an empty set of parameters",
+               ASSERT_RESULT(Result::OK, stream->setParameters({}, {})))
+
+TEST_IO_STREAM(setNonExistingParameter, "Set the values of an non existing parameter",
+               // Unfortunately, the set_parameter legacy interface did not return any
+               // error code when a key is not supported.
+               // To allow implementation to just wrapped the legacy one, consider OK as a
+               // valid result for setting a non existing parameter.
+               ASSERT_RESULT(invalidArgsOrNotSupportedOrOK,
+                             stream->setParameters({}, {{"non existing key", "0"}})))
+
+TEST_IO_STREAM(DebugDump, "Check that a stream can dump its state without error",
+               testDebugDump([this](const auto& handle) { return stream->debug(handle, {}); }))
+
+TEST_IO_STREAM(DebugDumpInvalidArguments,
+               "Check that the stream dump doesn't crash on invalid arguments",
+               ASSERT_OK(stream->debug(hidl_handle(), {})))
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////// addRemoveEffect ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_IO_STREAM(AddNonExistingEffect, "Adding a non existing effect should fail",
+               ASSERT_RESULT(Result::INVALID_ARGUMENTS, stream->addEffect(666)))
+TEST_IO_STREAM(RemoveNonExistingEffect, "Removing a non existing effect should fail",
+               ASSERT_RESULT(Result::INVALID_ARGUMENTS, stream->removeEffect(666)))
+
+// TODO: positive tests
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Control ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_IO_STREAM(standby, "Make sure the stream can be put in stanby",
+               ASSERT_OK(stream->standby()))  // can not fail
+
+static constexpr auto invalidStateOrNotSupported = {Result::INVALID_STATE, Result::NOT_SUPPORTED};
+
+TEST_IO_STREAM(startNoMmap, "Starting a mmaped stream before mapping it should fail",
+               ASSERT_RESULT(invalidStateOrNotSupported, stream->start()))
+
+TEST_IO_STREAM(stopNoMmap, "Stopping a mmaped stream before mapping it should fail",
+               ASSERT_RESULT(invalidStateOrNotSupported, stream->stop()))
+
+TEST_IO_STREAM(getMmapPositionNoMmap, "Get a stream Mmap position before mapping it should fail",
+               ASSERT_RESULT(invalidStateOrNotSupported, stream->stop()))
+
+TEST_IO_STREAM(close, "Make sure a stream can be closed", ASSERT_OK(closeStream()))
+TEST_IO_STREAM(closeTwice, "Make sure a stream can not be closed twice", ASSERT_OK(closeStream());
+               ASSERT_RESULT(Result::INVALID_STATE, closeStream()))
+
+static auto invalidArgsOrNotSupported = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED};
+static void testCreateTooBigMmapBuffer(IStream* stream) {
+    MmapBufferInfo info;
+    Result res;
+    // Assume that int max is a value too big to be allocated
+    // This is true currently with a 32bit media server, but might not when it
+    // will run in 64 bit
+    auto minSizeFrames = std::numeric_limits<int32_t>::max();
+    ASSERT_OK(stream->createMmapBuffer(minSizeFrames, returnIn(res, info)));
+    ASSERT_RESULT(invalidArgsOrNotSupported, res);
+}
+
+TEST_IO_STREAM(CreateTooBigMmapBuffer, "Create mmap buffer too big should fail",
+               testCreateTooBigMmapBuffer(stream.get()))
+
+static void testGetMmapPositionOfNonMmapedStream(IStream* stream) {
+    Result res;
+    MmapPosition position;
+    ASSERT_OK(stream->getMmapPosition(returnIn(res, position)));
+    ASSERT_RESULT(invalidArgsOrNotSupported, res);
+}
+
+TEST_IO_STREAM(GetMmapPositionOfNonMmapedStream,
+               "Retrieving the mmap position of a non mmaped stream should fail",
+               testGetMmapPositionOfNonMmapedStream(stream.get()))
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// StreamIn ///////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_P(InputStreamTest, GetAudioSource) {
+    doc::test("Retrieving the audio source of an input stream should always succeed");
+    AudioSource source;
+    ASSERT_OK(stream->getAudioSource(returnIn(res, source)));
+    if (res == Result::NOT_SUPPORTED) {
+        doc::partialTest("getAudioSource is not supported");
+        return;
+    }
+    ASSERT_OK(res);
+    ASSERT_EQ(AudioSource::DEFAULT, source);
+}
+
+static void testUnitaryGain(std::function<Return<Result>(float)> setGain) {
+    for (float value : (float[]){-INFINITY, -1.0, 1.0 + std::numeric_limits<float>::epsilon(), 2.0,
+                                 INFINITY, NAN}) {
+        EXPECT_RESULT(Result::INVALID_ARGUMENTS, setGain(value)) << "value=" << value;
+    }
+    // Do not consider -0.0 as an invalid value as it is == with 0.0
+    for (float value : {-0.0, 0.0, 0.01, 0.5, 0.09, 1.0 /* Restore volume*/}) {
+        EXPECT_OK(setGain(value)) << "value=" << value;
+    }
+}
+
+static void testOptionalUnitaryGain(std::function<Return<Result>(float)> setGain,
+                                    string debugName) {
+    auto result = setGain(1);
+    ASSERT_IS_OK(result);
+    if (result == Result::NOT_SUPPORTED) {
+        doc::partialTest(debugName + " is not supported");
+        return;
+    }
+    testUnitaryGain(setGain);
+}
+
+TEST_P(InputStreamTest, SetGain) {
+    doc::test("The gain of an input stream should only be set between [0,1]");
+    testOptionalUnitaryGain([this](float volume) { return stream->setGain(volume); },
+                            "InputStream::setGain");
+}
+
+static void testPrepareForReading(IStreamIn* stream, uint32_t frameSize, uint32_t framesCount) {
+    Result res;
+    // Ignore output parameters as the call should fail
+    ASSERT_OK(stream->prepareForReading(frameSize, framesCount,
+                                        [&res](auto r, auto&, auto&, auto&, auto&) { res = r; }));
+    EXPECT_RESULT(Result::INVALID_ARGUMENTS, res);
+}
+
+TEST_P(InputStreamTest, PrepareForReadingWithZeroBuffer) {
+    doc::test("Preparing a stream for reading with a 0 sized buffer should fail");
+    testPrepareForReading(stream.get(), 0, 0);
+}
+
+TEST_P(InputStreamTest, PrepareForReadingWithHugeBuffer) {
+    doc::test("Preparing a stream for reading with a 2^32 sized buffer should fail");
+    testPrepareForReading(stream.get(), 1, std::numeric_limits<uint32_t>::max());
+}
+
+TEST_P(InputStreamTest, PrepareForReadingCheckOverflow) {
+    doc::test(
+        "Preparing a stream for reading with a overflowing sized buffer should "
+        "fail");
+    auto uintMax = std::numeric_limits<uint32_t>::max();
+    testPrepareForReading(stream.get(), uintMax, uintMax);
+}
+
+TEST_P(InputStreamTest, GetInputFramesLost) {
+    doc::test("The number of frames lost on a never started stream should be 0");
+    auto ret = stream->getInputFramesLost();
+    ASSERT_IS_OK(ret);
+    uint32_t framesLost{ret};
+    ASSERT_EQ(0U, framesLost);
+}
+
+TEST_P(InputStreamTest, getCapturePosition) {
+    doc::test(
+        "The capture position of a non prepared stream should not be "
+        "retrievable");
+    uint64_t frames;
+    uint64_t time;
+    ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, time)));
+    ASSERT_RESULT(invalidStateOrNotSupported, res);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// StreamIn ///////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_P(OutputStreamTest, getLatency) {
+    doc::test("Make sure latency is over 0");
+    auto result = stream->getLatency();
+    ASSERT_IS_OK(result);
+    ASSERT_GT(result, 0U);
+}
+
+TEST_P(OutputStreamTest, setVolume) {
+    doc::test("Try to set the output volume");
+    testOptionalUnitaryGain([this](float volume) { return stream->setVolume(volume, volume); },
+                            "setVolume");
+}
+
+static void testPrepareForWriting(IStreamOut* stream, uint32_t frameSize, uint32_t framesCount) {
+    Result res;
+    // Ignore output parameters as the call should fail
+    ASSERT_OK(stream->prepareForWriting(frameSize, framesCount,
+                                        [&res](auto r, auto&, auto&, auto&, auto&) { res = r; }));
+    EXPECT_RESULT(Result::INVALID_ARGUMENTS, res);
+}
+
+TEST_P(OutputStreamTest, PrepareForWriteWithZeroBuffer) {
+    doc::test("Preparing a stream for writing with a 0 sized buffer should fail");
+    testPrepareForWriting(stream.get(), 0, 0);
+}
+
+TEST_P(OutputStreamTest, PrepareForWriteWithHugeBuffer) {
+    doc::test("Preparing a stream for writing with a 2^32 sized buffer should fail");
+    testPrepareForWriting(stream.get(), 1, std::numeric_limits<uint32_t>::max());
+}
+
+TEST_P(OutputStreamTest, PrepareForWritingCheckOverflow) {
+    doc::test(
+        "Preparing a stream for writing with a overflowing sized buffer should "
+        "fail");
+    auto uintMax = std::numeric_limits<uint32_t>::max();
+    testPrepareForWriting(stream.get(), uintMax, uintMax);
+}
+
+struct Capability {
+    Capability(IStreamOut* stream) {
+        EXPECT_OK(stream->supportsPauseAndResume(returnIn(pause, resume)));
+        auto ret = stream->supportsDrain();
+        EXPECT_IS_OK(ret);
+        if (ret.isOk()) {
+            drain = ret;
+        }
+    }
+    bool pause = false;
+    bool resume = false;
+    bool drain = false;
+};
+
+TEST_P(OutputStreamTest, SupportsPauseAndResumeAndDrain) {
+    doc::test("Implementation must expose pause, resume and drain capabilities");
+    Capability(stream.get());
+}
+
+template <class Value>
+static void checkInvalidStateOr0(Result res, Value value) {
+    switch (res) {
+        case Result::INVALID_STATE:
+            break;
+        case Result::OK:
+            ASSERT_EQ(0U, value);
+            break;
+        default:
+            FAIL() << "Unexpected result " << toString(res);
+    }
+}
+
+TEST_P(OutputStreamTest, GetRenderPosition) {
+    doc::test("A new stream render position should be 0 or INVALID_STATE");
+    uint32_t dspFrames;
+    ASSERT_OK(stream->getRenderPosition(returnIn(res, dspFrames)));
+    if (res == Result::NOT_SUPPORTED) {
+        doc::partialTest("getRenderPosition is not supported");
+        return;
+    }
+    checkInvalidStateOr0(res, dspFrames);
+}
+
+TEST_P(OutputStreamTest, GetNextWriteTimestamp) {
+    doc::test("A new stream next write timestamp should be 0 or INVALID_STATE");
+    uint64_t timestampUs;
+    ASSERT_OK(stream->getNextWriteTimestamp(returnIn(res, timestampUs)));
+    if (res == Result::NOT_SUPPORTED) {
+        doc::partialTest("getNextWriteTimestamp is not supported");
+        return;
+    }
+    checkInvalidStateOr0(res, timestampUs);
+}
+
+/** Stub implementation of out stream callback. */
+class MockOutCallbacks : public IStreamOutCallback {
+    Return<void> onWriteReady() override { return {}; }
+    Return<void> onDrainReady() override { return {}; }
+    Return<void> onError() override { return {}; }
+};
+
+static bool isAsyncModeSupported(IStreamOut* stream) {
+    auto res = stream->setCallback(new MockOutCallbacks);
+    stream->clearCallback();  // try to restore the no callback state, ignore
+                              // any error
+    auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED};
+    EXPECT_RESULT(okOrNotSupported, res);
+    return res.isOk() ? res == Result::OK : false;
+}
+
+TEST_P(OutputStreamTest, SetCallback) {
+    doc::test(
+        "If supported, registering callback for async operation should never "
+        "fail");
+    if (!isAsyncModeSupported(stream.get())) {
+        doc::partialTest("The stream does not support async operations");
+        return;
+    }
+    ASSERT_OK(stream->setCallback(new MockOutCallbacks));
+    ASSERT_OK(stream->setCallback(new MockOutCallbacks));
+}
+
+TEST_P(OutputStreamTest, clearCallback) {
+    doc::test(
+        "If supported, clearing a callback to go back to sync operation should "
+        "not fail");
+    if (!isAsyncModeSupported(stream.get())) {
+        doc::partialTest("The stream does not support async operations");
+        return;
+    }
+    // TODO: Clarify if clearing a non existing callback should fail
+    ASSERT_OK(stream->setCallback(new MockOutCallbacks));
+    ASSERT_OK(stream->clearCallback());
+}
+
+TEST_P(OutputStreamTest, Resume) {
+    doc::test(
+        "If supported, a stream should fail to resume if not previously "
+        "paused");
+    if (!Capability(stream.get()).resume) {
+        doc::partialTest("The output stream does not support resume");
+        return;
+    }
+    ASSERT_RESULT(Result::INVALID_STATE, stream->resume());
+}
+
+TEST_P(OutputStreamTest, Pause) {
+    doc::test(
+        "If supported, a stream should fail to pause if not previously "
+        "started");
+    if (!Capability(stream.get()).pause) {
+        doc::partialTest("The output stream does not support pause");
+        return;
+    }
+    ASSERT_RESULT(Result::INVALID_STATE, stream->resume());
+}
+
+static void testDrain(IStreamOut* stream, AudioDrain type) {
+    if (!Capability(stream).drain) {
+        doc::partialTest("The output stream does not support drain");
+        return;
+    }
+    ASSERT_RESULT(Result::OK, stream->drain(type));
+}
+
+TEST_P(OutputStreamTest, DrainAll) {
+    doc::test("If supported, a stream should always succeed to drain");
+    testDrain(stream.get(), AudioDrain::ALL);
+}
+
+TEST_P(OutputStreamTest, DrainEarlyNotify) {
+    doc::test("If supported, a stream should always succeed to drain");
+    testDrain(stream.get(), AudioDrain::EARLY_NOTIFY);
+}
+
+TEST_P(OutputStreamTest, FlushStop) {
+    doc::test("If supported, a stream should always succeed to flush");
+    auto ret = stream->flush();
+    ASSERT_IS_OK(ret);
+    if (ret == Result::NOT_SUPPORTED) {
+        doc::partialTest("Flush is not supported");
+        return;
+    }
+    ASSERT_OK(ret);
+}
+
+TEST_P(OutputStreamTest, GetPresentationPositionStop) {
+    doc::test(
+        "If supported, a stream should always succeed to retrieve the "
+        "presentation position");
+    uint64_t frames;
+    TimeSpec mesureTS;
+    ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, mesureTS)));
+    if (res == Result::NOT_SUPPORTED) {
+        doc::partialTest("getpresentationPosition is not supported");
+        return;
+    }
+    ASSERT_EQ(0U, frames);
+
+    if (mesureTS.tvNSec == 0 && mesureTS.tvSec == 0) {
+        // As the stream has never written a frame yet,
+        // the timestamp does not really have a meaning, allow to return 0
+        return;
+    }
+
+    // Make sure the return measure is not more than 1s old.
+    struct timespec currentTS;
+    ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &currentTS)) << errno;
+
+    auto toMicroSec = [](uint64_t sec, auto nsec) { return sec * 1e+6 + nsec / 1e+3; };
+    auto currentTime = toMicroSec(currentTS.tv_sec, currentTS.tv_nsec);
+    auto mesureTime = toMicroSec(mesureTS.tvSec, mesureTS.tvNSec);
+    ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, mesureTime);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// PrimaryDevice ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, setVoiceVolume) {
+    doc::test("Make sure setVoiceVolume only succeed if volume is in [0,1]");
+    testUnitaryGain([](float volume) { return device->setVoiceVolume(volume); });
+}
+
+TEST_F(AudioPrimaryHidlTest, setMode) {
+    doc::test(
+        "Make sure setMode always succeeds if mode is valid "
+        "and fails otherwise");
+    // Test Invalid values
+    for (int mode : {-1, 0, int(AudioMode::IN_COMMUNICATION) + 1}) {
+        SCOPED_TRACE("mode=" + to_string(mode));
+        ASSERT_RESULT(Result::INVALID_ARGUMENTS, device->setMode(AudioMode(mode)));
+    }
+    // Test valid values
+    for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE,
+                           AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) {
+        SCOPED_TRACE("mode=" + toString(mode));
+        ASSERT_OK(device->setMode(mode));
+    }
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, BtScoNrecEnabled) {
+    doc::test("Query and set the BT SCO NR&EC state");
+    testOptionalAccessors("BtScoNrecEnabled", {true, false, true},
+                          &IPrimaryDevice::setBtScoNrecEnabled,
+                          &IPrimaryDevice::getBtScoNrecEnabled);
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) {
+    doc::test("Query and set the SCO whideband state");
+    testOptionalAccessors("BtScoWideband", {true, false, true},
+                          &IPrimaryDevice::setBtScoWidebandEnabled,
+                          &IPrimaryDevice::getBtScoWidebandEnabled);
+}
+
+using TtyModeAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<TtyMode>;
+TEST_F(TtyModeAccessorPrimaryHidlTest, setGetTtyMode) {
+    doc::test("Query and set the TTY mode state");
+    testOptionalAccessors("TTY mode", {TtyMode::OFF, TtyMode::HCO, TtyMode::VCO, TtyMode::FULL},
+                          &IPrimaryDevice::setTtyMode, &IPrimaryDevice::getTtyMode);
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, setGetHac) {
+    doc::test("Query and set the HAC state");
+    testOptionalAccessors("HAC", {true, false, true}, &IPrimaryDevice::setHacEnabled,
+                          &IPrimaryDevice::getHacEnabled);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////// Clean caches on global tear down ////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char** argv) {
+    environment = new AudioHidlTestEnvironment;
+    ::testing::AddGlobalTestEnvironment(environment);
+    ::testing::InitGoogleTest(&argc, argv);
+    environment->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    return status;
+}
diff --git a/audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp b/audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp
new file mode 100644
index 0000000..a64513f
--- /dev/null
+++ b/audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <unistd.h>
+#include <string>
+
+#include "utility/ValidateXml.h"
+
+TEST(CheckConfig, audioPolicyConfigurationValidation) {
+    RecordProperty("description",
+                   "Verify that the audio policy configuration file "
+                   "is valid according to the schema");
+
+    std::vector<const char*> locations = {"/odm/etc", "/vendor/etc", "/system/etc"};
+    EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS("audio_policy_configuration.xml", locations,
+                                            "/data/local/tmp/audio_policy_configuration_V4_0.xsd");
+}
diff --git a/audio/effect/4.0/vts/OWNERS b/audio/effect/4.0/vts/OWNERS
new file mode 100644
index 0000000..8711a9f
--- /dev/null
+++ b/audio/effect/4.0/vts/OWNERS
@@ -0,0 +1,5 @@
+elaurent@google.com
+krocard@google.com
+mnaganov@google.com
+yim@google.com
+zhuoyao@google.com
\ No newline at end of file
diff --git a/audio/effect/4.0/vts/functional/Android.bp b/audio/effect/4.0/vts/functional/Android.bp
new file mode 100644
index 0000000..92b5db7
--- /dev/null
+++ b/audio/effect/4.0/vts/functional/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalAudioEffectV4_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "VtsHalAudioEffectV4_0TargetTest.cpp",
+        "ValidateAudioEffectsConfiguration.cpp"
+    ],
+    static_libs: [
+        "android.hardware.audio.common.test.utility",
+        "android.hardware.audio.common@4.0",
+        "android.hardware.audio.effect@4.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libxml2",
+    ],
+    shared_libs: [
+        "libeffectsconfig",
+        "libicuuc",
+    ],
+    header_libs: [
+        "android.hardware.audio.common.util@all-versions",
+    ],
+}
diff --git a/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp b/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp
new file mode 100644
index 0000000..6338563
--- /dev/null
+++ b/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 <unistd.h>
+#include <iterator>
+
+#include <media/EffectsConfig.h>
+
+#include "utility/ValidateXml.h"
+
+TEST(CheckConfig, audioEffectsConfigurationValidation) {
+    RecordProperty("description",
+                   "Verify that the effects configuration file is valid according to the schema");
+    using namespace android::effectsConfig;
+
+    std::vector<const char*> locations(std::begin(DEFAULT_LOCATIONS), std::end(DEFAULT_LOCATIONS));
+    EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(DEFAULT_NAME, locations,
+                                            "/data/local/tmp/audio_effects_conf_V4_0.xsd");
+}
diff --git a/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp b/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp
new file mode 100644
index 0000000..ec783c4
--- /dev/null
+++ b/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp
@@ -0,0 +1,852 @@
+/*
+ * 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 "AudioEffectHidlHalTest"
+#include <android-base/logging.h>
+#include <system/audio.h>
+
+#include <android/hardware/audio/effect/4.0/IEffect.h>
+#include <android/hardware/audio/effect/4.0/IEffectsFactory.h>
+#include <android/hardware/audio/effect/4.0/IEqualizerEffect.h>
+#include <android/hardware/audio/effect/4.0/ILoudnessEnhancerEffect.h>
+#include <android/hardware/audio/effect/4.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+
+#include <common/all-versions/VersionUtils.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+
+using android::hardware::audio::common::V4_0::AudioDevice;
+using android::hardware::audio::common::V4_0::AudioHandleConsts;
+using android::hardware::audio::common::V4_0::AudioMode;
+using android::hardware::audio::common::V4_0::AudioSource;
+using android::hardware::audio::common::V4_0::Uuid;
+using android::hardware::audio::common::utils::mkBitfield;
+using android::hardware::audio::effect::V4_0::AudioBuffer;
+using android::hardware::audio::effect::V4_0::EffectAuxChannelsConfig;
+using android::hardware::audio::effect::V4_0::EffectBufferConfig;
+using android::hardware::audio::effect::V4_0::EffectConfig;
+using android::hardware::audio::effect::V4_0::EffectDescriptor;
+using android::hardware::audio::effect::V4_0::EffectOffloadParameter;
+using android::hardware::audio::effect::V4_0::IEffect;
+using android::hardware::audio::effect::V4_0::IEffectsFactory;
+using android::hardware::audio::effect::V4_0::IEqualizerEffect;
+using android::hardware::audio::effect::V4_0::ILoudnessEnhancerEffect;
+using android::hardware::audio::effect::V4_0::Result;
+using android::hardware::MQDescriptorSync;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_memory;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hidl::allocator::V1_0::IAllocator;
+using android::hidl::memory::V1_0::IMemory;
+using android::sp;
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+#endif
+
+// Test environment for Audio Effects Factory HIDL HAL.
+class AudioEffectsFactoryHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static AudioEffectsFactoryHidlEnvironment* Instance() {
+        static AudioEffectsFactoryHidlEnvironment* instance =
+            new AudioEffectsFactoryHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<IEffectsFactory>(); }
+};
+
+// The main test class for Audio Effects Factory HIDL HAL.
+class AudioEffectsFactoryHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+  void SetUp() override {
+      effectsFactory = ::testing::VtsHalHidlTargetTestBase::getService<IEffectsFactory>(
+          AudioEffectsFactoryHidlEnvironment::Instance()->getServiceName<IEffectsFactory>());
+      ASSERT_NE(effectsFactory, nullptr);
+  }
+
+  void TearDown() override { effectsFactory.clear(); }
+
+ protected:
+  static void description(const std::string& description) {
+    RecordProperty("description", description);
+  }
+
+  sp<IEffectsFactory> effectsFactory;
+};
+
+TEST_F(AudioEffectsFactoryHidlTest, EnumerateEffects) {
+  description("Verify that EnumerateEffects returns at least one effect");
+  Result retval = Result::NOT_INITIALIZED;
+  size_t effectCount = 0;
+  Return<void> ret = effectsFactory->getAllDescriptors(
+      [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+        retval = r;
+        effectCount = result.size();
+      });
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  EXPECT_GT(effectCount, 0u);
+}
+
+TEST_F(AudioEffectsFactoryHidlTest, CreateEffect) {
+  description("Verify that an effect can be created via CreateEffect");
+  bool gotEffect = false;
+  Uuid effectUuid;
+  Return<void> ret = effectsFactory->getAllDescriptors(
+      [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+        if (r == Result::OK && result.size() > 0) {
+          gotEffect = true;
+          effectUuid = result[0].uuid;
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_TRUE(gotEffect);
+  Result retval = Result::NOT_INITIALIZED;
+  sp<IEffect> effect;
+  ret = effectsFactory->createEffect(
+      effectUuid, 1 /*session*/, 1 /*ioHandle*/,
+      [&](Result r, const sp<IEffect>& result, uint64_t /*effectId*/) {
+        retval = r;
+        if (r == Result::OK) {
+          effect = result;
+        }
+      });
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  EXPECT_NE(nullptr, effect.get());
+}
+
+TEST_F(AudioEffectsFactoryHidlTest, GetDescriptor) {
+  description(
+      "Verify that effects factory can provide an effect descriptor via "
+      "GetDescriptor");
+  hidl_vec<EffectDescriptor> allDescriptors;
+  Return<void> ret = effectsFactory->getAllDescriptors(
+      [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+        if (r == Result::OK) {
+          allDescriptors = result;
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_GT(allDescriptors.size(), 0u);
+  for (size_t i = 0; i < allDescriptors.size(); ++i) {
+    ret = effectsFactory->getDescriptor(
+        allDescriptors[i].uuid, [&](Result r, const EffectDescriptor& result) {
+          EXPECT_EQ(r, Result::OK);
+          EXPECT_EQ(result, allDescriptors[i]);
+        });
+  }
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectsFactoryHidlTest, DebugDumpInvalidArgument) {
+    description("Verify that debugDump doesn't crash on invalid arguments");
+    Return<void> ret = effectsFactory->debug(hidl_handle(), {});
+    ASSERT_TRUE(ret.isOk());
+}
+
+// Equalizer effect is required by CDD, but only the type is fixed.
+// This is the same UUID as AudioEffect.EFFECT_TYPE_EQUALIZER in Java.
+static const Uuid EQUALIZER_EFFECT_TYPE = {
+    0x0bed4300, 0xddd6, 0x11db, 0x8f34,
+    std::array<uint8_t, 6>{{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}};
+// Loudness Enhancer effect is required by CDD, but only the type is fixed.
+// This is the same UUID as AudioEffect.EFFECT_TYPE_LOUDNESS_ENHANCER in Java.
+static const Uuid LOUDNESS_ENHANCER_EFFECT_TYPE = {
+    0xfe3199be, 0xaed0, 0x413f, 0x87bb,
+    std::array<uint8_t, 6>{{0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}};
+
+// The main test class for Audio Effect HIDL HAL.
+class AudioEffectHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+  void SetUp() override {
+    effectsFactory =
+        ::testing::VtsHalHidlTargetTestBase::getService<IEffectsFactory>();
+    ASSERT_NE(nullptr, effectsFactory.get());
+
+    findAndCreateEffect(getEffectType());
+    ASSERT_NE(nullptr, effect.get());
+
+    Return<Result> ret = effect->init();
+    ASSERT_TRUE(ret.isOk());
+    ASSERT_EQ(Result::OK, ret);
+  }
+
+  void TearDown() override {
+    effect.clear();
+    effectsFactory.clear();
+  }
+
+ protected:
+  static void description(const std::string& description) {
+    RecordProperty("description", description);
+  }
+
+  virtual Uuid getEffectType() { return EQUALIZER_EFFECT_TYPE; }
+
+  void findAndCreateEffect(const Uuid& type);
+  void findEffectInstance(const Uuid& type, Uuid* uuid);
+  void getChannelCount(uint32_t* channelCount);
+
+  sp<IEffectsFactory> effectsFactory;
+  sp<IEffect> effect;
+};
+
+void AudioEffectHidlTest::findAndCreateEffect(const Uuid& type) {
+  Uuid effectUuid;
+  findEffectInstance(type, &effectUuid);
+  Return<void> ret = effectsFactory->createEffect(
+      effectUuid, 1 /*session*/, 1 /*ioHandle*/,
+      [&](Result r, const sp<IEffect>& result, uint64_t /*effectId*/) {
+        if (r == Result::OK) {
+          effect = result;
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+}
+
+void AudioEffectHidlTest::findEffectInstance(const Uuid& type, Uuid* uuid) {
+  bool effectFound = false;
+  Return<void> ret = effectsFactory->getAllDescriptors(
+      [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+        if (r == Result::OK) {
+          for (const auto& desc : result) {
+            if (desc.type == type) {
+              effectFound = true;
+              *uuid = desc.uuid;
+              break;
+            }
+          }
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_TRUE(effectFound);
+}
+
+void AudioEffectHidlTest::getChannelCount(uint32_t* channelCount) {
+  Result retval;
+  EffectConfig currentConfig;
+  Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
+    retval = r;
+    if (r == Result::OK) {
+      currentConfig = conf;
+    }
+  });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+  ASSERT_TRUE(audio_channel_mask_is_valid(
+      static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels)));
+  *channelCount = audio_channel_count_from_out_mask(
+      static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels));
+}
+
+TEST_F(AudioEffectHidlTest, Close) {
+  description("Verify that an effect can be closed");
+  Return<Result> ret = effect->close();
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+}
+
+TEST_F(AudioEffectHidlTest, GetDescriptor) {
+  description(
+      "Verify that an effect can return its own descriptor via GetDescriptor");
+  Result retval = Result::NOT_INITIALIZED;
+  Uuid actualType;
+  Return<void> ret =
+      effect->getDescriptor([&](Result r, const EffectDescriptor& desc) {
+        retval = r;
+        if (r == Result::OK) {
+          actualType = desc.type;
+        }
+      });
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  EXPECT_EQ(getEffectType(), actualType);
+}
+
+TEST_F(AudioEffectHidlTest, GetSetConfig) {
+  description(
+      "Verify that it is possible to manipulate effect config via Get / "
+      "SetConfig");
+  Result retval = Result::NOT_INITIALIZED;
+  EffectConfig currentConfig;
+  Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
+    retval = r;
+    if (r == Result::OK) {
+      currentConfig = conf;
+    }
+  });
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  Return<Result> ret2 = effect->setConfig(currentConfig, nullptr, nullptr);
+  EXPECT_TRUE(ret2.isOk());
+  EXPECT_EQ(Result::OK, ret2);
+}
+
+TEST_F(AudioEffectHidlTest, GetConfigReverse) {
+  description("Verify that GetConfigReverse does not crash");
+  Return<void> ret =
+      effect->getConfigReverse([&](Result, const EffectConfig&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, GetSupportedAuxChannelsConfigs) {
+  description("Verify that GetSupportedAuxChannelsConfigs does not crash");
+  Return<void> ret = effect->getSupportedAuxChannelsConfigs(
+      0, [&](Result, const hidl_vec<EffectAuxChannelsConfig>&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, GetAuxChannelsConfig) {
+  description("Verify that GetAuxChannelsConfig does not crash");
+  Return<void> ret = effect->getAuxChannelsConfig(
+      [&](Result, const EffectAuxChannelsConfig&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, SetAuxChannelsConfig) {
+  description("Verify that SetAuxChannelsConfig does not crash");
+  Return<Result> ret = effect->setAuxChannelsConfig(EffectAuxChannelsConfig());
+  EXPECT_TRUE(ret.isOk());
+}
+
+// Not generated automatically because AudioBuffer contains
+// instances of hidl_memory which can't be compared properly
+// in general case due to presence of handles.
+//
+// However, in this particular case, handles must not present
+// thus comparison is possible.
+//
+// operator== must be defined in the same namespace as the structures.
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace V4_0 {
+inline bool operator==(const AudioBuffer& lhs, const AudioBuffer& rhs) {
+  return lhs.id == rhs.id && lhs.frameCount == rhs.frameCount &&
+         lhs.data.handle() == nullptr && rhs.data.handle() == nullptr;
+}
+
+inline bool operator==(const EffectBufferConfig& lhs,
+                       const EffectBufferConfig& rhs) {
+  return lhs.buffer == rhs.buffer && lhs.samplingRateHz == rhs.samplingRateHz &&
+         lhs.channels == rhs.channels && lhs.format == rhs.format &&
+         lhs.accessMode == rhs.accessMode && lhs.mask == rhs.mask;
+}
+
+inline bool operator==(const EffectConfig& lhs, const EffectConfig& rhs) {
+  return lhs.inputCfg == rhs.inputCfg && lhs.outputCfg == rhs.outputCfg;
+}
+}  // namespace V4_0
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
+
+TEST_F(AudioEffectHidlTest, Reset) {
+  description("Verify that Reset preserves effect configuration");
+  Result retval = Result::NOT_INITIALIZED;
+  EffectConfig originalConfig;
+  Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
+    retval = r;
+    if (r == Result::OK) {
+      originalConfig = conf;
+    }
+  });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+  Return<Result> ret2 = effect->reset();
+  EXPECT_TRUE(ret2.isOk());
+  EXPECT_EQ(Result::OK, ret2);
+  EffectConfig configAfterReset;
+  ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
+    retval = r;
+    if (r == Result::OK) {
+      configAfterReset = conf;
+    }
+  });
+  EXPECT_EQ(originalConfig, configAfterReset);
+}
+
+TEST_F(AudioEffectHidlTest, DisableEnableDisable) {
+  description("Verify Disable -> Enable -> Disable sequence for an effect");
+  Return<Result> ret = effect->disable();
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::INVALID_ARGUMENTS, ret);
+  ret = effect->enable();
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+  ret = effect->disable();
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+}
+
+TEST_F(AudioEffectHidlTest, SetDevice) {
+  description("Verify that SetDevice works for an output chain effect");
+  Return<Result> ret = effect->setDevice(mkBitfield(AudioDevice::OUT_SPEAKER));
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+}
+
+TEST_F(AudioEffectHidlTest, SetAndGetVolume) {
+  description("Verify that SetAndGetVolume method works for an effect");
+  uint32_t channelCount;
+  getChannelCount(&channelCount);
+  hidl_vec<uint32_t> volumes;
+  volumes.resize(channelCount);
+  for (uint32_t i = 0; i < channelCount; ++i) {
+    volumes[i] = 0;
+  }
+  Result retval = Result::NOT_INITIALIZED;
+  Return<void> ret = effect->setAndGetVolume(
+      volumes, [&](Result r, const hidl_vec<uint32_t>&) { retval = r; });
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, retval);
+}
+
+TEST_F(AudioEffectHidlTest, VolumeChangeNotification) {
+  description("Verify that effect accepts VolumeChangeNotification");
+  uint32_t channelCount;
+  getChannelCount(&channelCount);
+  hidl_vec<uint32_t> volumes;
+  volumes.resize(channelCount);
+  for (uint32_t i = 0; i < channelCount; ++i) {
+    volumes[i] = 0;
+  }
+  Return<Result> ret = effect->volumeChangeNotification(volumes);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+}
+
+TEST_F(AudioEffectHidlTest, SetAudioMode) {
+  description("Verify that SetAudioMode works for an effect");
+  Return<Result> ret = effect->setAudioMode(AudioMode::NORMAL);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+}
+
+TEST_F(AudioEffectHidlTest, SetConfigReverse) {
+  description("Verify that SetConfigReverse does not crash");
+  Return<Result> ret =
+      effect->setConfigReverse(EffectConfig(), nullptr, nullptr);
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, SetInputDevice) {
+  description("Verify that SetInputDevice does not crash");
+  Return<Result> ret = effect->setInputDevice(mkBitfield(AudioDevice::IN_BUILTIN_MIC));
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, SetAudioSource) {
+  description("Verify that SetAudioSource does not crash");
+  Return<Result> ret = effect->setAudioSource(AudioSource::MIC);
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, Offload) {
+  description("Verify that calling Offload method does not crash");
+  EffectOffloadParameter offloadParam;
+  offloadParam.isOffload = false;
+  offloadParam.ioHandle =
+      static_cast<int>(AudioHandleConsts::AUDIO_IO_HANDLE_NONE);
+  Return<Result> ret = effect->offload(offloadParam);
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, PrepareForProcessing) {
+  description("Verify that PrepareForProcessing method works for an effect");
+  Result retval = Result::NOT_INITIALIZED;
+  Return<void> ret = effect->prepareForProcessing(
+      [&](Result r, const MQDescriptorSync<Result>&) { retval = r; });
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, retval);
+}
+
+TEST_F(AudioEffectHidlTest, SetProcessBuffers) {
+  description("Verify that SetProcessBuffers works for an effect");
+  sp<IAllocator> ashmem = IAllocator::getService("ashmem");
+  ASSERT_NE(nullptr, ashmem.get());
+  bool success = false;
+  AudioBuffer buffer;
+  Return<void> ret =
+      ashmem->allocate(1024, [&](bool s, const hidl_memory& memory) {
+        success = s;
+        if (s) {
+          buffer.data = memory;
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_TRUE(success);
+  Return<Result> ret2 = effect->setProcessBuffers(buffer, buffer);
+  EXPECT_TRUE(ret2.isOk());
+  EXPECT_EQ(Result::OK, ret2);
+}
+
+TEST_F(AudioEffectHidlTest, Command) {
+  description("Verify that Command does not crash");
+  Return<void> ret = effect->command(0, hidl_vec<uint8_t>(), 0,
+                                     [&](int32_t, const hidl_vec<uint8_t>&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, SetParameter) {
+  description("Verify that SetParameter does not crash");
+  Return<Result> ret =
+      effect->setParameter(hidl_vec<uint8_t>(), hidl_vec<uint8_t>());
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, GetParameter) {
+  description("Verify that GetParameter does not crash");
+  Return<void> ret = effect->getParameter(
+      hidl_vec<uint8_t>(), 0, [&](Result, const hidl_vec<uint8_t>&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, GetSupportedConfigsForFeature) {
+  description("Verify that GetSupportedConfigsForFeature does not crash");
+  Return<void> ret = effect->getSupportedConfigsForFeature(
+      0, 0, 0, [&](Result, uint32_t, const hidl_vec<uint8_t>&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, GetCurrentConfigForFeature) {
+  description("Verify that GetCurrentConfigForFeature does not crash");
+  Return<void> ret = effect->getCurrentConfigForFeature(
+      0, 0, [&](Result, const hidl_vec<uint8_t>&) {});
+  EXPECT_TRUE(ret.isOk());
+}
+
+TEST_F(AudioEffectHidlTest, SetCurrentConfigForFeature) {
+  description("Verify that SetCurrentConfigForFeature does not crash");
+  Return<Result> ret =
+      effect->setCurrentConfigForFeature(0, hidl_vec<uint8_t>());
+  EXPECT_TRUE(ret.isOk());
+}
+
+
+// The main test class for Equalizer Audio Effect HIDL HAL.
+class EqualizerAudioEffectHidlTest : public AudioEffectHidlTest {
+ public:
+  void SetUp() override {
+    AudioEffectHidlTest::SetUp();
+    equalizer = IEqualizerEffect::castFrom(effect);
+    ASSERT_NE(nullptr, equalizer.get());
+  }
+
+ protected:
+  Uuid getEffectType() override { return EQUALIZER_EFFECT_TYPE; }
+  void getNumBands(uint16_t* numBands);
+  void getLevelRange(int16_t* minLevel, int16_t* maxLevel);
+  void getBandFrequencyRange(uint16_t band, uint32_t* minFreq,
+                             uint32_t* centerFreq, uint32_t* maxFreq);
+  void getPresetCount(size_t* count);
+
+  sp<IEqualizerEffect> equalizer;
+};
+
+void EqualizerAudioEffectHidlTest::getNumBands(uint16_t* numBands) {
+  Result retval = Result::NOT_INITIALIZED;
+  Return<void> ret = equalizer->getNumBands([&](Result r, uint16_t b) {
+    retval = r;
+    if (retval == Result::OK) {
+      *numBands = b;
+    }
+  });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+}
+
+void EqualizerAudioEffectHidlTest::getLevelRange(int16_t* minLevel,
+                                                 int16_t* maxLevel) {
+  Result retval = Result::NOT_INITIALIZED;
+  Return<void> ret =
+      equalizer->getLevelRange([&](Result r, int16_t min, int16_t max) {
+        retval = r;
+        if (retval == Result::OK) {
+          *minLevel = min;
+          *maxLevel = max;
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+}
+
+void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band,
+                                                         uint32_t* minFreq,
+                                                         uint32_t* centerFreq,
+                                                         uint32_t* maxFreq) {
+  Result retval = Result::NOT_INITIALIZED;
+  Return<void> ret = equalizer->getBandFrequencyRange(
+      band, [&](Result r, uint32_t min, uint32_t max) {
+        retval = r;
+        if (retval == Result::OK) {
+          *minFreq = min;
+          *maxFreq = max;
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+  ret = equalizer->getBandCenterFrequency(band, [&](Result r, uint32_t center) {
+    retval = r;
+    if (retval == Result::OK) {
+      *centerFreq = center;
+    }
+  });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+}
+
+void EqualizerAudioEffectHidlTest::getPresetCount(size_t* count) {
+  Result retval = Result::NOT_INITIALIZED;
+  Return<void> ret = equalizer->getPresetNames(
+      [&](Result r, const hidl_vec<hidl_string>& names) {
+        retval = r;
+        if (retval == Result::OK) {
+          *count = names.size();
+        }
+      });
+  ASSERT_TRUE(ret.isOk());
+  ASSERT_EQ(Result::OK, retval);
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetNumBands) {
+  description("Verify that Equalizer effect reports at least one band");
+  uint16_t numBands = 0;
+  getNumBands(&numBands);
+  EXPECT_GT(numBands, 0);
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetLevelRange) {
+  description("Verify that Equalizer effect reports adequate band level range");
+  int16_t minLevel = 0x7fff, maxLevel = 0;
+  getLevelRange(&minLevel, &maxLevel);
+  EXPECT_GT(maxLevel, minLevel);
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetSetBandLevel) {
+  description(
+      "Verify that manipulating band levels works for Equalizer effect");
+  uint16_t numBands = 0;
+  getNumBands(&numBands);
+  ASSERT_GT(numBands, 0);
+  int16_t levels[3]{0x7fff, 0, 0};
+  getLevelRange(&levels[0], &levels[2]);
+  ASSERT_GT(levels[2], levels[0]);
+  levels[1] = (levels[2] + levels[0]) / 2;
+  for (uint16_t i = 0; i < numBands; ++i) {
+    for (size_t j = 0; j < ARRAY_SIZE(levels); ++j) {
+      Return<Result> ret = equalizer->setBandLevel(i, levels[j]);
+      EXPECT_TRUE(ret.isOk());
+      EXPECT_EQ(Result::OK, ret);
+      Result retval = Result::NOT_INITIALIZED;
+      int16_t actualLevel;
+      Return<void> ret2 = equalizer->getBandLevel(i, [&](Result r, int16_t l) {
+        retval = r;
+        if (retval == Result::OK) {
+          actualLevel = l;
+        }
+      });
+      EXPECT_TRUE(ret2.isOk());
+      EXPECT_EQ(Result::OK, retval);
+      EXPECT_EQ(levels[j], actualLevel);
+    }
+  }
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetBandCenterFrequencyAndRange) {
+  description(
+      "Verify that Equalizer effect reports adequate band frequency range");
+  uint16_t numBands = 0;
+  getNumBands(&numBands);
+  ASSERT_GT(numBands, 0);
+  for (uint16_t i = 0; i < numBands; ++i) {
+    uint32_t minFreq = 0xffffffff, centerFreq = 0xffffffff,
+             maxFreq = 0xffffffff;
+    getBandFrequencyRange(i, &minFreq, &centerFreq, &maxFreq);
+    // Note: NXP legacy implementation reports "1" as upper bound for last band,
+    // so this check fails.
+    EXPECT_GE(maxFreq, centerFreq);
+    EXPECT_GE(centerFreq, minFreq);
+  }
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetBandForFrequency) {
+  description(
+      "Verify that Equalizer effect supports GetBandForFrequency correctly");
+  uint16_t numBands = 0;
+  getNumBands(&numBands);
+  ASSERT_GT(numBands, 0);
+  for (uint16_t i = 0; i < numBands; ++i) {
+    uint32_t freqs[3]{0, 0, 0};
+    getBandFrequencyRange(i, &freqs[0], &freqs[1], &freqs[2]);
+    // NXP legacy implementation reports "1" as upper bound for last band, some
+    // of the checks fail.
+    for (size_t j = 0; j < ARRAY_SIZE(freqs); ++j) {
+      if (j == 0) {
+        freqs[j]++;
+      }  // Min frequency is an open interval.
+      Result retval = Result::NOT_INITIALIZED;
+      uint16_t actualBand = numBands + 1;
+      Return<void> ret =
+          equalizer->getBandForFrequency(freqs[j], [&](Result r, uint16_t b) {
+            retval = r;
+            if (retval == Result::OK) {
+              actualBand = b;
+            }
+          });
+      EXPECT_TRUE(ret.isOk());
+      EXPECT_EQ(Result::OK, retval);
+      EXPECT_EQ(i, actualBand) << "Frequency: " << freqs[j];
+    }
+  }
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetPresetNames) {
+  description("Verify that Equalizer effect reports at least one preset");
+  size_t presetCount;
+  getPresetCount(&presetCount);
+  EXPECT_GT(presetCount, 0u);
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetSetCurrentPreset) {
+  description(
+      "Verify that manipulating the current preset for Equalizer effect");
+  size_t presetCount;
+  getPresetCount(&presetCount);
+  ASSERT_GT(presetCount, 0u);
+  for (uint16_t i = 0; i < presetCount; ++i) {
+    Return<Result> ret = equalizer->setCurrentPreset(i);
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(Result::OK, ret);
+    Result retval = Result::NOT_INITIALIZED;
+    uint16_t actualPreset = 0xffff;
+    Return<void> ret2 = equalizer->getCurrentPreset([&](Result r, uint16_t p) {
+      retval = r;
+      if (retval == Result::OK) {
+        actualPreset = p;
+      }
+    });
+    EXPECT_TRUE(ret2.isOk());
+    EXPECT_EQ(Result::OK, retval);
+    EXPECT_EQ(i, actualPreset);
+  }
+}
+
+TEST_F(EqualizerAudioEffectHidlTest, GetSetAllProperties) {
+  description(
+      "Verify that setting band levels and presets works via Get / "
+      "SetAllProperties for Equalizer effect");
+  using AllProperties =
+      android::hardware::audio::effect::V4_0::IEqualizerEffect::AllProperties;
+  uint16_t numBands = 0;
+  getNumBands(&numBands);
+  ASSERT_GT(numBands, 0);
+  AllProperties props;
+  props.bandLevels.resize(numBands);
+  for (size_t i = 0; i < numBands; ++i) {
+    props.bandLevels[i] = 0;
+  }
+
+  AllProperties actualProps;
+  Result retval = Result::NOT_INITIALIZED;
+
+  // Verify setting of the band levels via properties.
+  props.curPreset = -1;
+  Return<Result> ret = equalizer->setAllProperties(props);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+  Return<void> ret2 =
+      equalizer->getAllProperties([&](Result r, AllProperties p) {
+        retval = r;
+        if (retval == Result::OK) {
+          actualProps = p;
+        }
+      });
+  EXPECT_TRUE(ret2.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  EXPECT_EQ(props.bandLevels, actualProps.bandLevels);
+
+  // Verify setting of the current preset via properties.
+  props.curPreset = 0;  // Assuming there is at least one preset.
+  ret = equalizer->setAllProperties(props);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+  ret2 = equalizer->getAllProperties([&](Result r, AllProperties p) {
+    retval = r;
+    if (retval == Result::OK) {
+      actualProps = p;
+    }
+  });
+  EXPECT_TRUE(ret2.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  EXPECT_EQ(props.curPreset, actualProps.curPreset);
+}
+
+// The main test class for Equalizer Audio Effect HIDL HAL.
+class LoudnessEnhancerAudioEffectHidlTest : public AudioEffectHidlTest {
+ public:
+  void SetUp() override {
+    AudioEffectHidlTest::SetUp();
+    enhancer = ILoudnessEnhancerEffect::castFrom(effect);
+    ASSERT_NE(nullptr, enhancer.get());
+  }
+
+ protected:
+  Uuid getEffectType() override { return LOUDNESS_ENHANCER_EFFECT_TYPE; }
+
+  sp<ILoudnessEnhancerEffect> enhancer;
+};
+
+TEST_F(LoudnessEnhancerAudioEffectHidlTest, GetSetTargetGain) {
+  description(
+      "Verify that manipulating the target gain works for Loudness Enhancer "
+      "effect");
+  const int32_t gain = 100;
+  Return<Result> ret = enhancer->setTargetGain(gain);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(Result::OK, ret);
+  int32_t actualGain = 0;
+  Result retval;
+  Return<void> ret2 = enhancer->getTargetGain([&](Result r, int32_t g) {
+    retval = r;
+    if (retval == Result::OK) {
+      actualGain = g;
+    }
+  });
+  EXPECT_TRUE(ret2.isOk());
+  EXPECT_EQ(Result::OK, retval);
+  EXPECT_EQ(gain, actualGain);
+}
+
+int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(AudioEffectsFactoryHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    AudioEffectsFactoryHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
diff --git a/automotive/vehicle/2.0/Android.bp b/automotive/vehicle/2.0/Android.bp
index bdc44ef..902a4e8 100644
--- a/automotive/vehicle/2.0/Android.bp
+++ b/automotive/vehicle/2.0/Android.bp
@@ -48,6 +48,7 @@
         "VehicleHvacFanDirection",
         "VehicleHwKeyInputAction",
         "VehicleIgnitionState",
+        "VehicleOilLevel",
         "VehiclePropConfig",
         "VehiclePropValue",
         "VehicleProperty",
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleUtils.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleUtils.h
index ce0b163..f97dfa1 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleUtils.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleUtils.h
@@ -33,10 +33,9 @@
 constexpr int32_t kAllSupportedAreas = 0;
 
 /** Returns underlying (integer) value for given enum. */
-template<typename ENUM>
-inline constexpr typename std::underlying_type<ENUM>::type toInt(
-        ENUM const value) {
-    return static_cast<typename std::underlying_type<ENUM>::type>(value);
+template<typename ENUM, typename U = typename std::underlying_type<ENUM>::type>
+inline constexpr U toInt(ENUM const value) {
+    return static_cast<U>(value);
 }
 
 inline constexpr VehiclePropertyType getPropType(int32_t prop) {
diff --git a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
index 2c3ebfc..f2aa421 100644
--- a/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehiclePropertyStore.cpp
@@ -52,6 +52,7 @@
     } else {
         valueToUpdate->timestamp = propValue.timestamp;
         valueToUpdate->value = propValue.value;
+        valueToUpdate->status = propValue.status;
     }
     return true;
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index c1c511f..e54de00 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -429,7 +429,7 @@
 
     {.config = {.prop = toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
                 .access = VehiclePropertyAccess::READ,
-                .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
+                .changeMode = VehiclePropertyChangeMode::STATIC},
      .initialValue = {.int32Values = {toInt(VehicleApPowerBootupReason::USER_POWER_ON)}}},
 
     {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 764bebd..dc34a50 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -138,6 +138,7 @@
             return status;
         }
     } else if (mHvacPowerProps.count(propValue.prop)) {
+        // TODO(75328113): this should be handled by property status
         auto hvacPowerOn = mPropStore->readValueOrNull(
             toInt(VehicleProperty::HVAC_POWER_ON),
             (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT));
@@ -165,6 +166,22 @@
         }
     }
 
+    if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
+        // Android side cannot set property status - this value is the
+        // purview of the HAL implementation to reflect the state of
+        // its underlying hardware
+        return StatusCode::INVALID_ARG;
+    }
+    auto currentPropValue = mPropStore->readValueOrNull(propValue);
+
+    if (currentPropValue == nullptr) {
+        return StatusCode::INVALID_ARG;
+    }
+    if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
+        // do not allow Android side to set() a disabled/error property
+        return StatusCode::NOT_AVAILABLE;
+    }
+
     if (!mPropStore->writeValue(propValue)) {
         return StatusCode::INVALID_ARG;
     }
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 3001213..87daedc 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -510,6 +510,7 @@
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ
+     * @data_enum VehicleTurnSignal
      */
     TURN_SIGNAL_STATE = (
         0x0408
@@ -522,6 +523,7 @@
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ
+     * @data_enum VehicleIgnitionState
      */
     IGNITION_STATE = (
         0x0409
diff --git a/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc b/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc
index 123d339..9bfd3ba 100644
--- a/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc
+++ b/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc
@@ -5,3 +5,4 @@
     class late_start
     user system
     group system input
+    writepid /dev/cpuset/system-background/tasks
\ No newline at end of file
diff --git a/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc b/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc
index e1f5faa..5a01ecc 100644
--- a/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc
+++ b/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc
@@ -1,5 +1,6 @@
 service vendor.bluetooth-1-0 /vendor/bin/hw/android.hardware.bluetooth@1.0-service
     class hal
+    capabilities NET_ADMIN SYS_NICE
     user bluetooth
     group bluetooth
     writepid /dev/stune/foreground/tasks
diff --git a/camera/common/1.0/default/Exif.cpp b/camera/common/1.0/default/Exif.cpp
index 3e894f9..6054999 100644
--- a/camera/common/1.0/default/Exif.cpp
+++ b/camera/common/1.0/default/Exif.cpp
@@ -983,15 +983,15 @@
     camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH);
     if (entry.count) {
         focal_length = entry.data.f[0];
+
+        if (!setFocalLength(
+                        static_cast<uint32_t>(focal_length * kRationalPrecision),
+                        kRationalPrecision)) {
+            ALOGE("%s: setting focal length failed.", __FUNCTION__);
+            return false;
+        }
     } else {
-        ALOGE("%s: Cannot find focal length in metadata.", __FUNCTION__);
-        return false;
-    }
-    if (!setFocalLength(
-                    static_cast<uint32_t>(focal_length * kRationalPrecision),
-                    kRationalPrecision)) {
-        ALOGE("%s: setting focal length failed.", __FUNCTION__);
-        return false;
+        ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__);
     }
 
     if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) {
diff --git a/camera/device/3.4/Android.bp b/camera/device/3.4/Android.bp
index 87acd25..2c649ba 100644
--- a/camera/device/3.4/Android.bp
+++ b/camera/device/3.4/Android.bp
@@ -25,7 +25,6 @@
         "HalStreamConfiguration",
         "PhysicalCameraMetadata",
         "PhysicalCameraSetting",
-        "RequestTemplate",
         "Stream",
         "StreamConfiguration",
     ],
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index 9a02ce8..6b05d4a 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -284,13 +284,6 @@
     const int32_t jpegMaxSize = mCfg.maxJpegBufSize;
     UPDATE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
 
-    const uint8_t jpegQuality = 90;
-    UPDATE(ANDROID_JPEG_QUALITY, &jpegQuality, 1);
-    UPDATE(ANDROID_JPEG_THUMBNAIL_QUALITY, &jpegQuality, 1);
-
-    const int32_t jpegOrientation = 0;
-    UPDATE(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
-
     // android.lens
     const uint8_t focusDistanceCalibration =
             ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
@@ -322,7 +315,6 @@
     // This means pipeline latency of X frame intervals. The maximum number is 4.
     const uint8_t requestPipelineMaxDepth = 4;
     UPDATE(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &requestPipelineMaxDepth, 1);
-    UPDATE(ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
 
     // Three numbers represent the maximum numbers of different types of output
     // streams simultaneously. The types are raw sensor, processed (but not
@@ -355,7 +347,6 @@
         ANDROID_SENSOR_TEST_PATTERN_MODE_OFF};
     UPDATE(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, testPatternModes,
            ARRAY_SIZE(testPatternModes));
-    UPDATE(ANDROID_SENSOR_TEST_PATTERN_MODE, &testPatternModes[0], 1);
 
     const uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
     UPDATE(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, &timestampSource, 1);
@@ -576,7 +567,6 @@
     std::vector<int32_t> streamConfigurations;
     std::vector<int64_t> minFrameDurations;
     std::vector<int64_t> stallDurations;
-    int64_t maxFrameDuration = 0;
     int32_t maxFps = std::numeric_limits<int32_t>::min();
     int32_t minFps = std::numeric_limits<int32_t>::max();
     std::set<int32_t> framerates;
@@ -604,9 +594,6 @@
             if (frameDuration < minFrameDuration) {
                 minFrameDuration = frameDuration;
             }
-            if (frameDuration > maxFrameDuration) {
-                maxFrameDuration = frameDuration;
-            }
             int32_t frameRateInt = static_cast<int32_t>(fr.getDouble());
             if (minFps > frameRateInt) {
                 minFps = frameRateInt;
@@ -639,14 +626,15 @@
     }
 
     std::vector<int32_t> fpsRanges;
-    // Variable range
-    fpsRanges.push_back(minFps);
-    fpsRanges.push_back(maxFps);
-    // Fixed ranges
+    // FPS ranges
     for (const auto& framerate : framerates) {
-        fpsRanges.push_back(framerate);
+        // Empirical: webcams often have close to 2x fps error and cannot support fixed fps range
+        fpsRanges.push_back(framerate / 2);
         fpsRanges.push_back(framerate);
     }
+    minFps /= 2;
+    int64_t maxFrameDuration = 1000000000LL / minFps;
+
     UPDATE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fpsRanges.data(),
            fpsRanges.size());
 
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 5569439..7015bcb 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -47,6 +47,9 @@
 const int kBadFramesAfterStreamOn = 1; // drop x frames after streamOn to get rid of some initial
                                        // bad frames. TODO: develop a better bad frame detection
                                        // method
+constexpr int MAX_RETRY = 15; // Allow retry some ioctl failures a few times to account for some
+                             // webcam showing temporarily ioctl failures.
+constexpr int IOCTL_RETRY_SLEEP_US = 33000; // 33ms * MAX_RETRY = 5 seconds
 
 bool tryLock(Mutex& mutex)
 {
@@ -1480,6 +1483,7 @@
 
     int jpegQuality, thumbQuality;
     Size thumbSize;
+    bool outputThumbnail = true;
 
     if (req->setting.exists(ANDROID_JPEG_QUALITY)) {
         camera_metadata_entry entry =
@@ -1505,6 +1509,9 @@
         thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]),
                            static_cast<uint32_t>(entry.data.i32[1])
         };
+        if (thumbSize.width == 0 && thumbSize.height == 0) {
+            outputThumbnail = false;
+        }
     } else {
         return lfail(
             "%s: ANDROID_JPEG_THUMBNAIL_SIZE not set", __FUNCTION__);
@@ -1532,14 +1539,16 @@
     /* Hold actual thumbnail and main image code sizes */
     size_t thumbCodeSize = 0, jpegCodeSize = 0;
     /* Temporary thumbnail code buffer */
-    std::vector<uint8_t> thumbCode(maxThumbCodeSize);
+    std::vector<uint8_t> thumbCode(outputThumbnail ? maxThumbCodeSize : 0);
 
     YCbCrLayout yu12Thumb;
-    ret = cropAndScaleThumbLocked(mYu12Frame, thumbSize, &yu12Thumb);
+    if (outputThumbnail) {
+        ret = cropAndScaleThumbLocked(mYu12Frame, thumbSize, &yu12Thumb);
 
-    if (ret != 0) {
-        return lfail(
-            "%s: crop and scale thumbnail failed!", __FUNCTION__);
+        if (ret != 0) {
+            return lfail(
+                "%s: crop and scale thumbnail failed!", __FUNCTION__);
+        }
     }
 
     /* Scale and crop main jpeg */
@@ -1550,12 +1559,14 @@
     }
 
     /* Encode the thumbnail image */
-    ret = encodeJpegYU12(thumbSize, yu12Thumb,
-            thumbQuality, 0, 0,
-            &thumbCode[0], maxThumbCodeSize, thumbCodeSize);
+    if (outputThumbnail) {
+        ret = encodeJpegYU12(thumbSize, yu12Thumb,
+                thumbQuality, 0, 0,
+                &thumbCode[0], maxThumbCodeSize, thumbCodeSize);
 
-    if (ret != 0) {
-        return lfail("%s: encodeJpegYU12 failed with %d",__FUNCTION__, ret);
+        if (ret != 0) {
+            return lfail("%s: thumbnail encodeJpegYU12 failed with %d",__FUNCTION__, ret);
+        }
     }
 
     /* Combine camera characteristics with request settings to form EXIF
@@ -1570,11 +1581,7 @@
 
     utils->setFromMetadata(meta, jpegSize.width, jpegSize.height);
 
-    /* Check if we made a non-zero-sized thumbnail. Currently not possible
-     * that we got this far and the code is size 0, but if this code moves
-     * around it might become relevant again */
-
-    ret = utils->generateApp1(thumbCodeSize ? &thumbCode[0] : 0, thumbCodeSize);
+    ret = utils->generateApp1(outputThumbnail ? &thumbCode[0] : 0, thumbCodeSize);
 
     if (!ret) {
         return lfail("%s: generating APP1 failed", __FUNCTION__);
@@ -2109,8 +2116,20 @@
     fmt.fmt.pix.pixelformat = v4l2Fmt.fourcc;
     ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_FMT, &fmt));
     if (ret < 0) {
-        ALOGE("%s: S_FMT ioctl failed: %s", __FUNCTION__, strerror(errno));
-        return -errno;
+        int numAttempt = 0;
+        while (ret < 0) {
+            ALOGW("%s: VIDIOC_S_FMT failed, wait 33ms and try again", __FUNCTION__);
+            usleep(IOCTL_RETRY_SLEEP_US); // sleep 100 ms and try again
+            ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_FMT, &fmt));
+            if (numAttempt == MAX_RETRY) {
+                break;
+            }
+            numAttempt++;
+        }
+        if (ret < 0) {
+            ALOGE("%s: S_FMT ioctl failed: %s", __FUNCTION__, strerror(errno));
+            return -errno;
+        }
     }
 
     if (v4l2Fmt.width != fmt.fmt.pix.width || v4l2Fmt.height != fmt.fmt.pix.height ||
@@ -2199,9 +2218,22 @@
 
     // VIDIOC_STREAMON: start streaming
     v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMON, &capture_type)) < 0) {
-        ALOGE("%s: VIDIOC_STREAMON failed: %s", __FUNCTION__, strerror(errno));
-        return -errno;
+    ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMON, &capture_type));
+    if (ret < 0) {
+        int numAttempt = 0;
+        while (ret < 0) {
+            ALOGW("%s: VIDIOC_STREAMON failed, wait 33ms and try again", __FUNCTION__);
+            usleep(IOCTL_RETRY_SLEEP_US); // sleep 100 ms and try again
+            ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMON, &capture_type));
+            if (numAttempt == MAX_RETRY) {
+                break;
+            }
+            numAttempt++;
+        }
+        if (ret < 0) {
+            ALOGE("%s: VIDIOC_STREAMON ioctl failed: %s", __FUNCTION__, strerror(errno));
+            return -errno;
+        }
     }
 
     // Swallow first few frames after streamOn to account for bad frames from some devices
@@ -2220,6 +2252,8 @@
         }
     }
 
+    ALOGI("%s: start V4L2 streaming %dx%d@%ffps",
+                __FUNCTION__, v4l2Fmt.width, v4l2Fmt.height, fps);
     mV4l2StreamingFmt = v4l2Fmt;
     mV4l2Streaming = true;
     return OK;
@@ -2388,6 +2422,7 @@
     uint32_t maxDim = 0;
     for (const auto& stream : config.streams) {
         float aspectRatio = ASPECT_RATIO(stream);
+        ALOGI("%s: request stream %dx%d", __FUNCTION__, stream.width, stream.height);
         if ((mCroppingType == VERTICAL && aspectRatio < desiredAr) ||
                 (mCroppingType == HORIZONTAL && aspectRatio > desiredAr)) {
             desiredAr = aspectRatio;
@@ -2572,6 +2607,9 @@
     const uint8_t nrMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
     UPDATE(md, ANDROID_NOISE_REDUCTION_MODE, &nrMode, 1);
 
+    const int32_t testPatternModes = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+    UPDATE(md, ANDROID_SENSOR_TEST_PATTERN_MODE, &testPatternModes, 1);
+
     const uint8_t fdMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
     UPDATE(md, ANDROID_STATISTICS_FACE_DETECT_MODE, &fdMode, 1);
 
@@ -2596,7 +2634,7 @@
         }
     }
     int32_t defaultFramerate = support30Fps ? 30 : maxFps;
-    int32_t defaultFpsRange[] = {defaultFramerate, defaultFramerate};
+    int32_t defaultFpsRange[] = {defaultFramerate / 2, defaultFramerate};
     UPDATE(md, ANDROID_CONTROL_AE_TARGET_FPS_RANGE, defaultFpsRange, ARRAY_SIZE(defaultFpsRange));
 
     uint8_t antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
@@ -2691,6 +2729,10 @@
     const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
     UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
 
+    // This means pipeline latency of X frame intervals. The maximum number is 4.
+    const uint8_t requestPipelineMaxDepth = 4;
+    UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
     // android.scaler
     const int32_t crop_region[] = {
           active_array_size.data.i32[0], active_array_size.data.i32[1],
diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp
index d28a4dd..2e2f73b 100644
--- a/camera/device/3.4/default/ExternalCameraUtils.cpp
+++ b/camera/device/3.4/default/ExternalCameraUtils.cpp
@@ -283,9 +283,8 @@
         numVideoBuffers(kDefaultNumVideoBuffer),
         numStillBuffers(kDefaultNumStillBuffer) {
     fpsLimits.push_back({/*Size*/{ 640,  480}, /*FPS upper bound*/30.0});
-    fpsLimits.push_back({/*Size*/{1280,  720}, /*FPS upper bound*/15.0});
-    fpsLimits.push_back({/*Size*/{1920, 1080}, /*FPS upper bound*/10.0});
-    fpsLimits.push_back({/*Size*/{4096, 3072}, /*FPS upper bound*/5.0});
+    fpsLimits.push_back({/*Size*/{1280,  720}, /*FPS upper bound*/7.5});
+    fpsLimits.push_back({/*Size*/{1920, 1080}, /*FPS upper bound*/5.0});
 }
 
 
diff --git a/camera/provider/2.4/ICameraProvider.hal b/camera/provider/2.4/ICameraProvider.hal
index abb6366..8773bc0 100644
--- a/camera/provider/2.4/ICameraProvider.hal
+++ b/camera/provider/2.4/ICameraProvider.hal
@@ -35,7 +35,8 @@
  * where
  *   - <major>/<minor> is the provider HAL HIDL version,
  *   - <type> is the type of devices this provider knows about, such as
- *     "internal", "legacy", "external", or "remote"
+ *     "internal", "legacy", "external", "remote" etc. The camera framework
+ *     must not differentiate or chage its behavior based on the specific type.
  *   - <instance> is a non-negative integer starting from 0 to disambiguate
  *     between multiple HALs of the same type.
  *
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index dd8d92d..370ffdd 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -182,7 +182,14 @@
         <interface>
             <name>IKeymasterDevice</name>
             <instance>default</instance>
-	    <!-- TODO: strongbox here? -->
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.keymaster</name>
+        <version>4.0</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>strongbox</instance>
         </interface>
     </hal>
     <hal format="hidl" optional="true">
@@ -214,6 +221,15 @@
         </interface>
     </hal>
     <hal format="hidl" optional="true">
+        <name>android.hardware.neuralnetworks</name>
+        <version>1.0</version>
+        <interface>
+            <name>IDevice</name>
+            <!-- TODO(b/73738616): This should be * (match any) -->
+            <instance>hvx</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
         <name>android.hardware.nfc</name>
         <version>1.0</version>
         <interface>
@@ -275,7 +291,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.soundtrigger</name>
-        <version>2.0</version>
+        <version>2.0-1</version>
         <interface>
             <name>ISoundTriggerHw</name>
             <instance>default</instance>
@@ -330,8 +346,16 @@
         </interface>
     </hal>
     <hal format="hidl" optional="true">
+        <name>android.hardware.usb.gadget</name>
+        <version>1.0</version>
+        <interface>
+            <name>IUsbGadget</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
         <name>android.hardware.vibrator</name>
-        <version>1.0-1</version>
+        <version>1.0-2</version>
         <interface>
             <name>IVibrator</name>
             <instance>default</instance>
@@ -355,13 +379,21 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.wifi</name>
-        <version>1.0-1</version>
+        <version>1.0-2</version>
         <interface>
             <name>IWifi</name>
             <instance>default</instance>
         </interface>
     </hal>
     <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.hostapd</name>
+        <version>1.0</version>
+        <interface>
+            <name>IHostapd</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
         <name>android.hardware.wifi.offload</name>
         <version>1.0</version>
         <interface>
@@ -371,7 +403,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.wifi.supplicant</name>
-        <version>1.0</version>
+        <version>1.0-1</version>
         <interface>
             <name>ISupplicant</name>
             <instance>default</instance>
diff --git a/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h b/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h
index 0d03591..6558799 100644
--- a/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h
+++ b/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h
@@ -105,6 +105,7 @@
     PromptUserConfirmation,
     DeliverSecureInputEvent,
     Abort,
+    Vendor,
 };
 
 template <Command cmd>
@@ -115,6 +116,7 @@
 DECLARE_COMMAND(PromptUserConfirmation);
 DECLARE_COMMAND(DeliverSecureInputEvent);
 DECLARE_COMMAND(Abort);
+DECLARE_COMMAND(Vendor);
 
 using PromptUserConfirmationMsg = Message<PromptUserConfirmation_t, hidl_string, hidl_vec<uint8_t>,
                                           hidl_string, hidl_vec<UIOption>>;
@@ -166,7 +168,7 @@
 }
 inline void zero(const volatile uint8_t*, const volatile uint8_t*) {}
 // This odd alignment function aligns the stream position to a 4byte and never 8byte boundary
-// It is to accommodate the 4 byte size field which is then followed by 8byte alligned data.
+// It is to accommodate the 4 byte size field which is then followed by 8byte aligned data.
 template <typename T>
 StreamState<T> unalign(StreamState<T> s) {
     uint8_t unalignment = uintptr_t(s.pos_) & 0x3;
diff --git a/current.txt b/current.txt
index 29f152f..69ad1ed 100644
--- a/current.txt
+++ b/current.txt
@@ -251,7 +251,7 @@
 
 # ABI preserving changes to HALs during Android P
 cf72ff5a52bfa4d08e9e1000cf3ab5952a2d280c7f13cdad5ab7905c08050766 android.hardware.camera.metadata@3.2::types
-7b4723305354193b889a24153e1292fec62d5a5127bdfba08a5a27440f092de9 android.hardware.camera.provider@2.4::ICameraProvider
+3902efc42097cba55f0655aa389e052ea70164e99ced1a6d1ef53dafc13f7650 android.hardware.camera.provider@2.4::ICameraProvider
 6fa9804a17a8bb7923a56bd10493a5483c20007e4c9026fd04287bee7c945a8c android.hardware.gnss@1.0::IGnssCallback
 fb92e2b40f8e9d494e8fd3b4ac18499a3216342e7cff160714c3bbf3660b6e79 android.hardware.gnss@1.0::IGnssConfiguration
 251594ea9b27447bfa005ebd806e58fb0ae4aad84a69938129c9800ec0c64eda android.hardware.gnss@1.0::IGnssMeasurementCallback
@@ -268,7 +268,7 @@
 888ac906461327fa0bd93854d5109be8c292a33afdb467164826970a8bd5b789 android.hardware.audio@4.0::IStreamOut
 15f6ae78e73344c8e7d68847ef03caec64fcd9f951bbcf59957d1712c247fcff android.hardware.audio@4.0::IStreamOutCallback
 e8abfc73b7f3c2095ea4fad83b5f42bced4315de2abcc3199376af880dac451b android.hardware.audio@4.0::types
-dac6018ece5af342708766287a2de2e7f3bd7e60f373dc2dddc4d4d1554e1b35 android.hardware.audio.common@4.0::types
+5d47a2ad2c136b8aba067dd45bb10d0ad390dd76340764154f580658f98f4fe6 android.hardware.audio.common@4.0::types
 b04b6b364938b80008e61fa2e318bc299622433e57c2e1f6cfba332a3f6e3f15 android.hardware.audio.effect@4.0::IAcousticEchoCancelerEffect
 1c17d4ece5c8ba3f7a646a305ee0dd109b0d51372e1bd585812e513cd40e1852 android.hardware.audio.effect@4.0::IAutomaticGainControlEffect
 34174259fe6fbb1bb14e7103e097f2f25529271a676687845b2f55d6d0d9d617 android.hardware.audio.effect@4.0::IBassBoostEffect
@@ -294,7 +294,7 @@
 3b17c1fdfc389e0abe626c37054954b07201127d890c2bc05d47613ec1f4de4f android.hardware.automotive.evs@1.0::types
 b3caf524c46a47d67e6453a34419e1881942d059e146cda740502670e9a752c3 android.hardware.automotive.vehicle@2.0::IVehicle
 80fb4156fa91ce86e49bd2cabe215078f6b69591d416a09e914532eae6712052 android.hardware.automotive.vehicle@2.0::IVehicleCallback
-442de3a3d3819ff8b8bfe9ec710592ca8af7c16bfdb5eb8911b898b8f12b2bb0 android.hardware.automotive.vehicle@2.0::types
+4ff0dcfb938a5df283eef47de33b4e1284fab73f584cfc0c94e97317bdb7bf26 android.hardware.automotive.vehicle@2.0::types
 32cc50cc2a7658ec613c0c2dd2accbf6a05113b749852879e818b8b7b438db19 android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioHost
 ff4be64d7992f8bec97dff37f35450e79b3430c61f85f54322ce45bef229dc3b android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload
 27f22d2e873e6201f9620cf4d8e2facb25bd0dd30a2b911e441b4600d560fa62 android.hardware.bluetooth.a2dp@1.0::types
@@ -316,7 +316,7 @@
 fef2f0ebde7704548fb203df46673ceb342272fc4fa9d0af25a980d2584a36e7 android.hardware.drm@1.1::IDrmPlugin
 5047a346ecce239404b9020959f60dd467318e9c17b290a6386bc3894df62c3c android.hardware.drm@1.1::types
 be794f5df97f134d6dcabb866b250d1305100e7ae07fb253b7841df890b931bb android.hardware.gnss@1.1::IGnss
-3b912f07e276de598155615491b35f151f59cb2d7a100c831f04a23f4d625311 android.hardware.gnss@1.1::IGnssCallback
+8ad55bc35bb3a83e65c018bdfde7ae5ebc749ff2bf6b79412ded0bc6c89b97d8 android.hardware.gnss@1.1::IGnssCallback
 3c5183d7506010be57e0f748e3640fc2ded1ba955784b6256ba427f4c399591c android.hardware.gnss@1.1::IGnssConfiguration
 b054af24fbb70d54cde1fb5cba126809e7c4e863e8f9115dc492321dfbcbc993 android.hardware.gnss@1.1::IGnssMeasurement
 83e7a10ff3702147bd7ffa04567b20d407a3b16bbb7705644af44d919afe9103 android.hardware.gnss@1.1::IGnssMeasurementCallback
diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp
index ed8196e..dba3e42 100644
--- a/drm/1.1/Android.bp
+++ b/drm/1.1/Android.bp
@@ -17,8 +17,10 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "DrmMetricGroup",
         "HdcpLevel",
         "KeyRequestType",
+        "SecureStopRelease",
         "SecurityLevel",
     ],
     gen_java: false,
diff --git a/gnss/1.1/IGnssCallback.hal b/gnss/1.1/IGnssCallback.hal
index fdd2ebe..7313340 100644
--- a/gnss/1.1/IGnssCallback.hal
+++ b/gnss/1.1/IGnssCallback.hal
@@ -30,6 +30,9 @@
      * This is a user-visible string that identifies the model and version of the GNSS HAL.
      * For example "ABC Co., Baseband Part 1234, RF Part 567, Software version 3.14.159"
      *
+     * For privacy reasons, this string must not contain any device-specific serial number or other
+     * identifier that uniquely identifies an individual device.
+     *
      * This must be called in response to IGnss::setCallback
      *
      * @param name String providing the name of the GNSS HAL implementation
diff --git a/graphics/mapper/2.1/Android.bp b/graphics/mapper/2.1/Android.bp
index d917e59..8527d3d 100644
--- a/graphics/mapper/2.1/Android.bp
+++ b/graphics/mapper/2.1/Android.bp
@@ -11,6 +11,7 @@
         "IMapper.hal",
     ],
     interfaces: [
+        "android.hardware.graphics.common@1.0",
         "android.hardware.graphics.common@1.1",
         "android.hardware.graphics.mapper@2.0",
         "android.hidl.base@1.0",
diff --git a/neuralnetworks/1.1/Android.bp b/neuralnetworks/1.1/Android.bp
index 9365d4e..81bcef3 100644
--- a/neuralnetworks/1.1/Android.bp
+++ b/neuralnetworks/1.1/Android.bp
@@ -15,6 +15,7 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "Capabilities",
         "Model",
         "Operation",
         "OperationType",
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworksV1_1BasicTest.cpp b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworksV1_1BasicTest.cpp
index 17f6744..10591dc 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworksV1_1BasicTest.cpp
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworksV1_1BasicTest.cpp
@@ -286,6 +286,169 @@
     EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus);
 }
 
+class NeuralnetworksInputsOutputsTest
+    : public NeuralnetworksHidlTest,
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
+   protected:
+    virtual void SetUp() { NeuralnetworksHidlTest::SetUp(); }
+    virtual void TearDown() { NeuralnetworksHidlTest::TearDown(); }
+    V1_1::Model createModel(const std::vector<uint32_t>& inputs,
+                            const std::vector<uint32_t>& outputs) {
+        // We set up the operands as floating-point with no designated
+        // model inputs and outputs, and then patch type and lifetime
+        // later on in this function.
+
+        std::vector<Operand> operands = {
+            {
+                .type = OperandType::TENSOR_FLOAT32,
+                .dimensions = {1},
+                .numberOfConsumers = 1,
+                .scale = 0.0f,
+                .zeroPoint = 0,
+                .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+                .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                .type = OperandType::TENSOR_FLOAT32,
+                .dimensions = {1},
+                .numberOfConsumers = 1,
+                .scale = 0.0f,
+                .zeroPoint = 0,
+                .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+                .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                .type = OperandType::INT32,
+                .dimensions = {},
+                .numberOfConsumers = 1,
+                .scale = 0.0f,
+                .zeroPoint = 0,
+                .lifetime = OperandLifeTime::CONSTANT_COPY,
+                .location = {.poolIndex = 0, .offset = 0, .length = sizeof(int32_t)},
+            },
+            {
+                .type = OperandType::TENSOR_FLOAT32,
+                .dimensions = {1},
+                .numberOfConsumers = 0,
+                .scale = 0.0f,
+                .zeroPoint = 0,
+                .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+                .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+        };
+
+        const std::vector<Operation> operations = {{
+            .type = OperationType::ADD, .inputs = {0, 1, 2}, .outputs = {3},
+        }};
+
+        std::vector<uint8_t> operandValues;
+        int32_t activation[1] = {static_cast<int32_t>(FusedActivationFunc::NONE)};
+        operandValues.insert(operandValues.end(), reinterpret_cast<const uint8_t*>(&activation[0]),
+                             reinterpret_cast<const uint8_t*>(&activation[1]));
+
+        if (kQuantized) {
+            for (auto& operand : operands) {
+                if (operand.type == OperandType::TENSOR_FLOAT32) {
+                    operand.type = OperandType::TENSOR_QUANT8_ASYMM;
+                    operand.scale = 1.0f;
+                    operand.zeroPoint = 0;
+                }
+            }
+        }
+
+        auto patchLifetime = [&operands](const std::vector<uint32_t>& operandIndexes,
+                                         OperandLifeTime lifetime) {
+            for (uint32_t index : operandIndexes) {
+                operands[index].lifetime = lifetime;
+            }
+        };
+        if (kInputHasPrecedence) {
+            patchLifetime(outputs, OperandLifeTime::MODEL_OUTPUT);
+            patchLifetime(inputs, OperandLifeTime::MODEL_INPUT);
+        } else {
+            patchLifetime(inputs, OperandLifeTime::MODEL_INPUT);
+            patchLifetime(outputs, OperandLifeTime::MODEL_OUTPUT);
+        }
+
+        return {
+            .operands = operands,
+            .operations = operations,
+            .inputIndexes = inputs,
+            .outputIndexes = outputs,
+            .operandValues = operandValues,
+            .pools = {},
+        };
+    }
+    void check(const std::string& name,
+               bool expectation,  // true = success
+               const std::vector<uint32_t>& inputs, const std::vector<uint32_t>& outputs) {
+        SCOPED_TRACE(name + " (HAL calls should " + (expectation ? "succeed" : "fail") + ", " +
+                     (kInputHasPrecedence ? "input" : "output") + " precedence, " +
+                     (kQuantized ? "quantized" : "float"));
+
+        V1_1::Model model = createModel(inputs, outputs);
+
+        // ensure that getSupportedOperations_1_1() checks model validity
+        ErrorStatus supportedOpsErrorStatus = ErrorStatus::GENERAL_FAILURE;
+        Return<void> supportedOpsReturn = device->getSupportedOperations_1_1(
+            model, [&model, &supportedOpsErrorStatus](ErrorStatus status,
+                                                      const hidl_vec<bool>& supported) {
+                supportedOpsErrorStatus = status;
+                if (status == ErrorStatus::NONE) {
+                    ASSERT_EQ(supported.size(), model.operations.size());
+                }
+            });
+        ASSERT_TRUE(supportedOpsReturn.isOk());
+        ASSERT_EQ(supportedOpsErrorStatus,
+                  (expectation ? ErrorStatus::NONE : ErrorStatus::INVALID_ARGUMENT));
+
+        // ensure that prepareModel_1_1() checks model validity
+        sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback;
+        ASSERT_NE(preparedModelCallback.get(), nullptr);
+        Return<ErrorStatus> prepareLaunchReturn =
+            device->prepareModel_1_1(model, preparedModelCallback);
+        ASSERT_TRUE(prepareLaunchReturn.isOk());
+        ASSERT_TRUE(prepareLaunchReturn == ErrorStatus::NONE ||
+                    prepareLaunchReturn == ErrorStatus::INVALID_ARGUMENT);
+        bool preparationOk = (prepareLaunchReturn == ErrorStatus::NONE);
+        if (preparationOk) {
+            preparedModelCallback->wait();
+            preparationOk = (preparedModelCallback->getStatus() == ErrorStatus::NONE);
+        }
+
+        if (preparationOk) {
+            ASSERT_TRUE(expectation);
+        } else {
+            // Preparation can fail for reasons other than an invalid model --
+            // for example, perhaps not all operations are supported, or perhaps
+            // the device hit some kind of capacity limit.
+            bool invalid = prepareLaunchReturn == ErrorStatus::INVALID_ARGUMENT ||
+                           preparedModelCallback->getStatus() == ErrorStatus::INVALID_ARGUMENT;
+            ASSERT_NE(expectation, invalid);
+        }
+    }
+
+    // Indicates whether an operand that appears in both the inputs
+    // and outputs vector should have lifetime appropriate for input
+    // rather than for output.
+    const bool kInputHasPrecedence = std::get<0>(GetParam());
+
+    // Indicates whether we should test TENSOR_QUANT8_ASYMM rather
+    // than TENSOR_FLOAT32.
+    const bool kQuantized = std::get<1>(GetParam());
+};
+
+TEST_P(NeuralnetworksInputsOutputsTest, Validate) {
+    check("Ok", true, {0, 1}, {3});
+    check("InputIsOutput", false, {0, 1}, {3, 0});
+    check("OutputIsInput", false, {0, 1, 3}, {3});
+    check("DuplicateInputs", false, {0, 1, 0}, {3});
+    check("DuplicateOutputs", false, {0, 1}, {3, 3});
+}
+
+INSTANTIATE_TEST_CASE_P(Flavor, NeuralnetworksInputsOutputsTest,
+                        ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
 }  // namespace functional
 }  // namespace vts
 }  // namespace V1_1
diff --git a/wifi/1.2/default/android.hardware.wifi@1.0-service.rc b/wifi/1.2/default/android.hardware.wifi@1.0-service.rc
index eecb6d0..cf849d0 100644
--- a/wifi/1.2/default/android.hardware.wifi@1.0-service.rc
+++ b/wifi/1.2/default/android.hardware.wifi@1.0-service.rc
@@ -1,4 +1,5 @@
 service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi@1.0-service
     class hal
+    capabilities NET_ADMIN NET_RAW SYS_MODULE
     user wifi
     group wifi gps
diff --git a/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
index 27c8d60..3928c9a 100644
--- a/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
+++ b/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
@@ -146,7 +146,7 @@
         } else if (type == IfaceType::STA) {
             chip_->createStaIface(
                 [&iface_name](const WifiStatus& status,
-                              const sp<IWifiStaIface>& iface) {
+                              const sp<V1_0::IWifiStaIface>& iface) {
                     if (WifiStatusCode::SUCCESS == status.code) {
                         ASSERT_NE(iface.get(), nullptr);
                         iface->getName([&iface_name](const WifiStatus& status,
diff --git a/wifi/1.2/default/wifi_legacy_hal.cpp b/wifi/1.2/default/wifi_legacy_hal.cpp
index 84af9c4..c314e64 100644
--- a/wifi/1.2/default/wifi_legacy_hal.cpp
+++ b/wifi/1.2/default/wifi_legacy_hal.cpp
@@ -492,6 +492,28 @@
         getIfaceHandle(iface_name), program.data(), program.size());
 }
 
+std::pair<wifi_error, std::vector<uint8_t>>
+WifiLegacyHal::readApfPacketFilterData(const std::string& iface_name) {
+    if (global_func_table_.wifi_read_packet_filter == nullptr) {
+        return {WIFI_ERROR_NOT_SUPPORTED, {}};
+    }
+
+    PacketFilterCapabilities caps;
+    wifi_error status = global_func_table_.wifi_get_packet_filter_capabilities(
+        getIfaceHandle(iface_name), &caps.version, &caps.max_len);
+    if (status != WIFI_SUCCESS) {
+        return {status, {}};
+    }
+
+    // Size the buffer to read the entire program & work memory.
+    std::vector<uint8_t> buffer(caps.max_len);
+
+    status = global_func_table_.wifi_read_packet_filter(
+        getIfaceHandle(iface_name), /*src_offset=*/0, buffer.data(),
+        buffer.size());
+    return {status, move(buffer)};
+}
+
 std::pair<wifi_error, wifi_gscan_capabilities>
 WifiLegacyHal::getGscanCapabilities(const std::string& iface_name) {
     wifi_gscan_capabilities caps;
diff --git a/wifi/1.2/default/wifi_legacy_hal.h b/wifi/1.2/default/wifi_legacy_hal.h
index dedbbf8..60905ab 100644
--- a/wifi/1.2/default/wifi_legacy_hal.h
+++ b/wifi/1.2/default/wifi_legacy_hal.h
@@ -156,7 +156,7 @@
  * Class that encapsulates all legacy HAL interactions.
  * This class manages the lifetime of the event loop thread used by legacy HAL.
  *
- * Note: aThere will only be a single instance of this class created in the Wifi
+ * Note: There will only be a single instance of this class created in the Wifi
  * object and will be valid for the lifetime of the process.
  */
 class WifiLegacyHal {
@@ -188,6 +188,8 @@
         const std::string& iface_name);
     wifi_error setPacketFilter(const std::string& iface_name,
                                const std::vector<uint8_t>& program);
+    std::pair<wifi_error, std::vector<uint8_t>> readApfPacketFilterData(
+        const std::string& iface_name);
     // Gscan functions.
     std::pair<wifi_error, wifi_gscan_capabilities> getGscanCapabilities(
         const std::string& iface_name);
diff --git a/wifi/1.2/default/wifi_sta_iface.cpp b/wifi/1.2/default/wifi_sta_iface.cpp
index 6faf009..ab99daa 100644
--- a/wifi/1.2/default/wifi_sta_iface.cpp
+++ b/wifi/1.2/default/wifi_sta_iface.cpp
@@ -94,6 +94,13 @@
                            hidl_status_cb, cmd_id, program);
 }
 
+Return<void> WifiStaIface::readApfPacketFilterData(
+    readApfPacketFilterData_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+                           &WifiStaIface::readApfPacketFilterDataInternal,
+                           hidl_status_cb);
+}
+
 Return<void> WifiStaIface::getBackgroundScanCapabilities(
     getBackgroundScanCapabilities_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
@@ -297,6 +304,15 @@
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
+std::pair<WifiStatus, std::vector<uint8_t>>
+WifiStaIface::readApfPacketFilterDataInternal() {
+    const std::pair<legacy_hal::wifi_error, std::vector<uint8_t>>
+        legacy_status_and_data =
+            legacy_hal_.lock()->readApfPacketFilterData(ifname_);
+    return {createWifiStatusFromLegacyError(legacy_status_and_data.first),
+            std::move(legacy_status_and_data.second)};
+}
+
 std::pair<WifiStatus, StaBackgroundScanCapabilities>
 WifiStaIface::getBackgroundScanCapabilitiesInternal() {
     legacy_hal::wifi_error legacy_status;
diff --git a/wifi/1.2/default/wifi_sta_iface.h b/wifi/1.2/default/wifi_sta_iface.h
index 423365c..a212888 100644
--- a/wifi/1.2/default/wifi_sta_iface.h
+++ b/wifi/1.2/default/wifi_sta_iface.h
@@ -18,8 +18,8 @@
 #define WIFI_STA_IFACE_H_
 
 #include <android-base/macros.h>
-#include <android/hardware/wifi/1.0/IWifiStaIface.h>
 #include <android/hardware/wifi/1.0/IWifiStaIfaceEventCallback.h>
+#include <android/hardware/wifi/1.2/IWifiStaIface.h>
 
 #include "hidl_callback_util.h"
 #include "wifi_legacy_hal.h"
@@ -34,7 +34,7 @@
 /**
  * HIDL interface object used to control a STA Iface instance.
  */
-class WifiStaIface : public V1_0::IWifiStaIface {
+class WifiStaIface : public V1_2::IWifiStaIface {
    public:
     WifiStaIface(const std::string& ifname,
                  const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
@@ -56,6 +56,8 @@
     Return<void> installApfPacketFilter(
         uint32_t cmd_id, const hidl_vec<uint8_t>& program,
         installApfPacketFilter_cb hidl_status_cb) override;
+    Return<void> readApfPacketFilterData(
+        readApfPacketFilterData_cb hidl_status_cb) override;
     Return<void> getBackgroundScanCapabilities(
         getBackgroundScanCapabilities_cb hidl_status_cb) override;
     Return<void> getValidFrequenciesForBand(
@@ -113,6 +115,8 @@
     getApfPacketFilterCapabilitiesInternal();
     WifiStatus installApfPacketFilterInternal(
         uint32_t cmd_id, const std::vector<uint8_t>& program);
+    std::pair<WifiStatus, std::vector<uint8_t>>
+    readApfPacketFilterDataInternal();
     std::pair<WifiStatus, StaBackgroundScanCapabilities>
     getBackgroundScanCapabilitiesInternal();
     std::pair<WifiStatus, std::vector<WifiChannelInMhz>>