Merge "Audio: Add AUDIO_FORMAT_DTS_UHD" into sc-dev
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 19b0a35..f5b53d3 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
@@ -1064,6 +1064,16 @@
                     .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
          .initialValue = {.stringValue = "Vendor String Property"}},
 
+        {.config = {.prop = toInt(VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_TYPE),
+                    .access = VehiclePropertyAccess::READ,
+                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
+         .initialValue = {.int32Values = {0}}},
+
+        {.config = {.prop = toInt(VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_STATUS),
+                    .access = VehiclePropertyAccess::READ,
+                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
+         .initialValue = {.int32Values = {0}}},
+
         {.config =
                  {
                          .prop = toInt(VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION),
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 1fe4bac..1fb7dc7 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -3207,6 +3207,66 @@
          | VehiclePropertyType:BYTES
          | VehicleArea:GLOBAL),
 
+    /**
+     * Electronic Toll Collection card type.
+     *
+     * This property indicates the type of ETC card in this vehicle.
+     * If the head unit is aware of an ETC card attached to the vehicle, this property should
+     * return the type of card attached; otherwise, this property should be UNAVAILABLE.
+     *
+     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+     * @access VehiclePropertyAccess:READ
+     * @data_enum ElectronicTollCollectionCardType
+     */
+     ELECTRONIC_TOLL_COLLECTION_CARD_TYPE = (
+         0x0F39
+         | VehiclePropertyGroup:SYSTEM
+         | VehiclePropertyType:INT32
+         | VehicleArea:GLOBAL),
+
+    /**
+     * Electronic Toll Collection card status.
+     *
+     * This property indicates the status of ETC card in this vehicle.
+     * If the head unit is aware of an ETC card attached to the vehicle,
+     * ELECTRONIC_TOLL_COLLECTION_CARD_TYPE gives that status of the card; otherwise,
+     * this property should be UNAVAILABLE.
+     *
+     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+     * @access VehiclePropertyAccess:READ
+     * @data_enum ElectronicTollCollectionCardStatus
+     */
+     ELECTRONIC_TOLL_COLLECTION_CARD_STATUS = (
+         0x0F3A
+         | VehiclePropertyGroup:SYSTEM
+         | VehiclePropertyType:INT32
+         | VehicleArea:GLOBAL),
+};
+
+/**
+ * Used by ELECTRONIC_TOLL_COLLECTION_CARD_TYPE.
+ */
+enum ElectronicTollCollectionCardType : int32_t {
+    // Type is unknown or not in the list below.
+    UNKNOWN = 0,
+    // A Japanese ETC card reader that does not support ETC 2.0.
+    JP_ELECTRONIC_TOLL_COLLECTION_CARD = 1,
+    // A Japanese ETC 2.0 card reader.
+    JP_ELECTRONIC_TOLL_COLLECTION_CARD_V2 = 2,
+};
+
+/**
+ * Used by ELECTRONIC_TOLL_COLLECTION_CARD_STATUS.
+ */
+enum ElectronicTollCollectionCardStatus : int32_t {
+    // Status could not be determined
+    UNKNOWN = 0,
+    // A valid ETC card is present
+    ELECTRONIC_TOLL_COLLECTION_CARD_VALID = 1,
+    // An ETC card is present, but it is expired or otherwise invalid
+    ELECTRONIC_TOLL_COLLECTION_CARD_INVALID = 2,
+    // No ETC card is inserted in the reader.
+    ELECTRONIC_TOLL_COLLECTION_CARD_NOT_INSERTED = 3,
 };
 
 /**
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index c656af2..bb22974 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -419,7 +419,7 @@
     </hal>
     <hal format="aidl" optional="false">
         <name>android.hardware.power</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>IPower</name>
             <instance>default</instance>
diff --git a/current.txt b/current.txt
index 6c576ca..270880f 100644
--- a/current.txt
+++ b/current.txt
@@ -782,6 +782,8 @@
 f729ee6a5f136b25d79ea6895d24700fce413df555baaecf2c39e4440d15d043 android.hardware.neuralnetworks@1.0::types
 a84f8dac7a9b75de1cc2936a9b429b9b62b32a31ea88ca52c29f98f5ddc0fa95 android.hardware.neuralnetworks@1.2::types
 cd331b92312d16ab89f475c39296abbf539efc4114a8c5c2b136ad99b904ef33 android.hardware.neuralnetworks@1.3::types
+c3fec5bd470984402997f78a74b6511efc4063b270f2bd9ee7b78f48b683a1bb android.hardware.neuralnetworks@1.3::IDevice
+0fdfad62c2ec33b52e6687004e5a1971c02d10b93ee4d26df5ccff7ce032494a android.hardware.neuralnetworks@1.3::IPreparedModel
 e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
 b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
 0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 8d6e74a..ea40971 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -117,6 +117,9 @@
 DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
 DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
 DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_SERIAL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_IMEI);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MEID);
 DECLARE_TYPED_TAG(AUTH_TIMEOUT);
 DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
 DECLARE_TYPED_TAG(BLOCK_MODE);
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index e0b04a8..de889e4 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -131,6 +131,14 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * Optionally, the driver may save the prepared model to cache during the
      * asynchronous preparation. Any error that occurs when saving to cache must
@@ -249,7 +257,15 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT}
      * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The
      * error due to an abort must be sent the same way as other errors,
-     * described above.
+     * described above. The deadline is represented as nanoseconds since the
+     * epoch of the steady clock (as if from
+     * std::chrono::steady_clock::time_point), but the service may convert it to
+     * the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * The only information that may be unknown to the model at this stage is
      * the shape of the tensors, which may only be known at execution time. As
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index e7d63f4..8b86a1a 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -74,6 +74,14 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * Any number of calls to the execute* and executeSynchronously* functions,
      * in any combination, may be made concurrently, even on the same
@@ -150,6 +158,14 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * Any number of calls to the execute* and executeSynchronously* functions,
      * in any combination, may be made concurrently, even on the same
@@ -231,6 +247,14 @@
      * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * If any of the sync fences in waitFor changes to error status after the executeFenced
      * call succeeds, or the execution is aborted because it cannot finish before the deadline
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 8083ae4..e8a4f55 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -42,6 +42,23 @@
 
 namespace {
 
+std::chrono::nanoseconds makeNanosFromUint64(uint64_t nanoseconds) {
+    constexpr auto kMaxCount = std::chrono::nanoseconds::max().count();
+    using CommonType = std::common_type_t<std::chrono::nanoseconds::rep, uint64_t>;
+    const auto count = std::min<CommonType>(kMaxCount, nanoseconds);
+    return std::chrono::nanoseconds{static_cast<std::chrono::nanoseconds::rep>(count)};
+}
+
+uint64_t makeUint64FromNanos(std::chrono::nanoseconds nanoseconds) {
+    if (nanoseconds < std::chrono::nanoseconds::zero()) {
+        return 0;
+    }
+    constexpr auto kMaxCount = std::numeric_limits<uint64_t>::max();
+    using CommonType = std::common_type_t<std::chrono::nanoseconds::rep, uint64_t>;
+    const auto count = std::min<CommonType>(kMaxCount, nanoseconds.count());
+    return static_cast<uint64_t>(count);
+}
+
 template <typename Type>
 constexpr std::underlying_type_t<Type> underlyingType(Type value) {
     return static_cast<std::underlying_type_t<Type>>(value);
@@ -237,8 +254,32 @@
     switch (optionalTimePoint.getDiscriminator()) {
         case Discriminator::none:
             return {};
-        case Discriminator::nanosecondsSinceEpoch:
-            return TimePoint{Duration{optionalTimePoint.nanosecondsSinceEpoch()}};
+        case Discriminator::nanosecondsSinceEpoch: {
+            const auto currentSteadyTime = std::chrono::steady_clock::now();
+            const auto currentBootTime = Clock::now();
+
+            const auto timeSinceEpoch =
+                    makeNanosFromUint64(optionalTimePoint.nanosecondsSinceEpoch());
+            const auto steadyTimePoint = std::chrono::steady_clock::time_point{timeSinceEpoch};
+
+            // Both steadyTimePoint and currentSteadyTime are guaranteed to be non-negative, so this
+            // subtraction will never overflow or underflow.
+            const auto timeRemaining = steadyTimePoint - currentSteadyTime;
+
+            // currentBootTime is guaranteed to be non-negative, so this code only protects against
+            // an overflow.
+            nn::TimePoint bootTimePoint;
+            constexpr auto kZeroNano = std::chrono::nanoseconds::zero();
+            constexpr auto kMaxTime = nn::TimePoint::max();
+            if (timeRemaining > kZeroNano && currentBootTime > kMaxTime - timeRemaining) {
+                bootTimePoint = kMaxTime;
+            } else {
+                bootTimePoint = currentBootTime + timeRemaining;
+            }
+
+            constexpr auto kZeroTime = nn::TimePoint{};
+            return std::max(bootTimePoint, kZeroTime);
+        }
     }
     return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
            << "Invalid OptionalTimePoint discriminator "
@@ -549,9 +590,33 @@
 
 nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
         const nn::OptionalTimePoint& optionalTimePoint) {
+    const auto currentSteadyTime = std::chrono::steady_clock::now();
+    const auto currentBootTime = nn::Clock::now();
+
     OptionalTimePoint ret;
     if (optionalTimePoint.has_value()) {
-        const auto count = optionalTimePoint.value().time_since_epoch().count();
+        const auto bootTimePoint = optionalTimePoint.value();
+
+        if (bootTimePoint < nn::TimePoint{}) {
+            return NN_ERROR() << "Trying to cast invalid time point";
+        }
+
+        // Both bootTimePoint and currentBootTime are guaranteed to be non-negative, so this
+        // subtraction will never overflow or underflow.
+        const auto timeRemaining = bootTimePoint - currentBootTime;
+
+        // currentSteadyTime is guaranteed to be non-negative, so this code only protects against an
+        // overflow.
+        std::chrono::steady_clock::time_point steadyTimePoint;
+        constexpr auto kZeroNano = std::chrono::nanoseconds::zero();
+        constexpr auto kMaxTime = std::chrono::steady_clock::time_point::max();
+        if (timeRemaining > kZeroNano && currentSteadyTime > kMaxTime - timeRemaining) {
+            steadyTimePoint = kMaxTime;
+        } else {
+            steadyTimePoint = currentSteadyTime + timeRemaining;
+        }
+
+        const uint64_t count = makeUint64FromNanos(steadyTimePoint.time_since_epoch());
         ret.nanosecondsSinceEpoch(count);
     }
     return ret;
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
index e17e0cd..c5b4ab1 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
@@ -307,10 +307,10 @@
      * @param priority The priority of the prepared model relative to other prepared models owned by
      *                 the client.
      * @param deadline The time by which the model is expected to be prepared. The time is measured
-     *                 in nanoseconds since epoch of the steady clock (as from
-     *                 std::chrono::steady_clock). If the model cannot be prepared by the deadline,
-     *                 the preparation may be aborted. Passing -1 means the deadline is omitted.
-     *                 Other negative values are invalid.
+     *                 in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME, &ts)
+     *                 or ::android::base::boot_clock). If the model cannot be prepared by the
+     *                 deadline, the preparation may be aborted. Passing -1 means the deadline is
+     *                 omitted. Other negative values are invalid.
      * @param modelCache A vector of file descriptors for the security-sensitive cache. The length
      *                   of the vector must either be 0 indicating that caching information is not
      *                   provided, or match the numModelCache returned from
@@ -396,10 +396,10 @@
      * different shapes of inputs on different (possibly concurrent) executions.
      *
      * @param deadline The time by which the model is expected to be prepared. The time is measured
-     *                 in nanoseconds since epoch of the steady clock (as from
-     *                 std::chrono::steady_clock). If the model cannot be prepared by the deadline,
-     *                 the preparation may be aborted. Passing -1 means the deadline is omitted.
-     *                 Other negative values are invalid.
+     *                 in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME, &ts) or
+     *                 ::android::base::boot_clock). If the model cannot be prepared by the
+     *                 deadline, the preparation may be aborted. Passing -1 means the deadline is
+     *                 omitted. Other negative values are invalid.
      * @param modelCache A vector of file descriptors for the security-sensitive cache. The length
      *                   of the vector must match the numModelCache returned from
      *                   getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
index 2a9757b..bfab906 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -73,8 +73,8 @@
      *                runs from the time the driver sees the call to the executeSynchronously
      *                function to the time the driver returns from the function.
      * @param deadline The time by which the execution is expected to complete. The time is measured
-     *                 in nanoseconds since epoch of the steady clock (as from
-     *                 std::chrono::steady_clock). If the execution cannot be finished by the
+     *                 in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME, &ts) or
+     *                 ::android::base::boot_clock). If the execution cannot be finished by the
      *                 deadline, the execution may be aborted. Passing -1 means the deadline is
      *                 omitted. Other negative values are invalid.
      * @param loopTimeoutDuration The maximum amount of time in nanoseconds that should be spent
@@ -138,8 +138,8 @@
      *                sync fences have been signaled.
      * @param measure Specifies whether or not to measure duration of the execution.
      * @param deadline The time by which the execution is expected to complete. The time is measured
-     *                 in nanoseconds since epoch of the steady clock (as from
-     *                 std::chrono::steady_clock).If the execution cannot be finished by the
+     *                 in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME, &ts) or
+     *                 ::android::base::boot_clock). If the execution cannot be finished by the
      *                 deadline, the execution may be aborted. Passing -1 means the deadline is
      *                 omitted. Other negative values are invalid.
      * @param loopTimeoutDuration The maximum amount of time in nanoseconds that should be spent
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index c74c509..d5f7f81 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -931,11 +931,12 @@
 }
 
 nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration) {
-    const uint64_t nanoseconds = duration.count();
-    if (nanoseconds > std::numeric_limits<int64_t>::max()) {
-        return std::numeric_limits<int64_t>::max();
+    if (duration < nn::Duration::zero()) {
+        return NN_ERROR() << "Unable to convert invalid (negative) duration";
     }
-    return static_cast<int64_t>(nanoseconds);
+    constexpr std::chrono::nanoseconds::rep kIntMax = std::numeric_limits<int64_t>::max();
+    const auto count = duration.count();
+    return static_cast<int64_t>(std::min(count, kIntMax));
 }
 
 nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration) {
diff --git a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
index 9ace1a9..e803e38 100644
--- a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
+#include <android-base/chrono_utils.h>
 #include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_status.h>
-
 #include <nnapi/hal/aidl/Conversions.h>
 
 #include "Callbacks.h"
@@ -61,16 +61,16 @@
         return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
     };
 
-    std::chrono::steady_clock::time_point timePoint;
+    ::android::base::boot_clock::time_point timePoint;
     switch (deadlineBoundType) {
         case DeadlineBoundType::NOW:
-            timePoint = std::chrono::steady_clock::now();
+            timePoint = ::android::base::boot_clock::now();
             break;
         case DeadlineBoundType::UNLIMITED:
-            timePoint = std::chrono::steady_clock::time_point::max();
+            timePoint = ::android::base::boot_clock::time_point::max();
             break;
         case DeadlineBoundType::SHORT:
-            timePoint = std::chrono::steady_clock::now() + kShortDuration;
+            timePoint = ::android::base::boot_clock::now() + kShortDuration;
             break;
     }
 
diff --git a/power/aidl/default/power-default.xml b/power/aidl/default/power-default.xml
index caf6ea2..9f56deb 100644
--- a/power/aidl/default/power-default.xml
+++ b/power/aidl/default/power-default.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.power</name>
+        <version>2</version>
         <fqname>IPower/default</fqname>
     </hal>
 </manifest>
diff --git a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
index bed3fdf..c7ba96c 100644
--- a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
+++ b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
@@ -40,6 +40,12 @@
 
 using ndk::SpAIBinder;
 
+#define ASSERT_OK(a)                                     \
+    do {                                                 \
+        auto ret = a;                                    \
+        ASSERT_TRUE(ret.isOk()) << ret.getDescription(); \
+    } while (0)
+
 class PowerStatsAidl : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -108,7 +114,7 @@
 // Each PowerEntity must have a valid name
 TEST_P(PowerStatsAidl, ValidatePowerEntityNames) {
     std::vector<PowerEntity> infos;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&infos).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&infos));
 
     for (auto info : infos) {
         testNameValid(info.name);
@@ -118,7 +124,7 @@
 // Each power entity must have a unique name
 TEST_P(PowerStatsAidl, ValidatePowerEntityUniqueNames) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     testUnique(entities, &PowerEntity::name);
 }
@@ -126,7 +132,7 @@
 // Each PowerEntity must have a unique ID
 TEST_P(PowerStatsAidl, ValidatePowerEntityIds) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     testUnique(entities, &PowerEntity::id);
 }
@@ -134,7 +140,7 @@
 // Each power entity must have at least one state
 TEST_P(PowerStatsAidl, ValidateStateSize) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     for (auto entity : entities) {
         EXPECT_GT(entity.states.size(), 0);
@@ -144,7 +150,7 @@
 // Each state must have a valid name
 TEST_P(PowerStatsAidl, ValidateStateNames) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     for (auto entity : entities) {
         for (auto state : entity.states) {
@@ -156,7 +162,7 @@
 // Each state must have a name that is unique to the given PowerEntity
 TEST_P(PowerStatsAidl, ValidateStateUniqueNames) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     for (auto entity : entities) {
         testUnique(entity.states, &State::name);
@@ -166,7 +172,7 @@
 // Each state must have an ID that is unique to the given PowerEntity
 TEST_P(PowerStatsAidl, ValidateStateUniqueIds) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     for (auto entity : entities) {
         testUnique(entity.states, &State::id);
@@ -176,16 +182,16 @@
 // State residency must return a valid status
 TEST_P(PowerStatsAidl, TestGetStateResidency) {
     std::vector<StateResidencyResult> results;
-    ASSERT_TRUE(powerstats->getStateResidency({}, &results).isOk());
+    ASSERT_OK(powerstats->getStateResidency({}, &results));
 }
 
 // State residency must return all results
 TEST_P(PowerStatsAidl, TestGetStateResidencyAllResults) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     std::vector<StateResidencyResult> results;
-    ASSERT_TRUE(powerstats->getStateResidency({}, &results).isOk());
+    ASSERT_OK(powerstats->getStateResidency({}, &results));
 
     testMatching(entities, &PowerEntity::id, results, &StateResidencyResult::id);
 }
@@ -193,10 +199,10 @@
 // Each result must contain all state residencies
 TEST_P(PowerStatsAidl, TestGetStateResidencyAllStateResidencies) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
 
     std::vector<StateResidencyResult> results;
-    ASSERT_TRUE(powerstats->getStateResidency({}, &results).isOk());
+    ASSERT_OK(powerstats->getStateResidency({}, &results));
 
     for (auto entity : entities) {
         auto it = std::find_if(results.begin(), results.end(),
@@ -210,7 +216,7 @@
 // State residency must return results for each requested power entity
 TEST_P(PowerStatsAidl, TestGetStateResidencySelectedResults) {
     std::vector<PowerEntity> entities;
-    ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
     if (entities.empty()) {
         return;
     }
@@ -222,7 +228,7 @@
     }
 
     std::vector<StateResidencyResult> selectedResults;
-    ASSERT_TRUE(powerstats->getStateResidency(selectedIds, &selectedResults).isOk());
+    ASSERT_OK(powerstats->getStateResidency(selectedIds, &selectedResults));
 
     testMatching(selectedEntities, &PowerEntity::id, selectedResults, &StateResidencyResult::id);
 }
@@ -230,15 +236,25 @@
 // Energy meter info must return a valid status
 TEST_P(PowerStatsAidl, TestGetEnergyMeterInfo) {
     std::vector<Channel> info;
-    ASSERT_TRUE(powerstats->getEnergyMeterInfo(&info).isOk());
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&info));
 }
 
-// Each channel must have a valid name and subsystem
+// Each channel must have a valid name
 TEST_P(PowerStatsAidl, ValidateChannelNames) {
     std::vector<Channel> channels;
-    ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
     for (auto channel : channels) {
         testNameValid(channel.name);
+    }
+}
+
+// Each channel must have a valid subsystem
+TEST_P(PowerStatsAidl, ValidateSubsystemNames) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+    for (auto channel : channels) {
         testNameValid(channel.subsystem);
     }
 }
@@ -246,7 +262,7 @@
 // Each channel must have a unique name
 TEST_P(PowerStatsAidl, ValidateChannelUniqueNames) {
     std::vector<Channel> channels;
-    ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
 
     testUnique(channels, &Channel::name);
 }
@@ -254,7 +270,7 @@
 // Each channel must have a unique ID
 TEST_P(PowerStatsAidl, ValidateChannelUniqueIds) {
     std::vector<Channel> channels;
-    ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
 
     testUnique(channels, &Channel::id);
 }
@@ -262,16 +278,16 @@
 // Reading energy meter must return a valid status
 TEST_P(PowerStatsAidl, TestReadEnergyMeter) {
     std::vector<EnergyMeasurement> data;
-    ASSERT_TRUE(powerstats->readEnergyMeter({}, &data).isOk());
+    ASSERT_OK(powerstats->readEnergyMeter({}, &data));
 }
 
 // Reading energy meter must return results for all available channels
 TEST_P(PowerStatsAidl, TestGetAllEnergyMeasurements) {
     std::vector<Channel> channels;
-    ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
 
     std::vector<EnergyMeasurement> measurements;
-    ASSERT_TRUE(powerstats->readEnergyMeter({}, &measurements).isOk());
+    ASSERT_OK(powerstats->readEnergyMeter({}, &measurements));
 
     testMatching(channels, &Channel::id, measurements, &EnergyMeasurement::id);
 }
@@ -279,7 +295,7 @@
 // Reading energy must must return results for each selected channel
 TEST_P(PowerStatsAidl, TestGetSelectedEnergyMeasurements) {
     std::vector<Channel> channels;
-    ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
     if (channels.empty()) {
         return;
     }
@@ -291,7 +307,7 @@
     }
 
     std::vector<EnergyMeasurement> selectedMeasurements;
-    ASSERT_TRUE(powerstats->readEnergyMeter(selectedIds, &selectedMeasurements).isOk());
+    ASSERT_OK(powerstats->readEnergyMeter(selectedIds, &selectedMeasurements));
 
     testMatching(selectedChannels, &Channel::id, selectedMeasurements, &EnergyMeasurement::id);
 }
@@ -299,13 +315,13 @@
 // Energy consumer info must return a valid status
 TEST_P(PowerStatsAidl, TestGetEnergyConsumerInfo) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
 }
 
 // Each energy consumer must have a unique id
 TEST_P(PowerStatsAidl, TestGetEnergyConsumerUniqueId) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
 
     testUnique(consumers, &EnergyConsumer::id);
 }
@@ -313,7 +329,7 @@
 // Each energy consumer must have a valid name
 TEST_P(PowerStatsAidl, ValidateEnergyConsumerNames) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
 
     for (auto consumer : consumers) {
         testNameValid(consumer.name);
@@ -323,7 +339,7 @@
 // Each energy consumer must have a unique name
 TEST_P(PowerStatsAidl, ValidateEnergyConsumerUniqueNames) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
 
     testUnique(consumers, &EnergyConsumer::name);
 }
@@ -331,7 +347,7 @@
 // Energy consumers of the same type must have ordinals that are 0,1,2,..., N - 1
 TEST_P(PowerStatsAidl, ValidateEnergyConsumerOrdinals) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
 
     std::unordered_map<EnergyConsumerType, std::set<int32_t>> ordinalMap;
 
@@ -350,16 +366,16 @@
 // Energy consumed must return a valid status
 TEST_P(PowerStatsAidl, TestGetEnergyConsumed) {
     std::vector<EnergyConsumerResult> results;
-    ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
 }
 
 // Energy consumed must return data for all energy consumers
 TEST_P(PowerStatsAidl, TestGetAllEnergyConsumed) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
 
     std::vector<EnergyConsumerResult> results;
-    ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
 
     testMatching(consumers, &EnergyConsumer::id, results, &EnergyConsumerResult::id);
 }
@@ -367,7 +383,7 @@
 // Energy consumed must return data for each selected energy consumer
 TEST_P(PowerStatsAidl, TestGetSelectedEnergyConsumed) {
     std::vector<EnergyConsumer> consumers;
-    ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
     if (consumers.empty()) {
         return;
     }
@@ -379,7 +395,7 @@
     }
 
     std::vector<EnergyConsumerResult> selectedResults;
-    ASSERT_TRUE(powerstats->getEnergyConsumed(selectedIds, &selectedResults).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumed(selectedIds, &selectedResults));
 
     testMatching(selectedConsumers, &EnergyConsumer::id, selectedResults,
                  &EnergyConsumerResult::id);
@@ -388,7 +404,7 @@
 // Energy consumed attribution uids must be unique for a given energy consumer
 TEST_P(PowerStatsAidl, ValidateEnergyConsumerAttributionUniqueUids) {
     std::vector<EnergyConsumerResult> results;
-    ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
 
     for (auto result : results) {
         testUnique(result.attribution, &EnergyConsumerAttribution::uid);
@@ -398,7 +414,7 @@
 // Energy consumed total energy >= sum total of uid-attributed energy
 TEST_P(PowerStatsAidl, TestGetEnergyConsumedAttributedEnergy) {
     std::vector<EnergyConsumerResult> results;
-    ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
 
     for (auto result : results) {
         int64_t totalAttributedEnergyUWs = 0;
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 6766d99..0c11f3b 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -26,6 +26,7 @@
             vndk: {
                 enabled: true,
             },
+            apps_enabled: false,
         },
         rust: {
             enabled: true,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
index 69ec4ce..b05a0f3 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -118,6 +118,7 @@
   MISSING_ISSUER_SUBJECT = -82,
   INVALID_ISSUER_SUBJECT = -83,
   BOOT_LEVEL_EXCEEDED = -84,
+  HARDWARE_NOT_YET_AVAILABLE = -85,
   UNIMPLEMENTED = -100,
   VERSION_MISMATCH = -101,
   UNKNOWN_ERROR = -1000,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index 4f6fb28..bf30999 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -33,7 +33,7 @@
 
 package android.hardware.security.keymint;
 /* @hide */
-@VintfStability
+@SensitiveData @VintfStability
 interface IKeyMintDevice {
   android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
   void addRngEntropy(in byte[] data);
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
index 5ac2b4a..4ab4ffe 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -33,7 +33,7 @@
 
 package android.hardware.security.keymint;
 /* @hide */
-@VintfStability
+@SensitiveData @VintfStability
 interface IKeyMintOperation {
   void updateAad(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
   byte[] update(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
index 0e2c5f2..137e6b6 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -107,6 +107,7 @@
     MISSING_ISSUER_SUBJECT = -82,
     INVALID_ISSUER_SUBJECT = -83,
     BOOT_LEVEL_EXCEEDED = -84,
+    HARDWARE_NOT_YET_AVAILABLE = -85,
 
     UNIMPLEMENTED = -100,
     VERSION_MISMATCH = -101,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 5aa3070..1c503c2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -214,6 +214,7 @@
  * @hide
  */
 @VintfStability
+@SensitiveData
 interface IKeyMintDevice {
     const int AUTH_TOKEN_MAC_LENGTH = 32;
 
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index 5ad54cd..d2a993f 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -22,6 +22,7 @@
 
 /** @hide */
 @VintfStability
+@SensitiveData
 interface IKeyMintOperation {
     /**
      * Provides additional authentication data (AAD) to a cryptographic operation begun with
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index fe04ede..4c4258b 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -66,6 +66,7 @@
 cc_library {
     name: "libcppcose",
     vendor_available: true,
+    host_supported: true,
     srcs: [
         "cppcose.cpp",
     ],
@@ -73,13 +74,8 @@
         "include",
     ],
     shared_libs: [
-        "libbinder_ndk",
         "libcppbor_external",
         "libcrypto",
         "liblog",
     ],
-    static_libs: [
-        // TODO(swillden): Remove keymint NDK
-        "android.hardware.security.keymint-V1-ndk_platform",
-    ],
 }
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 9a6ecf7..c4f610e 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -139,6 +139,8 @@
 
     // IP filter can be an MMTP filter's data source.
     caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00};
+    // Support time filter testing
+    caps.bTimeFilter = true;
     _hidl_cb(Result::SUCCESS, caps);
     return Void();
 }
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index c27a935..6187c73 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -35,6 +35,15 @@
         "DescramblerTests.cpp",
         "LnbTests.cpp",
     ],
+    generated_headers: [
+        "tuner_testing_dynamic_configuration_V1_0_enums",
+        "tuner_testing_dynamic_configuration_V1_0_parser",
+    ],
+    generated_sources: [
+        "tuner_testing_dynamic_configuration_V1_0_enums",
+        "tuner_testing_dynamic_configuration_V1_0_parser",
+    ],
+    header_libs: ["libxsdc-utils"],
     static_libs: [
         "android.hardware.cas@1.0",
         "android.hardware.cas@1.1",
@@ -49,9 +58,12 @@
     ],
     shared_libs: [
         "libbinder",
+        "libxml2",
     ],
     data: [
         ":tuner_frontend_input_ts",
+        ":tuner_frontend_input_es",
+        ":tuner_testing_dynamic_configuration_V1_0",
     ],
     test_suites: [
         "general-tests",
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.h b/tv/tuner/1.0/vts/functional/FrontendTests.h
index 4974ff3..33ff603 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.h
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.h
@@ -132,6 +132,7 @@
     static AssertionResult failure() { return ::testing::AssertionFailure(); }
     static AssertionResult success() { return ::testing::AssertionSuccess(); }
 
+    // TODO: replace with customized dvr input
     void getDefaultSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
         PlaybackSettings playbackSettings{
                 .statusMask = 0xf,
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 891619a..5fbdd2d 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -18,16 +18,15 @@
 
 namespace {
 
-AssertionResult TunerBroadcastHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerBroadcastHidlTest::filterDataOutputTest() {
     return filterDataOutputTestBase(mFilterTests);
 }
 
-AssertionResult TunerPlaybackHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerPlaybackHidlTest::filterDataOutputTest() {
     return filterDataOutputTestBase(mFilterTests);
 }
 
-AssertionResult TunerDescramblerHidlTest::filterDataOutputTest(
-        vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerDescramblerHidlTest::filterDataOutputTest() {
     return filterDataOutputTestBase(mFilterTests);
 }
 
@@ -62,8 +61,14 @@
     }
     uint32_t demuxId;
     sp<IDemux> demux;
+    DemuxCapabilities caps;
 
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    // TODO: add time filter hardware support checker
+    ASSERT_TRUE(mDemuxTests.getDemuxCaps(caps));
+    if (!caps.bTimeFilter) {
+        return;
+    }
     mFilterTests.setDemux(demux);
     ASSERT_TRUE(mFilterTests.openTimeFilterInDemux());
     ASSERT_TRUE(mFilterTests.setTimeStamp(filterConf.timeStamp));
@@ -75,26 +80,20 @@
 
 void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
                                                        FrontendConfig frontendConf) {
-    if (!frontendConf.enable) {
-        return;
-    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
     uint32_t filterId;
 
     mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    if (feId == INVALID_ID) {
-        // TODO broadcast test on Cuttlefish needs licensed ts input,
-        // these tests are runnable on vendor device with real frontend module
-        // or with manual ts installing and use DVBT frontend.
-        return;
-    }
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
     if (mLnbId) {
         ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
     }
+    if (frontendConf.isSoftwareFe) {
+        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+    }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFrontendTests.setDemux(demux);
@@ -106,7 +105,7 @@
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
     ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+    ASSERT_TRUE(filterDataOutputTest());
     ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -119,12 +118,12 @@
                                                               LnbConfig lnbConf) {
     vector<uint32_t> ids;
     ASSERT_TRUE(mLnbTests.getLnbIds(ids));
-    if (!lnbConf.usingLnb) {
+    if (ids.size() == 0) {
         return;
     }
     ASSERT_TRUE(ids.size() > 0);
     ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
-    *mLnbId = ids[0];
+    mLnbId = &ids[0];
     ASSERT_TRUE(mLnbTests.setLnbCallback());
     ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
     ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
@@ -152,7 +151,7 @@
     mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile, dvrConf.settings.playback());
     ASSERT_TRUE(mDvrTests.startDvrPlayback());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+    ASSERT_TRUE(filterDataOutputTest());
     mDvrTests.stopPlaybackThread();
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mDvrTests.stopDvrPlayback());
@@ -163,9 +162,6 @@
 
 void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
                                                  FrontendConfig frontendConf, DvrConfig dvrConf) {
-    if (!frontendConf.enable) {
-        return;
-    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
@@ -179,6 +175,9 @@
     if (mLnbId) {
         ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
     }
+    if (frontendConf.isSoftwareFe) {
+        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+    }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
@@ -215,12 +214,12 @@
                                                         DvrConfig dvrConf, LnbConfig lnbConf) {
     vector<uint32_t> ids;
     ASSERT_TRUE(mLnbTests.getLnbIds(ids));
-    if (!lnbConf.usingLnb) {
+    if (ids.size() == 0) {
         return;
     }
     ASSERT_TRUE(ids.size() > 0);
     ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
-    *mLnbId = ids[0];
+    mLnbId = &ids[0];
     ASSERT_TRUE(mLnbTests.setLnbCallback());
     ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
     ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
@@ -271,9 +270,6 @@
 void TunerDescramblerHidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
                                                       FrontendConfig frontendConf,
                                                       DescramblerConfig descConfig) {
-    if (!frontendConf.enable) {
-        return;
-    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
@@ -283,14 +279,11 @@
     set<uint32_t>::iterator id;
 
     mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    if (feId == INVALID_ID) {
-        // TODO broadcast test on Cuttlefish needs licensed ts input,
-        // these tests are runnable on vendor device with real frontend module
-        // or with manual ts installing and use DVBT frontend.
-        return;
-    }
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    if (frontendConf.isSoftwareFe) {
+        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+    }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
@@ -305,7 +298,7 @@
     TunerKeyToken token;
     ASSERT_TRUE(mDescramblerTests.getKeyToken(descConfig.casSystemId, descConfig.provisionStr,
                                               descConfig.hidlPvtData, token));
-    ASSERT_TRUE(mDescramblerTests.setKeyToken(token));
+    mDescramblerTests.setKeyToken(token);
     vector<DemuxPid> pids;
     DemuxPid pid;
     for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
@@ -319,7 +312,7 @@
     }
     // tune test
     ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+    ASSERT_TRUE(filterDataOutputTest());
     ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.stopFilter(*id));
@@ -337,21 +330,27 @@
 
 TEST_P(TunerFrontendHidlTest, TuneFrontend) {
     description("Tune one Frontend with specific setting and check Lock event");
-    mFrontendTests.tuneTest(frontendArray[defaultFrontend]);
+    mFrontendTests.tuneTest(frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFrontendHidlTest, AutoScanFrontend) {
     description("Run an auto frontend scan with specific setting and check lock scanMessage");
-    mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_AUTO);
+    mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
 }
 
 TEST_P(TunerFrontendHidlTest, BlindScanFrontend) {
     description("Run an blind frontend scan with specific setting and check lock scanMessage");
-    mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_BLIND);
+    mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
 }
 
 TEST_P(TunerLnbHidlTest, OpenLnbByName) {
     description("Open and configure an Lnb with name then send a diseqc msg to it.");
+    // TODO: add lnb hardware support checker
+    vector<uint32_t> ids;
+    ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+    if (ids.size() == 0) {
+        return;
+    }
     ASSERT_TRUE(mLnbTests.openLnbByName(lnbArray[LNB_EXTERNAL].name));
     ASSERT_TRUE(mLnbTests.setLnbCallback());
     ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB_EXTERNAL].voltage));
@@ -365,7 +364,7 @@
     description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
     vector<uint32_t> ids;
     ASSERT_TRUE(mLnbTests.getLnbIds(ids));
-    if (!lnbArray[LNB0].usingLnb) {
+    if (ids.size() == 0) {
         return;
     }
     ASSERT_TRUE(ids.size() > 0);
@@ -383,7 +382,7 @@
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
-    mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -403,7 +402,7 @@
     uint32_t avSyncHwId;
     sp<IFilter> mediaFilter;
 
-    mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -431,7 +430,7 @@
 TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
     description("Open and start a filter in Demux.");
     // TODO use paramterized tests
-    configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
+    configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
@@ -472,115 +471,74 @@
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
     description("Test Video Filter functionality in Broadcast use case.");
-    broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[defaultFrontend]);
+    broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
     description("Test Audio Filter functionality in Broadcast use case.");
-    broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[defaultFrontend]);
+    broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
     description("Test Section Filter functionality in Broadcast use case.");
-    broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[defaultFrontend]);
+    broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
     description("Test the av filter data bufferring.");
-    broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
+    broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, LnbBroadcastDataFlowVideoFilterTest) {
     description("Test Video Filter functionality in Broadcast with Lnb use case.");
-    broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBS]);
-}
-
-TEST_P(TunerBroadcastHidlTest, BroadcastEsDataFlowMediaFiltersTest) {
-    description("Test Meida Filters functionality in Broadcast use case with ES input.");
-    uint32_t feId;
-    uint32_t demuxId;
-    sp<IDemux> demux;
-    uint32_t filterId;
-
-    mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
-    if (feId == INVALID_ID) {
-        // TODO broadcast test on Cuttlefish needs licensed ts input,
-        // these tests are runnable on vendor device with real frontend module
-        // or with manual ts installing and use defaultFrontend frontend.
-        return;
-    }
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
-    mFrontendTests.setDemux(demux);
-    mFilterTests.setDemux(demux);
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_AUDIO1].type,
-                                               filterArray[TS_AUDIO1].bufferSize));
-    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_AUDIO1].settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterArray[TS_AUDIO1].getMqDesc));
-    ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_VIDEO1].type,
-                                               filterArray[TS_VIDEO1].bufferSize));
-    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_VIDEO1].settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterArray[TS_VIDEO1].getMqDesc));
-    ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    // tune test
-    PlaybackSettings playbackSettings{
-            .statusMask = 0xf,
-            .lowThreshold = 0x1000,
-            .highThreshold = 0x07fff,
-            .dataFormat = DataFormat::ES,
-            .packetSize = 188,
-    };
-    DvrConfig dvrConfig{
-            .type = DvrType::PLAYBACK,
-            .playbackInputFile = "/data/local/tmp/test.es",
-            .bufferSize = FMQ_SIZE_4M,
-    };
-    dvrConfig.settings.playback(playbackSettings);
-    mFrontendTests.setSoftwareFrontendDvrConfig(dvrConfig);
-    ASSERT_TRUE(
-            mFrontendTests.tuneFrontend(frontendArray[defaultFrontend], true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
-    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
-    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
-    ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+    broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
     description("Feed ts data from playback and configure Ts section filter to get output");
-    playbackSingleFilterTest(filterArray[TS_SECTION0], dvrArray[DVR_PLAYBACK0]);
+    if (!playback.support) {
+        return;
+    }
+    playbackSingleFilterTest(filterArray[TS_SECTION0], dvrMap[playback.dvrId]);
 }
 
 TEST_P(TunerRecordHidlTest, AttachFiltersToRecordTest) {
     description("Attach a single filter to the record dvr test.");
     // TODO use paramterized tests
-    attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
-                                      dvrArray[DVR_RECORD0]);
+    if (!record.support) {
+        return;
+    }
+    attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendMap[record.frontendId],
+                                      dvrMap[record.dvrRecordId]);
 }
 
 TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from frontend to recording and test with ts record filter");
-    recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
-                           dvrArray[DVR_RECORD0]);
+    if (!record.support) {
+        return;
+    }
+    recordSingleFilterTest(filterArray[TS_RECORD0], frontendMap[record.frontendId],
+                           dvrMap[record.dvrRecordId]);
 }
 
 TEST_P(TunerRecordHidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from Fe with Lnb to recording and test with ts record filter");
-    recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBS], dvrArray[DVR_RECORD0]);
+    if (record.support) {
+        return;
+    }
+    recordSingleFilterTestWithLnb(filterArray[TS_RECORD0], frontendMap[lnbRecord.frontendId],
+                                  dvrMap[record.dvrRecordId], lnbArray[LNB0]);
 }
 
 TEST_P(TunerDescramblerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
+    if (descrambling.support) {
+        return;
+    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
-    mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
+    mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -594,10 +552,14 @@
 
 TEST_P(TunerDescramblerHidlTest, ScrambledBroadcastDataFlowMediaFiltersTest) {
     description("Test ts audio filter in scrambled broadcast use case");
+    if (descrambling.support) {
+        return;
+    }
     set<FilterConfig> filterConfs;
     filterConfs.insert(filterArray[TS_AUDIO0]);
     filterConfs.insert(filterArray[TS_VIDEO1]);
-    scrambledBroadcastTest(filterConfs, frontendArray[defaultFrontend], descramblerArray[DESC_0]);
+    scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
+                           descramblerArray[DESC_0]);
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index 5a23ca5..9723c2d 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -28,14 +28,24 @@
 
 namespace {
 
-void initConfiguration() {
+bool initConfiguration() {
+    if (!TunerTestingConfigReader::checkConfigFileExists()) {
+        return false;
+    }
     initFrontendConfig();
-    initFrontendScanConfig();
+    initDvrConfig();
+    connectHardwaresToTestCases();
+    if (!validateConnections()) {
+        ALOGW("[vts] failed to validate connections.");
+        return false;
+    }
+
     initLnbConfig();
     initFilterConfig();
     initTimeFilterConfig();
-    initDvrConfig();
     initDescramblerConfig();
+
+    return true;
 }
 
 AssertionResult filterDataOutputTestBase(FilterTests tests) {
@@ -53,7 +63,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
     }
@@ -75,7 +85,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mLnbTests.setService(mService);
     }
@@ -97,7 +107,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -123,7 +133,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -152,7 +162,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -173,7 +183,7 @@
     LnbTests mLnbTests;
     DvrTests mDvrTests;
 
-    AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+    AssertionResult filterDataOutputTest();
 
     void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
     void broadcastSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
@@ -191,7 +201,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -210,7 +220,7 @@
     FilterTests mFilterTests;
     DvrTests mDvrTests;
 
-    AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+    AssertionResult filterDataOutputTest();
 
     void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
 };
@@ -223,7 +233,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -265,7 +275,7 @@
         mCasService = IMediaCasService::getService();
         ASSERT_NE(mService, nullptr);
         ASSERT_NE(mCasService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -281,7 +291,7 @@
 
     void scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
                                 FrontendConfig frontendConf, DescramblerConfig descConfig);
-    AssertionResult filterDataOutputTest(vector<string> /*goldenOutputFiles*/);
+    AssertionResult filterDataOutputTest();
 
     sp<ITuner> mService;
     sp<IMediaCasService> mCasService;
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 834e296..a1c5cd9 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,6 +21,9 @@
 #include <hidl/Status.h>
 #include <hidlmemory/FrameworkUtils.h>
 
+#include "../../../config/TunerTestingConfigReader.h"
+
+// TODO: remove unnecessary imports after config reader refactoring is done.
 using android::hardware::tv::tuner::V1_0::DataFormat;
 using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
 using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
@@ -54,7 +57,9 @@
 using android::hardware::tv::tuner::V1_0::RecordSettings;
 
 using namespace std;
+using namespace android::media::tuner::testing::configuration::V1_0;
 
+// TODO: remove all the constants and structs after config reader refactoring is done.
 const uint32_t FMQ_SIZE_512K = 0x80000;
 const uint32_t FMQ_SIZE_1M = 0x100000;
 const uint32_t FMQ_SIZE_4M = 0x400000;
@@ -99,12 +104,6 @@
 } Linkage;
 
 typedef enum {
-    DVBT,
-    DVBS,
-    FRONTEND_MAX,
-} Frontend;
-
-typedef enum {
     LNB0,
     LNB_EXTERNAL,
     LNB_MAX,
@@ -116,17 +115,6 @@
 } Diseqc;
 
 typedef enum {
-    SCAN_DVBT,
-    SCAN_MAX,
-} FrontendScan;
-
-typedef enum {
-    DVR_RECORD0,
-    DVR_PLAYBACK0,
-    DVR_MAX,
-} Dvr;
-
-typedef enum {
     DESC_0,
     DESC_MAX,
 } Descrambler;
@@ -145,15 +133,6 @@
     uint64_t timeStamp;
 };
 
-struct FrontendConfig {
-    bool enable;
-    bool isSoftwareFe;
-    FrontendType type;
-    FrontendSettings settings;
-    vector<FrontendStatusType> tuneStatusTypes;
-    vector<FrontendStatus> expectTuneStatuses;
-};
-
 struct LnbConfig {
     bool usingLnb;
     string name;
@@ -162,88 +141,119 @@
     LnbPosition position;
 };
 
-struct ChannelConfig {
-    int32_t frontendId;
-    int32_t channelId;
-    std::string channelName;
-    DemuxTpid videoPid;
-    DemuxTpid audioPid;
-};
-
-struct DvrConfig {
-    DvrType type;
-    uint32_t bufferSize;
-    DvrSettings settings;
-    string playbackInputFile;
-};
-
 struct DescramblerConfig {
     uint32_t casSystemId;
     string provisionStr;
     vector<uint8_t> hidlPvtData;
 };
 
-static FrontendConfig frontendArray[FILTER_MAX];
-static FrontendConfig frontendScanArray[SCAN_MAX];
+// TODO: remove all the manual config array after the dynamic config refactoring is done.
 static LnbConfig lnbArray[LNB_MAX];
 static vector<uint8_t> diseqcMsgArray[DISEQC_MAX];
-static ChannelConfig channelArray[FRONTEND_MAX];
 static FilterConfig filterArray[FILTER_MAX];
 static TimeFilterConfig timeFilterArray[TIMER_MAX];
 static DemuxFilterType filterLinkageTypes[LINKAGE_DIR][FILTER_MAIN_TYPE_BIT_COUNT];
-static DvrConfig dvrArray[DVR_MAX];
 static DescramblerConfig descramblerArray[DESC_MAX];
-static vector<string> goldenOutputFiles;
-static int defaultFrontend = DVBT;
-static int defaultScanFrontend = SCAN_DVBT;
 
-/** Configuration array for the frontend tune test */
+// Hardware configs
+static map<string, FrontendConfig> frontendMap;
+static map<string, DvrConfig> dvrMap;
+
+// Hardware and test cases connections
+static LiveBroadcastHardwareConnections live;
+static ScanHardwareConnections scan;
+static DvrPlaybackHardwareConnections playback;
+static DvrRecordHardwareConnections record;
+static DescramblingHardwareConnections descrambling;
+static LnbLiveHardwareConnections lnbLive;
+static LnbRecordHardwareConnections lnbRecord;
+
+/** Config all the frontends that would be used in the tests */
 inline void initFrontendConfig() {
+    // The test will use the internal default fe when default fe is connected to any data flow
+    // without overriding in the xml config.
+    string defaultFeId = "FE_DEFAULT";
     FrontendDvbtSettings dvbtSettings{
             .frequency = 578000,
             .transmissionMode = FrontendDvbtTransmissionMode::AUTO,
             .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
-            .constellation = FrontendDvbtConstellation::AUTO,
-            .hierarchy = FrontendDvbtHierarchy::AUTO,
-            .hpCoderate = FrontendDvbtCoderate::AUTO,
-            .lpCoderate = FrontendDvbtCoderate::AUTO,
-            .guardInterval = FrontendDvbtGuardInterval::AUTO,
             .isHighPriority = true,
-            .standard = FrontendDvbtStandard::T,
     };
-    frontendArray[DVBT].type = FrontendType::DVBT, frontendArray[DVBT].settings.dvbt(dvbtSettings);
+    frontendMap[defaultFeId].type = FrontendType::DVBT;
+    frontendMap[defaultFeId].settings.dvbt(dvbtSettings);
+
     vector<FrontendStatusType> types;
     types.push_back(FrontendStatusType::DEMOD_LOCK);
     FrontendStatus status;
     status.isDemodLocked(true);
     vector<FrontendStatus> statuses;
     statuses.push_back(status);
-    frontendArray[DVBT].tuneStatusTypes = types;
-    frontendArray[DVBT].expectTuneStatuses = statuses;
-    frontendArray[DVBT].isSoftwareFe = true;
-    frontendArray[DVBT].enable = true;
-    frontendArray[DVBS].type = FrontendType::DVBS;
-    frontendArray[DVBS].enable = true;
-    frontendArray[DVBS].isSoftwareFe = true;
+    frontendMap[defaultFeId].tuneStatusTypes = types;
+    frontendMap[defaultFeId].expectTuneStatuses = statuses;
+    frontendMap[defaultFeId].isSoftwareFe = true;
+
+    // Read customized config
+    TunerTestingConfigReader::readFrontendConfig1_0(frontendMap);
 };
 
-/** Configuration array for the frontend scan test */
-inline void initFrontendScanConfig() {
-    frontendScanArray[SCAN_DVBT].type = FrontendType::DVBT;
-    frontendScanArray[SCAN_DVBT].settings.dvbt({
-            .frequency = 578000,
-            .transmissionMode = FrontendDvbtTransmissionMode::MODE_8K,
-            .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
-            .constellation = FrontendDvbtConstellation::AUTO,
-            .hierarchy = FrontendDvbtHierarchy::AUTO,
-            .hpCoderate = FrontendDvbtCoderate::AUTO,
-            .lpCoderate = FrontendDvbtCoderate::AUTO,
-            .guardInterval = FrontendDvbtGuardInterval::AUTO,
-            .isHighPriority = true,
-            .standard = FrontendDvbtStandard::T,
-    });
+/** Config all the dvrs that would be used in the tests */
+inline void initDvrConfig() {
+    // Read customized config
+    TunerTestingConfigReader::readDvrConfig1_0(dvrMap);
 };
 
+/** Read the vendor configurations of which hardware to use for each test cases/data flows */
+inline void connectHardwaresToTestCases() {
+    TunerTestingConfigReader::connectLiveBroadcast(live);
+    TunerTestingConfigReader::connectScan(scan);
+    TunerTestingConfigReader::connectDvrPlayback(playback);
+    TunerTestingConfigReader::connectDvrRecord(record);
+    TunerTestingConfigReader::connectDescrambling(descrambling);
+    TunerTestingConfigReader::connectLnbLive(lnbLive);
+    TunerTestingConfigReader::connectLnbRecord(lnbRecord);
+};
+
+inline bool validateConnections() {
+    bool feIsValid = frontendMap.find(live.frontendId) != frontendMap.end() &&
+                     frontendMap.find(scan.frontendId) != frontendMap.end();
+    feIsValid &= record.support ? frontendMap.find(record.frontendId) != frontendMap.end() : true;
+    feIsValid &= descrambling.support
+                         ? frontendMap.find(descrambling.frontendId) != frontendMap.end()
+                         : true;
+    feIsValid &= lnbLive.support ? frontendMap.find(lnbLive.frontendId) != frontendMap.end() : true;
+    feIsValid &=
+            lnbRecord.support ? frontendMap.find(lnbRecord.frontendId) != frontendMap.end() : true;
+
+    if (!feIsValid) {
+        ALOGW("[vts config] dynamic config fe connection is invalid.");
+        return false;
+    }
+
+    bool dvrIsValid = frontendMap[live.frontendId].isSoftwareFe
+                              ? dvrMap.find(live.dvrSoftwareFeId) != dvrMap.end()
+                              : true;
+    dvrIsValid &= playback.support ? dvrMap.find(playback.dvrId) != dvrMap.end() : true;
+
+    if (record.support) {
+        if (frontendMap[record.frontendId].isSoftwareFe) {
+            dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+        }
+        dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
+    }
+
+    if (descrambling.support && frontendMap[descrambling.frontendId].isSoftwareFe) {
+        dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+    }
+
+    if (!dvrIsValid) {
+        ALOGW("[vts config] dynamic config dvr connection is invalid.");
+        return false;
+    }
+
+    return true;
+}
+
+// TODO: remove all the manual configs after the dynamic config refactoring is done.
 /** Configuration array for the Lnb test */
 inline void initLnbConfig() {
     lnbArray[LNB0].usingLnb = true;
@@ -353,31 +363,6 @@
     timeFilterArray[TIMER0].timeStamp = 1;
 }
 
-/** Configuration array for the dvr test */
-inline void initDvrConfig() {
-    RecordSettings recordSettings{
-            .statusMask = 0xf,
-            .lowThreshold = 0x1000,
-            .highThreshold = 0x07fff,
-            .dataFormat = DataFormat::TS,
-            .packetSize = 188,
-    };
-    dvrArray[DVR_RECORD0].type = DvrType::RECORD;
-    dvrArray[DVR_RECORD0].bufferSize = FMQ_SIZE_4M;
-    dvrArray[DVR_RECORD0].settings.record(recordSettings);
-    PlaybackSettings playbackSettings{
-            .statusMask = 0xf,
-            .lowThreshold = 0x1000,
-            .highThreshold = 0x07fff,
-            .dataFormat = DataFormat::TS,
-            .packetSize = 188,
-    };
-    dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
-    dvrArray[DVR_PLAYBACK0].playbackInputFile = "/data/local/tmp/segment000000.ts";
-    dvrArray[DVR_PLAYBACK0].bufferSize = FMQ_SIZE_4M;
-    dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
-};
-
 /** Configuration array for the descrambler test */
 inline void initDescramblerConfig() {
     descramblerArray[DESC_0].casSystemId = CLEAR_KEY_SYSTEM_ID;
diff --git a/tv/tuner/1.1/default/Tuner.cpp b/tv/tuner/1.1/default/Tuner.cpp
index 38b2a26..1e940ba 100644
--- a/tv/tuner/1.1/default/Tuner.cpp
+++ b/tv/tuner/1.1/default/Tuner.cpp
@@ -237,6 +237,8 @@
 
     // IP filter can be an MMTP filter's data source.
     caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00};
+    // Support time filter testing
+    caps.bTimeFilter = true;
     _hidl_cb(Result::SUCCESS, caps);
     return Void();
 }
diff --git a/tv/tuner/config/Android.bp b/tv/tuner/config/Android.bp
new file mode 100644
index 0000000..ddbf3a7
--- /dev/null
+++ b/tv/tuner/config/Android.bp
@@ -0,0 +1,31 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+xsd_config {
+    name: "tuner_testing_dynamic_configuration_V1_0",
+    srcs: ["tuner_testing_dynamic_configuration.xsd"],
+    package_name: "android.media.tuner.testing.configuration.V1_0",
+    nullability: true,
+}
+
+xsd_config {
+    name: "tuner_testing_dynamic_configuration_V1_0_enums",
+    srcs: ["tuner_testing_dynamic_configuration.xsd"],
+    package_name: "android.media.tuner.testing.configuration.V1_0",
+    nullability: true,
+    enums_only: true,
+}
+
+xsd_config {
+    name: "tuner_testing_dynamic_configuration_V1_0_parser",
+    srcs: ["tuner_testing_dynamic_configuration.xsd"],
+    package_name: "android.media.tuner.testing.configuration.V1_0",
+    nullability: true,
+    parser_only: true,
+}
diff --git a/tv/tuner/config/OWNERS b/tv/tuner/config/OWNERS
new file mode 100644
index 0000000..1b3d095
--- /dev/null
+++ b/tv/tuner/config/OWNERS
@@ -0,0 +1,4 @@
+nchalko@google.com
+amyjojo@google.com
+shubang@google.com
+quxiangfang@google.com
diff --git a/tv/tuner/config/TunerTestingConfigReader.h b/tv/tuner/config/TunerTestingConfigReader.h
new file mode 100644
index 0000000..aa2b75c
--- /dev/null
+++ b/tv/tuner/config/TunerTestingConfigReader.h
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2021 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 <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <android_media_tuner_testing_configuration_V1_0.h>
+#include <android_media_tuner_testing_configuration_V1_0_enums.h>
+#include <binder/MemoryDealer.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+
+using namespace std;
+using namespace android::media::tuner::testing::configuration::V1_0;
+
+using android::hardware::tv::tuner::V1_0::DataFormat;
+using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxTpid;
+using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using android::hardware::tv::tuner::V1_0::DvrSettings;
+using android::hardware::tv::tuner::V1_0::DvrType;
+using android::hardware::tv::tuner::V1_0::FrontendDvbsSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
+using android::hardware::tv::tuner::V1_0::FrontendSettings;
+using android::hardware::tv::tuner::V1_0::FrontendStatus;
+using android::hardware::tv::tuner::V1_0::FrontendStatusType;
+using android::hardware::tv::tuner::V1_0::FrontendType;
+using android::hardware::tv::tuner::V1_0::LnbPosition;
+using android::hardware::tv::tuner::V1_0::LnbTone;
+using android::hardware::tv::tuner::V1_0::LnbVoltage;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
+
+const string configFilePath = "/vendor/etc/tuner_vts_config.xml";
+
+struct FrontendConfig {
+    bool isSoftwareFe;
+    FrontendType type;
+    FrontendSettings settings;
+    vector<FrontendStatusType> tuneStatusTypes;
+    vector<FrontendStatus> expectTuneStatuses;
+};
+
+struct DvrConfig {
+    DvrType type;
+    uint32_t bufferSize;
+    DvrSettings settings;
+    string playbackInputFile;
+};
+
+struct LiveBroadcastHardwareConnections {
+    string frontendId;
+    string dvrSoftwareFeId;
+    /* string audioFilterId;
+    string videoFilterId;
+    list string of extra filters; */
+};
+
+struct ScanHardwareConnections {
+    string frontendId;
+};
+
+struct DvrPlaybackHardwareConnections {
+    bool support;
+    string frontendId;
+    string dvrId;
+    /* string audioFilterId;
+    string videoFilterId;
+    list string of extra filters; */
+};
+
+struct DvrRecordHardwareConnections {
+    bool support;
+    string frontendId;
+    string dvrRecordId;
+    string dvrSoftwareFeId;
+    /* string recordFilterId;
+    string dvrId; */
+};
+
+struct DescramblingHardwareConnections {
+    bool support;
+    string frontendId;
+    string dvrSoftwareFeId;
+    /* string descramblerId;
+    string audioFilterId;
+    string videoFilterId;
+    list string of extra filters; */
+};
+
+struct LnbLiveHardwareConnections {
+    bool support;
+    string frontendId;
+    /* string audioFilterId;
+    string videoFilterId;
+    list string of extra filters;
+    string lnbId; */
+};
+
+struct LnbRecordHardwareConnections {
+    bool support;
+    string frontendId;
+    /* string recordFilterId;
+    list string of extra filters;
+    string lnbId; */
+};
+
+struct TunerTestingConfigReader {
+  public:
+    static bool checkConfigFileExists() {
+        auto res = read(configFilePath.c_str());
+        if (res == nullopt) {
+            ALOGW("[ConfigReader] Couldn't read /vendor/etc/tuner_vts_config.xml."
+                  "Please check tuner_testing_dynamic_configuration.xsd"
+                  "and sample_tuner_vts_config.xml for more details on how to config Tune VTS.");
+        }
+        return (res != nullopt);
+    }
+
+    static void readFrontendConfig1_0(map<string, FrontendConfig>& frontendMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasFrontends()) {
+            // TODO: complete the tune status config
+            vector<FrontendStatusType> types;
+            types.push_back(FrontendStatusType::DEMOD_LOCK);
+            FrontendStatus status;
+            status.isDemodLocked(true);
+            vector<FrontendStatus> statuses;
+            statuses.push_back(status);
+
+            auto frontends = *hardwareConfig.getFirstFrontends();
+            for (auto feConfig : frontends.getFrontend()) {
+                string id = feConfig.getId();
+                if (id.compare(string("FE_DEFAULT")) == 0) {
+                    // overrid default
+                    frontendMap.erase(string("FE_DEFAULT"));
+                }
+                FrontendType type;
+                switch (feConfig.getType()) {
+                    case FrontendTypeEnum::UNDEFINED:
+                        type = FrontendType::UNDEFINED;
+                        break;
+                    // TODO: finish all other frontend settings
+                    case FrontendTypeEnum::ANALOG:
+                        type = FrontendType::ANALOG;
+                        break;
+                    case FrontendTypeEnum::ATSC:
+                        type = FrontendType::ATSC;
+                        break;
+                    case FrontendTypeEnum::ATSC3:
+                        type = FrontendType::ATSC3;
+                        break;
+                    case FrontendTypeEnum::DVBC:
+                        type = FrontendType::DVBC;
+                        break;
+                    case FrontendTypeEnum::DVBS:
+                        type = FrontendType::DVBS;
+                        frontendMap[id].settings.dvbs(readDvbsFrontendSettings(feConfig));
+                        break;
+                    case FrontendTypeEnum::DVBT: {
+                        type = FrontendType::DVBT;
+                        frontendMap[id].settings.dvbt(readDvbtFrontendSettings(feConfig));
+                        break;
+                    }
+                    case FrontendTypeEnum::ISDBS:
+                        type = FrontendType::ISDBS;
+                        break;
+                    case FrontendTypeEnum::ISDBS3:
+                        type = FrontendType::ISDBS3;
+                        break;
+                    case FrontendTypeEnum::ISDBT:
+                        type = FrontendType::ISDBT;
+                        break;
+                    case FrontendTypeEnum::DTMB:
+                        // dtmb will be handled in readFrontendConfig1_1;
+                        continue;
+                    case FrontendTypeEnum::UNKNOWN:
+                        ALOGW("[ConfigReader] invalid frontend type");
+                        return;
+                }
+                frontendMap[id].type = type;
+                frontendMap[id].isSoftwareFe = feConfig.getIsSoftwareFrontend();
+                // TODO: complete the tune status config
+                frontendMap[id].tuneStatusTypes = types;
+                frontendMap[id].expectTuneStatuses = statuses;
+            }
+        }
+    }
+
+    static void readDvrConfig1_0(map<string, DvrConfig>& dvrMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasDvrs()) {
+            auto dvrs = *hardwareConfig.getFirstDvrs();
+            for (auto dvrConfig : dvrs.getDvr()) {
+                string id = dvrConfig.getId();
+                DvrType type;
+                switch (dvrConfig.getType()) {
+                    case DvrTypeEnum::PLAYBACK:
+                        type = DvrType::PLAYBACK;
+                        dvrMap[id].settings.playback(readPlaybackSettings(dvrConfig));
+                        break;
+                    case DvrTypeEnum::RECORD:
+                        type = DvrType::RECORD;
+                        dvrMap[id].settings.record(readRecordSettings(dvrConfig));
+                        break;
+                    case DvrTypeEnum::UNKNOWN:
+                        ALOGW("[ConfigReader] invalid DVR type");
+                        return;
+                }
+                dvrMap[id].type = type;
+                dvrMap[id].bufferSize = static_cast<uint32_t>(dvrConfig.getBufferSize());
+                if (dvrConfig.hasInputFilePath()) {
+                    dvrMap[id].playbackInputFile = dvrConfig.getInputFilePath();
+                }
+            }
+        }
+    }
+
+    static void connectLiveBroadcast(LiveBroadcastHardwareConnections& live) {
+        auto liveConfig = getDataFlowConfiguration().getFirstClearLiveBroadcast();
+        live.frontendId = liveConfig->getFrontendConnection();
+        if (liveConfig->hasDvrSoftwareFeConnection()) {
+            live.dvrSoftwareFeId = liveConfig->getDvrSoftwareFeConnection();
+        }
+    }
+
+    static void connectScan(ScanHardwareConnections& scan) {
+        auto scanConfig = getDataFlowConfiguration().getFirstScan();
+        scan.frontendId = scanConfig->getFrontendConnection();
+    }
+
+    static void connectDvrPlayback(DvrPlaybackHardwareConnections& playback) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (!dataFlow.hasDvrPlayback()) {
+            playback.support = false;
+            return;
+        }
+        auto playbackConfig = dataFlow.getFirstDvrPlayback();
+        playback.dvrId = playbackConfig->getDvrConnection();
+    }
+
+    static void connectDvrRecord(DvrRecordHardwareConnections& record) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (!dataFlow.hasDvrRecord()) {
+            record.support = false;
+            return;
+        }
+        auto recordConfig = dataFlow.getFirstDvrRecord();
+        record.frontendId = recordConfig->getFrontendConnection();
+        record.dvrRecordId = recordConfig->getDvrRecordConnection();
+        if (recordConfig->hasDvrSoftwareFeConnection()) {
+            record.dvrSoftwareFeId = recordConfig->getDvrSoftwareFeConnection();
+        }
+    }
+
+    static void connectDescrambling(DescramblingHardwareConnections& descrambling) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (!dataFlow.hasDescrambling()) {
+            descrambling.support = false;
+            return;
+        }
+        auto descConfig = dataFlow.getFirstDescrambling();
+        descrambling.frontendId = descConfig->getFrontendConnection();
+        if (descConfig->hasDvrSoftwareFeConnection()) {
+            descrambling.dvrSoftwareFeId = descConfig->getDvrSoftwareFeConnection();
+        }
+    }
+
+    static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (!dataFlow.hasLnbLive()) {
+            lnbLive.support = false;
+            return;
+        }
+        auto lnbLiveConfig = dataFlow.getFirstLnbLive();
+        lnbLive.frontendId = lnbLiveConfig->getFrontendConnection();
+    }
+
+    static void connectLnbRecord(LnbRecordHardwareConnections& lnbRecord) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (!dataFlow.hasLnbRecord()) {
+            lnbRecord.support = false;
+            return;
+        }
+        auto lnbRecordConfig = dataFlow.getFirstLnbRecord();
+        lnbRecord.frontendId = lnbRecordConfig->getFrontendConnection();
+    }
+
+  private:
+    static FrontendDvbtSettings readDvbtFrontendSettings(Frontend feConfig) {
+        ALOGW("[ConfigReader] fe type is dvbt");
+        FrontendDvbtSettings dvbtSettings{
+                .frequency = (uint32_t)feConfig.getFrequency(),
+        };
+        if (!feConfig.hasDvbtFrontendSettings_optional()) {
+            ALOGW("[ConfigReader] no more dvbt settings");
+            return dvbtSettings;
+        }
+        dvbtSettings.transmissionMode = static_cast<FrontendDvbtTransmissionMode>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getTransmissionMode());
+        dvbtSettings.bandwidth = static_cast<FrontendDvbtBandwidth>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getBandwidth());
+        dvbtSettings.isHighPriority =
+                feConfig.getFirstDvbtFrontendSettings_optional()->getIsHighPriority();
+        return dvbtSettings;
+    }
+
+    static FrontendDvbsSettings readDvbsFrontendSettings(Frontend feConfig) {
+        ALOGW("[ConfigReader] fe type is dvbs");
+        FrontendDvbsSettings dvbsSettings{
+                .frequency = (uint32_t)feConfig.getFrequency(),
+        };
+        if (!feConfig.hasDvbsFrontendSettings_optional()) {
+            ALOGW("[ConfigReader] no more dvbs settings");
+            return dvbsSettings;
+        }
+        dvbsSettings.symbolRate = static_cast<uint32_t>(
+                feConfig.getFirstDvbsFrontendSettings_optional()->getSymbolRate());
+        dvbsSettings.inputStreamId = static_cast<uint32_t>(
+                feConfig.getFirstDvbsFrontendSettings_optional()->getInputStreamId());
+        return dvbsSettings;
+    }
+
+    static PlaybackSettings readPlaybackSettings(Dvr dvrConfig) {
+        ALOGW("[ConfigReader] dvr type is playback");
+        PlaybackSettings playbackSettings{
+                .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+                .lowThreshold = static_cast<uint32_t>(dvrConfig.getLowThreshold()),
+                .highThreshold = static_cast<uint32_t>(dvrConfig.getHighThreshold()),
+                .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+                .packetSize = static_cast<uint8_t>(dvrConfig.getPacketSize()),
+        };
+        return playbackSettings;
+    }
+
+    static RecordSettings readRecordSettings(Dvr dvrConfig) {
+        ALOGW("[ConfigReader] dvr type is record");
+        RecordSettings recordSettings{
+                .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+                .lowThreshold = static_cast<uint32_t>(dvrConfig.getLowThreshold()),
+                .highThreshold = static_cast<uint32_t>(dvrConfig.getHighThreshold()),
+                .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+                .packetSize = static_cast<uint8_t>(dvrConfig.getPacketSize()),
+        };
+        return recordSettings;
+    }
+
+    static TunerConfiguration getTunerConfig() { return *read(configFilePath.c_str()); }
+
+    static HardwareConfiguration getHardwareConfig() {
+        return *getTunerConfig().getFirstHardwareConfiguration();
+    }
+
+    static DataFlowConfiguration getDataFlowConfiguration() {
+        return *getTunerConfig().getFirstDataFlowConfiguration();
+    }
+};
diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt
new file mode 100644
index 0000000..1ebd8e1
--- /dev/null
+++ b/tv/tuner/config/api/current.txt
@@ -0,0 +1,212 @@
+// Signature format: 2.0
+package android.media.tuner.testing.configuration.V1_0 {
+
+  public class DataFlowConfiguration {
+    ctor public DataFlowConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.ClearLiveBroadcast getClearLiveBroadcast();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling getDescrambling();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback getDvrPlayback();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord getDvrRecord();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive getLnbLive();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord getLnbRecord();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan getScan();
+    method public void setClearLiveBroadcast(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.ClearLiveBroadcast);
+    method public void setDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling);
+    method public void setDvrPlayback(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback);
+    method public void setDvrRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord);
+    method public void setLnbLive(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive);
+    method public void setLnbRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord);
+    method public void setScan(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan);
+  }
+
+  public static class DataFlowConfiguration.ClearLiveBroadcast {
+    ctor public DataFlowConfiguration.ClearLiveBroadcast();
+    method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getFrontendConnection();
+    method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.Descrambling {
+    ctor public DataFlowConfiguration.Descrambling();
+    method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getFrontendConnection();
+    method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.DvrPlayback {
+    ctor public DataFlowConfiguration.DvrPlayback();
+    method @Nullable public String getDvrConnection();
+    method public void setDvrConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.DvrRecord {
+    ctor public DataFlowConfiguration.DvrRecord();
+    method @Nullable public String getDvrRecordConnection();
+    method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getFrontendConnection();
+    method public void setDvrRecordConnection(@Nullable String);
+    method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.LnbLive {
+    ctor public DataFlowConfiguration.LnbLive();
+    method @Nullable public String getFrontendConnection();
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.LnbRecord {
+    ctor public DataFlowConfiguration.LnbRecord();
+    method @Nullable public String getDvrRecordConnection();
+    method @Nullable public String getFrontendConnection();
+    method public void setDvrRecordConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.Scan {
+    ctor public DataFlowConfiguration.Scan();
+    method @Nullable public String getFrontendConnection();
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public class DvbsFrontendSettings {
+    ctor public DvbsFrontendSettings();
+    method @Nullable public java.math.BigInteger getInputStreamId();
+    method @Nullable public java.math.BigInteger getSymbolRate();
+    method public void setInputStreamId(@Nullable java.math.BigInteger);
+    method public void setSymbolRate(@Nullable java.math.BigInteger);
+  }
+
+  public class DvbtFrontendSettings {
+    ctor public DvbtFrontendSettings();
+    method @Nullable public java.math.BigInteger getBandwidth();
+    method @Nullable public java.math.BigInteger getIsHighPriority();
+    method @Nullable public java.math.BigInteger getTransmissionMode();
+    method public void setBandwidth(@Nullable java.math.BigInteger);
+    method public void setIsHighPriority(@Nullable java.math.BigInteger);
+    method public void setTransmissionMode(@Nullable java.math.BigInteger);
+  }
+
+  public class Dvr {
+    ctor public Dvr();
+    method @Nullable public java.math.BigInteger getBufferSize();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum getDataFormat();
+    method @Nullable public java.math.BigInteger getHighThreshold();
+    method @Nullable public String getId();
+    method @Nullable public String getInputFilePath();
+    method @Nullable public java.math.BigInteger getLowThreshold();
+    method @Nullable public java.math.BigInteger getPacketSize();
+    method @Nullable public java.math.BigInteger getStatusMask();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvrTypeEnum getType();
+    method public void setBufferSize(@Nullable java.math.BigInteger);
+    method public void setDataFormat(@Nullable android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum);
+    method public void setHighThreshold(@Nullable java.math.BigInteger);
+    method public void setId(@Nullable String);
+    method public void setInputFilePath(@Nullable String);
+    method public void setLowThreshold(@Nullable java.math.BigInteger);
+    method public void setPacketSize(@Nullable java.math.BigInteger);
+    method public void setStatusMask(@Nullable java.math.BigInteger);
+    method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.DvrTypeEnum);
+  }
+
+  public enum DvrDataFormatEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum ES;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum PES;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum SHV_TLV;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum TS;
+  }
+
+  public enum DvrStatusEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum DATA_READY;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum HIGH_WATER;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum LOW_WATER;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum OVERFLOW;
+  }
+
+  public enum DvrTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrTypeEnum PLAYBACK;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrTypeEnum RECORD;
+  }
+
+  public class Frontend {
+    ctor public Frontend();
+    method @Nullable public java.math.BigInteger getConnectToCicamId();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings getDvbsFrontendSettings_optional();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings getDvbtFrontendSettings_optional();
+    method @Nullable public java.math.BigInteger getEndFrequency();
+    method @Nullable public java.math.BigInteger getFrequency();
+    method @Nullable public String getId();
+    method @Nullable public boolean getIsSoftwareFrontend();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum getType();
+    method public void setConnectToCicamId(@Nullable java.math.BigInteger);
+    method public void setDvbsFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings);
+    method public void setDvbtFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings);
+    method public void setEndFrequency(@Nullable java.math.BigInteger);
+    method public void setFrequency(@Nullable java.math.BigInteger);
+    method public void setId(@Nullable String);
+    method public void setIsSoftwareFrontend(@Nullable boolean);
+    method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum);
+  }
+
+  public enum FrontendTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ANALOG;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ATSC;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ATSC3;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DTMB;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBC;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBS;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBT;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBS;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBS3;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBT;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum UNDEFINED;
+  }
+
+  public class HardwareConfiguration {
+    ctor public HardwareConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Dvrs getDvrs();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Frontends getFrontends();
+    method public void setDvrs(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Dvrs);
+    method public void setFrontends(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Frontends);
+  }
+
+  public static class HardwareConfiguration.Dvrs {
+    ctor public HardwareConfiguration.Dvrs();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Dvr> getDvr();
+  }
+
+  public static class HardwareConfiguration.Frontends {
+    ctor public HardwareConfiguration.Frontends();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Frontend> getFrontend();
+  }
+
+  public class TunerConfiguration {
+    ctor public TunerConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration getDataFlowConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration getHardwareConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.Version getVersion();
+    method public void setDataFlowConfiguration(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration);
+    method public void setHardwareConfiguration(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration);
+    method public void setVersion(@Nullable android.media.tuner.testing.configuration.V1_0.Version);
+  }
+
+  public enum Version {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.Version _1_0;
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method @Nullable public static android.media.tuner.testing.configuration.V1_0.TunerConfiguration read(@NonNull java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static String readText(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/tv/tuner/config/api/last_current.txt b/tv/tuner/config/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tv/tuner/config/api/last_current.txt
diff --git a/tv/tuner/config/api/last_removed.txt b/tv/tuner/config/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tv/tuner/config/api/last_removed.txt
diff --git a/tv/tuner/config/api/removed.txt b/tv/tuner/config/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/tv/tuner/config/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/tv/tuner/config/sample_tuner_vts_config.xml b/tv/tuner/config/sample_tuner_vts_config.xml
new file mode 100644
index 0000000..001e045
--- /dev/null
+++ b/tv/tuner/config/sample_tuner_vts_config.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<!-- The Sample Tuner Testing Configuration.
+    Name the customized xml with "tuner_vts_config.xml" and push into the device
+    "/vendor/etc" path. Please use "tuner_testing_dynamic_configuration.xsd" to verify the xml.
+    The version section contains a “version” tag in the form “major.minor” e.g version=”1.0”
+    This shows the tuner dynamic configuration version. -->
+<TunerConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <!-- Hardware Configuration section contains the configurations of all the hardwares
+        that would be used in the tests. In the "dataFlowConfiguration" section, each data flow
+        under test has its required/optional hardwares. The ids configured in the
+        "dataFlowConfiguration" would be used to connect the hardware to each data flow test. -->
+    <hardwareConfiguration>
+        <!-- Frontends section:
+            This section contains configurations of all the frontends that would be used
+                in the tests.
+                - This section is optional and can be skipped to use the default fe settings.
+                - The default settings can be found in the sample_tuner_vts_configurations.xml.
+                - The users can also override the default frontend settings using id="FE_DEFAULT".
+                - The users can configure 1 or more frontend elements in the frontends sections.
+
+            Each frontend element contain the following attributes:
+                "id": unique id of the frontend that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "type": the frontend type. The enums are defined in the xsd.
+                "isSoftwareFrontend": if the test environment is using hardware or software
+                    frontend. If using software, a ts input file path needs to be configured.
+                "softwareFeInputPath": used as the source of the software frontend.
+                "connectToCicamId": if the device supports frontend connecting to cicam, the target
+                    cicam id needs to be configured here. Supported in Tuner 1.1 or higher.
+                "frequency": the frequency used to configure tune and scan.
+                "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
+
+            Each frontend element also contains one and only one type-related "frontendSettings".
+                - The settings type should match the frontend "type" attribute.
+                - For example, when frontend type="DVBT", dvbtFrontendSettings can be configured.
+                - This is optional and skipping the settings would pass a setting with frequency
+                    config only to the hal.
+        -->
+        <frontends>
+            <frontend id="FE_DEFAULT" type="DVBT" isSoftwareFrontend="true"
+                      connectToCicamId="0" frequency="578000" endFrequency="800000">
+                <dvbtFrontendSettings bandwidth="8" transmissionMode="1" isHighPriority="1"/>
+            </frontend>
+            <frontend id="FE_DVBS_0" type="DVBS" isSoftwareFrontend="true"
+                      connectToCicamId="0" frequency="578000" endFrequency="800000">
+            </frontend>
+        </frontends>
+        <!-- Dvr section:
+            This section contains configurations of all the dvrs that would be used in the tests.
+                - This section is optional and can be skipped if DVR is not supported.
+                - The users can configure 1 or more dvr elements in the dvrs sections.
+
+            Each dvr element contain the following attributes:
+                "id": unique id of the dvr that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "type": the dvr type.
+                "bufferSize": the dvr buffer size.
+                "statusMask": register callbacks of specific status.
+                "lowThreshold": the dvr status low threshold.
+                "highThreshold": the dvr status high threshold.
+                "dataFormat": the dvr data format.
+                "packetSize": the dvr packet size.
+                "inputFilePath": the dvr playback input file path. Only required in playback dvr.
+        -->
+        <dvrs>
+            <dvr id="DVR_PLAYBACK_0" type="PLAYBACK" bufferSize="4194304"
+                 statusMask="15" lowThreshold="4096" highThreshold="32767"
+                 dataFormat="TS" packetSize="188" inputFilePath="/data/local/tmp/segment000000.ts"/>
+            <dvr id="DVR_RECORD_0" type="RECORD" bufferSize="4194304"
+                 statusMask="15" lowThreshold="4096" highThreshold="32767"
+                 dataFormat="TS" packetSize="188"/>
+            <dvr id="DVR_PLAYBACK_1" type="PLAYBACK" bufferSize="4194304"
+                 statusMask="15" lowThreshold="4096" highThreshold="32767"
+                 dataFormat="ES" packetSize="188" inputFilePath="/data/local/tmp/test.es"/>
+        </dvrs>
+    </hardwareConfiguration>
+
+    <!-- Data flow configuration section connects each data flow under test to the ids of the
+        hardwares that would be used during the tests. -->
+    <dataFlowConfiguration>
+        <clearLiveBroadcast frontendConnection="FE_DEFAULT"
+                            dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+        <scan frontendConnection="FE_DEFAULT"/>
+        <descrambling frontendConnection="FE_DEFAULT"
+                      dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+        <dvrRecord frontendConnection="FE_DEFAULT"
+                   dvrRecordConnection="DVR_RECORD_0"
+                   dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+        <lnbLive frontendConnection="FE_DVBS_0"/>
+	<lnbRecord frontendConnection="FE_DVBS_0"
+		   dvrRecordConnection="DVR_RECORD_0"/>
+    </dataFlowConfiguration>
+</TunerConfiguration>
diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
new file mode 100644
index 0000000..45d25e5
--- /dev/null
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- List the dynamic config versions supported by tuner testing. -->
+    <xs:simpleType name="version">
+        <xs:restriction base="xs:decimal">
+            <xs:enumeration value="1.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <!-- FRONTEND SESSION -->
+    <xs:simpleType name="frontendId">
+        <!-- Frontend id must be either FE_DEFAULT or FE_TYPE_NUM
+            <frontend id="FE_DVBS_0"/>
+        -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="FE_DEFAULT|FE_[A-Z]+_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="frontendTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="UNDEFINED" />
+            <xs:enumeration value="ANALOG" />
+            <xs:enumeration value="ATSC" />
+            <xs:enumeration value="ATSC3"/>
+            <xs:enumeration value="DVBC"/>
+            <xs:enumeration value="DVBS"/>
+            <xs:enumeration value="DVBT"/>
+            <xs:enumeration value="ISDBS"/>
+            <xs:enumeration value="ISDBS3"/>
+            <xs:enumeration value="ISDBT"/>
+            <xs:enumeration value="DTMB"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="dvbtFrontendSettings">
+        <xs:attribute name="bandwidth" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="transmissionMode" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="isHighPriority" type="xs:nonNegativeInteger" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="dvbsFrontendSettings">
+        <xs:attribute name="inputStreamId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="symbolRate" type="xs:nonNegativeInteger" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="frontend">
+        <xs:annotation>
+            <xs:documentation>
+                Each frontend element contain the following attributes:
+                    "id": unique id of the frontend that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "type": the frontend type. The enums are defined in the xsd.
+                    "isSoftwareFrontend": if the test environment is using hardware or software
+                        frontend. If using software, a ts input file path needs to be configured.
+                    "softwareFeInputPath": used as the source of the software frontend.
+                    "connectToCicamId": if the device supports frontend connecting to cicam, the
+                        target cicam id needs to be configured here. Supported in Tuner 1.1 or
+                        higher.
+                    "frequency": the frequency used to configure tune and scan.
+                    "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
+
+                Each frontend element also contains at most one type-related "frontendSettings".
+                    - The settings type should match the frontend "type" attribute.
+                    - For example, when frontend type="DVBT", dvbtFrontendSettings can be
+                        configured.
+                    - This is optional and skipping the settings would pass a setting with frequency
+                        config only to the hal.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="1">
+            <!-- TODO: finish all the frontend settings structures. -->
+            <!--xs:element name="analog" type="analogSettings"/>
+            <xs:element name="atsc" type="atscSettings"/>
+            <xs:element name="atsc3" type="atsc3Settings"/>
+            <xs:element name="dvbc" type="dvbcSettings"/-->
+            <xs:element name="dvbsFrontendSettings" type="dvbsFrontendSettings"/>
+            <xs:element name="dvbtFrontendSettings" type="dvbtFrontendSettings"/>
+            <!--xs:element name="isdbs" type="isdbsSettings"/>
+            <xs:element name="isdbs3" type="isdbs3Settings"/>
+            <xs:element name="isdbt" type="isdbtSettings"/>
+            <xs:element name="dtmb" type="dtmbSettings"/-->
+        </xs:choice>
+        <xs:attribute name="id" type="frontendId" use="required"/>
+        <xs:attribute name="type" type="frontendTypeEnum" use="required"/>
+        <!-- A dvr connection is required in the data flow config section when
+            "isSoftwareFrontend" is true. -->
+        <xs:attribute name="isSoftwareFrontend" type="xs:boolean" use="required"/>
+        <xs:attribute name="frequency" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="connectToCicamId" type="xs:nonNegativeInteger" use="optional"/>
+        <xs:attribute name="endFrequency" type="xs:nonNegativeInteger" use="optional"/>
+    </xs:complexType>
+
+    <!-- DVR SESSION -->
+    <xs:simpleType name="dvrId">
+        <!-- Dvr id must be DVR_TYPE_NUM. <dvr id="DVR_PLAYBACK_0"/> -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="DVR_RECORD_[0-9]+|DVR_PLAYBACK_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="dvrStatusMask">
+        <!-- Dvr status mask must masking the <dvrStatusEnum> -->
+        <xs:restriction base="xs:integer">
+            <xs:minInclusive value="0"/>
+            <xs:maxInclusive value="15"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="dvrStatusEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="DATA_READY" />
+            <xs:enumeration value="LOW_WATER" />
+            <xs:enumeration value="HIGH_WATER" />
+            <xs:enumeration value="OVERFLOW" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="dvrTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="PLAYBACK" />
+            <xs:enumeration value="RECORD" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="dvrDataFormatEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="TS" />
+            <xs:enumeration value="PES" />
+            <xs:enumeration value="ES" />
+            <xs:enumeration value="SHV_TLV" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="dvr">
+        <xs:annotation>
+            <xs:documentation>
+                Each dvr element contain the following attributes:
+                    "id": unique id of the dvr that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "type": the dvr type.
+                    "bufferSize": the dvr buffer size.
+                    "statusMask": register callbacks of specific status.
+                    "lowThreshold": the dvr status low threshold.
+                    "highThreshold": the dvr status high threshold.
+                    "dataFormat": the dvr data format.
+                    "packetSize": the dvr packet size.
+                    "inputFilePath": the dvr playback input file path. Only required in playback
+                        dvr.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="id" type="dvrId" use="required"/>
+        <xs:attribute name="type" type="dvrTypeEnum" use="required"/>
+        <xs:attribute name="bufferSize" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="statusMask" type="dvrStatusMask" use="required"/>
+        <xs:attribute name="lowThreshold" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="highThreshold" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="dataFormat" type="dvrDataFormatEnum" use="required"/>
+        <xs:attribute name="packetSize" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="inputFilePath" type="xs:anyURI" use="optional"/>
+    </xs:complexType>
+
+    <!-- HARDWARE CONFIGURATION SESSION -->
+    <xs:complexType name="hardwareConfiguration">
+        <xs:sequence>
+            <xs:element name="frontends" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the frontends that would be
+                                used in the tests.
+                                - This section is optional and can be skipped to use the default
+                                    fe settings.
+                                - The default settings can be found in the
+                                    sample_tuner_vts_configurations.xml.
+                                - The users can also override the default frontend settings using
+                                    id="FE_DEFAULT".
+                                - The users can configure 1 or more frontend elements in the
+                                    frontends sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="frontend" type="frontend" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="dvrs" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the dvrs that would be used
+                                in the tests.
+                                - This section is optional and can be skipped if the device does
+                                    not support dvr.
+                                - The users can configure 1 or more dvr elements in the dvrs
+                                   sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="dvr" type="dvr" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <!-- DATA FLOW CONFIGURATION SESSION -->
+    <xs:complexType name="dataFlowConfiguration">
+        <xs:sequence>
+            <xs:element name="clearLiveBroadcast" minOccurs="1" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <!-- DVR is only required when the frontend is using the software input -->
+                    <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="scan" minOccurs="1" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="descrambling" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <!-- DVR is only required when the frontend is using the software input -->
+                    <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="dvrPlayback" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="dvrConnection" type="dvrId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="dvrRecord" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+                    <!-- DVR is only required when the frontend is using the software input -->
+                    <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="lnbLive" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="lnbRecord" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <!-- Full Tuner Configuration. This is the root element of the configuration xml. -->
+    <xs:element name="TunerConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="hardwareConfiguration" type="hardwareConfiguration" minOccurs="1" maxOccurs="1"/>
+                <xs:element name="dataFlowConfiguration" type="dataFlowConfiguration" minOccurs="1" maxOccurs="1"/>
+            </xs:sequence>
+            <xs:attribute name="version" type="version"/>
+        </xs:complexType>
+        <xs:key name="frontendIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/frontends/frontend"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+    </xs:element>
+</xs:schema>