Merge "Moved emulated User HAL capabilities into a library." into rvc-dev
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 86e59d0..872b35b 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -138,6 +138,9 @@
 cc_binary {
     name: "android.hardware.automotive.vehicle@2.0-service",
     defaults: ["vhal_v2_0_defaults"],
+    vintf_fragments: [
+        "android.hardware.automotive.vehicle@2.0-service.xml",
+    ],
     init_rc: ["android.hardware.automotive.vehicle@2.0-service.rc"],
     vendor: true,
     relative_install_path: "hw",
diff --git a/automotive/vehicle/2.0/manifest.vehicle.xml b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.xml
similarity index 83%
rename from automotive/vehicle/2.0/manifest.vehicle.xml
rename to automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.xml
index 832b302..660b03d 100644
--- a/automotive/vehicle/2.0/manifest.vehicle.xml
+++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.xml
@@ -1,4 +1,4 @@
-<manifest version="1.0" type="device" target-level="3">
+<manifest version="1.0" type="device">
     <hal format="hidl">
         <name>android.hardware.automotive.vehicle</name>
         <transport>hwbinder</transport>
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 e50aafb..b8a606a 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
@@ -437,6 +437,16 @@
 
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::TIRE_PRESSURE_DISPLAY_UNITS),
+                         .access = VehiclePropertyAccess::READ_WRITE,
+                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                         .configArray = {(int)VehicleUnit::KILOPASCAL, (int)VehicleUnit::PSI,
+                                         (int)VehicleUnit::BAR},
+                 },
+         .initialValue = {.int32Values = {toInt(VehicleUnit::PSI)}}},
+
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::CURRENT_GEAR),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
@@ -1026,6 +1036,14 @@
                                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                         },
         },
+        {
+                .config =
+                        {
+                                .prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION),
+                                .access = VehiclePropertyAccess::READ_WRITE,
+                                .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                        },
+        },
 };
 
 }  // impl
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 23f9135..82f938c 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -2646,9 +2646,9 @@
      * If the request succeeded and Android has 3 users (0, 10, 11), the response would be:
      *
      * int32[0]: -108 // request id
-     * int32[1]:  5   // messageType = SwitchUserMessageType::ANDROID_SWITCH
+     * int32[1]:  5   // messageType = SwitchUserMessageType::ANDROID_POST_SWITCH
      * int32[2]:  11  // target user id
-     * int32[3]:  11  // target user id flags (none)
+     * int32[3]:  0   // target user id flags (none)
      * int32[4]:  11  // current user
      * int32[5]:  0   // current user flags (none)
      * int32[6]:  3   // number of users
@@ -2664,17 +2664,21 @@
      *
      * 5.ANDROID_POST_SWITCH
      * ---------------------
-     * Called by the Android System after a request to switch a user was made
+     * Called by the Android System after a request to switch a user was made.
      *
      * This property is called after switch requests of any type (i.e., LEGACY_ANDROID_SWITCH,
      * ANDROID_SWITCH, or VEHICLE_REQUEST) and can be used to determine if the request succeeded or
      * failed:
      *
-     * 1. When it succeeded, it's called when the Android user is in the boot locked state and the
-     *    value of the current and target users ids in the response are different. This would be
-     *    equivalent to receiving an Intent.ACTION_LOCKED_BOOT_COMPLETED in an Android app.
+     * 1. When it succeeded, it's called when the Android user is in the unlocked state and the
+     *    value of the current and target users ids in the response are the same. This would be
+     *    equivalent to receiving an Intent.ACTION_USER_UNLOCKED in an Android app.
      * 2. When it failed it's called right away and the value of the current and target users ids
-     *    in the response are the same.
+     *    in the response are different (as the current user didn't change to the target).
+     * 3. If a new switch request is made before the HAL responded to the previous one or before
+     *    the user was unlocked, then the ANDROID_POST_SWITCH request is not made. For example,
+     *    the driver could accidentally switch to the wrong user which has lock crentials, then
+     *    switch to the right one before entering the credentials.
      *
      * The HAL can update its internal state once it receives this request, but it doesn't need to
      * reply back to the Android System.
@@ -2824,7 +2828,7 @@
      * int32[0]: 0   (Android user flags)
      * int32[1]: 1   (number of associations being set)
      * int32[2]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
-     * int32[3]: 1   (1st value: UserIdentificationAssociationSETValue::ASSOCIATE_CURRENT_USER)
+     * int32[3]: 1   (1st value: UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER)
      *
      * If the request succeeded, the response would be simply:
      *
diff --git a/current.txt b/current.txt
index e553999..d2b7206 100644
--- a/current.txt
+++ b/current.txt
@@ -1,6 +1,10 @@
 # Do not change this file except to add new interfaces. Changing
 # pre-existing interfaces will fail VTS and break framework-only OTAs
 
+# Test HALs
+
+717c17cd380bb48710dff601d1a03351d4ebc28028353d5d60489248f506523c android.hardware.tests.lazy@1.0::ILazy
+
 # HALs released in Android O
 
 f219c3b5b8c6cb1d659d4c7328f67246abfe1a8613f469826fd3b9ad090417a2 android.hardware.audio@2.0::IDevice
@@ -656,9 +660,6 @@
 7d2e77ad86766bbc213fa7377eab739f44cc0866e567e6d33c0e27e7f99e27a8 android.hardware.automotive.sv@1.0::ISurroundViewSession
 d34769e55df919739bb46f25ae2e125e9c807733afa94daeca20feadd74de79c android.hardware.automotive.sv@1.0::ISurroundViewStream
 affd9c591f48a69773fcf43dc4a716d292cd4bc5ba2be8649276af0aedea435d android.hardware.automotive.sv@1.0::types
-b3caf524c46a47d67e6453a34419e1881942d059e146cda740502670e9a752c3 android.hardware.automotive.vehicle@2.0::IVehicle
-7ce8728b27600e840cacf0a832f6942819fe535f9d3797ae052d5eef5065921c android.hardware.automotive.vehicle@2.0::IVehicleCallback
-6b2564fce1d364baf9ba15a5cb00a8f08f86a5be5387c0ede795328ca536a2c7 android.hardware.automotive.vehicle@2.0::types
 140f8f62100ccf9cd282ae3685a0f4ef0a9f971d77dfbc7350ccb4e04cf295ec android.hardware.biometrics.fingerprint@2.2::IBiometricsFingerprint
 82cad99f5feb2ea9bcd4579055edf4af8feb9fc602a6e4827ddd727d254d4991 android.hardware.biometrics.fingerprint@2.2::IBiometricsFingerprintClientCallback
 ae6315fd42196478ac08441cb489d854118001bca5b9b9fd58af5110952be30e android.hardware.biometrics.fingerprint@2.2::types
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index 89f7f35..553a3d8 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -44,6 +44,8 @@
         return false;
     }
     storageKey_ = random.value();
+    startPersonalizationCalled_ = false;
+    firstEntry_ = true;
 
     return true;
 }
@@ -105,6 +107,12 @@
 
 ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
         int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
+    if (startPersonalizationCalled_) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
+    }
+
+    startPersonalizationCalled_ = true;
     numAccessControlProfileRemaining_ = accessControlProfileCount;
     remainingEntryCounts_ = entryCounts;
     entryNameSpace_ = "";
@@ -128,6 +136,13 @@
                 "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
     }
 
+    if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Access Control Profile id must be unique"));
+    }
+    accessControlProfileIds_.insert(id);
+
     // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
     // be zero.
     if (!userAuthenticationRequired && timeoutMillis != 0) {
@@ -184,12 +199,20 @@
     }
 
     // Handle initial beginEntry() call.
-    if (entryNameSpace_ == "") {
+    if (firstEntry_) {
+        firstEntry_ = false;
         entryNameSpace_ = nameSpace;
+        allNameSpaces_.insert(nameSpace);
     }
 
     // If the namespace changed...
     if (nameSpace != entryNameSpace_) {
+        if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Name space cannot be added in interleaving fashion"));
+        }
+
         // Then check that all entries in the previous namespace have been added..
         if (remainingEntryCounts_[0] != 0) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
@@ -197,6 +220,8 @@
                     "New namespace but a non-zero number of entries remain to be added"));
         }
         remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+        remainingEntryCounts_[0] -= 1;
+        allNameSpaces_.insert(nameSpace);
 
         if (signedDataCurrentNamespace_.size() > 0) {
             signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
@@ -330,6 +355,18 @@
 
 ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
         vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
+    if (numAccessControlProfileRemaining_ != 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "numAccessControlProfileRemaining_ is not 0 and expected zero"));
+    }
+
+    if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "More entry spaces remain than startPersonalization configured"));
+    }
+
     if (signedDataCurrentNamespace_.size() > 0) {
         signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
     }
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h
index b182862..cb91f7b 100644
--- a/identity/aidl/default/WritableIdentityCredential.h
+++ b/identity/aidl/default/WritableIdentityCredential.h
@@ -21,9 +21,11 @@
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
 
 #include <cppbor.h>
+#include <set>
 
 namespace aidl::android::hardware::identity {
 
+using ::std::set;
 using ::std::string;
 using ::std::vector;
 
@@ -66,6 +68,8 @@
 
     // This is set in initialize().
     vector<uint8_t> storageKey_;
+    bool startPersonalizationCalled_;
+    bool firstEntry_;
 
     // These are set in getAttestationCertificate().
     vector<uint8_t> credentialPrivKey_;
@@ -79,6 +83,9 @@
     cppbor::Map signedDataNamespaces_;
     cppbor::Array signedDataCurrentNamespace_;
 
+    // This field is initialized in addAccessControlProfile
+    set<int32_t> accessControlProfileIds_;
+
     // These fields are initialized during beginAddEntry()
     size_t entryRemainingBytes_;
     vector<uint8_t> entryAdditionalData_;
@@ -86,6 +93,7 @@
     string entryName_;
     vector<int32_t> entryAccessControlProfileIds_;
     vector<uint8_t> entryBytes_;
+    set<string> allNameSpaces_;
 };
 
 }  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index ef8beb4..e4780bf 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -4,7 +4,11 @@
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
     ],
-    srcs: ["VtsHalIdentityTargetTest.cpp"],
+    srcs: [
+        "VtsHalIdentityEndToEndTest.cpp",
+        "VtsIWritableIdentityCredentialTests.cpp",
+        "VtsIdentityTestUtils.cpp",
+    ],
     shared_libs: [
         "libbinder",
         "libcrypto",
diff --git a/identity/aidl/vts/VtsHalIdentityTargetTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
similarity index 69%
rename from identity/aidl/vts/VtsHalIdentityTargetTest.cpp
rename to identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
index ea37fdc..8a4e8a7 100644
--- a/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
+++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
@@ -28,8 +28,11 @@
 #include <future>
 #include <map>
 
+#include "VtsIdentityTestUtils.h"
+
 namespace android::hardware::identity {
 
+using std::endl;
 using std::map;
 using std::optional;
 using std::string;
@@ -41,51 +44,6 @@
 
 using ::android::hardware::keymaster::HardwareAuthToken;
 
-// ---------------------------------------------------------------------------
-// Test Data.
-// ---------------------------------------------------------------------------
-
-struct TestEntryData {
-    TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
-        : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
-
-    TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
-    }
-    TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
-                  vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        valueCbor = cppbor::Bstr(value).encode();
-    }
-    TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        valueCbor = cppbor::Bool(value).encode();
-    }
-    TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        if (value >= 0) {
-            valueCbor = cppbor::Uint(value).encode();
-        } else {
-            valueCbor = cppbor::Nint(-value).encode();
-        }
-    }
-
-    string nameSpace;
-    string name;
-    vector<uint8_t> valueCbor;
-    vector<int32_t> profileIds;
-};
-
-struct TestProfile {
-    uint16_t id;
-    vector<uint8_t> readerCertificate;
-    bool userAuthenticationRequired;
-    uint64_t timeoutMillis;
-};
-
-// ----------------------------------------------------------------
-
 class IdentityAidl : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -108,39 +66,26 @@
 TEST_P(IdentityAidl, createAndRetrieveCredential) {
     // First, generate a key-pair for the reader since its public key will be
     // part of the request data.
-    optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
-    ASSERT_TRUE(readerKeyPKCS8);
-    optional<vector<uint8_t>> readerPublicKey =
-            support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
-    optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
-    string serialDecimal = "1234";
-    string issuer = "Android Open Source Project";
-    string subject = "Android IdentityCredential VTS Test";
-    time_t validityNotBefore = time(nullptr);
-    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-    optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
-            readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
-            validityNotBefore, validityNotAfter);
+    vector<uint8_t> readerKey;
+    optional<vector<uint8_t>> readerCertificate =
+            test_utils::GenerateReaderCertificate("1234", readerKey);
     ASSERT_TRUE(readerCertificate);
 
     // Make the portrait image really big (just shy of 256 KiB) to ensure that
     // the chunking code gets exercised.
     vector<uint8_t> portraitImage;
-    portraitImage.resize(256 * 1024 - 10);
-    for (size_t n = 0; n < portraitImage.size(); n++) {
-        portraitImage[n] = (uint8_t)n;
-    }
+    test_utils::SetImageData(portraitImage);
 
     // Access control profiles:
-    const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
-                                              {0, readerCertificate.value(), false, 0},
-                                              // Profile 1 (no authentication)
-                                              {1, {}, false, 0}};
+    const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
+                                                          {0, readerCertificate.value(), false, 0},
+                                                          // Profile 1 (no authentication)
+                                                          {1, {}, false, 0}};
 
     HardwareAuthToken authToken;
 
     // Here's the actual test data:
-    const vector<TestEntryData> testEntries = {
+    const vector<test_utils::TestEntryData> testEntries = {
             {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
             {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
             {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
@@ -155,67 +100,33 @@
 
     string cborPretty;
     sp<IWritableIdentityCredential> writableCredential;
-    string docType = "org.iso.18013-5.2019.mdl";
-    bool testCredential = true;
-    ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential)
-                        .isOk());
-    ASSERT_NE(writableCredential, nullptr);
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
 
     string challenge = "attestationChallenge";
+    test_utils::AttestationData attData(writableCredential, challenge, {});
+    ASSERT_TRUE(attData.result.isOk())
+            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
+    ASSERT_EQ(binder::Status::EX_NONE, attData.result.exceptionCode());
+    ASSERT_EQ(IIdentityCredentialStore::STATUS_OK, attData.result.serviceSpecificErrorCode());
+
     // TODO: set it to something random and check it's in the cert chain
-    vector<uint8_t> attestationApplicationId = {};
-    vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
-    vector<Certificate> attestationCertificates;
-    ASSERT_TRUE(writableCredential
-                        ->getAttestationCertificate(attestationApplicationId, attestationChallenge,
-                                                    &attestationCertificates)
-                        .isOk());
-    ASSERT_GE(attestationCertificates.size(), 2);
+    ASSERT_GE(attData.attestationCertificate.size(), 2);
 
     ASSERT_TRUE(
             writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
                     .isOk());
 
-    vector<SecureAccessControlProfile> returnedSecureProfiles;
-    for (const auto& testProfile : testProfiles) {
-        SecureAccessControlProfile profile;
-        Certificate cert;
-        cert.encodedCertificate = testProfile.readerCertificate;
-        ASSERT_TRUE(writableCredential
-                            ->addAccessControlProfile(testProfile.id, cert,
-                                                      testProfile.userAuthenticationRequired,
-                                                      testProfile.timeoutMillis,
-                                                      0,  // secureUserId
-                                                      &profile)
-                            .isOk());
-        ASSERT_EQ(testProfile.id, profile.id);
-        ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
-        ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
-        ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
-        ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
-        returnedSecureProfiles.push_back(profile);
-    }
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
 
     // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
     // is a little hacky but it works well enough.
-    map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
 
     for (const auto& entry : testEntries) {
-        vector<vector<uint8_t>> chunks =
-                support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize);
-
-        ASSERT_TRUE(writableCredential
-                            ->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
-                                            entry.valueCbor.size())
-                            .isOk());
-
-        vector<vector<uint8_t>> encryptedChunks;
-        for (const auto& chunk : chunks) {
-            vector<uint8_t> encryptedChunk;
-            ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk());
-            encryptedChunks.push_back(encryptedChunk);
-        }
-        encryptedBlobs[&entry] = encryptedChunks;
+        ASSERT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                         encryptedBlobs, true));
     }
 
     vector<uint8_t> credentialData;
@@ -276,8 +187,8 @@
             "]",
             cborPretty);
 
-    optional<vector<uint8_t>> credentialPubKey =
-            support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate);
+    optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
+            attData.attestationCertificate[0].encodedCertificate);
     ASSERT_TRUE(credentialPubKey);
     EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
                                                  {},  // Additional data
@@ -347,8 +258,8 @@
                                          .add(cppbor::Semantic(24, itemsRequestBytes))
                                          .encode();
     optional<vector<uint8_t>> readerSignature =
-            support::coseSignEcDsa(readerKey.value(), {},  // content
-                                   dataToSign,             // detached content
+            support::coseSignEcDsa(readerKey, {},  // content
+                                   dataToSign,     // detached content
                                    readerCertificate.value());
     ASSERT_TRUE(readerSignature);
 
@@ -358,7 +269,7 @@
     ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
 
     ASSERT_TRUE(credential
-                        ->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
+                        ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
                                          signingKeyBlob, sessionTranscriptBytes,
                                          readerSignature.value(), testEntriesEntryCounts)
                         .isOk());
@@ -405,6 +316,8 @@
     cppbor::Array deviceAuthentication;
     deviceAuthentication.add("DeviceAuthentication");
     deviceAuthentication.add(sessionTranscript.clone());
+
+    string docType = "org.iso.18013-5.2019.mdl";
     deviceAuthentication.add(docType);
     deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
     vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
new file mode 100644
index 0000000..56b30af
--- /dev/null
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VtsIWritableIdentityCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+
+#include "VtsIdentityTestUtils.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+class IdentityCredentialTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+    }
+
+    sp<IIdentityCredentialStore> credentialStore_;
+};
+
+TEST_P(IdentityCredentialTests, verifyAttestationWithEmptyChallenge) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    vector<uint8_t> attestationChallenge;
+    vector<Certificate> attestationCertificate;
+    vector<uint8_t> attestationApplicationId = {};
+    result = writableCredential->getAttestationCertificate(
+            attestationApplicationId, attestationChallenge, &attestationCertificate);
+
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    EXPECT_TRUE(test_utils::ValidateAttestationCertificate(attestationCertificate));
+}
+
+TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
+    vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
+    vector<Certificate> attestationCertificate;
+    vector<uint8_t> attestationApplicationId = {};
+
+    result = writableCredential->getAttestationCertificate(
+            attestationApplicationId, attestationChallenge, &attestationCertificate);
+
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    EXPECT_TRUE(test_utils::ValidateAttestationCertificate(attestationCertificate));
+}
+
+TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    string challenge = "NotSoRandomChallenge1";
+    test_utils::AttestationData attData(writableCredential, challenge, {});
+    ASSERT_TRUE(test_utils::ValidateAttestationCertificate(attData.attestationCertificate));
+
+    string challenge2 = "NotSoRandomChallenge2";
+    test_utils::AttestationData attData2(writableCredential, challenge2, {});
+    EXPECT_FALSE(attData2.result.isOk()) << attData2.result.exceptionCode() << "; "
+                                         << attData2.result.exceptionMessage() << endl;
+    EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, attData2.result.exceptionCode());
+    EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, attData2.result.serviceSpecificErrorCode());
+}
+
+TEST_P(IdentityCredentialTests, verifyStartPersonalization) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    // First call should go through
+    const vector<int32_t> entryCounts = {2, 4};
+    result = writableCredential->startPersonalization(5, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    // Call personalization again to check if repeat call is allowed.
+    result = writableCredential->startPersonalization(7, entryCounts);
+
+    // Second call to startPersonalization should have failed.
+    EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                                << endl;
+    EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+    EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
+}
+
+TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    // Verify minimal number of profile count and entry count
+    const vector<int32_t> entryCounts = {1, 1};
+    writableCredential->startPersonalization(1, entryCounts);
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+}
+
+TEST_P(IdentityCredentialTests, verifyStartPersonalizationZero) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    const vector<int32_t> entryCounts = {0};
+    writableCredential->startPersonalization(0, entryCounts);
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+}
+
+TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    // Verify minimal number of profile count and entry count
+    const vector<int32_t> entryCounts = {1};
+    writableCredential->startPersonalization(1, entryCounts);
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+}
+
+TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    // Verify set a large number of profile count and entry count is ok
+    const vector<int32_t> entryCounts = {3000};
+    writableCredential->startPersonalization(3500, entryCounts);
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+}
+
+TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    // Enter mismatched entry and profile numbers
+    const vector<int32_t> entryCounts = {5, 6};
+    writableCredential->startPersonalization(5, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<uint8_t>> readerCertificate = test_utils::GenerateReaderCertificate("12345");
+    ASSERT_TRUE(readerCertificate);
+
+    const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
+                                                          {1, readerCertificate.value(), false, 0},
+                                                          {2, readerCertificate.value(), true, 1},
+                                                          // Profile 4 (no authentication)
+                                                          {4, {}, false, 0}};
+
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    result =
+            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+
+    // finishAddingEntries should fail because the number of addAccessControlProfile mismatched with
+    // startPersonalization, and begintest_utils::AddEntry was not called.
+    EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                                << endl;
+    EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+    EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
+}
+
+TEST_P(IdentityCredentialTests, verifyDuplicateProfileId) {
+    Status result;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    const vector<int32_t> entryCounts = {3, 6};
+    writableCredential->startPersonalization(3, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    const vector<test_utils::TestProfile> testProfiles = {// first profile should go though
+                                                          {1, {}, true, 2},
+                                                          // same id, different
+                                                          // authentication requirement
+                                                          {1, {}, true, 1},
+                                                          // same id, different certificate
+                                                          {1, {}, false, 0}};
+
+    bool expectOk = true;
+    for (const auto& testProfile : testProfiles) {
+        SecureAccessControlProfile profile;
+        Certificate cert;
+        cert.encodedCertificate = testProfile.readerCertificate;
+        result = writableCredential->addAccessControlProfile(
+                testProfile.id, cert, testProfile.userAuthenticationRequired,
+                testProfile.timeoutMillis, 0, &profile);
+
+        if (expectOk) {
+            expectOk = false;
+            // for profile should be allowed though as there are no duplications
+            // yet.
+            ASSERT_TRUE(result.isOk())
+                    << result.exceptionCode() << "; " << result.exceptionMessage()
+                    << "test profile id = " << testProfile.id << endl;
+
+            ASSERT_EQ(testProfile.id, profile.id);
+            ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
+            ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+            ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+            ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+        } else {
+            // should not allow duplicate id profiles.
+            ASSERT_FALSE(result.isOk())
+                    << result.exceptionCode() << "; " << result.exceptionMessage()
+                    << ". Test profile id = " << testProfile.id
+                    << ", timeout=" << testProfile.timeoutMillis << endl;
+            ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+            ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA,
+                      result.serviceSpecificErrorCode());
+        }
+    }
+}
+
+TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) {
+    Status result;
+
+    HardwareInformation hwInfo;
+    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    string challenge = "NotSoRandomChallenge1";
+    test_utils::AttestationData attData(writableCredential, challenge, {});
+    EXPECT_TRUE(attData.result.isOk())
+            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
+
+    const vector<int32_t> entryCounts = {1u};
+    writableCredential->startPersonalization(1, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<uint8_t>> readerCertificate1 = test_utils::GenerateReaderCertificate("123456");
+    ASSERT_TRUE(readerCertificate1);
+
+    const vector<test_utils::TestProfile> testProfiles = {{1, readerCertificate1.value(), true, 1}};
+
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
+
+    const vector<test_utils::TestEntryData> testEntries1 = {
+            {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+    };
+
+    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+    for (const auto& entry : testEntries1) {
+        ASSERT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                         encryptedBlobs, true));
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    result =
+            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    string cborPretty =
+            support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "      'readerCertificate' : <not printed>,\n"
+            "      'userAuthenticationRequired' : true,\n"
+            "      'timeoutMillis' : 1,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'Name Space' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Turing',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+
+    optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
+            attData.attestationCertificate[0].encodedCertificate);
+    ASSERT_TRUE(credentialPubKey);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey.value()));
+}
+
+TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) {
+    Status result;
+
+    HardwareInformation hwInfo;
+    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    string challenge = "NotSoRandomChallenge";
+    test_utils::AttestationData attData(writableCredential, challenge, {});
+    EXPECT_TRUE(attData.result.isOk())
+            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
+
+    optional<vector<uint8_t>> readerCertificate1 = test_utils::GenerateReaderCertificate("123456");
+    ASSERT_TRUE(readerCertificate1);
+
+    optional<vector<uint8_t>> readerCertificate2 = test_utils::GenerateReaderCertificate("1256");
+    ASSERT_TRUE(readerCertificate2);
+
+    const vector<test_utils::TestProfile> testProfiles = {
+            {1, readerCertificate1.value(), true, 1},
+            {2, readerCertificate2.value(), true, 2},
+    };
+    const vector<int32_t> entryCounts = {1u, 3u, 1u, 1u, 2u};
+    writableCredential->startPersonalization(testProfiles.size(), entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
+
+    vector<uint8_t> portraitImage1;
+    test_utils::SetImageData(portraitImage1);
+
+    vector<uint8_t> portraitImage2;
+    test_utils::SetImageData(portraitImage2);
+
+    const vector<test_utils::TestEntryData> testEntries1 = {
+            {"Name Space 1", "Last name", string("Turing"), vector<int32_t>{1, 2}},
+            {"Name Space2", "Home address", string("Maida Vale, London, England"),
+             vector<int32_t>{1}},
+            {"Name Space2", "Work address", string("Maida Vale2, London, England"),
+             vector<int32_t>{2}},
+            {"Name Space2", "Trailer address", string("Maida, London, England"),
+             vector<int32_t>{1}},
+            {"Image", "Portrait image", portraitImage1, vector<int32_t>{1}},
+            {"Image2", "Work image", portraitImage2, vector<int32_t>{1, 2}},
+            {"Name Space3", "xyzw", string("random stuff"), vector<int32_t>{1, 2}},
+            {"Name Space3", "Something", string("Some string"), vector<int32_t>{2}},
+    };
+
+    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+    for (const auto& entry : testEntries1) {
+        EXPECT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                         encryptedBlobs, true));
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    result =
+            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(),
+                                                 32,  //
+                                                 {"readerCertificate"});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "      'readerCertificate' : <not printed>,\n"
+            "      'userAuthenticationRequired' : true,\n"
+            "      'timeoutMillis' : 1,\n"
+            "    },\n"
+            "    {\n"
+            "      'id' : 2,\n"
+            "      'readerCertificate' : <not printed>,\n"
+            "      'userAuthenticationRequired' : true,\n"
+            "      'timeoutMillis' : 2,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'Name Space 1' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Turing',\n"
+            "        'accessControlProfiles' : [1, 2, ],\n"
+            "      },\n"
+            "    ],\n"
+            "    'Name Space2' : [\n"
+            "      {\n"
+            "        'name' : 'Home address',\n"
+            "        'value' : 'Maida Vale, London, England',\n"
+            "        'accessControlProfiles' : [1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Work address',\n"
+            "        'value' : 'Maida Vale2, London, England',\n"
+            "        'accessControlProfiles' : [2, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Trailer address',\n"
+            "        'value' : 'Maida, London, England',\n"
+            "        'accessControlProfiles' : [1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "    'Image' : [\n"
+            "      {\n"
+            "        'name' : 'Portrait image',\n"
+            "        'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+            "        'accessControlProfiles' : [1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "    'Image2' : [\n"
+            "      {\n"
+            "        'name' : 'Work image',\n"
+            "        'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+            "        'accessControlProfiles' : [1, 2, ],\n"
+            "      },\n"
+            "    ],\n"
+            "    'Name Space3' : [\n"
+            "      {\n"
+            "        'name' : 'xyzw',\n"
+            "        'value' : 'random stuff',\n"
+            "        'accessControlProfiles' : [1, 2, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Something',\n"
+            "        'value' : 'Some string',\n"
+            "        'accessControlProfiles' : [2, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+
+    optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
+            attData.attestationCertificate[0].encodedCertificate);
+    ASSERT_TRUE(credentialPubKey);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey.value()));
+}
+
+TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) {
+    Status result;
+
+    HardwareInformation hwInfo;
+    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    string challenge = "NotSoRandomChallenge";
+    test_utils::AttestationData attData(writableCredential, challenge, {});
+    ASSERT_TRUE(attData.result.isOk())
+            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
+
+    const vector<int32_t> entryCounts = {2u, 2u};
+    writableCredential->startPersonalization(3, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<uint8_t>> readerCertificate1 = test_utils::GenerateReaderCertificate("123456");
+    ASSERT_TRUE(readerCertificate1);
+
+    optional<vector<uint8_t>> readerCertificate2 =
+            test_utils::GenerateReaderCertificate("123456987987987987987987");
+    ASSERT_TRUE(readerCertificate2);
+
+    const vector<test_utils::TestProfile> testProfiles = {{0, readerCertificate1.value(), false, 0},
+                                                          {1, readerCertificate2.value(), true, 1},
+                                                          {2, {}, false, 0}};
+
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
+
+    const vector<test_utils::TestEntryData> testEntries1 = {
+            // test empty name space
+            {"", "t name", string("Turing"), vector<int32_t>{2}},
+            {"", "Birth", string("19120623"), vector<int32_t>{2}},
+            {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+            {"Name Space", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
+    };
+
+    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+    for (const auto& entry : testEntries1) {
+        EXPECT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                         encryptedBlobs, true));
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    result =
+            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+
+    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+}
+
+TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) {
+    Status result;
+
+    HardwareInformation hwInfo;
+    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_));
+
+    string challenge = "NotSoRandomChallenge";
+    test_utils::AttestationData attData(writableCredential, challenge, {});
+    ASSERT_TRUE(attData.result.isOk())
+            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
+
+    // Enter mismatched entry and profile numbers.
+    // Technically the 2nd name space of "Name Space" occurs intermittently, 2
+    // before "Image" and 2 after image, which is not correct.  All of same name
+    // space should occur together.  Let's see if this fails.
+    const vector<int32_t> entryCounts = {2u, 1u, 2u};
+    writableCredential->startPersonalization(3, entryCounts);
+    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                               << endl;
+
+    optional<vector<uint8_t>> readerCertificate1 = test_utils::GenerateReaderCertificate("123456");
+    ASSERT_TRUE(readerCertificate1);
+
+    optional<vector<uint8_t>> readerCertificate2 =
+            test_utils::GenerateReaderCertificate("123456987987987987987987");
+    ASSERT_TRUE(readerCertificate2);
+
+    const vector<test_utils::TestProfile> testProfiles = {{0, readerCertificate1.value(), false, 0},
+                                                          {1, readerCertificate2.value(), true, 1},
+                                                          {2, {}, false, 0}};
+
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::AddAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
+
+    const vector<test_utils::TestEntryData> testEntries1 = {
+            // test empty name space
+            {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+            {"Name Space", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
+    };
+
+    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+    for (const auto& entry : testEntries1) {
+        EXPECT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                         encryptedBlobs, true));
+    }
+    const test_utils::TestEntryData testEntry2 = {"Image", "Portrait image", string("asdfs"),
+                                                  vector<int32_t>{0, 1}};
+
+    EXPECT_TRUE(test_utils::AddEntry(writableCredential, testEntry2, hwInfo.dataChunkSize,
+                                     encryptedBlobs, true));
+
+    // We expect this to fail because the namespace is out of order, all "Name Space"
+    // should have been called together
+    const vector<test_utils::TestEntryData> testEntries3 = {
+            {"Name Space", "First name", string("Alan"), vector<int32_t>{0, 1}},
+            {"Name Space", "Home address", string("Maida Vale, London, England"),
+             vector<int32_t>{0}},
+    };
+
+    for (const auto& entry : testEntries3) {
+        EXPECT_FALSE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                          encryptedBlobs, false));
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    result =
+            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+
+    // should fail because test_utils::AddEntry should have failed earlier.
+    EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                                << endl;
+    EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+    EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        Identity, IdentityCredentialTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
new file mode 100644
index 0000000..3aeebc6
--- /dev/null
+++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2019, 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 "VtsIdentityTestUtils.h"
+
+#include <aidl/Gtest.h>
+#include <map>
+
+namespace android::hardware::identity::test_utils {
+
+using std::endl;
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+bool SetupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
+                             sp<IIdentityCredentialStore>& credentialStore) {
+    if (credentialStore == nullptr) {
+        return false;
+    }
+
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    Status result = credentialStore->createCredential(docType, testCredential, &writableCredential);
+
+    if (result.isOk() && writableCredential != nullptr) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+optional<vector<uint8_t>> GenerateReaderCertificate(string serialDecimal) {
+    vector<uint8_t> privKey;
+    return GenerateReaderCertificate(serialDecimal, privKey);
+}
+
+optional<vector<uint8_t>> GenerateReaderCertificate(string serialDecimal,
+                                                    vector<uint8_t>& readerPrivateKey) {
+    optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
+    if (!readerKeyPKCS8) {
+        return {};
+    }
+
+    optional<vector<uint8_t>> readerPublicKey =
+            support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
+    optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
+    if (!readerPublicKey || !readerKey) {
+        return {};
+    }
+
+    readerPrivateKey = readerKey.value();
+
+    string issuer = "Android Open Source Project";
+    string subject = "Android IdentityCredential VTS Test";
+    time_t validityNotBefore = time(nullptr);
+    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+    return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
+                                                   serialDecimal, issuer, subject,
+                                                   validityNotBefore, validityNotAfter);
+}
+
+optional<vector<SecureAccessControlProfile>> AddAccessControlProfiles(
+        sp<IWritableIdentityCredential>& writableCredential,
+        const vector<TestProfile>& testProfiles) {
+    Status result;
+
+    vector<SecureAccessControlProfile> secureProfiles;
+
+    for (const auto& testProfile : testProfiles) {
+        SecureAccessControlProfile profile;
+        Certificate cert;
+        cert.encodedCertificate = testProfile.readerCertificate;
+        result = writableCredential->addAccessControlProfile(
+                testProfile.id, cert, testProfile.userAuthenticationRequired,
+                testProfile.timeoutMillis, 0, &profile);
+
+        // Don't use assert so all errors can be outputed.  Then return
+        // instead of exit even on errors so caller can decide.
+        EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                                   << "test profile id = " << testProfile.id << endl;
+        EXPECT_EQ(testProfile.id, profile.id);
+        EXPECT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
+        EXPECT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+        EXPECT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+        EXPECT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+
+        if (!result.isOk() || testProfile.id != profile.id ||
+            testProfile.readerCertificate != profile.readerCertificate.encodedCertificate ||
+            testProfile.userAuthenticationRequired != profile.userAuthenticationRequired ||
+            testProfile.timeoutMillis != profile.timeoutMillis ||
+            support::kAesGcmTagSize + support::kAesGcmIvSize != profile.mac.size()) {
+            return {};
+        }
+
+        secureProfiles.push_back(profile);
+    }
+
+    return secureProfiles;
+}
+
+// Most test expects this function to pass. So we will print out additional
+// value if failed so more debug data can be provided.
+bool AddEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEntryData& entry,
+              int dataChunkSize, map<const TestEntryData*, vector<vector<uint8_t>>>& encryptedBlobs,
+              bool expectSuccess) {
+    Status result;
+    vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize);
+
+    result = writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
+                                               entry.valueCbor.size());
+
+    if (expectSuccess) {
+        EXPECT_TRUE(result.isOk())
+                << result.exceptionCode() << "; " << result.exceptionMessage() << endl
+                << "entry name = " << entry.name << ", name space=" << entry.nameSpace << endl;
+    }
+
+    if (!result.isOk()) {
+        return false;
+    }
+
+    vector<vector<uint8_t>> encryptedChunks;
+    for (const auto& chunk : chunks) {
+        vector<uint8_t> encryptedContent;
+        result = writableCredential->addEntryValue(chunk, &encryptedContent);
+        if (expectSuccess) {
+            EXPECT_TRUE(result.isOk())
+                    << result.exceptionCode() << "; " << result.exceptionMessage() << endl
+                    << "entry name = " << entry.name << ", name space = " << entry.nameSpace
+                    << endl;
+
+            EXPECT_GT(encryptedContent.size(), 0u) << "entry name = " << entry.name
+                                                   << ", name space = " << entry.nameSpace << endl;
+        }
+
+        if (!result.isOk() || encryptedContent.size() <= 0u) {
+            return false;
+        }
+
+        encryptedChunks.push_back(encryptedContent);
+    }
+
+    encryptedBlobs[&entry] = encryptedChunks;
+    return true;
+}
+
+bool ValidateAttestationCertificate(vector<Certificate>& inputCertificates) {
+    return (inputCertificates.size() >= 2);
+    // TODO: add parsing of the certificate and make sure it is genuine.
+}
+
+void SetImageData(vector<uint8_t>& image) {
+    image.resize(256 * 1024 - 10);
+    for (size_t n = 0; n < image.size(); n++) {
+        image[n] = (uint8_t)n;
+    }
+}
+
+}  // namespace android::hardware::identity::test_utils
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h
new file mode 100644
index 0000000..043ccd6
--- /dev/null
+++ b/identity/aidl/vts/VtsIdentityTestUtils.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VTS_IDENTITY_TEST_UTILS_H
+#define VTS_IDENTITY_TEST_UTILS_H
+
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace android::hardware::identity::test_utils {
+
+using ::std::map;
+using ::std::optional;
+using ::std::string;
+using ::std::vector;
+
+using ::android::sp;
+using ::android::binder::Status;
+
+struct AttestationData {
+    AttestationData(sp<IWritableIdentityCredential>& writableCredential, string challenge,
+                    vector<uint8_t> applicationId)
+        : attestationApplicationId(applicationId) {
+        // ASSERT_NE(writableCredential, nullptr);
+
+        if (!challenge.empty()) {
+            attestationChallenge.assign(challenge.begin(), challenge.end());
+        }
+
+        result = writableCredential->getAttestationCertificate(
+                attestationApplicationId, attestationChallenge, &attestationCertificate);
+    }
+
+    AttestationData() {}
+
+    vector<uint8_t> attestationChallenge;
+    vector<uint8_t> attestationApplicationId;
+    vector<Certificate> attestationCertificate;
+    Status result;
+};
+
+struct TestEntryData {
+    TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
+        : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
+
+    TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
+    }
+    TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
+                  vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Bstr(value).encode();
+    }
+    TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Bool(value).encode();
+    }
+    TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        if (value >= 0) {
+            valueCbor = cppbor::Uint(value).encode();
+        } else {
+            valueCbor = cppbor::Nint(-value).encode();
+        }
+    }
+
+    string nameSpace;
+    string name;
+    vector<uint8_t> valueCbor;
+    vector<int32_t> profileIds;
+};
+
+struct TestProfile {
+    uint16_t id;
+    vector<uint8_t> readerCertificate;
+    bool userAuthenticationRequired;
+    uint64_t timeoutMillis;
+};
+
+bool SetupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
+                             sp<IIdentityCredentialStore>& credentialStore);
+
+optional<vector<uint8_t>> GenerateReaderCertificate(string serialDecimal);
+
+optional<vector<uint8_t>> GenerateReaderCertificate(string serialDecimal,
+                                                    vector<uint8_t>& readerPrivateKey);
+
+optional<vector<SecureAccessControlProfile>> AddAccessControlProfiles(
+        sp<IWritableIdentityCredential>& writableCredential,
+        const vector<TestProfile>& testProfiles);
+
+bool AddEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEntryData& entry,
+              int dataChunkSize, map<const TestEntryData*, vector<vector<uint8_t>>>& encryptedBlobs,
+              bool expectSuccess);
+
+bool ValidateAttestationCertificate(vector<Certificate>& inputCertificates);
+
+void SetImageData(vector<uint8_t>& image);
+
+}  // namespace android::hardware::identity::test_utils
+
+#endif  // VTS_IDENTITY_TEST_UTILS_H
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index bf6a5c3..dc49ddc 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -958,12 +958,17 @@
 optional<vector<uint8_t>> createEcKeyPair() {
     auto ec_key = EC_KEY_Ptr(EC_KEY_new());
     auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
-    auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
     if (ec_key.get() == nullptr || pkey.get() == nullptr) {
         LOG(ERROR) << "Memory allocation failed";
         return {};
     }
 
+    auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+    if (group.get() == nullptr) {
+        LOG(ERROR) << "Error creating EC group by curve name";
+        return {};
+    }
+
     if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
         EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
         LOG(ERROR) << "Error generating key";
diff --git a/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp b/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp
index ba08ee7..7e0ae9c 100644
--- a/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp
+++ b/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp
@@ -19,6 +19,8 @@
 
 #include <cutils/properties.h>
 
+#include <android-base/file.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/power/1.0/IPower.h>
 #include <gtest/gtest.h>
@@ -73,26 +75,16 @@
 TEST_P(PowerHidlTest, TryDifferentGovernors) {
   Return<void> ret;
 
-  unique_fd fd1(open(CPU_GOVERNOR_PATH, O_RDWR));
-  unique_fd fd2(open(AVAILABLE_GOVERNORS_PATH, O_RDONLY));
-  if (fd1 < 0 || fd2 < 0) {
+  std::string old_governor, governors;
+  if (!android::base::ReadFileToString(CPU_GOVERNOR_PATH, &old_governor) ||
+      !android::base::ReadFileToString(AVAILABLE_GOVERNORS_PATH, &governors)) {
     // Files don't exist, so skip the rest of the test case
     SUCCEED();
     return;
   }
-
-  char old_governor[80];
-  ASSERT_LE(0, read(fd1, old_governor, 80));
-
-  char governors[1024];
-  unsigned len = read(fd2, governors, 1024);
-  ASSERT_LE(0u, len);
-  governors[len] = '\0';
-
-  char *saveptr;
-  char *name = strtok_r(governors, " \n", &saveptr);
-  while (name) {
-    ASSERT_LE(0, write(fd1, name, strlen(name)));
+  auto all_governors = android::base::Split(governors, " \n");
+  for (const auto &governor : all_governors) {
+      ASSERT_TRUE(android::base::WriteStringToFile(governor, CPU_GOVERNOR_PATH));
     ret = power->setInteractive(true);
     ASSERT_TRUE(ret.isOk());
 
@@ -104,11 +96,9 @@
 
     power->powerHint(PowerHint::LAUNCH, 1);
     power->powerHint(PowerHint::LAUNCH, 0);
-
-    name = strtok_r(NULL, " \n", &saveptr);
   }
 
-  ASSERT_LE(0, write(fd1, old_governor, strlen(old_governor)));
+  ASSERT_TRUE(android::base::WriteStringToFile(old_governor, CPU_GOVERNOR_PATH));
 }
 
 // Sanity check Power::powerHint on good and bad inputs.
diff --git a/secure_element/1.0/vts/OWNERS b/secure_element/1.0/vts/OWNERS
new file mode 100644
index 0000000..c7963e7
--- /dev/null
+++ b/secure_element/1.0/vts/OWNERS
@@ -0,0 +1,4 @@
+alisher@google.com
+jackcwyu@google.com
+georgekgchang@google.com
+zachoverflow@google.com
diff --git a/secure_element/1.1/vts/OWNERS b/secure_element/1.1/vts/OWNERS
new file mode 100644
index 0000000..c7963e7
--- /dev/null
+++ b/secure_element/1.1/vts/OWNERS
@@ -0,0 +1,4 @@
+alisher@google.com
+jackcwyu@google.com
+georgekgchang@google.com
+zachoverflow@google.com
diff --git a/secure_element/1.2/vts/OWNERS b/secure_element/1.2/vts/OWNERS
new file mode 100644
index 0000000..c7963e7
--- /dev/null
+++ b/secure_element/1.2/vts/OWNERS
@@ -0,0 +1,4 @@
+alisher@google.com
+jackcwyu@google.com
+georgekgchang@google.com
+zachoverflow@google.com
diff --git a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
index 98e4502..9392f14 100644
--- a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
+++ b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
@@ -85,6 +85,7 @@
  * Reset:
  * Calls reset()
  * Checks status
+ * Check onStateChange is received with connected state set to false
  * Check onStateChange is received with connected state set to true
  */
 TEST_P(SecureElementHidlTest, Reset) {
@@ -92,6 +93,10 @@
 
     auto res = se_cb_->WaitForCallback(kCallbackNameOnStateChange);
     EXPECT_TRUE(res.no_timeout);
+    EXPECT_FALSE(res.args->state_);
+
+    res = se_cb_->WaitForCallback(kCallbackNameOnStateChange);
+    EXPECT_TRUE(res.no_timeout);
     EXPECT_TRUE(res.args->state_);
 }
 
diff --git a/tests/lazy/1.0/.hidl_for_test b/tests/lazy/1.0/.hidl_for_test
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/lazy/1.0/.hidl_for_test
diff --git a/tests/lazy/1.0/Android.bp b/tests/lazy/1.0/Android.bp
new file mode 100644
index 0000000..d2f8175
--- /dev/null
+++ b/tests/lazy/1.0/Android.bp
@@ -0,0 +1,14 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.tests.lazy@1.0",
+    root: "android.hardware",
+    system_ext_specific: true,
+    srcs: [
+        "ILazy.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/tests/lazy/1.0/ILazy.hal b/tests/lazy/1.0/ILazy.hal
new file mode 100644
index 0000000..b0be48e
--- /dev/null
+++ b/tests/lazy/1.0/ILazy.hal
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.hardware.tests.lazy@1.0;
+
+interface ILazy {};
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 43c4e3a..95b4ebc 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -52,7 +52,7 @@
 
     mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
 
-    return startFrontendInputLoop();
+    return Result::SUCCESS;
 }
 
 Return<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
@@ -60,7 +60,6 @@
     ALOGV("%s", __FUNCTION__);
 
     uint32_t filterId;
-
     if (!mUnusedFilterIds.empty()) {
         filterId = *mUnusedFilterIds.begin();
 
@@ -83,7 +82,6 @@
         _hidl_cb(Result::UNKNOWN_ERROR, filter);
         return Void();
     }
-
     mFilters[filterId] = filter;
 
     _hidl_cb(Result::SUCCESS, filter);
@@ -234,11 +232,9 @@
     return mFilters[filterId]->getTpid();
 }
 
-Result Demux::startFrontendInputLoop() {
+void Demux::startFrontendInputLoop() {
     pthread_create(&mFrontendInputThread, NULL, __threadLoopFrontend, this);
     pthread_setname_np(mFrontendInputThread, "frontend_input_thread");
-
-    return Result::SUCCESS;
 }
 
 void* Demux::__threadLoopFrontend(void* user) {
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 1405d0c..759e348 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -89,6 +89,7 @@
     void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
     uint16_t getFilterTpid(uint32_t filterId);
     void setIsRecording(bool isRecording);
+    void startFrontendInputLoop();
 
   private:
     // Tuner service
@@ -104,7 +105,6 @@
         uint32_t filterId;
     };
 
-    Result startFrontendInputLoop();
     static void* __threadLoopFrontend(void* user);
     void frontendInputThreadLoop();
 
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index ae5f587..b509599 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -64,6 +64,7 @@
         return Result::INVALID_STATE;
     }
 
+    mTunerService->frontendStartTune(mId);
     mCallback->onEvent(FrontendEventType::LOCKED);
     mIsLocked = false;
     return Result::SUCCESS;
diff --git a/tv/tuner/1.0/default/Lnb.cpp b/tv/tuner/1.0/default/Lnb.cpp
index 51931d6..6025339 100644
--- a/tv/tuner/1.0/default/Lnb.cpp
+++ b/tv/tuner/1.0/default/Lnb.cpp
@@ -27,6 +27,9 @@
 namespace implementation {
 
 Lnb::Lnb() {}
+Lnb::Lnb(int id) {
+    mId = id;
+}
 
 Lnb::~Lnb() {}
 
@@ -66,9 +69,13 @@
     return Result::SUCCESS;
 }
 
+int Lnb::getId() {
+    return mId;
+}
+
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
 }  // namespace tv
 }  // namespace hardware
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/tv/tuner/1.0/default/Lnb.h b/tv/tuner/1.0/default/Lnb.h
index f285cb9..1e97214 100644
--- a/tv/tuner/1.0/default/Lnb.h
+++ b/tv/tuner/1.0/default/Lnb.h
@@ -38,6 +38,7 @@
 class Lnb : public ILnb {
   public:
     Lnb();
+    Lnb(int id);
 
     virtual Return<Result> setCallback(const sp<ILnbCallback>& callback) override;
 
@@ -51,7 +52,10 @@
 
     virtual Return<Result> close() override;
 
+    int getId();
+
   private:
+    int mId;
     virtual ~Lnb();
 };
 
@@ -62,4 +66,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_TV_TUNER_V1_0_LNB_H_
\ No newline at end of file
+#endif  // ANDROID_HARDWARE_TV_TUNER_V1_0_LNB_H_
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index e39333c..b1f2490 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -88,6 +88,10 @@
     caps = FrontendInfo::FrontendCapabilities();
     caps.atscCaps(FrontendAtscCapabilities());
     mFrontendCaps[7] = caps;
+
+    mLnbs.resize(2);
+    mLnbs[0] = new Lnb(0);
+    mLnbs[1] = new Lnb(1);
 }
 
 Tuner::~Tuner() {}
@@ -194,17 +198,24 @@
     ALOGV("%s", __FUNCTION__);
 
     vector<LnbId> lnbIds;
+    lnbIds.resize(mLnbs.size());
+    for (int i = 0; i < lnbIds.size(); i++) {
+        lnbIds[i] = mLnbs[i]->getId();
+    }
 
     _hidl_cb(Result::SUCCESS, lnbIds);
     return Void();
 }
 
-Return<void> Tuner::openLnbById(LnbId /* lnbId */, openLnbById_cb _hidl_cb) {
+Return<void> Tuner::openLnbById(LnbId lnbId, openLnbById_cb _hidl_cb) {
     ALOGV("%s", __FUNCTION__);
 
-    sp<ILnb> lnb = new Lnb();
+    if (lnbId >= mLnbs.size()) {
+        _hidl_cb(Result::INVALID_ARGUMENT, nullptr);
+        return Void();
+    }
 
-    _hidl_cb(Result::SUCCESS, lnb);
+    _hidl_cb(Result::SUCCESS, mLnbs[lnbId]);
     return Void();
 }
 
@@ -236,6 +247,15 @@
     }
 }
 
+void Tuner::frontendStartTune(uint32_t frontendId) {
+    map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
+    uint32_t demuxId;
+    if (it != mFrontendToDemux.end()) {
+        demuxId = it->second;
+        mDemuxes[demuxId]->startFrontendInputLoop();
+    }
+}
+
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.0/default/Tuner.h b/tv/tuner/1.0/default/Tuner.h
index d17b5ae..5de568f 100644
--- a/tv/tuner/1.0/default/Tuner.h
+++ b/tv/tuner/1.0/default/Tuner.h
@@ -21,6 +21,7 @@
 #include <map>
 #include "Demux.h"
 #include "Frontend.h"
+#include "Lnb.h"
 
 using namespace std;
 
@@ -62,6 +63,7 @@
 
     void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId);
 
+    void frontendStartTune(uint32_t frontendId);
     void frontendStopTune(uint32_t frontendId);
 
   private:
@@ -76,6 +78,7 @@
     // The last used demux id. Initial value is -1.
     // First used id will be 0.
     int mLastUsedId = -1;
+    vector<sp<Lnb>> mLnbs;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 448575e..b152a29 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -22,6 +22,7 @@
         "FrontendTests.cpp",
         "DemuxTests.cpp",
         "FilterTests.cpp",
+        "DvrTests.cpp",
     ],
     static_libs: [
         "android.hardware.tv.tuner@1.0",
diff --git a/tv/tuner/1.0/vts/functional/DemuxTests.h b/tv/tuner/1.0/vts/functional/DemuxTests.h
index a72c09f..6e1e395 100644
--- a/tv/tuner/1.0/vts/functional/DemuxTests.h
+++ b/tv/tuner/1.0/vts/functional/DemuxTests.h
@@ -38,8 +38,6 @@
 
 class DemuxTests {
   public:
-    sp<ITuner> mService;
-
     void setService(sp<ITuner> tuner) { mService = tuner; }
 
     AssertionResult openDemux(sp<IDemux>& demux, uint32_t& demuxId);
@@ -51,5 +49,6 @@
 
     static AssertionResult success() { return ::testing::AssertionSuccess(); }
 
+    sp<ITuner> mService;
     sp<IDemux> mDemux;
 };
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
new file mode 100644
index 0000000..a1ce23d
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2020 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 "DvrTests.h"
+
+void DvrCallback::startPlaybackInputThread(PlaybackConf playbackConf,
+                                           MQDesc& playbackMQDescriptor) {
+    mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
+    EXPECT_TRUE(mPlaybackMQ);
+    struct PlaybackThreadArgs* threadArgs =
+            (struct PlaybackThreadArgs*)malloc(sizeof(struct PlaybackThreadArgs));
+    threadArgs->user = this;
+    threadArgs->playbackConf = &playbackConf;
+    threadArgs->keepWritingPlaybackFMQ = &mKeepWritingPlaybackFMQ;
+
+    pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, (void*)threadArgs);
+    pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
+}
+
+void DvrCallback::stopPlaybackThread() {
+    mPlaybackThreadRunning = false;
+    mKeepWritingPlaybackFMQ = false;
+
+    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
+}
+
+void* DvrCallback::__threadLoopPlayback(void* threadArgs) {
+    DvrCallback* const self =
+            static_cast<DvrCallback*>(((struct PlaybackThreadArgs*)threadArgs)->user);
+    self->playbackThreadLoop(((struct PlaybackThreadArgs*)threadArgs)->playbackConf,
+                             ((struct PlaybackThreadArgs*)threadArgs)->keepWritingPlaybackFMQ);
+    return 0;
+}
+
+void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ) {
+    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
+    mPlaybackThreadRunning = true;
+
+    // Create the EventFlag that is used to signal the HAL impl that data have been
+    // written into the Playback FMQ
+    EventFlag* playbackMQEventFlag;
+    EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
+                android::OK);
+
+    // open the stream and get its length
+    std::ifstream inputData(playbackConf->inputDataFile, std::ifstream::binary);
+    int writeSize = playbackConf->setting.packetSize * 6;
+    char* buffer = new char[writeSize];
+    ALOGW("[vts] playback thread loop start %s", playbackConf->inputDataFile.c_str());
+    if (!inputData.is_open()) {
+        mPlaybackThreadRunning = false;
+        ALOGW("[vts] Error %s", strerror(errno));
+    }
+
+    while (mPlaybackThreadRunning) {
+        // move the stream pointer for packet size * 6 every read until the end
+        while (*keepWritingPlaybackFMQ) {
+            inputData.read(buffer, writeSize);
+            if (!inputData) {
+                int leftSize = inputData.gcount();
+                if (leftSize == 0) {
+                    mPlaybackThreadRunning = false;
+                    break;
+                }
+                inputData.clear();
+                inputData.read(buffer, leftSize);
+                // Write the left over of the input data and quit the thread
+                if (leftSize > 0) {
+                    EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], leftSize));
+                    playbackMQEventFlag->wake(
+                            static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+                }
+                mPlaybackThreadRunning = false;
+                break;
+            }
+            // Write input FMQ and notify the Tuner Implementation
+            EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
+            playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+            inputData.seekg(writeSize, inputData.cur);
+            sleep(1);
+        }
+    }
+
+    ALOGW("[vts] Playback thread end.");
+
+    delete[] buffer;
+    inputData.close();
+}
+
+void DvrCallback::testRecordOutput() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (mDataOutputBuffer.empty()) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
+            return;
+        }
+    }
+    stopRecordThread();
+    ALOGW("[vts] record pass and stop");
+}
+
+void DvrCallback::startRecordOutputThread(RecordSettings recordSettings,
+                                          MQDesc& recordMQDescriptor) {
+    mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */);
+    EXPECT_TRUE(mRecordMQ);
+    struct RecordThreadArgs* threadArgs =
+            (struct RecordThreadArgs*)malloc(sizeof(struct RecordThreadArgs));
+    threadArgs->user = this;
+    threadArgs->recordSettings = &recordSettings;
+    threadArgs->keepReadingRecordFMQ = &mKeepReadingRecordFMQ;
+
+    pthread_create(&mRecordThread, NULL, __threadLoopRecord, (void*)threadArgs);
+    pthread_setname_np(mRecordThread, "test_record_input_loop");
+}
+
+void* DvrCallback::__threadLoopRecord(void* threadArgs) {
+    DvrCallback* const self =
+            static_cast<DvrCallback*>(((struct RecordThreadArgs*)threadArgs)->user);
+    self->recordThreadLoop(((struct RecordThreadArgs*)threadArgs)->recordSettings,
+                           ((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
+    return 0;
+}
+
+void DvrCallback::recordThreadLoop(RecordSettings* /*recordSettings*/, bool* keepReadingRecordFMQ) {
+    ALOGD("[vts] DvrCallback record threadLoop start.");
+    android::Mutex::Autolock autoLock(mRecordThreadLock);
+    mRecordThreadRunning = true;
+
+    // Create the EventFlag that is used to signal the HAL impl that data have been
+    // read from the Record FMQ
+    EventFlag* recordMQEventFlag;
+    EXPECT_TRUE(EventFlag::createEventFlag(mRecordMQ->getEventFlagWord(), &recordMQEventFlag) ==
+                android::OK);
+
+    while (mRecordThreadRunning) {
+        while (*keepReadingRecordFMQ) {
+            uint32_t efState = 0;
+            android::status_t status = recordMQEventFlag->wait(
+                    static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
+                    true /* retry on spurious wake */);
+            if (status != android::OK) {
+                ALOGD("[vts] wait for data ready on the record FMQ");
+                continue;
+            }
+            // Our current implementation filter the data and write it into the filter FMQ
+            // immediately after the DATA_READY from the VTS/framework
+            if (!readRecordFMQ()) {
+                ALOGD("[vts] record data failed to be filtered. Ending thread");
+                mRecordThreadRunning = false;
+                break;
+            }
+        }
+    }
+
+    mRecordThreadRunning = false;
+    ALOGD("[vts] record thread ended.");
+}
+
+bool DvrCallback::readRecordFMQ() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    bool result = false;
+    mDataOutputBuffer.clear();
+    mDataOutputBuffer.resize(mRecordMQ->availableToRead());
+    result = mRecordMQ->read(mDataOutputBuffer.data(), mRecordMQ->availableToRead());
+    EXPECT_TRUE(result) << "can't read from Record MQ";
+    mMsgCondition.signal();
+    return result;
+}
+
+void DvrCallback::stopRecordThread() {
+    mKeepReadingRecordFMQ = false;
+    mRecordThreadRunning = false;
+    android::Mutex::Autolock autoLock(mRecordThreadLock);
+}
+
+AssertionResult DvrTests::openDvrInDemux(DvrType type) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+
+    // Create dvr callback
+    mDvrCallback = new DvrCallback();
+
+    mDemux->openDvr(type, FMQ_SIZE_1M, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
+        mDvr = dvr;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::configDvr(DvrSettings setting) {
+    Result status = mDvr->configure(setting);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::getDvrMQDescriptor() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+        mDvrMQDescriptor = dvrMQDesc;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::attachFilterToDvr(sp<IFilter> filter) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->attachFilter(filter);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::detachFilterToDvr(sp<IFilter> filter) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->detachFilter(filter);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::startDvr() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->start();
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::stopDvr() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    status = mDvr->stop();
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+void DvrTests::closeDvr() {
+    ASSERT_TRUE(mDemux);
+    ASSERT_TRUE(mDvr);
+    ASSERT_TRUE(mDvr->close() == Result::SUCCESS);
+}
\ No newline at end of file
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.h b/tv/tuner/1.0/vts/functional/DvrTests.h
new file mode 100644
index 0000000..74ef58e
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/DvrTests.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2020 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 <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/IDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/Status.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <fstream>
+#include <iostream>
+#include <map>
+
+#include "FilterTests.h"
+
+using android::Condition;
+using android::Mutex;
+using android::sp;
+using android::hardware::EventFlag;
+using android::hardware::kSynchronizedReadWrite;
+using android::hardware::MessageQueue;
+using android::hardware::MQDescriptorSync;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
+using android::hardware::tv::tuner::V1_0::DvrSettings;
+using android::hardware::tv::tuner::V1_0::DvrType;
+using android::hardware::tv::tuner::V1_0::IDvr;
+using android::hardware::tv::tuner::V1_0::IDvrCallback;
+using android::hardware::tv::tuner::V1_0::ITuner;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
+using android::hardware::tv::tuner::V1_0::RecordStatus;
+using android::hardware::tv::tuner::V1_0::Result;
+
+#define WAIT_TIMEOUT 3000000000
+
+struct PlaybackConf {
+    string inputDataFile;
+    PlaybackSettings setting;
+};
+
+class DvrCallback : public IDvrCallback {
+  public:
+    virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
+        ALOGW("[vts] record status %hhu", status);
+        switch (status) {
+            case DemuxFilterStatus::DATA_READY:
+                break;
+            case DemuxFilterStatus::LOW_WATER:
+                break;
+            case DemuxFilterStatus::HIGH_WATER:
+            case DemuxFilterStatus::OVERFLOW:
+                ALOGW("[vts] record overflow. Flushing");
+                break;
+        }
+        return Void();
+    }
+
+    virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
+        // android::Mutex::Autolock autoLock(mMsgLock);
+        ALOGW("[vts] playback status %d", status);
+        switch (status) {
+            case PlaybackStatus::SPACE_EMPTY:
+            case PlaybackStatus::SPACE_ALMOST_EMPTY:
+                ALOGW("[vts] keep playback inputing %d", status);
+                mKeepWritingPlaybackFMQ = true;
+                break;
+            case PlaybackStatus::SPACE_ALMOST_FULL:
+            case PlaybackStatus::SPACE_FULL:
+                ALOGW("[vts] stop playback inputing %d", status);
+                mKeepWritingPlaybackFMQ = false;
+                break;
+        }
+        return Void();
+    }
+
+    void stopPlaybackThread();
+    void testRecordOutput();
+    void stopRecordThread();
+
+    void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
+    void startRecordOutputThread(RecordSettings recordSettings, MQDesc& recordMQDescriptor);
+    static void* __threadLoopPlayback(void* threadArgs);
+    static void* __threadLoopRecord(void* threadArgs);
+    void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
+    void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
+
+    bool readRecordFMQ();
+
+  private:
+    struct PlaybackThreadArgs {
+        DvrCallback* user;
+        PlaybackConf* playbackConf;
+        bool* keepWritingPlaybackFMQ;
+    };
+    struct RecordThreadArgs {
+        DvrCallback* user;
+        RecordSettings* recordSettings;
+        bool* keepReadingRecordFMQ;
+    };
+    // uint16_t mDataLength = 0;
+    std::vector<uint8_t> mDataOutputBuffer;
+
+    std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterMQ;
+    std::unique_ptr<FilterMQ> mPlaybackMQ;
+    std::unique_ptr<FilterMQ> mRecordMQ;
+    std::map<uint32_t, EventFlag*> mFilterMQEventFlag;
+
+    android::Mutex mMsgLock;
+    android::Mutex mPlaybackThreadLock;
+    android::Mutex mRecordThreadLock;
+    android::Condition mMsgCondition;
+
+    bool mKeepWritingPlaybackFMQ = true;
+    bool mKeepReadingRecordFMQ = true;
+    bool mPlaybackThreadRunning;
+    bool mRecordThreadRunning;
+    pthread_t mPlaybackThread;
+    pthread_t mRecordThread;
+
+    // int mPidFilterOutputCount = 0;
+};
+
+class DvrTests {
+  public:
+    void setService(sp<ITuner> tuner) { mService = tuner; }
+    void setDemux(sp<IDemux> demux) { mDemux = demux; }
+
+    void startPlaybackInputThread(string dataInputFile, PlaybackSettings settings) {
+        PlaybackConf conf{
+                .inputDataFile = dataInputFile,
+                .setting = settings,
+        };
+        mDvrCallback->startPlaybackInputThread(conf, mDvrMQDescriptor);
+    };
+
+    void startRecordOutputThread(RecordSettings settings) {
+        mDvrCallback->startRecordOutputThread(settings, mDvrMQDescriptor);
+    };
+
+    void stopPlaybackThread() { mDvrCallback->stopPlaybackThread(); }
+    void testRecordOutput() { mDvrCallback->testRecordOutput(); }
+    void stopRecordThread() { mDvrCallback->stopPlaybackThread(); }
+
+    AssertionResult openDvrInDemux(DvrType type);
+    AssertionResult configDvr(DvrSettings setting);
+    AssertionResult getDvrMQDescriptor();
+    AssertionResult attachFilterToDvr(sp<IFilter> filter);
+    AssertionResult detachFilterToDvr(sp<IFilter> filter);
+    AssertionResult stopDvr();
+    AssertionResult startDvr();
+    void closeDvr();
+
+  protected:
+    static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+    static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+    sp<ITuner> mService;
+    sp<IDvr> mDvr;
+    sp<IDemux> mDemux;
+    sp<DvrCallback> mDvrCallback;
+    MQDesc mDvrMQDescriptor;
+
+    pthread_t mPlaybackshread;
+    bool mPlaybackThreadRunning;
+};
\ No newline at end of file
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index 3cc06e5..dc798c9 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -149,6 +149,7 @@
   public:
     void setService(sp<ITuner> tuner) { mService = tuner; }
     void setDemux(sp<IDemux> demux) { mDemux = demux; }
+    sp<IFilter> getFilterById(uint32_t filterId) { return mFilters[filterId]; }
 
     std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
 
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index d836c26..f211be2 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -17,181 +17,8 @@
 #include "VtsHalTvTunerV1_0TargetTest.h"
 
 namespace {
-
-/******************************** Start DvrCallback **********************************/
-void DvrCallback::startPlaybackInputThread(PlaybackConf playbackConf,
-                                           MQDesc& playbackMQDescriptor) {
-    mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
-    EXPECT_TRUE(mPlaybackMQ);
-    struct PlaybackThreadArgs* threadArgs =
-            (struct PlaybackThreadArgs*)malloc(sizeof(struct PlaybackThreadArgs));
-    threadArgs->user = this;
-    threadArgs->playbackConf = &playbackConf;
-    threadArgs->keepWritingPlaybackFMQ = &mKeepWritingPlaybackFMQ;
-
-    pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, (void*)threadArgs);
-    pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
-}
-
-void DvrCallback::stopPlaybackThread() {
-    mPlaybackThreadRunning = false;
-    mKeepWritingPlaybackFMQ = false;
-
-    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
-}
-
-void* DvrCallback::__threadLoopPlayback(void* threadArgs) {
-    DvrCallback* const self =
-            static_cast<DvrCallback*>(((struct PlaybackThreadArgs*)threadArgs)->user);
-    self->playbackThreadLoop(((struct PlaybackThreadArgs*)threadArgs)->playbackConf,
-                             ((struct PlaybackThreadArgs*)threadArgs)->keepWritingPlaybackFMQ);
-    return 0;
-}
-
-void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ) {
-    android::Mutex::Autolock autoLock(mPlaybackThreadLock);
-    mPlaybackThreadRunning = true;
-
-    // Create the EventFlag that is used to signal the HAL impl that data have been
-    // written into the Playback FMQ
-    EventFlag* playbackMQEventFlag;
-    EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
-                android::OK);
-
-    // open the stream and get its length
-    std::ifstream inputData(playbackConf->inputDataFile, std::ifstream::binary);
-    int writeSize = playbackConf->setting.packetSize * 6;
-    char* buffer = new char[writeSize];
-    ALOGW("[vts] playback thread loop start %s", playbackConf->inputDataFile.c_str());
-    if (!inputData.is_open()) {
-        mPlaybackThreadRunning = false;
-        ALOGW("[vts] Error %s", strerror(errno));
-    }
-
-    while (mPlaybackThreadRunning) {
-        // move the stream pointer for packet size * 6 every read until the end
-        while (*keepWritingPlaybackFMQ) {
-            inputData.read(buffer, writeSize);
-            if (!inputData) {
-                int leftSize = inputData.gcount();
-                if (leftSize == 0) {
-                    mPlaybackThreadRunning = false;
-                    break;
-                }
-                inputData.clear();
-                inputData.read(buffer, leftSize);
-                // Write the left over of the input data and quit the thread
-                if (leftSize > 0) {
-                    EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], leftSize));
-                    playbackMQEventFlag->wake(
-                            static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-                }
-                mPlaybackThreadRunning = false;
-                break;
-            }
-            // Write input FMQ and notify the Tuner Implementation
-            EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
-            playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-            inputData.seekg(writeSize, inputData.cur);
-            sleep(1);
-        }
-    }
-
-    ALOGW("[vts] Playback thread end.");
-
-    delete[] buffer;
-    inputData.close();
-}
-
-void DvrCallback::testRecordOutput() {
-    android::Mutex::Autolock autoLock(mMsgLock);
-    while (mDataOutputBuffer.empty()) {
-        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
-            EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
-            return;
-        }
-    }
-    stopRecordThread();
-    ALOGW("[vts] record pass and stop");
-}
-
-void DvrCallback::startRecordOutputThread(RecordSettings recordSetting,
-                                          MQDesc& recordMQDescriptor) {
-    mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */);
-    EXPECT_TRUE(mRecordMQ);
-    struct RecordThreadArgs* threadArgs =
-            (struct RecordThreadArgs*)malloc(sizeof(struct RecordThreadArgs));
-    threadArgs->user = this;
-    threadArgs->recordSetting = &recordSetting;
-    threadArgs->keepReadingRecordFMQ = &mKeepReadingRecordFMQ;
-
-    pthread_create(&mRecordThread, NULL, __threadLoopRecord, (void*)threadArgs);
-    pthread_setname_np(mRecordThread, "test_record_input_loop");
-}
-
-void* DvrCallback::__threadLoopRecord(void* threadArgs) {
-    DvrCallback* const self =
-            static_cast<DvrCallback*>(((struct RecordThreadArgs*)threadArgs)->user);
-    self->recordThreadLoop(((struct RecordThreadArgs*)threadArgs)->recordSetting,
-                           ((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
-    return 0;
-}
-
-void DvrCallback::recordThreadLoop(RecordSettings* /*recordSetting*/, bool* keepReadingRecordFMQ) {
-    ALOGD("[vts] DvrCallback record threadLoop start.");
-    android::Mutex::Autolock autoLock(mRecordThreadLock);
-    mRecordThreadRunning = true;
-
-    // Create the EventFlag that is used to signal the HAL impl that data have been
-    // read from the Record FMQ
-    EventFlag* recordMQEventFlag;
-    EXPECT_TRUE(EventFlag::createEventFlag(mRecordMQ->getEventFlagWord(), &recordMQEventFlag) ==
-                android::OK);
-
-    while (mRecordThreadRunning) {
-        while (*keepReadingRecordFMQ) {
-            uint32_t efState = 0;
-            android::status_t status = recordMQEventFlag->wait(
-                    static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
-                    true /* retry on spurious wake */);
-            if (status != android::OK) {
-                ALOGD("[vts] wait for data ready on the record FMQ");
-                continue;
-            }
-            // Our current implementation filter the data and write it into the filter FMQ
-            // immediately after the DATA_READY from the VTS/framework
-            if (!readRecordFMQ()) {
-                ALOGD("[vts] record data failed to be filtered. Ending thread");
-                mRecordThreadRunning = false;
-                break;
-            }
-        }
-    }
-
-    mRecordThreadRunning = false;
-    ALOGD("[vts] record thread ended.");
-}
-
-bool DvrCallback::readRecordFMQ() {
-    android::Mutex::Autolock autoLock(mMsgLock);
-    bool result = false;
-    mDataOutputBuffer.clear();
-    mDataOutputBuffer.resize(mRecordMQ->availableToRead());
-    result = mRecordMQ->read(mDataOutputBuffer.data(), mRecordMQ->availableToRead());
-    EXPECT_TRUE(result) << "can't read from Record MQ";
-    mMsgCondition.signal();
-    return result;
-}
-
-void DvrCallback::stopRecordThread() {
-    mKeepReadingRecordFMQ = false;
-    mRecordThreadRunning = false;
-    android::Mutex::Autolock autoLock(mRecordThreadLock);
-}
-/********************************** End DvrCallback ************************************/
-
 /*======================== Start Descrambler APIs Tests Implementation ========================*/
-AssertionResult TunerHidlTest::createDescrambler() {
+AssertionResult TunerHidlTest::createDescrambler(uint32_t demuxId) {
     Result status;
     mService->openDescrambler([&](Result result, const sp<IDescrambler>& descrambler) {
         mDescrambler = descrambler;
@@ -201,21 +28,19 @@
         return failure();
     }
 
-    status = mDescrambler->setDemuxSource(mDemuxId);
+    status = mDescrambler->setDemuxSource(demuxId);
     if (status != Result::SUCCESS) {
         return failure();
     }
 
     // Test if demux source can be set more than once.
-    status = mDescrambler->setDemuxSource(mDemuxId);
+    status = mDescrambler->setDemuxSource(demuxId);
     return AssertionResult(status == Result::INVALID_STATE);
 }
 
 AssertionResult TunerHidlTest::closeDescrambler() {
     Result status;
-    if (!mDescrambler && createDescrambler() == failure()) {
-        return failure();
-    }
+    EXPECT_TRUE(mDescrambler);
 
     status = mDescrambler->close();
     mDescrambler = nullptr;
@@ -223,40 +48,6 @@
 }
 /*========================= End Descrambler APIs Tests Implementation =========================*/
 
-/*============================ Start Dvr APIs Tests Implementation ============================*/
-AssertionResult TunerHidlTest::openDvrInDemux(DvrType type) {
-    Result status;
-
-    // Create dvr callback
-    mDvrCallback = new DvrCallback();
-
-    mDemux->openDvr(type, FMQ_SIZE_1M, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
-        mDvr = dvr;
-        status = result;
-    });
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-
-AssertionResult TunerHidlTest::configDvr(DvrSettings setting) {
-    Result status = mDvr->configure(setting);
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-
-AssertionResult TunerHidlTest::getDvrMQDescriptor() {
-    Result status;
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
-
-    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
-        mDvrMQDescriptor = dvrMQDesc;
-        status = result;
-    });
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-/*============================ End Dvr APIs Tests Implementation ============================*/
-
 /*========================== Start Data Flow Tests Implementation ==========================*/
 AssertionResult TunerHidlTest::broadcastDataFlowTest(vector<string> /*goldenOutputFiles*/) {
     // Data Verify Module
@@ -417,6 +208,10 @@
 void TunerHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
                                               FrontendConfig frontendConf) {
     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,
@@ -426,13 +221,12 @@
     }
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(mDemux, mDemuxId));
-    mFilterTests.setDemux(mDemux);
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFilterTests.setDemux(demux);
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
-    uint32_t filterId;
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterConf.setting, filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
@@ -445,6 +239,67 @@
     ASSERT_TRUE(mDemuxTests.closeDemux());
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
+
+void TunerDvrHidlTest::attachSingleFilterToDvrTest(FilterConfig filterConf,
+                                                   FrontendConfig frontendConf, DvrConfig dvrConf) {
+    description("Open and configure a Dvr in Demux.");
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint32_t filterId;
+    sp<IFilter> filter;
+
+    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+    ASSERT_TRUE(feId != INVALID_ID);
+    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFilterTests.setDemux(demux);
+    mDvrTests.setDemux(demux);
+    ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type));
+    ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
+    ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    filter = mFilterTests.getFilterById(filterId);
+    ASSERT_TRUE(filter != nullptr);
+    ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
+    ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    mDvrTests.closeDvr();
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
+void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
+                                                        FrontendConfig frontendConf) {
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint32_t filterId;
+
+    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+    ASSERT_TRUE(feId != INVALID_ID);
+    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFilterTests.setDemux(demux);
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
 /*================================== End Test Module ==================================*/
 /***************************** End Test Implementation *****************************/
 
@@ -467,50 +322,56 @@
 TEST_P(TunerDemuxHidlTest, openDemux) {
     description("Open and close a Demux.");
     uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
     mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(mDemux, mDemuxId));
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     ASSERT_TRUE(mDemuxTests.closeDemux());
 }
 
 TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
     description("Open and start a filter in Demux.");
-    uint32_t feId;
-    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(mDemux, mDemuxId));
-    mFilterTests.setDemux(mDemux);
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_VIDEO0].type));
-    uint32_t filterId;
-    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_VIDEO0].setting, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
-    ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
-    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
-    ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+    // TODO use paramterized tests
+    configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+}
+
+TEST_P(TunerDvrHidlTest, AttachFiltersToRecordTest) {
+    description("Attach a single filter to the record dvr test.");
+    // TODO use paramterized tests
+    attachSingleFilterToDvrTest(filterArray[TS_VIDEO0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
+}
+
+TEST_P(TunerDvrHidlTest, AttachFiltersToPlaybackTest) {
+    description("Attach a single filter to the playback dvr test.");
+    // TODO use paramterized tests
+    attachSingleFilterToDvrTest(filterArray[TS_VIDEO0], frontendArray[DVBT],
+                                dvrArray[DVR_PLAYBACK0]);
 }
 
 /*============================ Start Descrambler Tests ============================*/
 /*
  * TODO: re-enable the tests after finalizing the test refactoring.
  */
-/*TEST_P(TunerHidlTest, CreateDescrambler) {
+TEST_P(TunerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
-    ASSERT_TRUE(createDescrambler());
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
+    ASSERT_TRUE(feId != INVALID_ID);
+    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    ASSERT_TRUE(createDescrambler(demuxId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(closeDescrambler());
 }
 
-TEST_P(TunerHidlTest, CloseDescrambler) {
-    description("Close Descrambler");
-    ASSERT_TRUE(closeDescrambler());
-}*/
 /*============================== End Descrambler Tests ==============================*/
 
 /*============================== Start Data Flow Tests ==============================*/
@@ -642,4 +503,9 @@
         PerInstance, TunerFilterHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
         android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, TunerDvrHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+        android::hardware::PrintInstanceNameToString);
 }  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index f177047..37b2866 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -15,30 +15,13 @@
  */
 
 #include <android/hardware/tv/tuner/1.0/IDescrambler.h>
-#include <android/hardware/tv/tuner/1.0/IDvr.h>
-#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
-#include <fstream>
-#include <iostream>
 
 #include "DemuxTests.h"
-#include "FilterTests.h"
+#include "DvrTests.h"
 #include "FrontendTests.h"
 
 using android::hardware::tv::tuner::V1_0::DataFormat;
-using android::hardware::tv::tuner::V1_0::DvrSettings;
-using android::hardware::tv::tuner::V1_0::DvrType;
 using android::hardware::tv::tuner::V1_0::IDescrambler;
-using android::hardware::tv::tuner::V1_0::IDvr;
-using android::hardware::tv::tuner::V1_0::IDvrCallback;
-using android::hardware::tv::tuner::V1_0::PlaybackSettings;
-using android::hardware::tv::tuner::V1_0::PlaybackStatus;
-using android::hardware::tv::tuner::V1_0::RecordSettings;
-using android::hardware::tv::tuner::V1_0::RecordStatus;
-
-struct PlaybackConf {
-    string inputDataFile;
-    PlaybackSettings setting;
-};
 
 static AssertionResult failure() {
     return ::testing::AssertionFailure();
@@ -50,89 +33,6 @@
 
 namespace {
 
-class DvrCallback : public IDvrCallback {
-  public:
-    virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
-        ALOGW("[vts] record status %hhu", status);
-        switch (status) {
-            case DemuxFilterStatus::DATA_READY:
-                break;
-            case DemuxFilterStatus::LOW_WATER:
-                break;
-            case DemuxFilterStatus::HIGH_WATER:
-            case DemuxFilterStatus::OVERFLOW:
-                ALOGW("[vts] record overflow. Flushing");
-                break;
-        }
-        return Void();
-    }
-
-    virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
-        // android::Mutex::Autolock autoLock(mMsgLock);
-        ALOGW("[vts] playback status %d", status);
-        switch (status) {
-            case PlaybackStatus::SPACE_EMPTY:
-            case PlaybackStatus::SPACE_ALMOST_EMPTY:
-                ALOGW("[vts] keep playback inputing %d", status);
-                mKeepWritingPlaybackFMQ = true;
-                break;
-            case PlaybackStatus::SPACE_ALMOST_FULL:
-            case PlaybackStatus::SPACE_FULL:
-                ALOGW("[vts] stop playback inputing %d", status);
-                mKeepWritingPlaybackFMQ = false;
-                break;
-        }
-        return Void();
-    }
-
-    void testFilterDataOutput();
-    void stopPlaybackThread();
-    void testRecordOutput();
-    void stopRecordThread();
-
-    void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
-    void startRecordOutputThread(RecordSettings recordSetting, MQDesc& recordMQDescriptor);
-    static void* __threadLoopPlayback(void* threadArgs);
-    static void* __threadLoopRecord(void* threadArgs);
-    void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
-    void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
-
-    bool readRecordFMQ();
-
-  private:
-    struct PlaybackThreadArgs {
-        DvrCallback* user;
-        PlaybackConf* playbackConf;
-        bool* keepWritingPlaybackFMQ;
-    };
-    struct RecordThreadArgs {
-        DvrCallback* user;
-        RecordSettings* recordSetting;
-        bool* keepReadingRecordFMQ;
-    };
-    uint16_t mDataLength = 0;
-    std::vector<uint8_t> mDataOutputBuffer;
-
-    std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterMQ;
-    std::unique_ptr<FilterMQ> mPlaybackMQ;
-    std::unique_ptr<FilterMQ> mRecordMQ;
-    std::map<uint32_t, EventFlag*> mFilterMQEventFlag;
-
-    android::Mutex mMsgLock;
-    android::Mutex mPlaybackThreadLock;
-    android::Mutex mRecordThreadLock;
-    android::Condition mMsgCondition;
-
-    bool mKeepWritingPlaybackFMQ = true;
-    bool mKeepReadingRecordFMQ = true;
-    bool mPlaybackThreadRunning;
-    bool mRecordThreadRunning;
-    pthread_t mPlaybackThread;
-    pthread_t mRecordThread;
-
-    int mPidFilterOutputCount = 0;
-};
-
 class TunerFrontendHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -174,8 +74,6 @@
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
     DemuxTests mDemuxTests;
-    sp<IDemux> mDemux;
-    uint32_t mDemuxId;
 };
 
 class TunerFilterHidlTest : public testing::TestWithParam<std::string> {
@@ -197,21 +95,47 @@
         RecordProperty("description", description);
     }
 
+    void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
+
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
     DemuxTests mDemuxTests;
     FilterTests mFilterTests;
-    sp<IDemux> mDemux;
-    uint32_t mDemuxId;
+};
+
+class TunerDvrHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        mService = ITuner::getService(GetParam());
+        ASSERT_NE(mService, nullptr);
+        initFrontendConfig();
+        initFrontendScanConfig();
+        initFilterConfig();
+        initDvrConfig();
+
+        mFrontendTests.setService(mService);
+        mDemuxTests.setService(mService);
+        mFilterTests.setService(mService);
+        mDvrTests.setService(mService);
+    }
+
+  protected:
+    static void description(const std::string& description) {
+        RecordProperty("description", description);
+    }
+
+    void attachSingleFilterToDvrTest(FilterConfig filterConf, FrontendConfig frontendConf,
+                                     DvrConfig dvrConf);
+
+    sp<ITuner> mService;
+    FrontendTests mFrontendTests;
+    DemuxTests mDemuxTests;
+    FilterTests mFilterTests;
+    DvrTests mDvrTests;
 };
 
 class TunerHidlTest : public testing::TestWithParam<std::string> {
   public:
-    sp<ITuner> mService;
-    FrontendTests mFrontendTests;
-    DemuxTests mDemuxTests;
-    FilterTests mFilterTests;
-
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
@@ -229,23 +153,14 @@
         RecordProperty("description", description);
     }
 
+    sp<ITuner> mService;
+    FrontendTests mFrontendTests;
+    DemuxTests mDemuxTests;
+    FilterTests mFilterTests;
+
     sp<IDescrambler> mDescrambler;
-    sp<IDvr> mDvr;
-    sp<IDemux> mDemux;
-    uint32_t mDemuxId;
 
-    sp<DvrCallback> mDvrCallback;
-    MQDesc mDvrMQDescriptor;
-    MQDesc mRecordMQDescriptor;
-
-    pthread_t mPlaybackshread;
-    bool mPlaybackThreadRunning;
-
-    AssertionResult openDvrInDemux(DvrType type);
-    AssertionResult configDvr(DvrSettings setting);
-    AssertionResult getDvrMQDescriptor();
-
-    AssertionResult createDescrambler();
+    AssertionResult createDescrambler(uint32_t demuxId);
     AssertionResult closeDescrambler();
 
     AssertionResult playbackDataFlowTest(vector<FilterConfig> filterConf, PlaybackConf playbackConf,
@@ -257,4 +172,4 @@
 
     void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
 };
-}  // namespace
\ No newline at end of file
+}  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 538773a..a74fa02 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,12 +21,15 @@
 #include <hidl/Status.h>
 #include <hidlmemory/FrameworkUtils.h>
 
+using android::hardware::tv::tuner::V1_0::DataFormat;
 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::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::FrontendDvbtBandwidth;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
@@ -37,6 +40,8 @@
 using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
 using android::hardware::tv::tuner::V1_0::FrontendSettings;
 using android::hardware::tv::tuner::V1_0::FrontendType;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
 
 using namespace std;
 
@@ -62,9 +67,15 @@
     SCAN_MAX,
 } FrontendScan;
 
+typedef enum {
+    DVR_RECORD0,
+    DVR_PLAYBACK0,
+    DVR_MAX,
+} Dvr;
+
 struct FilterConfig {
     DemuxFilterType type;
-    DemuxFilterSettings setting;
+    DemuxFilterSettings settings;
 };
 
 struct FrontendConfig {
@@ -80,10 +91,16 @@
     DemuxTpid audioPid;
 };
 
+struct DvrConfig {
+    DvrType type;
+    DvrSettings settings;
+};
+
 static FrontendConfig frontendArray[FILTER_MAX];
 static FrontendConfig frontendScanArray[SCAN_MAX];
 static ChannelConfig channelArray[FRONTEND_MAX];
 static FilterConfig filterArray[FILTER_MAX];
+static DvrConfig dvrArray[DVR_MAX];
 static vector<string> goldenOutputFiles;
 
 /** Configuration array for the frontend tune test */
@@ -126,40 +143,62 @@
     // TS VIDEO filter setting for default implementation testing
     filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[TS_VIDEO0].setting.ts().tpid = 119;
-    filterArray[TS_VIDEO0].setting.ts().filterSettings.av({.isPassthrough = false});
+    filterArray[TS_VIDEO0].settings.ts().tpid = 119;
+    filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
     filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[TS_VIDEO1].setting.ts().tpid = 81;
-    filterArray[TS_VIDEO1].setting.ts().filterSettings.av({.isPassthrough = false});
+    filterArray[TS_VIDEO1].settings.ts().tpid = 81;
+    filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false});
     // TS AUDIO filter setting
     filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
-    filterArray[TS_AUDIO0].setting.ts().tpid = 84;
-    filterArray[TS_AUDIO0].setting.ts().filterSettings.av({.isPassthrough = false});
+    filterArray[TS_AUDIO0].settings.ts().tpid = 84;
+    filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false});
     // TS PES filter setting
     filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES);
-    filterArray[TS_PES0].setting.ts().tpid = 256;
-    filterArray[TS_PES0].setting.ts().filterSettings.pesData({
+    filterArray[TS_PES0].settings.ts().tpid = 256;
+    filterArray[TS_PES0].settings.ts().filterSettings.pesData({
             .isRaw = false,
             .streamId = 0xbd,
     });
     // TS PCR filter setting
     filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
-    filterArray[TS_PCR0].setting.ts().tpid = 81;
-    filterArray[TS_PCR0].setting.ts().filterSettings.noinit();
+    filterArray[TS_PCR0].settings.ts().tpid = 81;
+    filterArray[TS_PCR0].settings.ts().filterSettings.noinit();
     // TS filter setting
     filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS);
-    filterArray[TS_TS0].setting.ts().tpid = 48;
-    filterArray[TS_TS0].setting.ts().filterSettings.noinit();
+    filterArray[TS_TS0].settings.ts().tpid = 48;
+    filterArray[TS_TS0].settings.ts().filterSettings.noinit();
     // TS SECTION filter setting
     filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION);
-    filterArray[TS_SECTION0].setting.ts().tpid = 48;
-    filterArray[TS_SECTION0].setting.ts().filterSettings.section({
+    filterArray[TS_SECTION0].settings.ts().tpid = 48;
+    filterArray[TS_SECTION0].settings.ts().filterSettings.section({
             .isRaw = false,
     });
 };
+
+/** 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].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].settings.playback(playbackSettings);
+};
\ No newline at end of file