Merge "Add test for subsample metadata for YUV420 format" into rvc-dev
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index 1f69f09..e699fd0 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -105,6 +105,10 @@
      * Master role has become available
      */
     MASTER_RELEASED,
+    /**
+     * Any other erroneous streaming events
+     */
+    STREAM_ERROR,
 };
 
 /**
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 6a386c3..8b68fd6 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -2262,6 +2262,7 @@
 
     // Allocate buffers to use
     hidl_vec<BufferDesc> buffers;
+    buffers.resize(kBuffersToHold);
     for (auto i = 0; i < kBuffersToHold; ++i) {
         unsigned pixelsPerLine;
         buffer_handle_t memHandle = nullptr;
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index a4fd641..d9ac239 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -15,12 +15,10 @@
 cc_defaults {
     name: "vhal_v2_0_defaults",
     shared_libs: [
-        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libutils",
         "android.hardware.automotive.vehicle@2.0",
-        "carwatchdog_aidl_interface-ndk_platform",
     ],
     cflags: [
         "-Wall",
@@ -29,6 +27,15 @@
     ],
 }
 
+cc_defaults {
+    name: "vhal_v2_0_target_defaults",
+    defaults: ["vhal_v2_0_defaults"],
+    shared_libs: [
+        "libbinder_ndk",
+        "carwatchdog_aidl_interface-ndk_platform",
+    ],
+}
+
 cc_library_headers {
     name: "vhal_v2_0_common_headers",
     vendor: true,
@@ -39,7 +46,7 @@
 cc_library {
     name: "android.hardware.automotive.vehicle@2.0-manager-lib",
     vendor: true,
-    defaults: ["vhal_v2_0_defaults"],
+    defaults: ["vhal_v2_0_target_defaults"],
     srcs: [
         "common/src/Obd2SensorStore.cpp",
         "common/src/SubscriptionManager.cpp",
@@ -61,7 +68,7 @@
 cc_library_static {
     name: "android.hardware.automotive.vehicle@2.0-default-impl-lib",
     vendor: true,
-    defaults: ["vhal_v2_0_defaults"],
+    defaults: ["vhal_v2_0_target_defaults"],
     srcs: [
         "impl/vhal_v2_0/CommConn.cpp",
         "impl/vhal_v2_0/EmulatedVehicleConnector.cpp",
@@ -97,16 +104,59 @@
 cc_library_static {
     name: "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
     vendor: true,
-    defaults: ["vhal_v2_0_defaults"],
+    defaults: ["vhal_v2_0_target_defaults"],
     srcs: [
         "impl/vhal_v2_0/EmulatedUserHal.cpp",
     ],
 }
 
+// Vehicle HAL Server reference impl lib
+cc_library_static {
+    name: "android.hardware.automotive.vehicle@2.0-server-common-lib",
+    vendor: true,
+    host_supported: true,
+    defaults: ["vhal_v2_0_defaults"],
+    local_include_dirs: ["common/include/vhal_v2_0"],
+    export_include_dirs: ["common/include"],
+    srcs: [
+        "common/src/Obd2SensorStore.cpp",
+        "common/src/VehicleObjectPool.cpp",
+        "common/src/VehicleUtils.cpp",
+    ],
+}
+
+// Vehicle HAL Server default implementation
+cc_library_static {
+    name: "android.hardware.automotive.vehicle@2.0-server-impl-lib",
+    vendor: true,
+    host_supported: true,
+    defaults: ["vhal_v2_0_defaults"],
+    local_include_dirs: ["common/include/vhal_v2_0"],
+    export_include_dirs: ["impl"],
+    srcs: [
+        "impl/vhal_v2_0/EmulatedUserHal.cpp",
+        "impl/vhal_v2_0/GeneratorHub.cpp",
+        "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
+        "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
+        "impl/vhal_v2_0/ProtoMessageConverter.cpp",
+        "impl/vhal_v2_0/VehicleHalServer.cpp",
+    ],
+    whole_static_libs: [
+        "android.hardware.automotive.vehicle@2.0-server-common-lib",
+    ],
+    static_libs: [
+        "android.hardware.automotive.vehicle@2.0-libproto-native",
+    ],
+    shared_libs: [
+        "libbase",
+        "libjsoncpp",
+    ],
+}
+
 cc_test {
     name: "android.hardware.automotive.vehicle@2.0-manager-unit-tests",
     vendor: true,
-    defaults: ["vhal_v2_0_defaults"],
+    defaults: ["vhal_v2_0_target_defaults"],
     whole_static_libs: ["android.hardware.automotive.vehicle@2.0-manager-lib"],
     srcs: [
         "tests/RecurrentTimer_test.cpp",
@@ -126,7 +176,7 @@
 cc_test {
     name: "android.hardware.automotive.vehicle@2.0-default-impl-unit-tests",
     vendor: true,
-    defaults: ["vhal_v2_0_defaults"],
+    defaults: ["vhal_v2_0_target_defaults"],
     srcs: [
         "impl/vhal_v2_0/tests/ProtoMessageConverter_test.cpp",
     ],
@@ -140,7 +190,7 @@
 
 cc_binary {
     name: "android.hardware.automotive.vehicle@2.0-service",
-    defaults: ["vhal_v2_0_defaults"],
+    defaults: ["vhal_v2_0_target_defaults"],
     vintf_fragments: [
         "android.hardware.automotive.vehicle@2.0-service.xml",
     ],
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
index f712ea2..ea38cb3 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
@@ -71,35 +71,45 @@
 }
 
 android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetProperty(
-        int32_t prop) {
-    ALOGV("onGetProperty(%d)", prop);
-    switch (prop) {
+        const VehiclePropValue& value) {
+    ALOGV("onGetProperty(%s)", toString(value).c_str());
+    switch (value.prop) {
         case INITIAL_USER_INFO:
         case SWITCH_USER:
         case CREATE_USER:
         case REMOVE_USER:
-            ALOGE("onGetProperty(): %d is only supported on SET", prop);
+            ALOGE("onGetProperty(): %d is only supported on SET", value.prop);
             return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
                    << "only supported on SET";
         case USER_IDENTIFICATION_ASSOCIATION:
-            if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
-                ALOGI("onGetProperty(%d): returning %s", prop,
-                      toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
-                auto value = std::unique_ptr<VehiclePropValue>(
-                        new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
-                return value;
-            }
-            ALOGE("onGetProperty(%d): USER_IDENTIFICATION_ASSOCIATION not set by lshal", prop);
-            return android::base::Error(static_cast<int>(StatusCode::NOT_AVAILABLE))
-                   << "not set by lshal";
+            return onGetUserIdentificationAssociation(value);
         default:
-            ALOGE("onGetProperty(): %d is not supported", prop);
+            ALOGE("onGetProperty(): %d is not supported", value.prop);
             return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
                    << "not supported by User HAL";
     }
 }
 
 android::base::Result<std::unique_ptr<VehiclePropValue>>
+EmulatedUserHal::onGetUserIdentificationAssociation(const VehiclePropValue& value) {
+    if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+        ALOGI("get(USER_IDENTIFICATION_ASSOCIATION): returning %s",
+              toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+        auto newValue = std::unique_ptr<VehiclePropValue>(
+                new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+        // Must use the same requestId
+        if (value.value.int32Values.size() > 0) {
+            newValue->value.int32Values[0] = value.value.int32Values[0];
+        } else {
+            ALOGE("get(USER_IDENTIFICATION_ASSOCIATION): no requestId on %s",
+                  toString(value).c_str());
+        }
+        return newValue;
+    }
+    return defaultUserIdentificationAssociation(value);
+}
+
+android::base::Result<std::unique_ptr<VehiclePropValue>>
 EmulatedUserHal::onSetInitialUserInfoResponse(const VehiclePropValue& value) {
     if (value.value.int32Values.size() == 0) {
         ALOGE("set(INITIAL_USER_INFO): no int32values, ignoring it: %s", toString(value).c_str());
@@ -158,6 +168,20 @@
         return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), requestId);
     }
 
+    if (value.value.int32Values.size() > 1) {
+        auto messageType = static_cast<SwitchUserMessageType>(value.value.int32Values[1]);
+        switch (messageType) {
+            case SwitchUserMessageType::LEGACY_ANDROID_SWITCH:
+                ALOGI("request is LEGACY_ANDROID_SWITCH; ignoring it");
+                return {};
+            case SwitchUserMessageType::ANDROID_POST_SWITCH:
+                ALOGI("request is ANDROID_POST_SWITCH; ignoring it");
+                return {};
+            default:
+                break;
+        }
+    }
+
     // Returns default response
     auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
     updatedValue->prop = SWITCH_USER;
@@ -236,16 +260,14 @@
     }
 
     // Returns default response
-    auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
-    updatedValue->prop = USER_IDENTIFICATION_ASSOCIATION;
-    updatedValue->timestamp = elapsedRealtimeNano();
-    updatedValue->value.int32Values.resize(1);
-    updatedValue->value.int32Values[0] = requestId;
-    updatedValue->value.stringValue = "Response not set by LSHAL";
+    return defaultUserIdentificationAssociation(value);
+}
 
-    ALOGI("no lshal response; replying with an error message: %s", toString(*updatedValue).c_str());
-
-    return updatedValue;
+android::base::Result<std::unique_ptr<VehiclePropValue>>
+EmulatedUserHal::defaultUserIdentificationAssociation(const VehiclePropValue& request) {
+    // TODO(b/159498909): return a response with NOT_ASSOCIATED_ANY_USER for all requested types
+    ALOGE("no lshal response for %s; replying with NOT_AVAILABLE", toString(request).c_str());
+    return android::base::Error(static_cast<int>(StatusCode::NOT_AVAILABLE)) << "not set by lshal";
 }
 
 android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::sendUserHalResponse(
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
index 5243b96..db2f117 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
@@ -58,7 +58,8 @@
      *
      * @return property value and StatusCode
      */
-    android::base::Result<std::unique_ptr<VehiclePropValue>> onGetProperty(int32_t prop);
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onGetProperty(
+            const VehiclePropValue& value);
 
     /**
      * Shows the User HAL emulation help.
@@ -111,12 +112,25 @@
             const VehiclePropValue& value);
 
     /**
-     * Used to emulate USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+     * Used to emulate set USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
      * usage.
      */
     android::base::Result<std::unique_ptr<VehiclePropValue>> onSetUserIdentificationAssociation(
             const VehiclePropValue& value);
 
+    /**
+     * Used to emulate get USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+     * usage.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onGetUserIdentificationAssociation(
+            const VehiclePropValue& value);
+
+    /**
+     * Creates a default USER_IDENTIFICATION_ASSOCIATION when it was not set by lshal.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> defaultUserIdentificationAssociation(
+            const VehiclePropValue& request);
+
     android::base::Result<std::unique_ptr<VehiclePropValue>> sendUserHalResponse(
             std::unique_ptr<VehiclePropValue> response, int32_t requestId);
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 9cfcc1c..a0b566d 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -153,7 +153,7 @@
         default:
             if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
                 ALOGI("get(): getting value for prop %d from User HAL", propId);
-                const auto& ret = mEmulatedUserHal->onGetProperty(propId);
+                const auto& ret = mEmulatedUserHal->onGetProperty(requestedPropValue);
                 if (!ret.ok()) {
                     ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
                     *outStatus = StatusCode(ret.error().code());
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
index 31ba8ab..c5b9ed6 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
@@ -16,6 +16,7 @@
 cc_library_static {
     name: "android.hardware.automotive.vehicle@2.0-libproto-native",
     vendor: true,
+    host_supported: true,
     proto: {
         export_proto_headers: true,
         type: "lite",
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 341aae7..b4aa11d 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -2546,9 +2546,8 @@
      * NOTE: if the HAL doesn't support user management, then it should not define this property,
      * which in turn would disable the other user-related properties (for example, the Android
      * system would never issue them and user-related requests from the HAL layer would be ignored
-     * by the Android System). But if it supports user management, then it must support all
-     * user-related properties (INITIAL_USER_INFO, SWITCH_USER, CREATE_USER, REMOVE_USER,
-     *       and USER_IDENTIFICATION_ASSOCIATION).
+     * by the Android System). But if it supports user management, then it must support all core
+     * user-related properties (INITIAL_USER_INFO, SWITCH_USER, CREATE_USER, and REMOVE_USER).
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ_WRITE
@@ -2818,6 +2817,10 @@
      * Property used to associate (or query the association) the current user with vehicle-specific
      * identification mechanisms (such as key FOB).
      *
+     * This is an optional user management property - the OEM could still support user management
+     * without defining it. In fact, this property could be used without supporting the core
+     * user-related functions described on INITIAL_USER_INFO.
+     *
      * To query the association, the Android system gets the property, passing a VehiclePropValue
      * containing the types of associations are being queried, as defined by
      * UserIdentificationGetRequest. The HAL must return right away, returning a VehiclePropValue
@@ -4277,6 +4280,16 @@
      * Admin users have additional privileges such as permission to create other users.
      */
     ADMIN = 0x08,
+
+    /**
+     * Disabled users are marked for deletion.
+     */
+    DISABLED = 0x10,
+
+     /**
+     * Profile user is a profile of another user.
+     */
+    PROFILE = 0x20,
 };
 
 /**
@@ -4291,10 +4304,16 @@
     /** The current foreground user. */
     UserInfo currentUser;
 
-    /** Number of existing users (includes the current user). */
+    /**
+     * Number of existing users; includes the current user, recently removed users (with DISABLED
+     * flag), and profile users (with PROFILE flag).
+     */
     int32_t numberUsers;
 
-    /** List of existing users (includes the current user). */
+    /**
+     * List of existing users; includes the current user, recently removed users (with DISABLED
+     * flag), and profile users (with PROFILE flag).
+     */
     vec<UserInfo> existingUsers;
  };
 
diff --git a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
index 7ac44a4..78f93af 100644
--- a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
+++ b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
@@ -28,6 +28,7 @@
 #include <chrono>
 #include <cstdint>
 #include <random>
+#include <thread>
 
 using android::sp;
 using android::hardware::hidl_vec;
@@ -144,7 +145,10 @@
         ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
     }
 
-    void TearDown() override {}
+    void TearDown() override {
+        // Hack to allow the asynchronous operations to finish on time.
+        std::this_thread::sleep_for(std::chrono::milliseconds(250));
+    }
 
     sp<IBiometricsFace> mService;
     sp<FaceCallback> mCallback;
diff --git a/camera/device/3.2/ICameraDeviceCallback.hal b/camera/device/3.2/ICameraDeviceCallback.hal
index dec3bd8..206a649 100644
--- a/camera/device/3.2/ICameraDeviceCallback.hal
+++ b/camera/device/3.2/ICameraDeviceCallback.hal
@@ -87,15 +87,19 @@
      * ERROR_RESULT message.
      *
      * If an output buffer cannot be filled, its status field must be set to
-     * STATUS_ERROR. In addition, notify() must be called with a ERROR_BUFFER
-     * message.
+     * STATUS_ERROR. In this case, notify() isn't required to be called with
+     * an ERROR_BUFFER message. The framework will simply treat the notify()
+     * call with ERROR_BUFFER as a no-op, and derive whether and when to notify
+     * the application of buffer loss based on the buffer status and whether or not
+     * the entire capture has failed.
      *
      * If the entire capture has failed, then this method still needs to be
      * called to return the output buffers to the framework. All the buffer
      * statuses must be STATUS_ERROR, and the result metadata must be an
      * empty buffer. In addition, notify() must be called with a ERROR_REQUEST
      * message. In this case, individual ERROR_RESULT/ERROR_BUFFER messages
-     * must not be sent.
+     * must not be sent. Note that valid partial results are still allowed
+     * as long as the final result metadata fails to be generated.
      *
      * Performance requirements:
      *
diff --git a/camera/device/3.2/default/convert.cpp b/camera/device/3.2/default/convert.cpp
index d878deb..06ad7e9 100644
--- a/camera/device/3.2/default/convert.cpp
+++ b/camera/device/3.2/default/convert.cpp
@@ -38,7 +38,7 @@
     }
 
     const uint8_t* data = src.data();
-    // sanity check the size of CameraMetadata match underlying camera_metadata_t
+    // check that the size of CameraMetadata match underlying camera_metadata_t
     if (get_camera_metadata_size((camera_metadata_t*)data) != src.size()) {
         ALOGE("%s: input CameraMetadata is corrupt!", __FUNCTION__);
         return false;
diff --git a/camera/provider/2.4/vts/functional/AndroidTest.xml b/camera/provider/2.4/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..05e1639
--- /dev/null
+++ b/camera/provider/2.4/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalCameraProviderV2_4TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalCameraProviderV2_4TargetTest->/data/local/tmp/VtsHalCameraProviderV2_4TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalCameraProviderV2_4TargetTest" />
+        <option name="native-test-timeout" value="180000"/>
+    </test>
+</configuration>
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index f6860cf..3b8f833 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -1894,7 +1894,7 @@
 }
 
 // Verify that the device resource cost can be retrieved and the values are
-// sane.
+// correct.
 TEST_P(CameraHidlTest, getResourceCost) {
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
 
@@ -2544,7 +2544,7 @@
     }
 }
 
-// Basic sanity tests related to camera parameters.
+// Basic correctness tests related to camera parameters.
 TEST_P(CameraHidlTest, getSetParameters) {
     hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
 
diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal
index ed1d31d..b8873a6 100644
--- a/camera/provider/2.6/ICameraProvider.hal
+++ b/camera/provider/2.6/ICameraProvider.hal
@@ -76,12 +76,16 @@
      * configuration settings exposed through camera metadata), should the sum
      * of resource costs for the combination be <= 100.
      *
-     * The lists of camera id combinations returned by this method may contain
-     * hidden physical camera ids. If a combination does contain hidden physical
-     * camera ids, the camera framework must be able to open any logical cameras
-     * that contain these hidden physical camera ids in their
-     * ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS list, in addition to the other
-     * camera ids advertised in the combination, for concurrent operation.
+     * For guaranteed concurrent camera operation, the camera framework must call
+     * ICameraDevice.open() on all devices (intended for concurrent operation), before configuring
+     * any streams on them. This gives the camera HAL process an opportunity to potentially
+     * distribute hardware resources better before stream configuration.
+     *
+     * Due to potential hardware constraints around internal switching of physical camera devices,
+     * a device's complete ZOOM_RATIO_RANGE(if supported), may not apply during concurrent
+     * operation. If ZOOM_RATIO is supported, camera HALs must ensure ZOOM_RATIO_RANGE of
+     * [1.0, ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM] is supported by that device, during
+     * concurrent operation.
      *
      * @return status Status code for the operation
      * @return cameraIds a list of camera id combinations that support
diff --git a/current.txt b/current.txt
index e2d1408..90402b4 100644
--- a/current.txt
+++ b/current.txt
@@ -588,6 +588,7 @@
 578f640c653726d58f99c84a7e1bb63862e21ef7cbb4f7d95c3cc62de00dca35 android.hardware.automotive.evs@1.0::IEvsDisplay
 f5bc6aa840db933cb9fd36668b06d3e2021cf5384bb70e459f22e2f2f921fba5 android.hardware.automotive.evs@1.0::IEvsEnumerator
 d3a344b7bd4c0d2658ae7209f55a979b8f53f361fd00f4fca29d5baa56d11fd2 android.hardware.automotive.evs@1.0::types
+2924c3e43858190ee3e2da4c2fb93bba8ae065fe314451f035a7ec52cb80c94a android.hardware.camera.device@3.2::ICameraDeviceCallback # b/155353799
 2410dd02d67786a732d36e80b0f8ccf55086604ef37f9838e2013ff2c571e404 android.hardware.camera.device@3.5::types
 cd06a7911b9acd4a653bbf7133888878fbcb3f84be177c7a3f1becaae3d8618f android.hardware.camera.metadata@3.2::types
 5cf81b1001296fbb3c5b3d275a859244f61cec5fa858d7be9cca46c5b7dfa733 android.hardware.camera.metadata@3.2::types # b/150331548
@@ -657,7 +658,7 @@
 87958d728d7c0ee9b9391ab4a072b097914921a7b38f7dc3df427f933a5b528e android.hardware.automotive.evs@1.1::IEvsEnumerator
 f53b4e8de6209c6d0fa9036005671b34a2f98328b51423d3a5137a43bf42c84d android.hardware.automotive.evs@1.1::IEvsUltrasonicsArray
 0460bacbde906a846a3d71b2b7b33d6927cac3ff072e523ffac7853577464406 android.hardware.automotive.evs@1.1::IEvsUltrasonicsArrayStream
-3e374b5c4777f959f62a320abb3b9edca8874e24e383dbb19c66d224f151b363 android.hardware.automotive.evs@1.1::types
+f27cf8283e7b953d33dd258734749d2fca9cc63502ea41353060ffa78d8ce9f6 android.hardware.automotive.evs@1.1::types
 4e4904c4067dadae974ddf90351f362331dcd04bba1d890d313cc8ba91f68c15 android.hardware.automotive.sv@1.0::ISurroundView2dSession
 63336e9d03f545020ff2982ff76d9d8c44fa76ad476293b5ef6732cbbd71e61b android.hardware.automotive.sv@1.0::ISurroundView3dSession
 b7015428cd52ce8192d13bfcbf2c4455cda3727d57f2aac80d65a1747104f5ac android.hardware.automotive.sv@1.0::ISurroundViewService
@@ -676,7 +677,7 @@
 a718c8a3acaa938de5a57923e8c4625ed7ca051e05a1d930ba6998557d7b57c8 android.hardware.camera.device@3.6::ICameraOfflineSession
 a35d5151b48505f06a775b38c0e2e265f80a845d92802324c643565807f81c53 android.hardware.camera.device@3.6::types
 02bdf82dba7dce273a554b4474468a8fb1fb4f61ab65da95eb16e080df63fff6 android.hardware.camera.metadata@3.5::types
-21086e1c7a2acc0ebe0ff8561b11f3c2009be687a92d79b608a5f00b16c5f598 android.hardware.camera.provider@2.6::ICameraProvider
+7d6b362681f4a4fd0be95535d8913d8de9a26f0765c1bdda4bd837dea8c25db6 android.hardware.camera.provider@2.6::ICameraProvider
 8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
 1edf7aef68ef3bd577a1175b1462fb82e3e39f01c6915dda61fba121028df283 android.hardware.camera.provider@2.6::types
 c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp
index 0d540b7..94bfb89 100644
--- a/gnss/1.1/vts/functional/Android.bp
+++ b/gnss/1.1/vts/functional/Android.bp
@@ -25,6 +25,7 @@
     static_libs: [
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
+        "android.hardware.gnss@2.0",
         "android.hardware.gnss@common-vts-lib",
     ],
     shared_libs: [
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp
index 59e18f3..1cb44c5 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp
@@ -247,3 +247,46 @@
     capabilities_cbq_.store(capabilities);
     return Void();
 }
+
+GnssConstellationType_1_0 GnssHalTest::startLocationAndGetNonGpsConstellation() {
+    const int kLocationsToAwait = 3;
+
+    gnss_cb_->location_cbq_.reset();
+    StartAndCheckLocations(kLocationsToAwait);
+    const int location_called_count = gnss_cb_->location_cbq_.calledCount();
+
+    // Tolerate 1 less sv status to handle edge cases in reporting.
+    int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
+    EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
+    ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
+          sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
+
+    // Find first non-GPS constellation to blacklist. Exclude IRNSS in GnssConstellationType_2_0
+    // as blacklisting of this constellation is not supported in gnss@2.0.
+    const int kGnssSvStatusTimeout = 2;
+    GnssConstellationType_1_0 constellation_to_blacklist = GnssConstellationType_1_0::UNKNOWN;
+    for (int i = 0; i < sv_info_list_cbq_size; ++i) {
+        hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
+        gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
+        for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
+            if ((sv_info.v1_0.svFlag & IGnssCallback_2_0::GnssSvFlags::USED_IN_FIX) &&
+                (sv_info.constellation != GnssConstellationType_2_0::UNKNOWN) &&
+                (sv_info.constellation != GnssConstellationType_2_0::IRNSS) &&
+                (sv_info.constellation != GnssConstellationType_2_0::GPS)) {
+                // found a non-GPS V1_0 constellation
+                constellation_to_blacklist = Utils::mapConstellationType(sv_info.constellation);
+                break;
+            }
+        }
+        if (constellation_to_blacklist != GnssConstellationType_1_0::UNKNOWN) {
+            break;
+        }
+    }
+
+    if (constellation_to_blacklist == GnssConstellationType_1_0::UNKNOWN) {
+        ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
+        // Proceed functionally to blacklist something.
+        constellation_to_blacklist = GnssConstellationType_1_0::GLONASS;
+    }
+    return constellation_to_blacklist;
+}
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h
index a02a9ff..7fbd735 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.h
+++ b/gnss/2.0/vts/functional/gnss_hal_test.h
@@ -31,6 +31,9 @@
 using android::hardware::gnss::V1_0::GnssLocationFlags;
 using android::hardware::gnss::V2_0::IGnss;
 
+using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
+
 using GnssLocation_1_0 = android::hardware::gnss::V1_0::GnssLocation;
 using GnssLocation_2_0 = android::hardware::gnss::V2_0::GnssLocation;
 
@@ -194,6 +197,16 @@
      */
     void SetPositionMode(const int min_interval_msec, const bool low_power_mode);
 
+    /*
+     * startLocationAndGetNonGpsConstellation:
+     * 1. Start location
+     * 2. Find and return first non-GPS constellation
+     *
+     * Note that location is not stopped in this method. The client should call
+     * StopAndClearLocations() after the call.
+     */
+    GnssConstellationType_1_0 startLocationAndGetNonGpsConstellation();
+
     sp<IGnss> gnss_hal_;         // GNSS HAL to call into
     sp<GnssCallback> gnss_cb_;   // Primary callback interface
 };
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 094c7c1..51dcf0d 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -24,8 +24,6 @@
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 
-using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
-using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
 using IGnssConfiguration_2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
 using IGnssConfiguration_1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
 using IAGnssRil_2_0 = android::hardware::gnss::V2_0::IAGnssRil;
@@ -492,31 +490,6 @@
 }
 
 /*
- * MapConstellationType:
- * Given a GnssConstellationType_2_0 type constellation, maps to its equivalent
- * GnssConstellationType_1_0 type constellation. For constellations that do not have
- * an equivalent value, maps to GnssConstellationType_1_0::UNKNOWN
- */
-GnssConstellationType_1_0 MapConstellationType(GnssConstellationType_2_0 constellation) {
-    switch (constellation) {
-        case GnssConstellationType_2_0::GPS:
-            return GnssConstellationType_1_0::GPS;
-        case GnssConstellationType_2_0::SBAS:
-            return GnssConstellationType_1_0::SBAS;
-        case GnssConstellationType_2_0::GLONASS:
-            return GnssConstellationType_1_0::GLONASS;
-        case GnssConstellationType_2_0::QZSS:
-            return GnssConstellationType_1_0::QZSS;
-        case GnssConstellationType_2_0::BEIDOU:
-            return GnssConstellationType_1_0::BEIDOU;
-        case GnssConstellationType_2_0::GALILEO:
-            return GnssConstellationType_1_0::GALILEO;
-        default:
-            return GnssConstellationType_1_0::UNKNOWN;
-    }
-}
-
-/*
  * FindStrongFrequentNonGpsSource:
  *
  * Search through a GnssSvStatus list for the strongest non-GPS satellite observed enough times
@@ -555,7 +528,7 @@
                 (sv_info.constellation != GnssConstellationType_2_0::GPS)) {
                 ComparableBlacklistedSource source;
                 source.id.svid = sv_info.v1_0.svid;
-                source.id.constellation = MapConstellationType(sv_info.constellation);
+                source.id.constellation = Utils::mapConstellationType(sv_info.constellation);
 
                 const auto& itSignal = mapSignals.find(source);
                 if (itSignal == mapSignals.end()) {
@@ -694,7 +667,7 @@
         hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
         gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
         for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
-            auto constellation = MapConstellationType(sv_info.constellation);
+            auto constellation = Utils::mapConstellationType(sv_info.constellation);
             EXPECT_FALSE((sv_info.v1_0.svid == source_to_blacklist.svid) &&
                          (constellation == source_to_blacklist.constellation) &&
                          (sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
@@ -736,7 +709,7 @@
             hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
             gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
             for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
-                auto constellation = MapConstellationType(sv_info.constellation);
+                auto constellation = Utils::mapConstellationType(sv_info.constellation);
                 if ((sv_info.v1_0.svid == source_to_blacklist.svid) &&
                     (constellation == source_to_blacklist.constellation) &&
                     (sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX)) {
@@ -752,7 +725,7 @@
 }
 
 /*
- * BlacklistConstellation:
+ * BlacklistConstellationWithLocationOff:
  *
  * 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
  * GnssStatus for any non-GPS constellations.
@@ -761,12 +734,11 @@
  * GnssStatus does not use any constellation but GPS.
  * 4a & b) Clean up by turning off location, and send in empty blacklist.
  */
-TEST_P(GnssHalTest, BlacklistConstellation) {
+TEST_P(GnssHalTest, BlacklistConstellationWithLocationOff) {
     if (!IsGnssHalVersion_2_0()) {
         ALOGI("Test BlacklistConstellation skipped. GNSS HAL version is greater than 2.0.");
         return;
     }
-
     if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) {
         ALOGI("Test BlacklistConstellation skipped. SATELLITE_BLACKLIST capability not supported.");
         return;
@@ -774,43 +746,12 @@
 
     const int kLocationsToAwait = 3;
 
-    gnss_cb_->location_cbq_.reset();
-    StartAndCheckLocations(kLocationsToAwait);
-    const int location_called_count = gnss_cb_->location_cbq_.calledCount();
+    // Find first non-GPS constellation to blacklist
+    GnssConstellationType_1_0 constellation_to_blacklist = startLocationAndGetNonGpsConstellation();
 
-    // Tolerate 1 less sv status to handle edge cases in reporting.
-    int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
-    EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
-    ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
-          sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
+    // Turns off location
+    StopAndClearLocations();
 
-    // Find first non-GPS constellation to blacklist. Exclude IRNSS in GnssConstellationType_2_0
-    // as blacklisting of this constellation is not supported in gnss@2.0.
-    const int kGnssSvStatusTimeout = 2;
-    GnssConstellationType_1_0 constellation_to_blacklist = GnssConstellationType_1_0::UNKNOWN;
-    for (int i = 0; i < sv_info_list_cbq_size; ++i) {
-        hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
-        gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
-        for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
-            if ((sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
-                (sv_info.constellation != GnssConstellationType_2_0::UNKNOWN) &&
-                (sv_info.constellation != GnssConstellationType_2_0::IRNSS) &&
-                (sv_info.constellation != GnssConstellationType_2_0::GPS)) {
-                // found a non-GPS V1_0 constellation
-                constellation_to_blacklist = MapConstellationType(sv_info.constellation);
-                break;
-            }
-        }
-        if (constellation_to_blacklist != GnssConstellationType_1_0::UNKNOWN) {
-            break;
-        }
-    }
-
-    if (constellation_to_blacklist == GnssConstellationType_1_0::UNKNOWN) {
-        ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
-        // Proceed functionally to blacklist something.
-        constellation_to_blacklist = GnssConstellationType_1_0::GLONASS;
-    }
     IGnssConfiguration_1_1::BlacklistedSource source_to_blacklist;
     source_to_blacklist.constellation = constellation_to_blacklist;
     source_to_blacklist.svid = 0;  // documented wildcard for all satellites in this constellation
@@ -824,6 +765,7 @@
     sources.resize(1);
     sources[0] = source_to_blacklist;
 
+    // setBlacklist when location is off.
     auto result = gnss_configuration_hal->setBlacklist(sources);
     ASSERT_TRUE(result.isOk());
     EXPECT_TRUE(result);
@@ -835,15 +777,93 @@
     StartAndCheckLocations(kLocationsToAwait);
 
     // Tolerate 1 less sv status to handle edge cases in reporting.
-    sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
+    int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
     EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
     ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", sv_info_list_cbq_size,
           kLocationsToAwait);
+    const int kGnssSvStatusTimeout = 2;
     for (int i = 0; i < sv_info_list_cbq_size; ++i) {
         hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
         gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
         for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
-            auto constellation = MapConstellationType(sv_info.constellation);
+            auto constellation = Utils::mapConstellationType(sv_info.constellation);
+            EXPECT_FALSE((constellation == source_to_blacklist.constellation) &&
+                         (sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
+        }
+    }
+
+    // clean up
+    StopAndClearLocations();
+    sources.resize(0);
+    result = gnss_configuration_hal->setBlacklist(sources);
+    ASSERT_TRUE(result.isOk());
+    EXPECT_TRUE(result);
+}
+
+/*
+ * BlacklistConstellationWithLocationOn:
+ *
+ * 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
+ * GnssStatus for any non-GPS constellations.
+ * 2a & b) Blacklist first non-GPS constellations, and turns off location.
+ * 3) Restart location, wait for 3 locations, ensuring they are valid, and checks corresponding
+ * GnssStatus does not use any constellation but GPS.
+ * 4a & b) Clean up by turning off location, and send in empty blacklist.
+ */
+TEST_P(GnssHalTest, BlacklistConstellationWithLocationOn) {
+    if (!IsGnssHalVersion_2_0()) {
+        ALOGI("Test BlacklistConstellation skipped. GNSS HAL version is greater than 2.0.");
+        return;
+    }
+
+    if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) {
+        ALOGI("Test BlacklistConstellation skipped. SATELLITE_BLACKLIST capability not supported.");
+        return;
+    }
+
+    const int kLocationsToAwait = 3;
+
+    // Find first non-GPS constellation to blacklist
+    GnssConstellationType_1_0 constellation_to_blacklist = startLocationAndGetNonGpsConstellation();
+
+    IGnssConfiguration_1_1::BlacklistedSource source_to_blacklist;
+    source_to_blacklist.constellation = constellation_to_blacklist;
+    source_to_blacklist.svid = 0;  // documented wildcard for all satellites in this constellation
+
+    auto gnss_configuration_hal_return = gnss_hal_->getExtensionGnssConfiguration_1_1();
+    ASSERT_TRUE(gnss_configuration_hal_return.isOk());
+    sp<IGnssConfiguration_1_1> gnss_configuration_hal = gnss_configuration_hal_return;
+    ASSERT_NE(gnss_configuration_hal, nullptr);
+
+    hidl_vec<IGnssConfiguration_1_1::BlacklistedSource> sources;
+    sources.resize(1);
+    sources[0] = source_to_blacklist;
+
+    // setBlacklist when location is on.
+    auto result = gnss_configuration_hal->setBlacklist(sources);
+    ASSERT_TRUE(result.isOk());
+    EXPECT_TRUE(result);
+
+    // Turns off location
+    StopAndClearLocations();
+
+    // retry and ensure constellation not used
+    gnss_cb_->sv_info_list_cbq_.reset();
+
+    gnss_cb_->location_cbq_.reset();
+    StartAndCheckLocations(kLocationsToAwait);
+
+    // Tolerate 1 less sv status to handle edge cases in reporting.
+    int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
+    EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
+    ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", sv_info_list_cbq_size,
+          kLocationsToAwait);
+    const int kGnssSvStatusTimeout = 2;
+    for (int i = 0; i < sv_info_list_cbq_size; ++i) {
+        hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
+        gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
+        for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
+            auto constellation = Utils::mapConstellationType(sv_info.constellation);
             EXPECT_FALSE((constellation == source_to_blacklist.constellation) &&
                          (sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
         }
diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp
index fd9613b..4c6d443 100644
--- a/gnss/common/utils/vts/Android.bp
+++ b/gnss/common/utils/vts/Android.bp
@@ -29,6 +29,7 @@
     export_include_dirs: ["include"],
     shared_libs: [
         "android.hardware.gnss@1.0",
+        "android.hardware.gnss@2.0",
         "android.hardware.gnss.measurement_corrections@1.0",
         "android.hardware.gnss.measurement_corrections@1.1",
     ],
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index 4b5a50f..9bf68e6 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -169,6 +169,31 @@
     return mockCorrections_1_1;
 }
 
+/*
+ * MapConstellationType:
+ * Given a GnssConstellationType_2_0 type constellation, maps to its equivalent
+ * GnssConstellationType_1_0 type constellation. For constellations that do not have
+ * an equivalent value, maps to GnssConstellationType_1_0::UNKNOWN
+ */
+GnssConstellationType_1_0 Utils::mapConstellationType(GnssConstellationType_2_0 constellation) {
+    switch (constellation) {
+        case GnssConstellationType_2_0::GPS:
+            return GnssConstellationType_1_0::GPS;
+        case GnssConstellationType_2_0::SBAS:
+            return GnssConstellationType_1_0::SBAS;
+        case GnssConstellationType_2_0::GLONASS:
+            return GnssConstellationType_1_0::GLONASS;
+        case GnssConstellationType_2_0::QZSS:
+            return GnssConstellationType_1_0::QZSS;
+        case GnssConstellationType_2_0::BEIDOU:
+            return GnssConstellationType_1_0::BEIDOU;
+        case GnssConstellationType_2_0::GALILEO:
+            return GnssConstellationType_1_0::GALILEO;
+        default:
+            return GnssConstellationType_1_0::UNKNOWN;
+    }
+}
+
 }  // namespace common
 }  // namespace gnss
 }  // namespace hardware
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
index c3cdd18..9c838b2 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -18,9 +18,12 @@
 #define android_hardware_gnss_common_vts_Utils_H_
 
 #include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
 #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
 #include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
 
+using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
 using GnssLocation = ::android::hardware::gnss::V1_0::GnssLocation;
 using namespace android::hardware::gnss::measurement_corrections::V1_0;
 
@@ -44,6 +47,8 @@
                               bool check_more_accuracies);
     static const MeasurementCorrections_1_0 getMockMeasurementCorrections();
     static const MeasurementCorrections_1_1 getMockMeasurementCorrections_1_1();
+
+    static GnssConstellationType_1_0 mapConstellationType(GnssConstellationType_2_0 constellation);
 };
 
 }  // namespace common
diff --git a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl
index ccb0690..b329cb2 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl
@@ -100,10 +100,11 @@
     long totalSizeInBytes;
 
     /**
-     * Horizontal and vertical subsampling. Must be a positive power of 2.
+     * Horizontal and vertical subsampling. Must be a positive power of 2. A value of 1
+     * indicates no subsampling.
      *
      * These fields indicate the number of horizontally or vertically adjacent pixels that use
-     * the same pixel data. A value of 1 indicates no subsampling.
+     * the same pixel data.
      */
     long horizontalSubsampling;
     long verticalSubsampling;
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 16be5db..bb775dc 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -587,8 +587,8 @@
                                static_cast<int32_t>(info.height)};
     unique_fd fence;
     uint8_t* data;
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     // RGBA_8888
     fillRGBA8888(data, info.height, stride * 4, info.width * 4);
@@ -596,8 +596,8 @@
     ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
 
     // lock again for reading
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     ASSERT_NO_FATAL_FAILURE(
             verifyRGBA8888(bufferHandle, data, info.height, stride * 4, info.width * 4));
@@ -605,6 +605,9 @@
     ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
 }
 
+/**
+ *  Test multiple operations associated with different color formats
+ */
 TEST_P(GraphicsMapperHidlTest, Lock_YCRCB_420_SP) {
     auto info = mDummyDescriptorInfo;
     info.format = PixelFormat::YCRCB_420_SP;
@@ -624,8 +627,8 @@
     unique_fd fence;
     uint8_t* data;
 
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     android_ycbcr yCbCr;
     int64_t hSubsampling = 0;
@@ -647,8 +650,8 @@
     ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
 
     // lock again for reading
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     ASSERT_NO_FATAL_FAILURE(
             getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
@@ -723,8 +726,8 @@
     unique_fd fence;
     uint8_t* data;
 
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     android_ycbcr yCbCr;
     int64_t hSubsampling = 0;
@@ -746,8 +749,8 @@
     ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
 
     // lock again for reading
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     ASSERT_NO_FATAL_FAILURE(
             getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
@@ -772,8 +775,8 @@
     unique_fd fence;
     uint8_t* data;
 
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     android_ycbcr yCbCr;
     int64_t hSubsampling = 0;
@@ -790,8 +793,8 @@
     ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
 
     // lock again for reading
-    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
-                                    mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+    ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage,
+                                                                        region, fence.release())));
 
     ASSERT_NO_FATAL_FAILURE(
             getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
@@ -801,6 +804,90 @@
     ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
 }
 
+TEST_P(GraphicsMapperHidlTest, Lock_RAW10) {
+    auto info = mDummyDescriptorInfo;
+    info.format = PixelFormat::RAW10;
+
+    const native_handle_t* bufferHandle;
+    uint32_t stride;
+    ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(
+                                    info, true, Tolerance::kToleranceUnSupported, &stride));
+    if (bufferHandle == nullptr) {
+        GTEST_SUCCEED() << "RAW10 format is unsupported";
+        return;
+    }
+
+    const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+                               static_cast<int32_t>(info.height)};
+    unique_fd fence;
+
+    ASSERT_NO_FATAL_FAILURE(mGralloc->lock(bufferHandle, info.usage, region, fence.release()));
+
+    hidl_vec<uint8_t> vec;
+    ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec));
+    std::vector<PlaneLayout> planeLayouts;
+    ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+
+    ASSERT_EQ(1, planeLayouts.size());
+    auto planeLayout = planeLayouts[0];
+
+    EXPECT_EQ(0, planeLayout.sampleIncrementInBits);
+    EXPECT_EQ(1, planeLayout.horizontalSubsampling);
+    EXPECT_EQ(1, planeLayout.verticalSubsampling);
+
+    ASSERT_EQ(1, planeLayout.components.size());
+    auto planeLayoutComponent = planeLayout.components[0];
+
+    EXPECT_EQ(PlaneLayoutComponentType::RAW,
+              static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value));
+    EXPECT_EQ(0, planeLayoutComponent.offsetInBits % 8);
+    EXPECT_EQ(-1, planeLayoutComponent.sizeInBits);
+
+    ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+}
+
+TEST_P(GraphicsMapperHidlTest, Lock_RAW12) {
+    auto info = mDummyDescriptorInfo;
+    info.format = PixelFormat::RAW12;
+
+    const native_handle_t* bufferHandle;
+    uint32_t stride;
+    ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(
+                                    info, true, Tolerance::kToleranceUnSupported, &stride));
+    if (bufferHandle == nullptr) {
+        GTEST_SUCCEED() << "RAW12 format is unsupported";
+        return;
+    }
+
+    const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+                               static_cast<int32_t>(info.height)};
+    unique_fd fence;
+
+    ASSERT_NO_FATAL_FAILURE(mGralloc->lock(bufferHandle, info.usage, region, fence.release()));
+
+    hidl_vec<uint8_t> vec;
+    ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec));
+    std::vector<PlaneLayout> planeLayouts;
+    ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+
+    ASSERT_EQ(1, planeLayouts.size());
+    auto planeLayout = planeLayouts[0];
+
+    EXPECT_EQ(0, planeLayout.sampleIncrementInBits);
+    EXPECT_EQ(1, planeLayout.horizontalSubsampling);
+    EXPECT_EQ(1, planeLayout.verticalSubsampling);
+
+    ASSERT_EQ(1, planeLayout.components.size());
+    auto planeLayoutComponent = planeLayout.components[0];
+
+    EXPECT_EQ(PlaneLayoutComponentType::RAW,
+              static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value));
+    EXPECT_EQ(0, planeLayoutComponent.offsetInBits % 8);
+    EXPECT_EQ(-1, planeLayoutComponent.sizeInBits);
+
+    ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+}
+
 /**
  * Test IMapper::unlock with bad access region
  */
@@ -1757,6 +1844,84 @@
 }
 
 /**
+ * Test get::metadata with cloned native_handle
+ */
+TEST_P(GraphicsMapperHidlTest, GetMetadataClonedHandle) {
+    const native_handle_t* bufferHandle = nullptr;
+    ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+    const auto dataspace = Dataspace::SRGB_LINEAR;
+    {
+        hidl_vec<uint8_t> metadata;
+        ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(dataspace, &metadata));
+
+        Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_Dataspace, metadata);
+        if (err == Error::UNSUPPORTED) {
+            GTEST_SUCCEED() << "setting this metadata is unsupported";
+            return;
+        }
+        ASSERT_EQ(Error::NONE, err);
+    }
+
+    const native_handle_t* importedHandle;
+    {
+        auto clonedHandle = native_handle_clone(bufferHandle);
+        ASSERT_NO_FATAL_FAILURE(importedHandle = mGralloc->importBuffer(clonedHandle));
+        native_handle_close(clonedHandle);
+        native_handle_delete(clonedHandle);
+    }
+
+    Dataspace realSpace = Dataspace::UNKNOWN;
+    {
+        hidl_vec<uint8_t> metadata;
+        ASSERT_EQ(Error::NONE,
+                  mGralloc->get(importedHandle, gralloc4::MetadataType_Dataspace, &metadata));
+        ASSERT_NO_FATAL_FAILURE(gralloc4::decodeDataspace(metadata, &realSpace));
+    }
+
+    EXPECT_EQ(dataspace, realSpace);
+}
+
+/**
+ * Test set::metadata with cloned native_handle
+ */
+TEST_P(GraphicsMapperHidlTest, SetMetadataClonedHandle) {
+    const native_handle_t* bufferHandle = nullptr;
+    ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+    const native_handle_t* importedHandle;
+    {
+        auto clonedHandle = native_handle_clone(bufferHandle);
+        ASSERT_NO_FATAL_FAILURE(importedHandle = mGralloc->importBuffer(clonedHandle));
+        native_handle_close(clonedHandle);
+        native_handle_delete(clonedHandle);
+    }
+
+    const auto dataspace = Dataspace::SRGB_LINEAR;
+    {
+        hidl_vec<uint8_t> metadata;
+        ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(dataspace, &metadata));
+
+        Error err = mGralloc->set(importedHandle, gralloc4::MetadataType_Dataspace, metadata);
+        if (err == Error::UNSUPPORTED) {
+            GTEST_SUCCEED() << "setting this metadata is unsupported";
+            return;
+        }
+        ASSERT_EQ(Error::NONE, err);
+    }
+
+    Dataspace realSpace = Dataspace::UNKNOWN;
+    {
+        hidl_vec<uint8_t> metadata;
+        ASSERT_EQ(Error::NONE,
+                  mGralloc->get(bufferHandle, gralloc4::MetadataType_Dataspace, &metadata));
+        ASSERT_NO_FATAL_FAILURE(gralloc4::decodeDataspace(metadata, &realSpace));
+    }
+
+    EXPECT_EQ(dataspace, realSpace);
+}
+
+/**
  * Test IMapper::set(metadata) for constant metadata
  */
 TEST_P(GraphicsMapperHidlTest, SetConstantMetadata) {
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index d7f47e8..3b8fbd9 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -160,17 +160,10 @@
      *       ItemsRequestBytes
      *     ]
      *
-     *     SessionTranscript = [
-     *       DeviceEngagementBytes,
-     *       EReaderKeyBytes
-     *     ]
+     *     SessionTranscript = any
      *
-     *     DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
-     *     EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
      *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
      *
-     *     EReaderKey.Pub = COSE_Key    ; Ephemeral public key provided by reader
-     *
      * The public key corresponding to the key used to made signature, can be found in the
      * 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
      * in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
@@ -184,8 +177,12 @@
      *
      * If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
      * part of the key-pair previously generated by createEphemeralKeyPair() must appear
-     * somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
-     * uncompressed form. If this is not satisfied, the call fails with
+     * somewhere in the bytes of the CBOR. Each of these coordinates must appear encoded
+     * with the most significant bits first and use the exact amount of bits indicated by
+     * the key size of the ephemeral keys. For example, if the ephemeral key is using the
+     * P-256 curve then the 32 bytes for the X coordinate encoded with the most significant
+     * bits first must appear somewhere in the CBOR and ditto for the 32 bytes for the Y
+     * coordinate. If this is not satisfied, the call fails with
      * STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
      *
      * @param accessControlProfiles
@@ -298,13 +295,8 @@
      *
      *        DocType = tstr
      *
-     *        SessionTranscript = [
-     *            DeviceEngagementBytes,
-     *            EReaderKeyBytes
-     *        ]
+     *        SessionTranscript = any
      *
-     *        DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
-     *        EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
      *        DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
      *
      *    where
@@ -356,8 +348,9 @@
      *
      *  - subjectPublicKeyInfo: must contain attested public key.
      *
-     * @param out signingKeyBlob contains an encrypted copy of the newly-generated private
-     *     signing key.
+     * @param out signingKeyBlob contains an AES-GCM-ENC(storageKey, R, signingKey, docType)
+     *     where signingKey is an EC private key in uncompressed form. That is, the returned
+     *     blob is an encrypted copy of the newly-generated private signing key.
      *
      * @return an X.509 certificate for the new signing key, signed by the credential key.
      */
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index b7ad283..297fd1d 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -29,9 +29,27 @@
      * Gets the certificate chain for credentialKey which can be used to prove the hardware
      * characteristics to an issuing authority.  Must not be called more than once.
      *
+     * The following non-optional fields for the X.509 certificate shall be set as follows:
+     *
+     *  - version: INTEGER 2 (means v3 certificate).
+     *
+     *  - serialNumber: INTEGER 1 (fixed value: same on all certs).
+     *
+     *  - signature: must be set to ECDSA.
+     *
+     *  - subject: CN shall be set to "Android Identity Credential Key".
+     *
+     *  - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
+     *    values returned in HardwareInformation.
+     *
+     *  - validity: should be from current time and expire at the same time as the
+     *    attestation batch certificate used.
+     *
+     *  - subjectPublicKeyInfo: must contain attested public key.
+     *
      * The certificate chain must be generated using Keymaster Attestation
      * (see https://source.android.com/security/keystore/attestation) with the
-     * following additional requirements:
+     * following additional requirements on the data in the attestation extension:
      *
      *  - The attestationVersion field in the attestation extension must be at least 3.
      *
@@ -109,7 +127,8 @@
      *     in Tag::ATTESTATION_APPLICATION_ID. This schema is described in
      *     https://developer.android.com/training/articles/security-key-attestation#certificate_schema_attestationid
      *
-     * @param attestationChallenge a challenge set by the issuer to ensure freshness.
+     * @param attestationChallenge a challenge set by the issuer to ensure freshness. If
+     *    this is empty, the call fails with STATUS_INVALID_DATA.
      *
      * @return the X.509 certificate chain for the credentialKey
      */
@@ -250,6 +269,7 @@
      *         CredentialKeys = [
      *              bstr,   ; storageKey, a 128-bit AES key
      *              bstr    ; credentialPrivKey, the private key for credentialKey
+     *                      ; in uncompressed form
      *         ]
      *
      * @param out proofOfProvisioningSignature proves to the IA that the credential was imported
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
index 8a00d22..f3c4bbf 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -164,6 +164,7 @@
     }
 
     *outChallenge = challenge;
+    authChallenge_ = challenge;
     return ndk::ScopedAStatus::ok();
 }
 
@@ -223,7 +224,8 @@
         }
 
         if (authToken.challenge != int64_t(authChallenge)) {
-            LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
+            LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
+                       << "doesn't match the challenge we created (" << authChallenge << ")";
             return false;
         }
         return true;
@@ -341,28 +343,6 @@
     //
     // We do this by just searching for the X and Y coordinates.
     if (sessionTranscript.size() > 0) {
-        const cppbor::Array* array = sessionTranscriptItem_->asArray();
-        if (array == nullptr || array->size() != 2) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
-                    "SessionTranscript is not an array with two items"));
-        }
-        const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
-        if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
-                    "First item in SessionTranscript array is not a "
-                    "semantic with value 24"));
-        }
-        const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
-        if (encodedDE == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
-                    "Child of semantic in first item in SessionTranscript "
-                    "array is not a bstr"));
-        }
-        const vector<uint8_t>& bytesDE = encodedDE->value();
-
         auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
         if (!getXYSuccess) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
@@ -370,8 +350,10 @@
                     "Error extracting X and Y from ePub"));
         }
         if (sessionTranscript.size() > 0 &&
-            !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
-              memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
+            !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
+                     ePubX.size()) != nullptr &&
+              memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
+                     ePubY.size()) != nullptr)) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
                     "Did not find ephemeral public key's X and Y coordinates in "
@@ -478,9 +460,10 @@
     }
 
     // Validate all the access control profiles in the requestData.
-    bool haveAuthToken = (authToken.mac.size() > 0);
+    bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
     for (const auto& profile : accessControlProfiles) {
         if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
+            LOG(ERROR) << "Error checking MAC for profile";
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_INVALID_DATA,
                     "Error checking MAC for profile"));
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index 7732c33..c218866 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -65,6 +65,10 @@
                 IIdentityCredentialStore::STATUS_FAILED,
                 "Error attestation certificate previously generated"));
     }
+    if (attestationChallenge.empty()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
+    }
 
     vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
     vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
@@ -165,6 +169,13 @@
                 "userAuthenticationRequired is false but timeout is non-zero"));
     }
 
+    // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
+    if (userAuthenticationRequired && secureUserId == 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "userAuthenticationRequired is true but secureUserId is zero"));
+    }
+
     profile.id = id;
     profile.readerCertificate = readerCertificate;
     profile.userAuthenticationRequired = userAuthenticationRequired;
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 5b075c6..c1f44e7 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -10,20 +10,23 @@
         "VtsIdentityTestUtils.cpp",
         "VtsAttestationTests.cpp",
         "VtsAttestationParserSupport.cpp",
+        "UserAuthTests.cpp",
+        "ReaderAuthTests.cpp",
     ],
     shared_libs: [
-        "android.hardware.keymaster@4.0",
         "libbinder",
         "libcrypto",
-        "libkeymaster_portable",
-        "libsoft_attestation_cert",
-        "libpuresoftkeymasterdevice",
     ],
     static_libs: [
         "libcppbor",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
+        "android.hardware.keymaster@4.0",
         "android.hardware.identity-support-lib",
         "android.hardware.identity-cpp",
         "android.hardware.keymaster-cpp",
+        "android.hardware.keymaster-ndk_platform",
     ],
     test_suites: [
         "general-tests",
diff --git a/identity/aidl/vts/ReaderAuthTests.cpp b/identity/aidl/vts/ReaderAuthTests.cpp
new file mode 100644
index 0000000..680ba5b
--- /dev/null
+++ b/identity/aidl/vts/ReaderAuthTests.cpp
@@ -0,0 +1,596 @@
+/*
+ * 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 "ReaderAuthTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.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 <utility>
+
+#include "VtsIdentityTestUtils.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class ReaderAuthTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+    }
+
+    void provisionData();
+    void retrieveData(const vector<uint8_t>& readerPrivateKey,
+                      const vector<vector<uint8_t>>& readerCertChain, bool expectSuccess,
+                      bool leaveOutAccessibleToAllFromRequestMessage);
+
+    // Set by provisionData
+    vector<uint8_t> readerPublicKey_;
+    vector<uint8_t> readerPrivateKey_;
+    vector<uint8_t> intermediateAPublicKey_;
+    vector<uint8_t> intermediateAPrivateKey_;
+    vector<uint8_t> intermediateBPublicKey_;
+    vector<uint8_t> intermediateBPrivateKey_;
+    vector<uint8_t> intermediateCPublicKey_;
+    vector<uint8_t> intermediateCPrivateKey_;
+
+    vector<uint8_t> cert_A_SelfSigned_;
+
+    vector<uint8_t> cert_B_SelfSigned_;
+
+    vector<uint8_t> cert_B_SignedBy_C_;
+
+    vector<uint8_t> cert_C_SelfSigned_;
+
+    vector<uint8_t> cert_reader_SelfSigned_;
+    vector<uint8_t> cert_reader_SignedBy_A_;
+    vector<uint8_t> cert_reader_SignedBy_B_;
+
+    SecureAccessControlProfile sacp0_;
+    SecureAccessControlProfile sacp1_;
+    SecureAccessControlProfile sacp2_;
+    SecureAccessControlProfile sacp3_;
+
+    vector<uint8_t> encContentAccessibleByA_;
+    vector<uint8_t> encContentAccessibleByAorB_;
+    vector<uint8_t> encContentAccessibleByB_;
+    vector<uint8_t> encContentAccessibleByC_;
+    vector<uint8_t> encContentAccessibleByAll_;
+    vector<uint8_t> encContentAccessibleByNone_;
+
+    vector<uint8_t> credentialData_;
+
+    // Set by retrieveData()
+    bool canGetAccessibleByA_;
+    bool canGetAccessibleByAorB_;
+    bool canGetAccessibleByB_;
+    bool canGetAccessibleByC_;
+    bool canGetAccessibleByAll_;
+    bool canGetAccessibleByNone_;
+
+    sp<IIdentityCredentialStore> credentialStore_;
+};
+
+pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey() {
+    optional<vector<uint8_t>> keyPKCS8 = support::createEcKeyPair();
+    optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPKCS8.value());
+    optional<vector<uint8_t>> privateKey = support::ecKeyPairGetPrivateKey(keyPKCS8.value());
+    return make_pair(publicKey.value(), privateKey.value());
+}
+
+vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
+                                   const vector<uint8_t>& signingKey) {
+    time_t validityNotBefore = 0;
+    time_t validityNotAfter = 0xffffffff;
+    optional<vector<uint8_t>> cert =
+            support::ecPublicKeyGenerateCertificate(publicKey, signingKey, "24601", "Issuer",
+                                                    "Subject", validityNotBefore, validityNotAfter);
+    return cert.value();
+}
+
+void ReaderAuthTests::provisionData() {
+    // Keys and certificates for intermediates.
+    tie(intermediateAPublicKey_, intermediateAPrivateKey_) = generateReaderKey();
+    tie(intermediateBPublicKey_, intermediateBPrivateKey_) = generateReaderKey();
+    tie(intermediateCPublicKey_, intermediateCPrivateKey_) = generateReaderKey();
+
+    cert_A_SelfSigned_ = generateReaderCert(intermediateAPublicKey_, intermediateAPrivateKey_);
+
+    cert_B_SelfSigned_ = generateReaderCert(intermediateBPublicKey_, intermediateBPrivateKey_);
+
+    cert_B_SignedBy_C_ = generateReaderCert(intermediateBPublicKey_, intermediateCPrivateKey_);
+
+    cert_C_SelfSigned_ = generateReaderCert(intermediateCPublicKey_, intermediateCPrivateKey_);
+
+    // Key and self-signed certificate reader
+    tie(readerPublicKey_, readerPrivateKey_) = generateReaderKey();
+    cert_reader_SelfSigned_ = generateReaderCert(readerPublicKey_, readerPrivateKey_);
+
+    // Certificate for reader signed by intermediates
+    cert_reader_SignedBy_A_ = generateReaderCert(readerPublicKey_, intermediateAPrivateKey_);
+    cert_reader_SignedBy_B_ = generateReaderCert(readerPublicKey_, intermediateBPrivateKey_);
+
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    size_t proofOfProvisioningSize =
+            465 + cert_A_SelfSigned_.size() + cert_B_SelfSigned_.size() + cert_C_SelfSigned_.size();
+    ASSERT_TRUE(wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize).isOk());
+
+    // Not in v1 HAL, may fail
+    wc->startPersonalization(4 /* numAccessControlProfiles */,
+                             {6} /* numDataElementsPerNamespace */);
+
+    // AIDL expects certificates wrapped in the Certificate type...
+    Certificate cert_A;
+    Certificate cert_B;
+    Certificate cert_C;
+    cert_A.encodedCertificate = cert_A_SelfSigned_;
+    cert_B.encodedCertificate = cert_B_SelfSigned_;
+    cert_C.encodedCertificate = cert_C_SelfSigned_;
+
+    // Access control profile 0: accessible by A
+    ASSERT_TRUE(wc->addAccessControlProfile(0, cert_A, false, 0, 0, &sacp0_).isOk());
+
+    // Access control profile 1: accessible by B
+    ASSERT_TRUE(wc->addAccessControlProfile(1, cert_B, false, 0, 0, &sacp1_).isOk());
+
+    // Access control profile 2: accessible by C
+    ASSERT_TRUE(wc->addAccessControlProfile(2, cert_C, false, 0, 0, &sacp2_).isOk());
+
+    // Access control profile 3: open access
+    ASSERT_TRUE(wc->addAccessControlProfile(3, {}, false, 0, 0, &sacp3_).isOk());
+
+    // Data Element: "Accessible by A"
+    ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Accessible by A", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByA_).isOk());
+
+    // Data Element: "Accessible by A or B"
+    ASSERT_TRUE(wc->beginAddEntry({0, 1}, "ns", "Accessible by A or B", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAorB_).isOk());
+
+    // Data Element: "Accessible by B"
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Accessible by B", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByB_).isOk());
+
+    // Data Element: "Accessible by C"
+    ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Accessible by C", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByC_).isOk());
+
+    // Data Element: "Accessible by All"
+    ASSERT_TRUE(wc->beginAddEntry({3}, "ns", "Accessible by All", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAll_).isOk());
+
+    // Data Element: "Accessible by None"
+    ASSERT_TRUE(wc->beginAddEntry({}, "ns", "Accessible by None", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByNone_).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    ASSERT_TRUE(wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature).isOk());
+}
+
+RequestDataItem buildRequestDataItem(const string& name, size_t size,
+                                     vector<int32_t> accessControlProfileIds) {
+    RequestDataItem item;
+    item.name = name;
+    item.size = size;
+    item.accessControlProfileIds = accessControlProfileIds;
+    return item;
+}
+
+void ReaderAuthTests::retrieveData(const vector<uint8_t>& readerPrivateKey,
+                                   const vector<vector<uint8_t>>& readerCertChain,
+                                   bool expectSuccess,
+                                   bool leaveOutAccessibleToAllFromRequestMessage) {
+    canGetAccessibleByA_ = false;
+    canGetAccessibleByAorB_ = false;
+    canGetAccessibleByB_ = false;
+    canGetAccessibleByC_ = false;
+    canGetAccessibleByAll_ = false;
+    canGetAccessibleByNone_ = false;
+
+    sp<IIdentityCredential> c;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &c)
+                        .isOk());
+
+    optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
+    optional<vector<uint8_t>> readerEPublicKey =
+            support::ecKeyPairGetPublicKey(readerEKeyPair.value());
+    ASSERT_TRUE(c->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
+
+    vector<uint8_t> eKeyPair;
+    ASSERT_TRUE(c->createEphemeralKeyPair(&eKeyPair).isOk());
+    optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
+
+    // Calculate requestData field and sign it with the reader key.
+    auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey.value());
+    ASSERT_TRUE(getXYSuccess);
+    cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+    vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+    vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+    cppbor::Array sessionTranscript = cppbor::Array()
+                                              .add(cppbor::Semantic(24, deviceEngagementBytes))
+                                              .add(cppbor::Semantic(24, eReaderPubBytes));
+    vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+    vector<uint8_t> itemsRequestBytes;
+    if (leaveOutAccessibleToAllFromRequestMessage) {
+        itemsRequestBytes =
+                cppbor::Map("nameSpaces",
+                            cppbor::Map().add("ns", cppbor::Map()
+                                                            .add("Accessible by A", false)
+                                                            .add("Accessible by A or B", false)
+                                                            .add("Accessible by B", false)
+                                                            .add("Accessible by C", false)
+                                                            .add("Accessible by None", false)))
+                        .encode();
+    } else {
+        itemsRequestBytes =
+                cppbor::Map("nameSpaces",
+                            cppbor::Map().add("ns", cppbor::Map()
+                                                            .add("Accessible by A", false)
+                                                            .add("Accessible by A or B", false)
+                                                            .add("Accessible by B", false)
+                                                            .add("Accessible by C", false)
+                                                            .add("Accessible by All", false)
+                                                            .add("Accessible by None", false)))
+                        .encode();
+    }
+    vector<uint8_t> dataToSign = cppbor::Array()
+                                         .add("ReaderAuthentication")
+                                         .add(sessionTranscript.clone())
+                                         .add(cppbor::Semantic(24, itemsRequestBytes))
+                                         .encode();
+
+    optional<vector<uint8_t>> readerSignature =
+            support::coseSignEcDsa(readerPrivateKey,  // private key for reader
+                                   {},                // content
+                                   dataToSign,        // detached content
+                                   support::certificateChainJoin(readerCertChain));
+    ASSERT_TRUE(readerSignature);
+
+    // Generate the key that will be used to sign AuthenticatedData.
+    vector<uint8_t> signingKeyBlob;
+    Certificate signingKeyCertificate;
+    ASSERT_TRUE(c->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+    RequestNamespace rns;
+    rns.namespaceName = "ns";
+    rns.items.push_back(buildRequestDataItem("Accessible by A", 1, {0}));
+    rns.items.push_back(buildRequestDataItem("Accessible by A or B", 1, {0, 1}));
+    rns.items.push_back(buildRequestDataItem("Accessible by B", 1, {1}));
+    rns.items.push_back(buildRequestDataItem("Accessible by C", 1, {2}));
+    rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {3}));
+    rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
+    // OK to fail, not available in v1 HAL
+    c->setRequestedNamespaces({rns}).isOk();
+
+    // It doesn't matter since no user auth is needed in this particular test,
+    // but for good measure, clear out the tokens we pass to the HAL.
+    HardwareAuthToken authToken;
+    VerificationToken verificationToken;
+    authToken.challenge = 0;
+    authToken.userId = 0;
+    authToken.authenticatorId = 0;
+    authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+    authToken.timestamp.milliSeconds = 0;
+    authToken.mac.clear();
+    verificationToken.challenge = 0;
+    verificationToken.timestamp.milliSeconds = 0;
+    verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
+    verificationToken.mac.clear();
+    // OK to fail, not available in v1 HAL
+    c->setVerificationToken(verificationToken);
+
+    Status status = c->startRetrieval(
+            {sacp0_, sacp1_, sacp2_, sacp3_}, authToken, itemsRequestBytes, signingKeyBlob,
+            sessionTranscriptBytes, readerSignature.value(), {6 /* numDataElementsPerNamespace */});
+    if (expectSuccess) {
+        ASSERT_TRUE(status.isOk());
+    } else {
+        ASSERT_FALSE(status.isOk());
+        return;
+    }
+
+    vector<uint8_t> decrypted;
+
+    status = c->startRetrieveEntryValue("ns", "Accessible by A", 1, {0});
+    if (status.isOk()) {
+        canGetAccessibleByA_ = true;
+        ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByA_, &decrypted).isOk());
+    }
+
+    status = c->startRetrieveEntryValue("ns", "Accessible by A or B", 1, {0, 1});
+    if (status.isOk()) {
+        canGetAccessibleByAorB_ = true;
+        ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByAorB_, &decrypted).isOk());
+    }
+
+    status = c->startRetrieveEntryValue("ns", "Accessible by B", 1, {1});
+    if (status.isOk()) {
+        canGetAccessibleByB_ = true;
+        ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByB_, &decrypted).isOk());
+    }
+
+    status = c->startRetrieveEntryValue("ns", "Accessible by C", 1, {2});
+    if (status.isOk()) {
+        canGetAccessibleByC_ = true;
+        ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByC_, &decrypted).isOk());
+    }
+
+    status = c->startRetrieveEntryValue("ns", "Accessible by All", 1, {3});
+    if (status.isOk()) {
+        canGetAccessibleByAll_ = true;
+        ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByAll_, &decrypted).isOk());
+    }
+
+    status = c->startRetrieveEntryValue("ns", "Accessible by None", 1, {});
+    if (status.isOk()) {
+        canGetAccessibleByNone_ = true;
+        ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByNone_, &decrypted).isOk());
+    }
+
+    vector<uint8_t> mac;
+    vector<uint8_t> deviceNameSpaces;
+    ASSERT_TRUE(c->finishRetrieval(&mac, &deviceNameSpaces).isOk());
+}
+
+TEST_P(ReaderAuthTests, presentingChain_Reader) {
+    provisionData();
+    retrieveData(readerPrivateKey_, {cert_reader_SelfSigned_}, true /* expectSuccess */,
+                 false /* leaveOutAccessibleToAllFromRequestMessage */);
+    EXPECT_FALSE(canGetAccessibleByA_);
+    EXPECT_FALSE(canGetAccessibleByAorB_);
+    EXPECT_FALSE(canGetAccessibleByB_);
+    EXPECT_FALSE(canGetAccessibleByC_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(ReaderAuthTests, presentingChain_Reader_A) {
+    provisionData();
+    retrieveData(readerPrivateKey_, {cert_reader_SignedBy_A_, cert_A_SelfSigned_},
+                 true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+    EXPECT_TRUE(canGetAccessibleByA_);
+    EXPECT_TRUE(canGetAccessibleByAorB_);
+    EXPECT_FALSE(canGetAccessibleByB_);
+    EXPECT_FALSE(canGetAccessibleByC_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(ReaderAuthTests, presentingChain_Reader_B) {
+    provisionData();
+    retrieveData(readerPrivateKey_, {cert_reader_SignedBy_B_, cert_B_SelfSigned_},
+                 true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+    EXPECT_FALSE(canGetAccessibleByA_);
+    EXPECT_TRUE(canGetAccessibleByAorB_);
+    EXPECT_TRUE(canGetAccessibleByB_);
+    EXPECT_FALSE(canGetAccessibleByC_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// This test proves that for the purpose of determining inclusion of an ACP certificate
+// in a presented reader chain, certificate equality is done by comparing public keys,
+// not bitwise comparison of the certificates.
+//
+// Specifically for this test, the ACP is configured with cert_B_SelfSigned_ and the
+// reader is presenting cert_B_SignedBy_C_. Both certificates have the same public
+// key - intermediateBPublicKey_ - but they are signed by different keys.
+//
+TEST_P(ReaderAuthTests, presentingChain_Reader_B_C) {
+    provisionData();
+    retrieveData(readerPrivateKey_,
+                 {cert_reader_SignedBy_B_, cert_B_SignedBy_C_, cert_C_SelfSigned_},
+                 true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+    EXPECT_FALSE(canGetAccessibleByA_);
+    EXPECT_TRUE(canGetAccessibleByAorB_);
+    EXPECT_TRUE(canGetAccessibleByB_);
+    EXPECT_TRUE(canGetAccessibleByC_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// This test presents a reader chain where the chain is invalid because
+// the 2nd certificate in the chain isn't signed by the 3rd one.
+//
+TEST_P(ReaderAuthTests, presentingInvalidChain) {
+    provisionData();
+    retrieveData(readerPrivateKey_,
+                 {cert_reader_SignedBy_B_, cert_B_SelfSigned_, cert_C_SelfSigned_},
+                 false /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+}
+
+// This tests presents a valid reader chain but where requestMessage isn't
+// signed by the private key corresponding to the public key in the top-level
+// certificate.
+//
+TEST_P(ReaderAuthTests, presentingMessageSignedNotByTopLevel) {
+    provisionData();
+    retrieveData(intermediateBPrivateKey_,
+                 {cert_reader_SignedBy_B_, cert_B_SignedBy_C_, cert_C_SelfSigned_},
+                 false /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+}
+
+// This test leaves out "Accessible by All" data element from the signed request
+// message (the CBOR from the reader) while still including this data element at
+// the API level. The call on the API level for said element will fail with
+// STATUS_NOT_IN_REQUEST_MESSAGE but this doesn't prevent the other elements
+// from being returned (if authorized, of course).
+//
+// This test verifies that.
+//
+TEST_P(ReaderAuthTests, limitedMessage) {
+    provisionData();
+    retrieveData(readerPrivateKey_, {cert_reader_SelfSigned_}, true /* expectSuccess */,
+                 true /* leaveOutAccessibleToAllFromRequestMessage */);
+    EXPECT_FALSE(canGetAccessibleByA_);
+    EXPECT_FALSE(canGetAccessibleByAorB_);
+    EXPECT_FALSE(canGetAccessibleByB_);
+    EXPECT_FALSE(canGetAccessibleByC_);
+    EXPECT_FALSE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(ReaderAuthTests, ephemeralKeyNotInSessionTranscript) {
+    provisionData();
+
+    sp<IIdentityCredential> c;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &c)
+                        .isOk());
+
+    optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
+    optional<vector<uint8_t>> readerEPublicKey =
+            support::ecKeyPairGetPublicKey(readerEKeyPair.value());
+    ASSERT_TRUE(c->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
+
+    vector<uint8_t> eKeyPair;
+    ASSERT_TRUE(c->createEphemeralKeyPair(&eKeyPair).isOk());
+    optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
+
+    // Calculate requestData field and sign it with the reader key.
+    auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey.value());
+    ASSERT_TRUE(getXYSuccess);
+    // Instead of include the X and Y coordinates (|ephX| and |ephY|), add NUL bytes instead.
+    vector<uint8_t> nulls(32);
+    cppbor::Map deviceEngagement = cppbor::Map().add("ephX", nulls).add("ephY", nulls);
+    vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+    vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+    cppbor::Array sessionTranscript = cppbor::Array()
+                                              .add(cppbor::Semantic(24, deviceEngagementBytes))
+                                              .add(cppbor::Semantic(24, eReaderPubBytes));
+    vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+    vector<uint8_t> itemsRequestBytes;
+    itemsRequestBytes =
+            cppbor::Map("nameSpaces",
+                        cppbor::Map().add("ns", cppbor::Map()
+                                                        .add("Accessible by A", false)
+                                                        .add("Accessible by A or B", false)
+                                                        .add("Accessible by B", false)
+                                                        .add("Accessible by C", false)
+                                                        .add("Accessible by None", false)))
+                    .encode();
+    vector<uint8_t> dataToSign = cppbor::Array()
+                                         .add("ReaderAuthentication")
+                                         .add(sessionTranscript.clone())
+                                         .add(cppbor::Semantic(24, itemsRequestBytes))
+                                         .encode();
+
+    vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
+    optional<vector<uint8_t>> readerSignature =
+            support::coseSignEcDsa(readerPrivateKey_,  // private key for reader
+                                   {},                 // content
+                                   dataToSign,         // detached content
+                                   support::certificateChainJoin(readerCertChain));
+    ASSERT_TRUE(readerSignature);
+
+    // Generate the key that will be used to sign AuthenticatedData.
+    vector<uint8_t> signingKeyBlob;
+    Certificate signingKeyCertificate;
+    ASSERT_TRUE(c->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+    RequestNamespace rns;
+    rns.namespaceName = "ns";
+    rns.items.push_back(buildRequestDataItem("Accessible by A", 1, {0}));
+    rns.items.push_back(buildRequestDataItem("Accessible by A or B", 1, {0, 1}));
+    rns.items.push_back(buildRequestDataItem("Accessible by B", 1, {1}));
+    rns.items.push_back(buildRequestDataItem("Accessible by C", 1, {2}));
+    rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {3}));
+    rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
+    // OK to fail, not available in v1 HAL
+    c->setRequestedNamespaces({rns}).isOk();
+
+    // It doesn't matter since no user auth is needed in this particular test,
+    // but for good measure, clear out the tokens we pass to the HAL.
+    HardwareAuthToken authToken;
+    VerificationToken verificationToken;
+    authToken.challenge = 0;
+    authToken.userId = 0;
+    authToken.authenticatorId = 0;
+    authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+    authToken.timestamp.milliSeconds = 0;
+    authToken.mac.clear();
+    verificationToken.challenge = 0;
+    verificationToken.timestamp.milliSeconds = 0;
+    verificationToken.securityLevel =
+            ::android::hardware::keymaster::SecurityLevel::TRUSTED_ENVIRONMENT;
+    verificationToken.mac.clear();
+    // OK to fail, not available in v1 HAL
+    c->setVerificationToken(verificationToken);
+
+    // Finally check that STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND is returned.
+    // This proves that the TA checked for X and Y coordinatets and didn't find
+    // them.
+    Status status = c->startRetrieval(
+            {sacp0_, sacp1_, sacp2_, sacp3_}, authToken, itemsRequestBytes, signingKeyBlob,
+            sessionTranscriptBytes, readerSignature.value(), {6 /* numDataElementsPerNamespace */});
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+    ASSERT_EQ(IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+              status.serviceSpecificErrorCode());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        Identity, ReaderAuthTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UserAuthTests.cpp b/identity/aidl/vts/UserAuthTests.cpp
new file mode 100644
index 0000000..5b4c8f1
--- /dev/null
+++ b/identity/aidl/vts/UserAuthTests.cpp
@@ -0,0 +1,473 @@
+/*
+ * 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 "UserAuthTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.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 <utility>
+
+#include "VtsIdentityTestUtils.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class UserAuthTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+    }
+
+    void provisionData();
+    void setupRetrieveData();
+    pair<HardwareAuthToken, VerificationToken> mintTokens(uint64_t challengeForAuthToken,
+                                                          int64_t ageOfAuthTokenMilliSeconds);
+    void retrieveData(HardwareAuthToken authToken, VerificationToken verificationToken,
+                      bool expectSuccess, bool useSessionTranscript);
+
+    // Set by provisionData
+    SecureAccessControlProfile sacp0_;
+    SecureAccessControlProfile sacp1_;
+    SecureAccessControlProfile sacp2_;
+
+    vector<uint8_t> encContentUserAuthPerSession_;
+    vector<uint8_t> encContentUserAuthTimeout_;
+    vector<uint8_t> encContentAccessibleByAll_;
+    vector<uint8_t> encContentAccessibleByNone_;
+
+    vector<uint8_t> credentialData_;
+
+    // Set by setupRetrieveData().
+    int64_t authChallenge_;
+    cppbor::Map sessionTranscript_;
+    sp<IIdentityCredential> credential_;
+
+    // Set by retrieveData()
+    bool canGetUserAuthPerSession_;
+    bool canGetUserAuthTimeout_;
+    bool canGetAccessibleByAll_;
+    bool canGetAccessibleByNone_;
+
+    sp<IIdentityCredentialStore> credentialStore_;
+};
+
+void UserAuthTests::provisionData() {
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    size_t proofOfProvisioningSize = 381;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(3 /* numAccessControlProfiles */,
+                                         {4} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: user auth every session (timeout = 0)
+    ASSERT_TRUE(wc->addAccessControlProfile(0, {}, true, 0, 65 /* secureUserId */, &sacp0_).isOk());
+
+    // Access control profile 1: user auth, 60 seconds timeout
+    ASSERT_TRUE(
+            wc->addAccessControlProfile(1, {}, true, 60000, 65 /* secureUserId */, &sacp1_).isOk());
+
+    // Access control profile 2: open access
+    ASSERT_TRUE(wc->addAccessControlProfile(2, {}, false, 0, 0, &sacp2_).isOk());
+
+    // Data Element: "UserAuth Per Session"
+    ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "UserAuth Per Session", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentUserAuthPerSession_).isOk());
+
+    // Data Element: "UserAuth Timeout"
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "UserAuth Timeout", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentUserAuthTimeout_).isOk());
+
+    // Data Element: "Accessible by All"
+    ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Accessible by All", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAll_).isOk());
+
+    // Data Element: "Accessible by None"
+    ASSERT_TRUE(wc->beginAddEntry({}, "ns", "Accessible by None", 1).isOk());
+    ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByNone_).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+// From ReaderAuthTest.cpp - TODO: consolidate with VtsIdentityTestUtils.h
+pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey();
+vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
+                                   const vector<uint8_t>& signingKey);
+RequestDataItem buildRequestDataItem(const string& name, size_t size,
+                                     vector<int32_t> accessControlProfileIds);
+
+cppbor::Map calcSessionTranscript(const vector<uint8_t>& ePublicKey) {
+    auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey);
+    cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+    vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+    vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+    // Let SessionTranscript be a map here (it's an array in EndToEndTest) just
+    // to check that the implementation can deal with either.
+    cppbor::Map sessionTranscript;
+    sessionTranscript.add(42, cppbor::Semantic(24, deviceEngagementBytes));
+    sessionTranscript.add(43, cppbor::Semantic(24, eReaderPubBytes));
+    return sessionTranscript;
+}
+
+void UserAuthTests::setupRetrieveData() {
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &credential_)
+                        .isOk());
+
+    optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
+    optional<vector<uint8_t>> readerEPublicKey =
+            support::ecKeyPairGetPublicKey(readerEKeyPair.value());
+    ASSERT_TRUE(credential_->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
+
+    vector<uint8_t> eKeyPair;
+    ASSERT_TRUE(credential_->createEphemeralKeyPair(&eKeyPair).isOk());
+    optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
+    sessionTranscript_ = calcSessionTranscript(ePublicKey.value());
+
+    Status status = credential_->createAuthChallenge(&authChallenge_);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+void UserAuthTests::retrieveData(HardwareAuthToken authToken, VerificationToken verificationToken,
+                                 bool expectSuccess, bool useSessionTranscript) {
+    canGetUserAuthPerSession_ = false;
+    canGetUserAuthTimeout_ = false;
+    canGetAccessibleByAll_ = false;
+    canGetAccessibleByNone_ = false;
+
+    vector<uint8_t> itemsRequestBytes;
+    vector<uint8_t> sessionTranscriptBytes;
+    if (useSessionTranscript) {
+        sessionTranscriptBytes = sessionTranscript_.encode();
+
+        itemsRequestBytes =
+                cppbor::Map("nameSpaces",
+                            cppbor::Map().add("ns", cppbor::Map()
+                                                            .add("UserAuth Per Session", false)
+                                                            .add("UserAuth Timeout", false)
+                                                            .add("Accessible by All", false)
+                                                            .add("Accessible by None", false)))
+                        .encode();
+        vector<uint8_t> dataToSign = cppbor::Array()
+                                             .add("ReaderAuthentication")
+                                             .add(sessionTranscript_.clone())
+                                             .add(cppbor::Semantic(24, itemsRequestBytes))
+                                             .encode();
+    }
+
+    // Generate the key that will be used to sign AuthenticatedData.
+    vector<uint8_t> signingKeyBlob;
+    Certificate signingKeyCertificate;
+    ASSERT_TRUE(
+            credential_->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+    RequestNamespace rns;
+    rns.namespaceName = "ns";
+    rns.items.push_back(buildRequestDataItem("UserAuth Per Session", 1, {0}));
+    rns.items.push_back(buildRequestDataItem("UserAuth Timeout", 1, {1}));
+    rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {2}));
+    rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
+    // OK to fail, not available in v1 HAL
+    credential_->setRequestedNamespaces({rns}).isOk();
+
+    // OK to fail, not available in v1 HAL
+    credential_->setVerificationToken(verificationToken);
+
+    Status status = credential_->startRetrieval({sacp0_, sacp1_, sacp2_}, authToken,
+                                                itemsRequestBytes, signingKeyBlob,
+                                                sessionTranscriptBytes, {} /* readerSignature */,
+                                                {4 /* numDataElementsPerNamespace */});
+    if (expectSuccess) {
+        ASSERT_TRUE(status.isOk());
+    } else {
+        ASSERT_FALSE(status.isOk());
+        return;
+    }
+
+    vector<uint8_t> decrypted;
+
+    status = credential_->startRetrieveEntryValue("ns", "UserAuth Per Session", 1, {0});
+    if (status.isOk()) {
+        canGetUserAuthPerSession_ = true;
+        ASSERT_TRUE(
+                credential_->retrieveEntryValue(encContentUserAuthPerSession_, &decrypted).isOk());
+    }
+
+    status = credential_->startRetrieveEntryValue("ns", "UserAuth Timeout", 1, {1});
+    if (status.isOk()) {
+        canGetUserAuthTimeout_ = true;
+        ASSERT_TRUE(credential_->retrieveEntryValue(encContentUserAuthTimeout_, &decrypted).isOk());
+    }
+
+    status = credential_->startRetrieveEntryValue("ns", "Accessible by All", 1, {2});
+    if (status.isOk()) {
+        canGetAccessibleByAll_ = true;
+        ASSERT_TRUE(credential_->retrieveEntryValue(encContentAccessibleByAll_, &decrypted).isOk());
+    }
+
+    status = credential_->startRetrieveEntryValue("ns", "Accessible by None", 1, {});
+    if (status.isOk()) {
+        canGetAccessibleByNone_ = true;
+        ASSERT_TRUE(
+                credential_->retrieveEntryValue(encContentAccessibleByNone_, &decrypted).isOk());
+    }
+
+    vector<uint8_t> mac;
+    vector<uint8_t> deviceNameSpaces;
+    ASSERT_TRUE(credential_->finishRetrieval(&mac, &deviceNameSpaces).isOk());
+}
+
+pair<HardwareAuthToken, VerificationToken> UserAuthTests::mintTokens(
+        uint64_t challengeForAuthToken, int64_t ageOfAuthTokenMilliSeconds) {
+    HardwareAuthToken authToken;
+    VerificationToken verificationToken;
+
+    uint64_t epochMilliseconds = 1000ULL * 1000ULL * 1000ULL * 1000ULL;
+
+    authToken.challenge = challengeForAuthToken;
+    authToken.userId = 65;
+    authToken.authenticatorId = 0;
+    authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+    authToken.timestamp.milliSeconds = epochMilliseconds - ageOfAuthTokenMilliSeconds;
+    authToken.mac.clear();
+    verificationToken.challenge = authChallenge_;
+    verificationToken.timestamp.milliSeconds = epochMilliseconds;
+    verificationToken.securityLevel =
+            ::android::hardware::keymaster::SecurityLevel::TRUSTED_ENVIRONMENT;
+    verificationToken.mac.clear();
+    return make_pair(authToken, verificationToken);
+}
+
+TEST_P(UserAuthTests, GoodChallenge) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(authChallenge_,  // challengeForAuthToken
+                                                     0);              // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_TRUE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, OtherChallenge) {
+    provisionData();
+    setupRetrieveData();
+    uint64_t otherChallenge = authChallenge_ ^ 0x12345678;
+    auto [authToken, verificationToken] = mintTokens(otherChallenge,  // challengeForAuthToken
+                                                     0);              // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, NoChallenge) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(0,   // challengeForAuthToken
+                                                     0);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenAgeZero) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(0,   // challengeForAuthToken
+                                                     0);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenFromTheFuture) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(0,           // challengeForAuthToken
+                                                     -1 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_FALSE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenInsideTimeout) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(0,           // challengeForAuthToken
+                                                     30 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenOutsideTimeout) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(0,           // challengeForAuthToken
+                                                     61 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_FALSE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// The API works even when there's no SessionTranscript / itemsRequest.
+// Verify that.
+TEST_P(UserAuthTests, NoSessionTranscript) {
+    provisionData();
+    setupRetrieveData();
+    auto [authToken, verificationToken] = mintTokens(0,          // challengeForAuthToken
+                                                     1 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 false /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// This test verifies that it's possible to do multiple requests as long
+// as the sessionTranscript doesn't change.
+//
+TEST_P(UserAuthTests, MultipleRequestsSameSessionTranscript) {
+    provisionData();
+    setupRetrieveData();
+
+    // First we try with a stale authToken
+    //
+    auto [authToken, verificationToken] = mintTokens(0,           // challengeForAuthToken
+                                                     61 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_FALSE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+
+    // Then we get a new authToken and try again.
+    tie(authToken, verificationToken) = mintTokens(0,          // challengeForAuthToken
+                                                   5 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_TRUE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// Like MultipleRequestsSameSessionTranscript but we change the sessionTranscript
+// between the two calls. This test verifies that change is detected and the
+// second request fails.
+//
+TEST_P(UserAuthTests, MultipleRequestsSessionTranscriptChanges) {
+    provisionData();
+    setupRetrieveData();
+
+    // First we try with a stale authToken
+    //
+    auto [authToken, verificationToken] = mintTokens(0,           // challengeForAuthToken
+                                                     61 * 1000);  // ageOfAuthTokenMilliSeconds
+    retrieveData(authToken, verificationToken, true /* expectSuccess */,
+                 true /* useSessionTranscript */);
+    EXPECT_FALSE(canGetUserAuthPerSession_);
+    EXPECT_FALSE(canGetUserAuthTimeout_);
+    EXPECT_TRUE(canGetAccessibleByAll_);
+    EXPECT_FALSE(canGetAccessibleByNone_);
+
+    // Then we get a new authToken and try again.
+    tie(authToken, verificationToken) = mintTokens(0,          // challengeForAuthToken
+                                                   5 * 1000);  // ageOfAuthTokenMilliSeconds
+
+    // Change sessionTranscript...
+    optional<vector<uint8_t>> eKeyPairNew = support::createEcKeyPair();
+    optional<vector<uint8_t>> ePublicKeyNew = support::ecKeyPairGetPublicKey(eKeyPairNew.value());
+    sessionTranscript_ = calcSessionTranscript(ePublicKeyNew.value());
+
+    // ... and expect failure.
+    retrieveData(authToken, verificationToken, false /* expectSuccess */,
+                 true /* useSessionTranscript */);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        Identity, UserAuthTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 00b5893..c7cdfc7 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -61,51 +61,6 @@
     sp<IIdentityCredentialStore> credentialStore_;
 };
 
-TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeEmptyId) {
-    Status result;
-
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
-    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);
-
-    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
-                               << endl;
-
-    EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
-                                               attestationApplicationId, hwInfo));
-}
-
-TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeNonemptyId) {
-    Status result;
-
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
-    sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
-
-    vector<uint8_t> attestationChallenge;
-    vector<Certificate> attestationCertificate;
-    string applicationId = "Attestation Verification";
-    vector<uint8_t> attestationApplicationId = {applicationId.begin(), applicationId.end()};
-
-    result = writableCredential->getAttestationCertificate(
-            attestationApplicationId, attestationChallenge, &attestationCertificate);
-
-    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
-                               << endl;
-    EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
-                                               attestationApplicationId, hwInfo));
-}
-
 TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeEmptyId) {
     Status result;
 
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
index 464ab0c..a0c4416 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
@@ -27,15 +27,18 @@
 #include <gtest/gtest.h>
 #include <future>
 #include <map>
+#include <tuple>
 
 #include "VtsIdentityTestUtils.h"
 
 namespace android::hardware::identity {
 
 using std::endl;
+using std::make_tuple;
 using std::map;
 using std::optional;
 using std::string;
+using std::tuple;
 using std::vector;
 
 using ::android::sp;
@@ -66,6 +69,61 @@
     ASSERT_GE(info.dataChunkSize, 256);
 }
 
+tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
+        const vector<uint8_t>& credentialData) {
+    string docType;
+    vector<uint8_t> storageKey;
+    vector<uint8_t> credentialPrivKey;
+
+    auto [item, _, message] = cppbor::parse(credentialData);
+    if (item == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+
+    const cppbor::Array* arrayItem = item->asArray();
+    if (arrayItem == nullptr || arrayItem->size() != 3) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+
+    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+    const cppbor::Bool* testCredentialItem =
+            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+                                                    : nullptr);
+    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+    if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+        encryptedCredentialKeysItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+
+    docType = docTypeItem->value();
+
+    vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
+    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+    const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
+    optional<vector<uint8_t>> decryptedCredentialKeys =
+            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+    if (!decryptedCredentialKeys) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+
+    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+    if (dckItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+    const cppbor::Array* dckArrayItem = dckItem->asArray();
+    if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+    const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+    if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey);
+    }
+    storageKey = storageKeyItem->value();
+    credentialPrivKey = credentialPrivKeyItem->value();
+    return make_tuple(true, docType, storageKey, credentialPrivKey);
+}
+
 TEST_P(IdentityAidl, createAndRetrieveCredential) {
     // First, generate a key-pair for the reader since its public key will be
     // part of the request data.
@@ -155,6 +213,7 @@
             writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
                     .isOk());
 
+    // Validate the proofOfProvisioning which was returned
     optional<vector<uint8_t>> proofOfProvisioning =
             support::coseSignGetPayload(proofOfProvisioningSignature);
     ASSERT_TRUE(proofOfProvisioning);
@@ -215,6 +274,22 @@
                                                  credentialPubKey.value()));
     writableCredential = nullptr;
 
+    // Extract doctype, storage key, and credentialPrivKey from credentialData... this works
+    // only because we asked for a test-credential meaning that the HBK is all zeroes.
+    auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey] =
+            extractFromTestCredentialData(credentialData);
+    ASSERT_TRUE(exSuccess);
+    ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
+    // ... check that the public key derived from the private key matches what was
+    // in the certificate.
+    optional<vector<uint8_t>> exCredentialKeyPair =
+            support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
+    ASSERT_TRUE(exCredentialKeyPair);
+    optional<vector<uint8_t>> exCredentialPubKey =
+            support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
+    ASSERT_TRUE(exCredentialPubKey);
+    ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
+
     // Now that the credential has been provisioned, read it back and check the
     // correct data is returned.
     sp<IIdentityCredential> credential;
@@ -287,6 +362,24 @@
     vector<uint8_t> signingKeyBlob;
     Certificate signingKeyCertificate;
     ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+    optional<vector<uint8_t>> signingPubKey =
+            support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+    EXPECT_TRUE(signingPubKey);
+
+    // Since we're using a test-credential we know storageKey meaning we can get the
+    // private key. Do this, derive the public key from it, and check this matches what
+    // is in the certificate...
+    const vector<uint8_t> exDocTypeVec(exDocType.begin(), exDocType.end());
+    optional<vector<uint8_t>> exSigningPrivKey =
+            support::decryptAes128Gcm(exStorageKey, signingKeyBlob, exDocTypeVec);
+    ASSERT_TRUE(exSigningPrivKey);
+    optional<vector<uint8_t>> exSigningKeyPair =
+            support::ecPrivateKeyToKeyPair(exSigningPrivKey.value());
+    ASSERT_TRUE(exSigningKeyPair);
+    optional<vector<uint8_t>> exSigningPubKey =
+            support::ecKeyPairGetPublicKey(exSigningKeyPair.value());
+    ASSERT_TRUE(exSigningPubKey);
+    ASSERT_EQ(exSigningPubKey.value(), signingPubKey.value());
 
     vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
     // OK to fail, not available in v1 HAL
@@ -316,6 +409,9 @@
             content.insert(content.end(), chunk.begin(), chunk.end());
         }
         EXPECT_EQ(content, entry.valueCbor);
+
+        // TODO: also use |exStorageKey| to decrypt data and check it's the same as whatt
+        // the HAL returns...
     }
 
     vector<uint8_t> mac;
@@ -346,15 +442,12 @@
     deviceAuthentication.add(docType);
     deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
     vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
-    optional<vector<uint8_t>> signingPublicKey =
-            support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
-    EXPECT_TRUE(signingPublicKey);
 
     // Derive the key used for MACing.
     optional<vector<uint8_t>> readerEphemeralPrivateKey =
             support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
     optional<vector<uint8_t>> sharedSecret =
-            support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
+            support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
     ASSERT_TRUE(sharedSecret);
     vector<uint8_t> salt = {0x00};
     vector<uint8_t> info = {};
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 8b0c050..b572b0f 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -69,11 +69,10 @@
     result = writableCredential->getAttestationCertificate(
             attestationApplicationId, attestationChallenge, &attestationCertificate);
 
-    EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
-                               << endl;
-
-    EXPECT_TRUE(test_utils::validateAttestationCertificate(
-            attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo));
+    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, verifyAttestationSuccessWithChallenge) {
@@ -130,6 +129,7 @@
 
     // First call should go through
     const vector<int32_t> entryCounts = {2, 4};
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
     result = writableCredential->startPersonalization(5, entryCounts);
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
@@ -151,18 +151,8 @@
 
     // 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);
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
+    result = writableCredential->startPersonalization(1, entryCounts);
     EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 }
@@ -174,7 +164,8 @@
 
     // Verify minimal number of profile count and entry count
     const vector<int32_t> entryCounts = {1};
-    writableCredential->startPersonalization(1, entryCounts);
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
+    result = writableCredential->startPersonalization(1, entryCounts);
     EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 }
@@ -186,7 +177,8 @@
 
     // Verify set a large number of profile count and entry count is ok
     const vector<int32_t> entryCounts = {3000};
-    writableCredential->startPersonalization(3500, entryCounts);
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
+    result = writableCredential->startPersonalization(25, entryCounts);
     EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 }
@@ -198,7 +190,8 @@
 
     // Enter mismatched entry and profile numbers
     const vector<int32_t> entryCounts = {5, 6};
-    writableCredential->startPersonalization(5, entryCounts);
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
+    result = writableCredential->startPersonalization(5, entryCounts);
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
@@ -234,7 +227,8 @@
     ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
 
     const vector<int32_t> entryCounts = {3, 6};
-    writableCredential->startPersonalization(3, entryCounts);
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
+    result = writableCredential->startPersonalization(3, entryCounts);
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
@@ -251,9 +245,10 @@
         SecureAccessControlProfile profile;
         Certificate cert;
         cert.encodedCertificate = testProfile.readerCertificate;
+        int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0;
         result = writableCredential->addAccessControlProfile(
                 testProfile.id, cert, testProfile.userAuthenticationRequired,
-                testProfile.timeoutMillis, 0, &profile);
+                testProfile.timeoutMillis, secureUserId, &profile);
 
         if (expectOk) {
             expectOk = false;
@@ -554,7 +549,7 @@
     ;
     // OK to fail, not available in v1 HAL
     writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
-    writableCredential->startPersonalization(3, entryCounts);
+    result = writableCredential->startPersonalization(3, entryCounts);
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
@@ -608,7 +603,8 @@
     // 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);
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
+    result = writableCredential->startPersonalization(3, entryCounts);
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
@@ -674,6 +670,7 @@
     ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
 
     const vector<int32_t> entryCounts = {1};
+    writableCredential->setExpectedProofOfProvisioningSize(123456);
     Status result = writableCredential->startPersonalization(1, entryCounts);
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
index aaebcbe..b6ed80f 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp
@@ -96,9 +96,10 @@
         SecureAccessControlProfile profile;
         Certificate cert;
         cert.encodedCertificate = testProfile.readerCertificate;
+        int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0;
         result = writableCredential->addAccessControlProfile(
                 testProfile.id, cert, testProfile.userAuthenticationRequired,
-                testProfile.timeoutMillis, 0, &profile);
+                testProfile.timeoutMillis, secureUserId, &profile);
 
         // Don't use assert so all errors can be outputed.  Then return
         // instead of exit even on errors so caller can decide.
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 507e914..f7ec7c5 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -33,6 +33,7 @@
 using ::std::string;
 using ::std::tuple;
 using ::std::vector;
+using ::std::pair;
 
 // ---------------------------------------------------------------------------
 // Miscellaneous utilities.
@@ -119,6 +120,12 @@
 optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
         const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId);
 
+// Like createEcKeyPairAndAttestation() but allows you to choose the public key.
+//
+optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
+        const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge,
+        const vector<uint8_t>& applicationId);
+
 // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
 // PKCS#8 encoded key-pair.
 //
@@ -134,6 +141,11 @@
 //
 optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair);
 
+// Creates a PKCS#8 encoded key-pair from a private key (which must be uncompressed,
+// e.g. 32 bytes). The public key is derived from the given private key..
+//
+optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey);
+
 // For an EC key |keyPair| encoded in PKCS#8 format, creates a PKCS#12 structure
 // with the key-pair (not using a password to encrypt the data). The public key
 // in the created structure is included as a certificate, using the given fields
@@ -150,6 +162,12 @@
 //
 optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data);
 
+// Like signEcDsa() but instead of taking the data to be signed, takes a digest
+// of it instead.
+//
+optional<vector<uint8_t>> signEcDsaDigest(const vector<uint8_t>& key,
+                                          const vector<uint8_t>& dataDigest);
+
 // Calculates the HMAC with SHA-256 for |data| using |key|. The calculated HMAC
 // is returned and will be 32 bytes.
 //
@@ -170,6 +188,27 @@
 //
 optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& certificateChain);
 
+// Extracts the public-key from the top-most certificate in |certificateChain|
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// Return offset and size of the public-key
+//
+optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate);
+
+// Extracts the TbsCertificate from the top-most certificate in |certificateChain|
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// Return offset and size of the TbsCertificate
+//
+optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>& x509Certificate);
+
+// Extracts the Signature from the top-most certificate in |certificateChain|
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// Return offset and size of the Signature
+//
+optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate);
+
 // Generates a X.509 certificate for |publicKey| (which must be in the format
 // returned by ecKeyPairGetPublicKey()).
 //
@@ -226,6 +265,11 @@
 //
 bool certificateChainValidate(const vector<uint8_t>& certificateChain);
 
+// Returns true if |certificate| is signed by |publicKey|.
+//
+bool certificateSignedByPublicKey(const vector<uint8_t>& certificate,
+                                  const vector<uint8_t>& publicKey);
+
 // Signs |data| and |detachedContent| with |key| (which must be in the format
 // returned by ecKeyPairGetPrivateKey()).
 //
@@ -238,6 +282,21 @@
                                         const vector<uint8_t>& detachedContent,
                                         const vector<uint8_t>& certificateChain);
 
+// Creates a COSE_Signature1 where |signatureToBeSigned| is the ECDSA signature
+// of the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process".
+//
+// The |signatureToBeSigned| is expected to be 64 bytes and contain the R value,
+// then the S value.
+//
+// The |data| parameter will be included in the COSE_Sign1 CBOR.
+//
+// If |certificateChain| is non-empty it's included in the 'x5chain'
+// protected header element (as as described in'draft-ietf-cose-x509-04').
+//
+optional<vector<uint8_t>> coseSignEcDsaWithSignature(const vector<uint8_t>& signatureToBeSigned,
+                                                     const vector<uint8_t>& data,
+                                                     const vector<uint8_t>& certificateChain);
+
 // Checks that |signatureCoseSign1| (in COSE_Sign1 format) is a valid signature
 // made with |public_key| (which must be in the format returned by
 // ecKeyPairGetPublicKey()) where |detachedContent| is the detached content.
@@ -246,9 +305,23 @@
                              const vector<uint8_t>& detachedContent,
                              const vector<uint8_t>& publicKey);
 
+// Converts a DER-encoded signature to the format used in 'signature' bstr in COSE_Sign1.
+bool ecdsaSignatureDerToCose(const vector<uint8_t>& ecdsaDerSignature,
+                             vector<uint8_t>& ecdsaCoseSignature);
+
+// Converts from the format in in 'signature' bstr in COSE_Sign1 to DER encoding.
+bool ecdsaSignatureCoseToDer(const vector<uint8_t>& ecdsaCoseSignature,
+                             vector<uint8_t>& ecdsaDerSignature);
+
 // Extracts the payload from a COSE_Sign1.
 optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1);
 
+// Extracts the signature (of the ToBeSigned CBOR) from a COSE_Sign1.
+optional<vector<uint8_t>> coseSignGetSignature(const vector<uint8_t>& signatureCoseSign1);
+
+// Extracts the signature algorithm from a COSE_Sign1.
+optional<int> coseSignGetAlg(const vector<uint8_t>& signatureCoseSign1);
+
 // Extracts the X.509 certificate chain, if present. Returns the data as a
 // concatenated chain of DER-encoded X.509 certificates
 //
@@ -264,6 +337,16 @@
 optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint8_t>& data,
                                    const vector<uint8_t>& detachedContent);
 
+// Creates a COSE_Mac0 where |digestToBeMaced| is the HMAC-SHA256
+// of the ToBeMaced CBOR from RFC 8051 "6.3. How to Compute and Verify a MAC".
+//
+// The |digestToBeMaced| is expected to be 32 bytes.
+//
+// The |data| parameter will be included in the COSE_Mac0 CBOR.
+//
+optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMaced,
+                                            const vector<uint8_t>& data);
+
 // ---------------------------------------------------------------------------
 // Utility functions specific to IdentityCredential.
 // ---------------------------------------------------------------------------
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index dc49ddc..8e099e7 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -24,6 +24,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <time.h>
+#include <chrono>
 #include <iomanip>
 
 #include <openssl/aes.h>
@@ -684,6 +685,48 @@
     return true;
 }
 
+bool certificateSignedByPublicKey(const vector<uint8_t>& certificate,
+                                  const vector<uint8_t>& publicKey) {
+    const unsigned char* p = certificate.data();
+    auto x509 = X509_Ptr(d2i_X509(nullptr, &p, certificate.size()));
+    if (x509 == nullptr) {
+        LOG(ERROR) << "Error parsing X509 certificate";
+        return false;
+    }
+
+    auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+    auto point = EC_POINT_Ptr(EC_POINT_new(group.get()));
+    if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) !=
+        1) {
+        LOG(ERROR) << "Error decoding publicKey";
+        return false;
+    }
+    auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (ecKey.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return false;
+    }
+    if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) {
+        LOG(ERROR) << "Error setting group";
+        return false;
+    }
+    if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
+        LOG(ERROR) << "Error setting point";
+        return false;
+    }
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+        LOG(ERROR) << "Error setting key";
+        return false;
+    }
+
+    if (X509_verify(x509.get(), pkey.get()) != 1) {
+        return false;
+    }
+
+    return true;
+}
+
 // TODO: Right now the only check we perform is to check that each certificate
 //       is signed by its successor. We should - but currently don't - also check
 //       things like valid dates etc.
@@ -770,7 +813,8 @@
     return ret;
 }
 
-optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) {
+optional<vector<uint8_t>> signEcDsaDigest(const vector<uint8_t>& key,
+                                          const vector<uint8_t>& dataDigest) {
     auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr));
     if (bn.get() == nullptr) {
         LOG(ERROR) << "Error creating BIGNUM";
@@ -783,8 +827,7 @@
         return {};
     }
 
-    auto digest = sha256(data);
-    ECDSA_SIG* sig = ECDSA_do_sign(digest.data(), digest.size(), ec_key.get());
+    ECDSA_SIG* sig = ECDSA_do_sign(dataDigest.data(), dataDigest.size(), ec_key.get());
     if (sig == nullptr) {
         LOG(ERROR) << "Error signing digest";
         return {};
@@ -798,6 +841,10 @@
     return signature;
 }
 
+optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) {
+    return signEcDsaDigest(key, sha256(data));
+}
+
 optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<uint8_t>& data) {
     HMAC_CTX ctx;
     HMAC_CTX_init(&ctx);
@@ -955,6 +1002,51 @@
     return make_pair(keyPair, attestationCert.value());
 }
 
+optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
+        const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge,
+        const vector<uint8_t>& applicationId) {
+    auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+    auto point = EC_POINT_Ptr(EC_POINT_new(group.get()));
+    if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) !=
+        1) {
+        LOG(ERROR) << "Error decoding publicKey";
+        return {};
+    }
+    auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (ecKey.get() == nullptr || pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return {};
+    }
+    if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) {
+        LOG(ERROR) << "Error setting group";
+        return {};
+    }
+    if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) {
+        LOG(ERROR) << "Error setting point";
+        return {};
+    }
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+        LOG(ERROR) << "Error setting key";
+        return {};
+    }
+
+    uint64_t now = (std::chrono::duration_cast<std::chrono::nanoseconds>(
+                    std::chrono::system_clock::now().time_since_epoch()).
+                    count()/ 1000000000);
+    uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
+    uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+
+    optional<vector<vector<uint8_t>>> attestationCert =
+            createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+    if (!attestationCert) {
+        LOG(ERROR) << "Error create attestation from key and challenge";
+        return {};
+    }
+
+    return attestationCert.value();
+}
+
 optional<vector<uint8_t>> createEcKeyPair() {
     auto ec_key = EC_KEY_Ptr(EC_KEY_new());
     auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
@@ -1047,6 +1139,42 @@
     return privateKey;
 }
 
+optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey) {
+    auto bn = BIGNUM_Ptr(BN_bin2bn(privateKey.data(), privateKey.size(), nullptr));
+    if (bn.get() == nullptr) {
+        LOG(ERROR) << "Error creating BIGNUM";
+        return {};
+    }
+
+    auto ecKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+    if (EC_KEY_set_private_key(ecKey.get(), bn.get()) != 1) {
+        LOG(ERROR) << "Error setting private key from BIGNUM";
+        return {};
+    }
+
+    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    if (pkey.get() == nullptr) {
+        LOG(ERROR) << "Memory allocation failed";
+        return {};
+    }
+
+    if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+        LOG(ERROR) << "Error getting private key";
+        return {};
+    }
+
+    int size = i2d_PrivateKey(pkey.get(), nullptr);
+    if (size == 0) {
+        LOG(ERROR) << "Error generating public key encoding";
+        return {};
+    }
+    vector<uint8_t> keyPair;
+    keyPair.resize(size);
+    unsigned char* p = keyPair.data();
+    i2d_PrivateKey(pkey.get(), &p);
+    return keyPair;
+}
+
 optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, const string& name,
                                              const string& serialDecimal, const string& issuer,
                                              const string& subject, time_t validityNotBefore,
@@ -1441,6 +1569,120 @@
     return publicKey;
 }
 
+optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate) {
+    vector<X509_Ptr> certs;
+    if (!parseX509Certificates(x509Certificate, certs)) {
+        return {};
+    }
+    if (certs.size() < 1) {
+        LOG(ERROR) << "No certificates in chain";
+        return {};
+    }
+
+    auto pkey = EVP_PKEY_Ptr(X509_get_pubkey(certs[0].get()));
+    if (pkey.get() == nullptr) {
+        LOG(ERROR) << "No public key";
+        return {};
+    }
+
+    auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pkey.get()));
+    if (ecKey.get() == nullptr) {
+        LOG(ERROR) << "Failed getting EC key";
+        return {};
+    }
+
+    auto ecGroup = EC_KEY_get0_group(ecKey.get());
+    auto ecPoint = EC_KEY_get0_public_key(ecKey.get());
+    int size = EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0,
+                                  nullptr);
+    if (size == 0) {
+        LOG(ERROR) << "Error generating public key encoding";
+        return {};
+    }
+    vector<uint8_t> publicKey;
+    publicKey.resize(size);
+    EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(),
+                       publicKey.size(), nullptr);
+
+    size_t publicKeyOffset = 0;
+    size_t publicKeySize = (size_t)size;
+    void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(),
+                            (const void*)publicKey.data(), publicKey.size());
+
+    if (location == NULL) {
+        LOG(ERROR) << "Error finding publicKey from x509Certificate";
+        return {};
+    }
+    publicKeyOffset = (size_t)((const char*)location - (const char*)x509Certificate.data());
+
+    return std::make_pair(publicKeyOffset, publicKeySize);
+}
+
+optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>& x509Certificate) {
+    vector<X509_Ptr> certs;
+    if (!parseX509Certificates(x509Certificate, certs)) {
+        return {};
+    }
+    if (certs.size() < 1) {
+        LOG(ERROR) << "No certificates in chain";
+        return {};
+    }
+
+    unsigned char* buf = NULL;
+    int len = i2d_re_X509_tbs(certs[0].get(), &buf);
+    if ((len < 0) || (buf == NULL)) {
+        LOG(ERROR) << "fail to extract tbsCertificate in x509Certificate";
+        return {};
+    }
+
+    vector<uint8_t> tbsCertificate(len);
+    memcpy(tbsCertificate.data(), buf, len);
+
+    size_t tbsCertificateOffset = 0;
+    size_t tbsCertificateSize = (size_t)len;
+    void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(),
+                            (const void*)tbsCertificate.data(), tbsCertificate.size());
+
+    if (location == NULL) {
+        LOG(ERROR) << "Error finding tbsCertificate from x509Certificate";
+        return {};
+    }
+    tbsCertificateOffset = (size_t)((const char*)location - (const char*)x509Certificate.data());
+
+    return std::make_pair(tbsCertificateOffset, tbsCertificateSize);
+}
+
+optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate) {
+    vector<X509_Ptr> certs;
+    if (!parseX509Certificates(x509Certificate, certs)) {
+        return {};
+    }
+    if (certs.size() < 1) {
+        LOG(ERROR) << "No certificates in chain";
+        return {};
+    }
+
+    ASN1_BIT_STRING* psig;
+    X509_ALGOR* palg;
+    X509_get0_signature((const ASN1_BIT_STRING**)&psig, (const X509_ALGOR**)&palg, certs[0].get());
+
+    vector<char> signature(psig->length);
+    memcpy(signature.data(), psig->data, psig->length);
+
+    size_t signatureOffset = 0;
+    size_t signatureSize = (size_t)psig->length;
+    void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(),
+                            (const void*)signature.data(), signature.size());
+
+    if (location == NULL) {
+        LOG(ERROR) << "Error finding signature from x509Certificate";
+        return {};
+    }
+    signatureOffset = (size_t)((const char*)location - (const char*)x509Certificate.data());
+
+    return std::make_pair(signatureOffset, signatureSize);
+}
+
 // ---------------------------------------------------------------------------
 // COSE Utility Functions
 // ---------------------------------------------------------------------------
@@ -1538,6 +1780,55 @@
     return true;
 }
 
+optional<vector<uint8_t>> coseSignEcDsaWithSignature(const vector<uint8_t>& signatureToBeSigned,
+                                                     const vector<uint8_t>& data,
+                                                     const vector<uint8_t>& certificateChain) {
+    if (signatureToBeSigned.size() != 64) {
+        LOG(ERROR) << "Invalid size for signatureToBeSigned, expected 64 got "
+                   << signatureToBeSigned.size();
+        return {};
+    }
+
+    cppbor::Map unprotectedHeaders;
+    cppbor::Map protectedHeaders;
+
+    protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_ECDSA_256);
+
+    if (certificateChain.size() != 0) {
+        optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certificateChain);
+        if (!certs) {
+            LOG(ERROR) << "Error splitting certificate chain";
+            return {};
+        }
+        if (certs.value().size() == 1) {
+            unprotectedHeaders.add(COSE_LABEL_X5CHAIN, certs.value()[0]);
+        } else {
+            cppbor::Array certArray;
+            for (const vector<uint8_t>& cert : certs.value()) {
+                certArray.add(cert);
+            }
+            unprotectedHeaders.add(COSE_LABEL_X5CHAIN, std::move(certArray));
+        }
+    }
+
+    vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders);
+
+    cppbor::Array coseSign1;
+    coseSign1.add(encodedProtectedHeaders);
+    coseSign1.add(std::move(unprotectedHeaders));
+    if (data.size() == 0) {
+        cppbor::Null nullValue;
+        coseSign1.add(std::move(nullValue));
+    } else {
+        coseSign1.add(data);
+    }
+    coseSign1.add(signatureToBeSigned);
+    vector<uint8_t> signatureCoseSign1;
+    signatureCoseSign1 = coseSign1.encode();
+
+    return signatureCoseSign1;
+}
+
 optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data,
                                         const vector<uint8_t>& detachedContent,
                                         const vector<uint8_t>& certificateChain) {
@@ -1673,6 +1964,35 @@
     return true;
 }
 
+// Extracts the signature (of the ToBeSigned CBOR) from a COSE_Sign1.
+optional<vector<uint8_t>> coseSignGetSignature(const vector<uint8_t>& signatureCoseSign1) {
+    auto [item, _, message] = cppbor::parse(signatureCoseSign1);
+    if (item == nullptr) {
+        LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message;
+        return {};
+    }
+    const cppbor::Array* array = item->asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array";
+        return {};
+    }
+    if (array->size() != 4) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4";
+        return {};
+    }
+
+    vector<uint8_t> signature;
+    const cppbor::Bstr* signatureAsBstr = (*array)[3]->asBstr();
+    if (signatureAsBstr == nullptr) {
+        LOG(ERROR) << "Value for signature is not a bstr";
+        return {};
+    }
+    // Copy payload into |data|
+    signature = signatureAsBstr->value();
+
+    return signature;
+}
+
 optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1) {
     auto [item, _, message] = cppbor::parse(signatureCoseSign1);
     if (item == nullptr) {
@@ -1710,6 +2030,59 @@
     return data;
 }
 
+optional<int> coseSignGetAlg(const vector<uint8_t>& signatureCoseSign1) {
+    auto [item, _, message] = cppbor::parse(signatureCoseSign1);
+    if (item == nullptr) {
+        LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message;
+        return {};
+    }
+    const cppbor::Array* array = item->asArray();
+    if (array == nullptr) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array";
+        return {};
+    }
+    if (array->size() != 4) {
+        LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4";
+        return {};
+    }
+
+    const cppbor::Bstr* protectedHeadersBytes = (*array)[0]->asBstr();
+    if (protectedHeadersBytes == nullptr) {
+        LOG(ERROR) << "Value for protectedHeaders is not a bstr";
+        return {};
+    }
+    auto [item2, _2, message2] = cppbor::parse(protectedHeadersBytes->value());
+    if (item2 == nullptr) {
+        LOG(ERROR) << "Error parsing protectedHeaders: " << message2;
+        return {};
+    }
+    const cppbor::Map* protectedHeaders = item2->asMap();
+    if (protectedHeaders == nullptr) {
+        LOG(ERROR) << "Decoded CBOR for protectedHeaders is not a map";
+        return {};
+    }
+
+    for (size_t n = 0; n < protectedHeaders->size(); n++) {
+        auto [keyItem, valueItem] = (*protectedHeaders)[n];
+        const cppbor::Int* number = keyItem->asInt();
+        if (number == nullptr) {
+            LOG(ERROR) << "Key item in top-level map is not a number";
+            return {};
+        }
+        int label = number->value();
+        if (label == COSE_LABEL_ALG) {
+            const cppbor::Int* number = valueItem->asInt();
+            if (number != nullptr) {
+                return number->value();
+            }
+            LOG(ERROR) << "Value for COSE_LABEL_ALG label is not a number";
+            return {};
+        }
+    }
+    LOG(ERROR) << "Did not find COSE_LABEL_ALG label in protected headers";
+    return {};
+}
+
 optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCoseSign1) {
     auto [item, _, message] = cppbor::parse(signatureCoseSign1);
     if (item == nullptr) {
@@ -1825,6 +2198,28 @@
     return array.encode();
 }
 
+optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMaced,
+                                            const vector<uint8_t>& data) {
+    cppbor::Map unprotectedHeaders;
+    cppbor::Map protectedHeaders;
+
+    protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_HMAC_256_256);
+
+    vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders);
+
+    cppbor::Array array;
+    array.add(encodedProtectedHeaders);
+    array.add(std::move(unprotectedHeaders));
+    if (data.size() == 0) {
+        cppbor::Null nullValue;
+        array.add(std::move(nullValue));
+    } else {
+        array.add(data);
+    }
+    array.add(digestToBeMaced);
+    return array.encode();
+}
+
 // ---------------------------------------------------------------------------
 // Utility functions specific to IdentityCredential.
 // ---------------------------------------------------------------------------
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 6cbe4da..aa2de2a 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -438,10 +438,10 @@
             EXPECT_TRUE(device_locked);
         }
 
-        // Check that the expected result from VBMeta matches the build type. Only a user build
-        // should have AVB reporting the device is locked.
-        EXPECT_NE(property_get("ro.build.type", property_value, ""), 0);
-        if (!strcmp(property_value, "user")) {
+        // Check that the device is locked if not debuggable, e.g., user build
+        // images in CTS. For VTS, debuggable images are used to allow adb root
+        // and the device is unlocked.
+        if (!property_get_bool("ro.debuggable", false)) {
             EXPECT_TRUE(device_locked);
         } else {
             EXPECT_FALSE(device_locked);
diff --git a/media/omx/1.0/vts/functional/common/Android.bp b/media/omx/1.0/vts/functional/common/Android.bp
index 2c024a0..720ea9f 100644
--- a/media/omx/1.0/vts/functional/common/Android.bp
+++ b/media/omx/1.0/vts/functional/common/Android.bp
@@ -74,5 +74,6 @@
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
         "libstagefright_foundation",
+        "libstagefright_omx_utils",
     ],
 }
diff --git a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
index 9b4722e..4e25313 100644
--- a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
+++ b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
@@ -33,6 +33,7 @@
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
+#include <media/stagefright/omx/OMXUtils.h>
 
 using ::android::sp;
 using ::android::base::Join;
@@ -87,71 +88,6 @@
     }
 }
 
-/*
- * Returns the role based on is_encoder and mime.
- *
- * The mapping from a pair (is_encoder, mime) to a role string is
- * defined in frameworks/av/media/libmedia/MediaDefs.cpp and
- * frameworks/av/media/libstagefright/omx/OMXUtils.cpp. This function
- * does essentially the same work as GetComponentRole() in
- * OMXUtils.cpp.
- *
- * Args:
- *   is_encoder: A boolean indicating whether the role is for an
- *       encoder or a decoder.
- *   mime: A string of the desired mime type.
- *
- * Returns:
- *   A const string for the requested role name, empty if mime is not
- *   recognized.
- */
-const std::string getComponentRole(bool isEncoder, const std::string mime) {
-    // Mapping from mime types to roles.
-    // These values come from MediaDefs.cpp and OMXUtils.cpp
-    const std::map<const std::string, const std::string> audioMimeToRole = {
-            {"3gpp", "amrnb"},         {"ac3", "ac3"},     {"amr-wb", "amrwb"},
-            {"eac3", "eac3"},          {"flac", "flac"},   {"g711-alaw", "g711alaw"},
-            {"g711-mlaw", "g711mlaw"}, {"gsm", "gsm"},     {"mp4a-latm", "aac"},
-            {"mpeg", "mp3"},           {"mpeg-L1", "mp1"}, {"mpeg-L2", "mp2"},
-            {"opus", "opus"},          {"raw", "raw"},     {"vorbis", "vorbis"},
-    };
-    const std::map<const std::string, const std::string> videoMimeToRole = {
-            {"3gpp", "h263"},         {"avc", "avc"},           {"dolby-vision", "dolby-vision"},
-            {"hevc", "hevc"},         {"mp4v-es", "mpeg4"},     {"mpeg2", "mpeg2"},
-            {"x-vnd.on2.vp8", "vp8"}, {"x-vnd.on2.vp9", "vp9"},
-    };
-    const std::map<const std::string, const std::string> imageMimeToRole = {
-            {"vnd.android.heic", "heic"},
-    };
-
-    // Suffix begins after the mime prefix.
-    const size_t prefixEnd = mime.find("/");
-    if (prefixEnd == std::string::npos || prefixEnd == mime.size()) return "";
-    const std::string mime_suffix = mime.substr(prefixEnd + 1, mime.size() - 1);
-    const std::string middle = isEncoder ? "encoder." : "decoder.";
-    std::string prefix;
-    std::string suffix;
-    if (mime.rfind("audio/", 0) != std::string::npos) {
-        const auto it = audioMimeToRole.find(mime_suffix);
-        if (it == audioMimeToRole.end()) return "";
-        prefix = "audio_";
-        suffix = it->second;
-    } else if (mime.rfind("video/", 0) != std::string::npos) {
-        const auto it = videoMimeToRole.find(mime_suffix);
-        if (it == videoMimeToRole.end()) return "";
-        prefix = "video_";
-        suffix = it->second;
-    } else if (mime.rfind("image/", 0) != std::string::npos) {
-        const auto it = imageMimeToRole.find(mime_suffix);
-        if (it == imageMimeToRole.end()) return "";
-        prefix = "image_";
-        suffix = it->second;
-    } else {
-        return "";
-    }
-    return prefix + middle + suffix;
-}
-
 void validateAttributes(
         const std::map<const std::string, const testing::internal::RE>& knownPatterns,
         const std::vector<const struct AttributePattern>& unknownPatterns,
@@ -328,7 +264,8 @@
 
         // Make sure role name follows expected format based on type and
         // isEncoder
-        const std::string role_name = getComponentRole(role.isEncoder, role.type);
+        const std::string role_name(
+                ::android::GetComponentRole(role.isEncoder, role.type.c_str()));
         EXPECT_EQ(role_name, role.role) << "Role \"" << role.role << "\" does not match "
                                         << (role.isEncoder ? "an encoder " : "a decoder ")
                                         << "for mime type \"" << role.type << ".";
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index 87e8519..d802911 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -62,11 +62,12 @@
     defaults: ["neuralnetworks_vts_functional_defaults"],
     srcs: [
         "BasicTests.cpp",
+        "GeneratedTestHarness.cpp",
         "TestAssertions.cpp",
+        "TestMain.cpp",
         "ValidateModel.cpp",
         "ValidateRequest.cpp",
         "VtsHalNeuralnetworks.cpp",
-        "GeneratedTestHarness.cpp",
     ],
     shared_libs: [
         "libfmq",
diff --git a/neuralnetworks/1.0/vts/functional/AndroidTest.xml b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
index 54e6e91..13671f9 100644
--- a/neuralnetworks/1.0/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <!-- b/155577050, temporarily disable the failing tests.
-             Must be deleted after corresponding driver issues are fixed.
-        -->
-        <option name="native-test-flag" value="--gtest_filter=-*Validation*:*CycleTest*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_0TargetTest" />
     </test>
diff --git a/neuralnetworks/1.0/vts/functional/TestMain.cpp b/neuralnetworks/1.0/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    testing::UnitTest::GetInstance()->listeners().Append(
+            new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+    return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.0/vts/functional/include/1.0/LogTestCaseToLogcat.h b/neuralnetworks/1.0/vts/functional/include/1.0/LogTestCaseToLogcat.h
new file mode 100644
index 0000000..f1413ef
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/include/1.0/LogTestCaseToLogcat.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_0_LOG_TEST_CASE_TO_LOGCAT_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_LOG_TEST_CASE_TO_LOGCAT_H
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::neuralnetworks {
+
+class LogTestCaseToLogcat : public ::testing::EmptyTestEventListener {
+  public:
+    void OnTestStart(const ::testing::TestInfo& test_info) override {
+        LOG(INFO) << "[Test Case] " << test_info.test_suite_name() << "." << test_info.name()
+                  << " BEGIN";
+    }
+
+    void OnTestEnd(const ::testing::TestInfo& test_info) override {
+        LOG(INFO) << "[Test Case] " << test_info.test_suite_name() << "." << test_info.name()
+                  << " END";
+    }
+};
+
+}  // namespace android::hardware::neuralnetworks
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_LOG_TEST_CASE_TO_LOGCAT_H
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index 9afa0af..405548f 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -20,6 +20,7 @@
     srcs: [
         "BasicTests.cpp",
         "TestAssertions.cpp",
+        "TestMain.cpp",
         "ValidateModel.cpp",
         "ValidateRequest.cpp",
         "VtsHalNeuralnetworks.cpp",
diff --git a/neuralnetworks/1.1/vts/functional/AndroidTest.xml b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
index a6f812f..cfde60c 100644
--- a/neuralnetworks/1.1/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <!-- b/155577050, temporarily disable the failing tests.
-             Must be deleted after corresponding driver issues are fixed.
-        -->
-        <option name="native-test-flag" value="--gtest_filter=-*Validation*:*CycleTest*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_1TargetTest" />
     </test>
diff --git a/neuralnetworks/1.1/vts/functional/TestMain.cpp b/neuralnetworks/1.1/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.1/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    testing::UnitTest::GetInstance()->listeners().Append(
+            new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+    return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 182f716..93edca6 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -40,6 +40,7 @@
         "CompilationCachingTests.cpp",
         "GeneratedTestHarness.cpp",
         "TestAssertions.cpp",
+        "TestMain.cpp",
         "ValidateBurst.cpp",
         "ValidateModel.cpp",
         "ValidateRequest.cpp",
diff --git a/neuralnetworks/1.2/vts/functional/AndroidTest.xml b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
index adbdf40..3f91618 100644
--- a/neuralnetworks/1.2/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <!-- b/155577050, b/155674368, b/153876253, temporarily disable the test.
-             Must be deleted after corresponding driver issues are fixed.
-        -->
-        <option name="native-test-flag" value="--gtest_filter=-*Validation*:*squeeze*_all*_inputs*:*strided_slice*_all*_inputs*:*transpose*_all*_inputs*:*l2_normalization_axis_corner_case*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_2TargetTest" />
     </test>
diff --git a/neuralnetworks/1.2/vts/functional/TestMain.cpp b/neuralnetworks/1.2/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    testing::UnitTest::GetInstance()->listeners().Append(
+            new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+    return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
index d01336e..c4e2b15 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
@@ -21,6 +21,7 @@
 #include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
 #include <android/hardware/neuralnetworks/1.2/types.h>
 #include <gtest/gtest.h>
+#include <vector>
 #include "1.0/Utils.h"
 #include "1.2/Callbacks.h"
 
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index 771fc54..b17d445 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -43,6 +43,7 @@
         "MemoryDomainTests.cpp",
         "QualityOfServiceTests.cpp",
         "TestAssertions.cpp",
+        "TestMain.cpp",
         "ValidateBurst.cpp",
         "ValidateModel.cpp",
         "ValidateRequest.cpp",
diff --git a/neuralnetworks/1.3/vts/functional/AndroidTest.xml b/neuralnetworks/1.3/vts/functional/AndroidTest.xml
index 30cff2e..e5acd90 100644
--- a/neuralnetworks/1.3/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.3/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <!-- b/156691406, b/155577050, b/155674368, b/153876253, temporarily disable the test.
-             Must be deleted after corresponding driver issues are fixed.
-        -->
-        <option name="native-test-flag" value="--gtest_filter=-*Validation*:*DynamicOutputShapeTest*:*FencedComputeTest*:*MemoryDomain*:*QuantizationCouplingTest*:*DeadlineTest*:*resize_*_v1_3*:*squeeze*_all*_inputs*:*strided_slice*_all*_inputs*:*transpose*_all*_inputs*:*l2_normalization_axis_corner_case*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_3TargetTest" />
     </test>
diff --git a/neuralnetworks/1.3/vts/functional/TestMain.cpp b/neuralnetworks/1.3/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    testing::UnitTest::GetInstance()->listeners().Append(
+            new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+    return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
index de082c3..a2e5071 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
@@ -21,6 +21,7 @@
 #include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
 #include <android/hardware/neuralnetworks/1.3/types.h>
 #include <gtest/gtest.h>
+#include <vector>
 #include "1.0/Utils.h"
 #include "1.3/Callbacks.h"
 
diff --git a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
index 02dcbab..08121fd 100644
--- a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
@@ -49,7 +49,6 @@
         }
         EXPECT_EQ(CardState::ABSENT, cardStatus.cardState);
     }
-#endif
 
     /* Test setSimCardPower power up */
     serial = GetRandomSerialNumber();
@@ -60,6 +59,7 @@
     ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_1->rspInfo.error,
                                  {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED,
                                   RadioError::INVALID_ARGUMENTS, RadioError::RADIO_NOT_AVAILABLE}));
+#endif
 
     /**
      * If the sim card status for the testing environment is PRESENT,
diff --git a/radio/1.5/vts/functional/radio_response.cpp b/radio/1.5/vts/functional/radio_response.cpp
index 8cbb2d0..9b6d450 100644
--- a/radio/1.5/vts/functional/radio_response.cpp
+++ b/radio/1.5/vts/functional/radio_response.cpp
@@ -1017,8 +1017,10 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_5::sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& /*info*/,
+Return<void> RadioResponse_v1_5::sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& info,
                                                                const SendSmsResult& /*sms*/) {
+    rspInfo = info;
+    parent_v1_5.notify(info.serial);
     return Void();
 }
 
diff --git a/rebootescrow/aidl/vts/functional/README.md b/rebootescrow/aidl/vts/functional/README.md
new file mode 100644
index 0000000..9ae5caf
--- /dev/null
+++ b/rebootescrow/aidl/vts/functional/README.md
@@ -0,0 +1,7 @@
+Many of the tests in this directory may require that TEE Keymaster
+"EARLY_BOOT_ONLY" keys be usable when this test runs. In order to accomplish
+this, a build of "vold" that omits the call to "earlyBootEnded()" function
+should be made. Then these DISABLED tests may be run successfully.
+
+The CTS test ResumeOnRebootHostTests will test the functionality without a
+special build.
diff --git a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
index cd8cc3e..809a3b5 100644
--- a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
+++ b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
@@ -60,7 +60,10 @@
     };
 };
 
-TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) {
+// This test assumes that it can retrieve keys immediately, but some
+// implementations use the TEE's EARLY_BOOT_ONLY keys. This means that the
+// earlyBootEnded() calls will need to be disabled to test this correctly.
+TEST_P(RebootEscrowAidlTest, DISABLED_StoreAndRetrieve_Success) {
     SKIP_UNSUPPORTED;
 
     ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
@@ -70,7 +73,10 @@
     EXPECT_EQ(actualKey, KEY_1);
 }
 
-TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) {
+// This test assumes that it can retrieve keys immediately, but some
+// implementations use the TEE's EARLY_BOOT_ONLY keys. This means that the
+// earlyBootEnded() calls will need to be disabled to test this correctly.
+TEST_P(RebootEscrowAidlTest, DISABLED_StoreAndRetrieve_SecondRetrieveSucceeds) {
     SKIP_UNSUPPORTED;
 
     ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
@@ -83,7 +89,10 @@
     EXPECT_EQ(actualKey, KEY_1);
 }
 
-TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) {
+// This test assumes that it can retrieve keys immediately, but some
+// implementations use the TEE's EARLY_BOOT_ONLY keys. This means that the
+// earlyBootEnded() calls will need to be disabled to test this correctly.
+TEST_P(RebootEscrowAidlTest, DISABLED_StoreTwiceOverwrites_Success) {
     SKIP_UNSUPPORTED;
 
     ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
@@ -94,7 +103,10 @@
     EXPECT_EQ(actualKey, KEY_2);
 }
 
-TEST_P(RebootEscrowAidlTest, StoreEmpty_AfterGetEmptyKey_Success) {
+// This test assumes that it can retrieve keys immediately, but some
+// implementations use the TEE's EARLY_BOOT_ONLY keys. This means that the
+// earlyBootEnded() calls will need to be disabled to test this correctly.
+TEST_P(RebootEscrowAidlTest, DISABLED_StoreEmpty_AfterGetEmptyKey_Success) {
     SKIP_UNSUPPORTED;
 
     rebootescrow->storeKey(KEY_1);
@@ -105,6 +117,12 @@
     EXPECT_EQ(actualKey, EMPTY_KEY);
 }
 
+TEST_P(RebootEscrowAidlTest, Store_Success) {
+    SKIP_UNSUPPORTED;
+
+    rebootescrow->storeKey(KEY_1);
+}
+
 INSTANTIATE_TEST_SUITE_P(
         RebootEscrow, RebootEscrowAidlTest,
         testing::ValuesIn(android::getAidlHalInstanceNames(IRebootEscrow::descriptor)),
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index b74f6ec..67eff1b 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -48,8 +48,6 @@
         return Result::INVALID_STATE;
     }
 
-    mFrontendSourceFile = mFrontend->getSourceFile();
-
     mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
 
     return Result::SUCCESS;
@@ -62,8 +60,6 @@
     uint32_t filterId;
     filterId = ++mLastUsedFilterId;
 
-    mUsedFilterIds.insert(filterId);
-
     if (cb == nullptr) {
         ALOGW("[Demux] callback can't be null");
         _hidl_cb(Result::INVALID_ARGUMENT, new Filter());
@@ -82,8 +78,13 @@
         mPcrFilterIds.insert(filterId);
     }
     bool result = true;
-    if (mDvr != nullptr && mDvr->getType() == DvrType::PLAYBACK) {
-        result = mDvr->addPlaybackFilter(filter);
+    if (!filter->isRecordFilter()) {
+        // Only save non-record filters for now. Record filters are saved when the
+        // IDvr.attacheFilter is called.
+        mPlaybackFilterIds.insert(filterId);
+        if (mDvrPlayback != nullptr) {
+            result = mDvrPlayback->addPlaybackFilter(filterId, filter);
+        }
     }
 
     _hidl_cb(result ? Result::SUCCESS : Result::INVALID_ARGUMENT, filter);
@@ -93,9 +94,9 @@
 Return<void> Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) {
     ALOGV("%s", __FUNCTION__);
 
-    sp<TimeFilter> timeFilter = new TimeFilter(this);
+    mTimeFilter = new TimeFilter(this);
 
-    _hidl_cb(Result::SUCCESS, timeFilter);
+    _hidl_cb(Result::SUCCESS, mTimeFilter);
     return Void();
 }
 
@@ -154,7 +155,13 @@
 Return<Result> Demux::close() {
     ALOGV("%s", __FUNCTION__);
 
-    mUsedFilterIds.clear();
+    set<uint32_t>::iterator it;
+    for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
+        mDvrPlayback->removePlaybackFilter(*it);
+    }
+    mPlaybackFilterIds.clear();
+    mRecordFilterIds.clear();
+    mFilters.clear();
     mLastUsedFilterId = -1;
 
     return Result::SUCCESS;
@@ -170,15 +177,38 @@
         return Void();
     }
 
-    mDvr = new Dvr(type, bufferSize, cb, this);
+    set<uint32_t>::iterator it;
+    switch (type) {
+        case DvrType::PLAYBACK:
+            mDvrPlayback = new Dvr(type, bufferSize, cb, this);
+            if (!mDvrPlayback->createDvrMQ()) {
+                _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback);
+                return Void();
+            }
 
-    if (!mDvr->createDvrMQ()) {
-        _hidl_cb(Result::UNKNOWN_ERROR, mDvr);
-        return Void();
+            for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
+                if (!mDvrPlayback->addPlaybackFilter(*it, mFilters[*it])) {
+                    ALOGE("[Demux] Can't get filter info for DVR playback");
+                    _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback);
+                    return Void();
+                }
+            }
+
+            _hidl_cb(Result::SUCCESS, mDvrPlayback);
+            return Void();
+        case DvrType::RECORD:
+            mDvrRecord = new Dvr(type, bufferSize, cb, this);
+            if (!mDvrRecord->createDvrMQ()) {
+                _hidl_cb(Result::UNKNOWN_ERROR, mDvrRecord);
+                return Void();
+            }
+
+            _hidl_cb(Result::SUCCESS, mDvrRecord);
+            return Void();
+        default:
+            _hidl_cb(Result::INVALID_ARGUMENT, nullptr);
+            return Void();
     }
-
-    _hidl_cb(Result::SUCCESS, mDvr);
-    return Void();
 }
 
 Return<Result> Demux::connectCiCam(uint32_t ciCamId) {
@@ -198,8 +228,10 @@
 Result Demux::removeFilter(uint32_t filterId) {
     ALOGV("%s", __FUNCTION__);
 
-    // resetFilterRecords(filterId);
-    mUsedFilterIds.erase(filterId);
+    if (mDvrPlayback != nullptr) {
+        mDvrPlayback->removePlaybackFilter(filterId);
+    }
+    mPlaybackFilterIds.erase(filterId);
     mRecordFilterIds.erase(filterId);
     mFilters.erase(filterId);
 
@@ -212,7 +244,7 @@
     if (DEBUG_DEMUX) {
         ALOGW("[Demux] start ts filter pid: %d", pid);
     }
-    for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+    for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
         if (pid == mFilters[*it]->getTpid()) {
             mFilters[*it]->updateFilterOutput(data);
         }
@@ -233,7 +265,7 @@
     set<uint32_t>::iterator it;
 
     // Handle the output data per filter type
-    for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+    for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
         if (mFilters[*it]->startFilterHandler() != Result::SUCCESS) {
             return false;
         }
@@ -280,58 +312,27 @@
 void Demux::frontendInputThreadLoop() {
     std::lock_guard<std::mutex> lock(mFrontendInputThreadLock);
     mFrontendInputThreadRunning = true;
-    mKeepFetchingDataFromFrontend = true;
-
-    // open the stream and get its length
-    std::ifstream inputData(mFrontendSourceFile, std::ifstream::binary);
-    // TODO take the packet size from the frontend setting
-    int packetSize = 188;
-    int writePacketAmount = 6;
-    char* buffer = new char[packetSize];
-    ALOGW("[Demux] Frontend input thread loop start %s", mFrontendSourceFile.c_str());
-    if (!inputData.is_open()) {
-        mFrontendInputThreadRunning = false;
-        ALOGW("[Demux] Error %s", strerror(errno));
-    }
 
     while (mFrontendInputThreadRunning) {
-        // move the stream pointer for packet size * 6 every read until the end
-        while (mKeepFetchingDataFromFrontend) {
-            for (int i = 0; i < writePacketAmount; i++) {
-                inputData.read(buffer, packetSize);
-                if (!inputData) {
-                    mKeepFetchingDataFromFrontend = false;
-                    mFrontendInputThreadRunning = false;
-                    break;
-                }
-                // filter and dispatch filter output
-                vector<uint8_t> byteBuffer;
-                byteBuffer.resize(packetSize);
-                for (int index = 0; index < byteBuffer.size(); index++) {
-                    byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
-                }
-                if (mIsRecording) {
-                    // Feed the data into the Dvr recording input
-                    sendFrontendInputToRecord(byteBuffer);
-                } else {
-                    // Feed the data into the broadcast demux filter
-                    startBroadcastTsFilter(byteBuffer);
-                }
-            }
-            if (mIsRecording) {
-                // Dispatch the data into the broadcasting filters.
-                startRecordFilterDispatcher();
-            } else {
-                // Dispatch the data into the broadcasting filters.
-                startBroadcastFilterDispatcher();
-            }
-            usleep(100);
+        uint32_t efState = 0;
+        status_t status = mDvrPlayback->getDvrEventFlag()->wait(
+                static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
+                true /* retry on spurious wake */);
+        if (status != OK) {
+            ALOGD("[Demux] wait for data ready on the playback 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 (!mDvrPlayback->readPlaybackFMQ(true /*isVirtualFrontend*/, mIsRecording) ||
+            !mDvrPlayback->startFilterDispatcher(true /*isVirtualFrontend*/, mIsRecording)) {
+            ALOGE("[Demux] playback data failed to be filtered. Ending thread");
+            break;
         }
     }
 
+    mFrontendInputThreadRunning = false;
     ALOGW("[Demux] Frontend Input thread end.");
-    delete[] buffer;
-    inputData.close();
 }
 
 void Demux::stopFrontendInput() {
@@ -346,18 +347,19 @@
 }
 
 bool Demux::attachRecordFilter(int filterId) {
-    if (mFilters[filterId] == nullptr || mDvr == nullptr) {
+    if (mFilters[filterId] == nullptr || mDvrRecord == nullptr ||
+        !mFilters[filterId]->isRecordFilter()) {
         return false;
     }
 
     mRecordFilterIds.insert(filterId);
-    mFilters[filterId]->attachFilterToRecord(mDvr);
+    mFilters[filterId]->attachFilterToRecord(mDvrRecord);
 
     return true;
 }
 
 bool Demux::detachRecordFilter(int filterId) {
-    if (mFilters[filterId] == nullptr || mDvr == nullptr) {
+    if (mFilters[filterId] == nullptr || mDvrRecord == nullptr) {
         return false;
     }
 
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 6c46b0d..7f282b2 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -91,13 +91,23 @@
     void setIsRecording(bool isRecording);
     void startFrontendInputLoop();
 
+    /**
+     * A dispatcher to read and dispatch input data to all the started filters.
+     * Each filter handler handles the data filtering/output writing/filterEvent updating.
+     * Note that recording filters are not included.
+     */
+    bool startBroadcastFilterDispatcher();
+    void startBroadcastTsFilter(vector<uint8_t> data);
+
+    void sendFrontendInputToRecord(vector<uint8_t> data);
+    bool startRecordFilterDispatcher();
+
   private:
     // Tuner service
     sp<Tuner> mTunerService;
 
     // Frontend source
     sp<Frontend> mFrontend;
-    string mFrontendSourceFile;
 
     // A struct that passes the arguments to a newly created filter thread
     struct ThreadArgs {
@@ -117,16 +127,6 @@
      */
     void deleteEventFlag();
     bool readDataFromMQ();
-    /**
-     * A dispatcher to read and dispatch input data to all the started filters.
-     * Each filter handler handles the data filtering/output writing/filterEvent updating.
-     * Note that recording filters are not included.
-     */
-    bool startBroadcastFilterDispatcher();
-    void startBroadcastTsFilter(vector<uint8_t> data);
-
-    void sendFrontendInputToRecord(vector<uint8_t> data);
-    bool startRecordFilterDispatcher();
 
     uint32_t mDemuxId;
     uint32_t mCiCamId;
@@ -137,25 +137,31 @@
      */
     uint32_t mLastUsedFilterId = -1;
     /**
-     * Record all the used filter Ids.
+     * Record all the used playback filter Ids.
      * Any removed filter id should be removed from this set.
      */
-    set<uint32_t> mUsedFilterIds;
+    set<uint32_t> mPlaybackFilterIds;
     /**
      * Record all the attached record filter Ids.
      * Any removed filter id should be removed from this set.
      */
     set<uint32_t> mRecordFilterIds;
     /**
-     * A list of created FilterMQ ptrs.
+     * A list of created Filter sp.
      * The array number is the filter ID.
      */
     std::map<uint32_t, sp<Filter>> mFilters;
 
     /**
+     * Local reference to the opened Timer Filter instance.
+     */
+    sp<TimeFilter> mTimeFilter;
+
+    /**
      * Local reference to the opened DVR object.
      */
-    sp<Dvr> mDvr;
+    sp<Dvr> mDvrPlayback;
+    sp<Dvr> mDvrRecord;
 
     // Thread handlers
     pthread_t mFrontendInputThread;
diff --git a/tv/tuner/1.0/default/Dvr.cpp b/tv/tuner/1.0/default/Dvr.cpp
index adb2635..68e175c 100644
--- a/tv/tuner/1.0/default/Dvr.cpp
+++ b/tv/tuner/1.0/default/Dvr.cpp
@@ -70,8 +70,7 @@
         return status;
     }
 
-    // check if the attached filter is a record filter
-    mFilters[filterId] = filter;
+    // TODO check if the attached filter is a record filter
     if (!mDemux->attachRecordFilter(filterId)) {
         return Result::INVALID_ARGUMENT;
     }
@@ -94,19 +93,8 @@
         return status;
     }
 
-    std::map<uint32_t, sp<IFilter>>::iterator it;
-
-    it = mFilters.find(filterId);
-    if (it != mFilters.end()) {
-        mFilters.erase(filterId);
-        if (!mDemux->detachRecordFilter(filterId)) {
-            return Result::INVALID_ARGUMENT;
-        }
-    }
-
-    // If all the filters are detached, record can't be started
-    if (mFilters.empty()) {
-        mIsRecordFilterAttached = false;
+    if (!mDemux->detachRecordFilter(filterId)) {
+        return Result::INVALID_ARGUMENT;
     }
 
     return Result::SUCCESS;
@@ -183,6 +171,10 @@
     return true;
 }
 
+EventFlag* Dvr::getDvrEventFlag() {
+    return mDvrEventFlag;
+}
+
 void* Dvr::__threadLoopPlayback(void* user) {
     Dvr* const self = static_cast<Dvr*>(user);
     self->playbackThreadLoop();
@@ -205,8 +197,9 @@
         }
         // Our current implementation filter the data and write it into the filter FMQ immediately
         // after the DATA_READY from the VTS/framework
-        if (!readPlaybackFMQ() || !startFilterDispatcher()) {
-            ALOGD("[Dvr] playback data failed to be filtered. Ending thread");
+        if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) ||
+            !startFilterDispatcher(false /*isVirtualFrontend*/, false /*isRecording*/)) {
+            ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
             break;
         }
 
@@ -245,7 +238,7 @@
     return mPlaybackStatus;
 }
 
-bool Dvr::readPlaybackFMQ() {
+bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
     // Read playback data from the input FMQ
     int size = mDvrMQ->availableToRead();
     int playbackPacketSize = mDvrSettings.playback().packetSize;
@@ -256,7 +249,15 @@
         if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
             return false;
         }
-        startTpidFilter(dataOutputBuffer);
+        if (isVirtualFrontend) {
+            if (isRecording) {
+                mDemux->sendFrontendInputToRecord(dataOutputBuffer);
+            } else {
+                mDemux->startBroadcastTsFilter(dataOutputBuffer);
+            }
+        } else {
+            startTpidFilter(dataOutputBuffer);
+        }
     }
 
     return true;
@@ -275,7 +276,15 @@
     }
 }
 
-bool Dvr::startFilterDispatcher() {
+bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
+    if (isVirtualFrontend) {
+        if (isRecording) {
+            return mDemux->startRecordFilterDispatcher();
+        } else {
+            return mDemux->startBroadcastFilterDispatcher();
+        }
+    }
+
     std::map<uint32_t, sp<IFilter>>::iterator it;
     // Handle the output data per filter type
     for (it = mFilters.begin(); it != mFilters.end(); it++) {
@@ -329,27 +338,15 @@
     return mRecordStatus;
 }
 
-bool Dvr::addPlaybackFilter(sp<IFilter> filter) {
-    uint32_t filterId;
-    Result status;
-
-    filter->getId([&](Result result, uint32_t id) {
-        filterId = id;
-        status = result;
-    });
-
-    if (status != Result::SUCCESS) {
-        return false;
-    }
-
+bool Dvr::addPlaybackFilter(uint32_t filterId, sp<IFilter> filter) {
     mFilters[filterId] = filter;
     return true;
 }
 
-DvrType Dvr::getType() {
-    return mType;
+bool Dvr::removePlaybackFilter(uint32_t filterId) {
+    mFilters.erase(filterId);
+    return true;
 }
-
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.0/default/Dvr.h b/tv/tuner/1.0/default/Dvr.h
index 08afd5d..a63a256 100644
--- a/tv/tuner/1.0/default/Dvr.h
+++ b/tv/tuner/1.0/default/Dvr.h
@@ -81,8 +81,11 @@
     bool createDvrMQ();
     void sendBroadcastInputToDvrRecord(vector<uint8_t> byteBuffer);
     bool writeRecordFMQ(const std::vector<uint8_t>& data);
-    DvrType getType();
-    bool addPlaybackFilter(sp<IFilter> filter);
+    bool addPlaybackFilter(uint32_t filterId, sp<IFilter> filter);
+    bool removePlaybackFilter(uint32_t filterId);
+    bool readPlaybackFMQ(bool isVirtualFrontend, bool isRecording);
+    bool startFilterDispatcher(bool isVirtualFrontend, bool isRecording);
+    EventFlag* getDvrEventFlag();
 
   private:
     // Demux service
@@ -105,9 +108,7 @@
      * A dispatcher to read and dispatch input data to all the started filters.
      * Each filter handler handles the data filtering/output writing/filterEvent updating.
      */
-    bool readPlaybackFMQ();
     void startTpidFilter(vector<uint8_t> data);
-    bool startFilterDispatcher();
     static void* __threadLoopPlayback(void* user);
     static void* __threadLoopRecord(void* user);
     void playbackThreadLoop();
@@ -123,7 +124,6 @@
 
     // Thread handlers
     pthread_t mDvrThread;
-    pthread_t mBroadcastInputThread;
 
     // FMQ status local records
     PlaybackStatus mPlaybackStatus;
@@ -132,7 +132,6 @@
      * If a specific filter's writing loop is still running
      */
     bool mDvrThreadRunning;
-    bool mBroadcastInputThreadRunning;
     bool mKeepFetchingDataFromFrontend;
     /**
      * Lock to protect writes to the FMQs
@@ -143,7 +142,6 @@
      */
     std::mutex mPlaybackStatusLock;
     std::mutex mRecordStatusLock;
-    std::mutex mBroadcastInputThreadLock;
     std::mutex mDvrThreadLock;
 
     const bool DEBUG_DVR = false;
@@ -151,7 +149,6 @@
     // Booleans to check if recording is running.
     // Recording is ready when both of the following are set to true.
     bool mIsRecordStarted = false;
-    bool mIsRecordFilterAttached = false;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
index 8bca70c..30b19c0 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -47,12 +47,18 @@
             if (mType.subType.tsFilterType() == DemuxTsFilterType::PCR) {
                 mIsPcrFilter = true;
             }
+            if (mType.subType.tsFilterType() == DemuxTsFilterType::RECORD) {
+                mIsRecordFilter = true;
+            }
             break;
         case DemuxFilterMainType::MMTP:
             if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
                 mType.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
                 mIsMediaFilter = true;
             }
+            if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::RECORD) {
+                mIsRecordFilter = true;
+            }
             break;
         case DemuxFilterMainType::IP:
             break;
@@ -535,12 +541,6 @@
 }
 
 Result Filter::startRecordFilterHandler() {
-    /*DemuxFilterTsRecordEvent tsRecordEvent;
-    tsRecordEvent.pid.tPid(0);
-    tsRecordEvent.indexMask.tsIndexMask(0x01);
-    mFilterEvent.events.resize(1);
-    mFilterEvent.events[0].tsRecord(tsRecordEvent);
-*/
     std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
     if (mRecordFilterOutput.empty()) {
         return Result::SUCCESS;
@@ -567,7 +567,7 @@
 
 bool Filter::writeSectionsAndCreateEvent(vector<uint8_t> data) {
     // TODO check how many sections has been read
-    ALOGD("[Filter] section hander");
+    ALOGD("[Filter] section handler");
     std::lock_guard<std::mutex> lock(mFilterEventLock);
     if (!writeDataToFilterMQ(data)) {
         return false;
diff --git a/tv/tuner/1.0/default/Filter.h b/tv/tuner/1.0/default/Filter.h
index 09e9604..9386dca 100644
--- a/tv/tuner/1.0/default/Filter.h
+++ b/tv/tuner/1.0/default/Filter.h
@@ -91,6 +91,7 @@
     void freeAvHandle();
     bool isMediaFilter() { return mIsMediaFilter; };
     bool isPcrFilter() { return mIsPcrFilter; };
+    bool isRecordFilter() { return mIsRecordFilter; };
 
   private:
     // Tuner service
@@ -107,6 +108,7 @@
     DemuxFilterType mType;
     bool mIsMediaFilter = false;
     bool mIsPcrFilter = false;
+    bool mIsRecordFilter = false;
     DemuxFilterSettings mFilterSettings;
 
     uint16_t mTpid;
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 61bbbf8..8bf0ec5 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -66,7 +66,7 @@
 
     mTunerService->frontendStartTune(mId);
     mCallback->onEvent(FrontendEventType::LOCKED);
-    mIsLocked = false;
+    mIsLocked = true;
     return Result::SUCCESS;
 }
 
@@ -268,14 +268,14 @@
     return mId;
 }
 
-string Frontend::getSourceFile() {
-    return FRONTEND_STREAM_FILE;
-}
-
 bool Frontend::supportsSatellite() {
     return mType == FrontendType::DVBS || mType == FrontendType::ISDBS ||
            mType == FrontendType::ISDBS3;
 }
+
+bool Frontend::isLocked() {
+    return mIsLocked;
+}
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace tuner
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index c0d1613..a529b74 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -68,6 +68,8 @@
 
     string getSourceFile();
 
+    bool isLocked();
+
   private:
     virtual ~Frontend();
     bool supportsSatellite();
@@ -77,7 +79,6 @@
     FrontendId mId = 0;
     bool mIsLocked = false;
 
-    const string FRONTEND_STREAM_FILE = "/vendor/etc/segment000000.ts";
     std::ifstream mFrontendData;
 };
 
diff --git a/tv/tuner/1.0/default/TimeFilter.cpp b/tv/tuner/1.0/default/TimeFilter.cpp
index 0b1fd1c..cec824f 100644
--- a/tv/tuner/1.0/default/TimeFilter.cpp
+++ b/tv/tuner/1.0/default/TimeFilter.cpp
@@ -34,24 +34,32 @@
 
 TimeFilter::~TimeFilter() {}
 
-Return<Result> TimeFilter::setTimeStamp(uint64_t /* timeStamp */) {
+Return<Result> TimeFilter::setTimeStamp(uint64_t timeStamp) {
     ALOGV("%s", __FUNCTION__);
+    if (timeStamp == INVALID_TIME_STAMP) {
+        return Result::INVALID_ARGUMENT;
+    }
+    mTimeStamp = timeStamp;
+    mBeginTime = time(NULL);
 
     return Result::SUCCESS;
 }
 
 Return<Result> TimeFilter::clearTimeStamp() {
     ALOGV("%s", __FUNCTION__);
+    mTimeStamp = INVALID_TIME_STAMP;
 
     return Result::SUCCESS;
 }
 
 Return<void> TimeFilter::getTimeStamp(getTimeStamp_cb _hidl_cb) {
     ALOGV("%s", __FUNCTION__);
+    if (mTimeStamp == INVALID_TIME_STAMP) {
+        _hidl_cb(Result::INVALID_STATE, mTimeStamp);
+    }
 
-    uint64_t timeStamp = 0;
-
-    _hidl_cb(Result::SUCCESS, timeStamp);
+    uint64_t currentTimeStamp = mTimeStamp + difftime(time(NULL), mBeginTime) * 900000;
+    _hidl_cb(Result::SUCCESS, currentTimeStamp);
     return Void();
 }
 
@@ -66,6 +74,7 @@
 
 Return<Result> TimeFilter::close() {
     ALOGV("%s", __FUNCTION__);
+    mTimeStamp = INVALID_TIME_STAMP;
 
     return Result::SUCCESS;
 }
diff --git a/tv/tuner/1.0/default/TimeFilter.h b/tv/tuner/1.0/default/TimeFilter.h
index 7131df8..cb3f29d 100644
--- a/tv/tuner/1.0/default/TimeFilter.h
+++ b/tv/tuner/1.0/default/TimeFilter.h
@@ -19,6 +19,7 @@
 
 #include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
 #include "Demux.h"
+#include "time.h"
 
 using namespace std;
 
@@ -35,6 +36,8 @@
 
 using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
 
+#define INVALID_TIME_STAMP -1
+
 class Demux;
 
 class TimeFilter : public ITimeFilter {
@@ -57,6 +60,8 @@
 
   private:
     sp<Demux> mDemux;
+    uint64_t mTimeStamp = INVALID_TIME_STAMP;
+    time_t mBeginTime;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index ae8070c..48ce384 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -231,6 +231,9 @@
 
 void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) {
     mFrontendToDemux[frontendId] = demuxId;
+    if (mFrontends[frontendId] != nullptr && mFrontends[frontendId]->isLocked()) {
+        mDemuxes[demuxId]->startFrontendInputLoop();
+    }
 }
 
 void Tuner::frontendStopTune(uint32_t frontendId) {
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
index 7e7f8e6..0dfc032 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -49,49 +49,73 @@
     EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
                 android::OK);
 
-    // open the stream and get its length
-    std::ifstream inputData(mInputDataFile.c_str(), std::ifstream::binary);
-    int writeSize = mPlaybackSettings.packetSize * 6;
-    char* buffer = new char[writeSize];
-    ALOGW("[vts] playback thread loop start %s!", mInputDataFile.c_str());
-    if (!inputData.is_open()) {
+    int fd = open(mInputDataFile.c_str(), O_RDONLY | O_LARGEFILE);
+    int readBytes;
+    uint32_t regionSize = 0;
+    uint8_t* buffer;
+    ALOGW("[vts] playback thread loop start %s", mInputDataFile.c_str());
+    if (fd < 0) {
         mPlaybackThreadRunning = false;
         ALOGW("[vts] Error %s", strerror(errno));
     }
 
     while (mPlaybackThreadRunning) {
-        // move the stream pointer for packet size * 6 every read until the end
         while (mKeepWritingPlaybackFMQ) {
-            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));
-                }
+            int totalWrite = mPlaybackMQ->availableToWrite();
+            if (totalWrite * 4 < mPlaybackMQ->getQuantumCount()) {
+                // Wait for the HAL implementation to read more data then write.
+                continue;
+            }
+            MessageQueue<uint8_t, kSynchronizedReadWrite>::MemTransaction memTx;
+            if (!mPlaybackMQ->beginWrite(totalWrite, &memTx)) {
+                ALOGW("[vts] Fail to write into Playback fmq.");
                 mPlaybackThreadRunning = false;
                 break;
             }
-            // Write input FMQ and notify the Tuner Implementation
-            EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
+            auto first = memTx.getFirstRegion();
+            buffer = first.getAddress();
+            regionSize = first.getLength();
+
+            if (regionSize > 0) {
+                readBytes = read(fd, buffer, regionSize);
+                if (readBytes <= 0) {
+                    if (readBytes < 0) {
+                        ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
+                    } else {
+                        ALOGW("[vts] playback input EOF.");
+                    }
+                    mPlaybackThreadRunning = false;
+                    break;
+                }
+            }
+            if (regionSize == 0 || (readBytes == regionSize && regionSize < totalWrite)) {
+                auto second = memTx.getSecondRegion();
+                buffer = second.getAddress();
+                regionSize = second.getLength();
+                int ret = read(fd, buffer, regionSize);
+                if (ret <= 0) {
+                    if (ret < 0) {
+                        ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
+                    } else {
+                        ALOGW("[vts] playback input EOF.");
+                    }
+                    mPlaybackThreadRunning = false;
+                    break;
+                }
+                readBytes += ret;
+            }
+            if (!mPlaybackMQ->commitWrite(readBytes)) {
+                ALOGW("[vts] Failed to commit write playback fmq.");
+                mPlaybackThreadRunning = false;
+                break;
+            }
             playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-            inputData.seekg(writeSize, inputData.cur);
-            sleep(1);
         }
     }
 
+    mPlaybackThreadRunning = false;
     ALOGW("[vts] Playback thread end.");
-
-    delete[] buffer;
-    inputData.close();
+    close(fd);
 }
 
 void DvrCallback::testRecordOutput() {
@@ -186,32 +210,65 @@
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
 
     // Create dvr callback
-    mDvrCallback = new DvrCallback();
+    if (type == DvrType::PLAYBACK) {
+        mDvrPlaybackCallback = new DvrCallback();
+        mDemux->openDvr(type, bufferSize, mDvrPlaybackCallback,
+                        [&](Result result, const sp<IDvr>& dvr) {
+                            mDvrPlayback = dvr;
+                            status = result;
+                        });
+        if (status == Result::SUCCESS) {
+            mDvrPlaybackCallback->setDvr(mDvrPlayback);
+        }
+    }
 
-    mDemux->openDvr(type, bufferSize, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
-        mDvr = dvr;
+    if (type == DvrType::RECORD) {
+        mDvrRecordCallback = new DvrCallback();
+        mDemux->openDvr(type, bufferSize, mDvrRecordCallback,
+                        [&](Result result, const sp<IDvr>& dvr) {
+                            mDvrRecord = dvr;
+                            status = result;
+                        });
+        if (status == Result::SUCCESS) {
+            mDvrRecordCallback->setDvr(mDvrRecord);
+        }
+    }
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::configDvrPlayback(DvrSettings setting) {
+    Result status = mDvrPlayback->configure(setting);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::configDvrRecord(DvrSettings setting) {
+    Result status = mDvrRecord->configure(setting);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::getDvrPlaybackMQDescriptor() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
+
+    mDvrPlayback->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+        mDvrPlaybackMQDescriptor = dvrMQDesc;
         status = result;
     });
 
-    if (status == Result::SUCCESS) {
-        mDvrCallback->setDvr(mDvr);
-    }
     return AssertionResult(status == Result::SUCCESS);
 }
 
-AssertionResult DvrTests::configDvr(DvrSettings setting) {
-    Result status = mDvr->configure(setting);
-
-    return AssertionResult(status == Result::SUCCESS);
-}
-
-AssertionResult DvrTests::getDvrMQDescriptor() {
+AssertionResult DvrTests::getDvrRecordMQDescriptor() {
     Result status;
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+    EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
 
-    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
-        mDvrMQDescriptor = dvrMQDesc;
+    mDvrRecord->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+        mDvrRecordMQDescriptor = dvrMQDesc;
         status = result;
     });
 
@@ -221,9 +278,9 @@
 AssertionResult DvrTests::attachFilterToDvr(sp<IFilter> filter) {
     Result status;
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+    EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
 
-    status = mDvr->attachFilter(filter);
+    status = mDvrRecord->attachFilter(filter);
 
     return AssertionResult(status == Result::SUCCESS);
 }
@@ -231,35 +288,61 @@
 AssertionResult DvrTests::detachFilterToDvr(sp<IFilter> filter) {
     Result status;
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+    EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
 
-    status = mDvr->detachFilter(filter);
+    status = mDvrRecord->detachFilter(filter);
 
     return AssertionResult(status == Result::SUCCESS);
 }
 
-AssertionResult DvrTests::startDvr() {
+AssertionResult DvrTests::startDvrPlayback() {
     Result status;
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+    EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
 
-    status = mDvr->start();
+    status = mDvrPlayback->start();
 
     return AssertionResult(status == Result::SUCCESS);
 }
 
-AssertionResult DvrTests::stopDvr() {
+AssertionResult DvrTests::stopDvrPlayback() {
     Result status;
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
-    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+    EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
 
-    status = mDvr->stop();
+    status = mDvrPlayback->stop();
 
     return AssertionResult(status == Result::SUCCESS);
 }
 
-void DvrTests::closeDvr() {
+void DvrTests::closeDvrPlayback() {
     ASSERT_TRUE(mDemux);
-    ASSERT_TRUE(mDvr);
-    ASSERT_TRUE(mDvr->close() == Result::SUCCESS);
+    ASSERT_TRUE(mDvrPlayback);
+    ASSERT_TRUE(mDvrPlayback->close() == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::startDvrRecord() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+    status = mDvrRecord->start();
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::stopDvrRecord() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+    status = mDvrRecord->stop();
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+void DvrTests::closeDvrRecord() {
+    ASSERT_TRUE(mDemux);
+    ASSERT_TRUE(mDvrRecord);
+    ASSERT_TRUE(mDvrRecord->close() == Result::SUCCESS);
 }
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.h b/tv/tuner/1.0/vts/functional/DvrTests.h
index dd00c27..3997839 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.h
+++ b/tv/tuner/1.0/vts/functional/DvrTests.h
@@ -14,14 +14,15 @@
  * 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 <fcntl.h>
 #include <fmq/MessageQueue.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
 #include <hidl/Status.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
@@ -52,6 +53,8 @@
 using android::hardware::tv::tuner::V1_0::RecordStatus;
 using android::hardware::tv::tuner::V1_0::Result;
 
+using namespace std;
+
 #define WAIT_TIMEOUT 3000000000
 
 class DvrCallback : public IDvrCallback {
@@ -149,25 +152,31 @@
     void setDemux(sp<IDemux> demux) { mDemux = demux; }
 
     void startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings) {
-        mDvrCallback->startPlaybackInputThread(dataInputFile, settings, mDvrMQDescriptor);
+        mDvrPlaybackCallback->startPlaybackInputThread(dataInputFile, settings,
+                                                       mDvrPlaybackMQDescriptor);
     };
 
     void startRecordOutputThread(RecordSettings settings) {
-        mDvrCallback->startRecordOutputThread(settings, mDvrMQDescriptor);
+        mDvrRecordCallback->startRecordOutputThread(settings, mDvrRecordMQDescriptor);
     };
 
-    void stopPlaybackThread() { mDvrCallback->stopPlaybackThread(); }
-    void testRecordOutput() { mDvrCallback->testRecordOutput(); }
-    void stopRecordThread() { mDvrCallback->stopPlaybackThread(); }
+    void stopPlaybackThread() { mDvrPlaybackCallback->stopPlaybackThread(); }
+    void testRecordOutput() { mDvrRecordCallback->testRecordOutput(); }
+    void stopRecordThread() { mDvrRecordCallback->stopRecordThread(); }
 
     AssertionResult openDvrInDemux(DvrType type, uint32_t bufferSize);
-    AssertionResult configDvr(DvrSettings setting);
-    AssertionResult getDvrMQDescriptor();
+    AssertionResult configDvrPlayback(DvrSettings setting);
+    AssertionResult configDvrRecord(DvrSettings setting);
+    AssertionResult getDvrPlaybackMQDescriptor();
+    AssertionResult getDvrRecordMQDescriptor();
     AssertionResult attachFilterToDvr(sp<IFilter> filter);
     AssertionResult detachFilterToDvr(sp<IFilter> filter);
-    AssertionResult stopDvr();
-    AssertionResult startDvr();
-    void closeDvr();
+    AssertionResult stopDvrPlayback();
+    AssertionResult startDvrPlayback();
+    AssertionResult stopDvrRecord();
+    AssertionResult startDvrRecord();
+    void closeDvrPlayback();
+    void closeDvrRecord();
 
   protected:
     static AssertionResult failure() { return ::testing::AssertionFailure(); }
@@ -175,11 +184,11 @@
     static AssertionResult success() { return ::testing::AssertionSuccess(); }
 
     sp<ITuner> mService;
-    sp<IDvr> mDvr;
+    sp<IDvr> mDvrPlayback;
+    sp<IDvr> mDvrRecord;
     sp<IDemux> mDemux;
-    sp<DvrCallback> mDvrCallback;
-    MQDesc mDvrMQDescriptor;
-
-    pthread_t mPlaybackshread;
-    bool mPlaybackThreadRunning;
+    sp<DvrCallback> mDvrPlaybackCallback;
+    sp<DvrCallback> mDvrRecordCallback;
+    MQDesc mDvrPlaybackMQDescriptor;
+    MQDesc mDvrRecordMQDescriptor;
 };
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.cpp b/tv/tuner/1.0/vts/functional/FilterTests.cpp
index ca8bd19..0ecdf73 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FilterTests.cpp
@@ -149,6 +149,44 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
+AssertionResult FilterTests::openTimeFilterInDemux() {
+    if (!mDemux) {
+        ALOGW("[vts] Test with openDemux first.");
+        return failure();
+    }
+
+    // Add time filter to the local demux
+    Result status;
+    mDemux->openTimeFilter([&](Result result, const sp<ITimeFilter>& filter) {
+        mTimeFilter = filter;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult FilterTests::setTimeStamp(uint64_t timeStamp) {
+    if (!mTimeFilter) {
+        ALOGW("[vts] Test with openTimeFilterInDemux first.");
+        return failure();
+    }
+
+    mBeginTimeStamp = timeStamp;
+    return AssertionResult(mTimeFilter->setTimeStamp(timeStamp) == Result::SUCCESS);
+}
+
+AssertionResult FilterTests::getTimeStamp() {
+    if (!mTimeFilter) {
+        ALOGW("[vts] Test with openTimeFilterInDemux first.");
+        return failure();
+    }
+
+    Result status;
+    mTimeFilter->getTimeStamp([&](Result result, uint64_t /*timeStamp*/) { status = result; });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
 AssertionResult FilterTests::getNewlyOpenedFilterId(uint32_t& filterId) {
     Result status;
     EXPECT_TRUE(mDemux) << "Test with openDemux first.";
@@ -229,6 +267,15 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
+AssertionResult FilterTests::clearTimeStamp() {
+    if (!mTimeFilter) {
+        ALOGW("[vts] Test with openTimeFilterInDemux first.");
+        return failure();
+    }
+
+    return AssertionResult(mTimeFilter->clearTimeStamp() == Result::SUCCESS);
+}
+
 AssertionResult FilterTests::closeFilter(uint32_t filterId) {
     EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
     Result status = mFilters[filterId]->close();
@@ -244,3 +291,12 @@
     }
     return AssertionResult(status == Result::SUCCESS);
 }
+
+AssertionResult FilterTests::closeTimeFilter() {
+    if (!mTimeFilter) {
+        ALOGW("[vts] Test with openTimeFilterInDemux first.");
+        return failure();
+    }
+
+    return AssertionResult(mTimeFilter->close() == Result::SUCCESS);
+}
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index 2aa1b90..a76a6b9 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -57,6 +57,7 @@
 using android::hardware::tv::tuner::V1_0::IDemux;
 using android::hardware::tv::tuner::V1_0::IFilter;
 using android::hardware::tv::tuner::V1_0::IFilterCallback;
+using android::hardware::tv::tuner::V1_0::ITimeFilter;
 using android::hardware::tv::tuner::V1_0::ITuner;
 using android::hardware::tv::tuner::V1_0::Result;
 
@@ -151,14 +152,19 @@
     std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
 
     AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize);
+    AssertionResult openTimeFilterInDemux();
+    AssertionResult setTimeStamp(uint64_t timeStamp);
+    AssertionResult getTimeStamp();
     AssertionResult getNewlyOpenedFilterId(uint32_t& filterId);
     AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId);
     AssertionResult getFilterMQDescriptor(uint32_t filterId);
     AssertionResult setFilterDataSource(uint32_t sourceFilterId, uint32_t sinkFilterId);
     AssertionResult setFilterDataSourceToDemux(uint32_t filterId);
     AssertionResult startFilter(uint32_t filterId);
+    AssertionResult clearTimeStamp();
     AssertionResult stopFilter(uint32_t filterId);
     AssertionResult closeFilter(uint32_t filterId);
+    AssertionResult closeTimeFilter();
 
     FilterEventType getFilterEventType(DemuxFilterType type) {
         FilterEventType eventType = FilterEventType::UNDEFINED;
@@ -214,6 +220,7 @@
 
     sp<ITuner> mService;
     sp<IFilter> mFilter;
+    sp<ITimeFilter> mTimeFilter;
     sp<IDemux> mDemux;
     std::map<uint32_t, sp<IFilter>> mFilters;
     std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks;
@@ -223,4 +230,5 @@
     vector<uint32_t> mUsedFilterIds;
 
     uint32_t mFilterId = -1;
+    uint64_t mBeginTimeStamp;
 };
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.cpp b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
index d094510..45951d2 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
@@ -360,13 +360,28 @@
     ASSERT_TRUE(status == Result::SUCCESS);
 }
 
-AssertionResult FrontendTests::tuneFrontend(FrontendConfig config) {
+AssertionResult FrontendTests::tuneFrontend(FrontendConfig config, bool testWithDemux) {
     EXPECT_TRUE(mFrontendCallback)
             << "test with openFrontendById/setFrontendCallback/getFrontendInfo first.";
 
     EXPECT_TRUE(mFrontendInfo.type == config.type)
             << "FrontendConfig does not match the frontend info of the given id.";
 
+    mIsSoftwareFe = config.isSoftwareFe;
+    bool result = true;
+    if (mIsSoftwareFe && testWithDemux) {
+        DvrConfig dvrConfig;
+        getSoftwareFrontendPlaybackConfig(dvrConfig);
+        result &= mDvrTests.openDvrInDemux(dvrConfig.type, dvrConfig.bufferSize) == success();
+        result &= mDvrTests.configDvrPlayback(dvrConfig.settings) == success();
+        result &= mDvrTests.getDvrPlaybackMQDescriptor() == success();
+        mDvrTests.startPlaybackInputThread(dvrConfig.playbackInputFile,
+                                           dvrConfig.settings.playback());
+        if (!result) {
+            ALOGW("[vts] Software frontend dvr configure failed.");
+            return failure();
+        }
+    }
     mFrontendCallback->tuneTestOnLock(mFrontend, config.settings);
     return AssertionResult(true);
 }
@@ -379,10 +394,14 @@
     return AssertionResult(mFrontend->setLnb(lnbId) == Result::SUCCESS);
 }
 
-AssertionResult FrontendTests::stopTuneFrontend() {
+AssertionResult FrontendTests::stopTuneFrontend(bool testWithDemux) {
     EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
     Result status;
     status = mFrontend->stopTune();
+    if (mIsSoftwareFe && testWithDemux) {
+        mDvrTests.stopPlaybackThread();
+        mDvrTests.closeDvrPlayback();
+    }
     return AssertionResult(status == Result::SUCCESS);
 }
 
@@ -415,9 +434,9 @@
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(openFrontendById(feId));
     ASSERT_TRUE(setFrontendCallback());
-    ASSERT_TRUE(tuneFrontend(frontendConf));
+    ASSERT_TRUE(tuneFrontend(frontendConf, false /*testWithDemux*/));
     verifyFrontendStatus(frontendConf.tuneStatusTypes, frontendConf.expectTuneStatuses);
-    ASSERT_TRUE(stopTuneFrontend());
+    ASSERT_TRUE(stopTuneFrontend(false /*testWithDemux*/));
     ASSERT_TRUE(closeFrontend());
 }
 
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.h b/tv/tuner/1.0/vts/functional/FrontendTests.h
index b8b9f47..c536325 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.h
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.h
@@ -31,6 +31,7 @@
 #include <utils/Mutex.h>
 #include <map>
 
+#include "DvrTests.h"
 #include "VtsHalTvTunerV1_0TestConfigurations.h"
 
 #define WAIT_TIMEOUT 3000000000
@@ -100,7 +101,10 @@
   public:
     sp<ITuner> mService;
 
-    void setService(sp<ITuner> tuner) { mService = tuner; }
+    void setService(sp<ITuner> tuner) {
+        mService = tuner;
+        mDvrTests.setService(tuner);
+    }
 
     AssertionResult getFrontendIds();
     AssertionResult getFrontendInfo(uint32_t frontendId);
@@ -108,23 +112,43 @@
     AssertionResult setFrontendCallback();
     AssertionResult scanFrontend(FrontendConfig config, FrontendScanType type);
     AssertionResult stopScanFrontend();
-    AssertionResult tuneFrontend(FrontendConfig config);
+    AssertionResult tuneFrontend(FrontendConfig config, bool testWithDemux);
     AssertionResult setLnb(uint32_t lnbId);
     void verifyFrontendStatus(vector<FrontendStatusType> statusTypes,
                               vector<FrontendStatus> expectStatuses);
-    AssertionResult stopTuneFrontend();
+    AssertionResult stopTuneFrontend(bool testWithDemux);
     AssertionResult closeFrontend();
 
     void getFrontendIdByType(FrontendType feType, uint32_t& feId);
     void tuneTest(FrontendConfig frontendConf);
     void scanTest(FrontendConfig frontend, FrontendScanType type);
 
+    void setDvrTests(DvrTests dvrTests) { mDvrTests = dvrTests; }
+    void setDemux(sp<IDemux> demux) { mDvrTests.setDemux(demux); }
+
   protected:
     static AssertionResult failure() { return ::testing::AssertionFailure(); }
     static AssertionResult success() { return ::testing::AssertionSuccess(); }
 
+    void getSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
+        PlaybackSettings playbackSettings{
+                .statusMask = 0xf,
+                .lowThreshold = 0x1000,
+                .highThreshold = 0x07fff,
+                .dataFormat = DataFormat::TS,
+                .packetSize = 188,
+        };
+        dvrConfig.type = DvrType::PLAYBACK;
+        dvrConfig.playbackInputFile = "/data/local/tmp/segment000000.ts";
+        dvrConfig.bufferSize = FMQ_SIZE_4M;
+        dvrConfig.settings.playback(playbackSettings);
+    }
+
     sp<IFrontend> mFrontend;
     FrontendInfo mFrontendInfo;
     sp<FrontendCallback> mFrontendCallback;
     hidl_vec<FrontendId> mFeIds;
+
+    DvrTests mDvrTests;
+    bool mIsSoftwareFe = false;
 };
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index e1284b4..6819659 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -56,6 +56,23 @@
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
+void TunerFilterHidlTest::testTimeFilter(TimeFilterConfig filterConf) {
+    if (!filterConf.supportTimeFilter) {
+        return;
+    }
+    uint32_t demuxId;
+    sp<IDemux> demux;
+
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    mFilterTests.setDemux(demux);
+    ASSERT_TRUE(mFilterTests.openTimeFilterInDemux());
+    ASSERT_TRUE(mFilterTests.setTimeStamp(filterConf.timeStamp));
+    ASSERT_TRUE(mFilterTests.getTimeStamp());
+    ASSERT_TRUE(mFilterTests.clearTimeStamp());
+    ASSERT_TRUE(mFilterTests.closeTimeFilter());
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
 void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
                                                        FrontendConfig frontendConf) {
     uint32_t feId;
@@ -77,6 +94,7 @@
     }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFrontendTests.setDemux(demux);
     mFilterTests.setDemux(demux);
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -84,9 +102,9 @@
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
     ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     ASSERT_TRUE(mDemuxTests.closeDemux());
@@ -122,21 +140,21 @@
     mFilterTests.setDemux(demux);
     mDvrTests.setDemux(demux);
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
-    ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
-    ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+    ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrConf.settings));
+    ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
     mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile, dvrConf.settings.playback());
-    ASSERT_TRUE(mDvrTests.startDvr());
+    ASSERT_TRUE(mDvrTests.startDvrPlayback());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
     mDvrTests.stopPlaybackThread();
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
-    ASSERT_TRUE(mDvrTests.stopDvr());
+    ASSERT_TRUE(mDvrTests.stopDvrPlayback());
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
-    mDvrTests.closeDvr();
+    mDvrTests.closeDvrPlayback();
     ASSERT_TRUE(mDemuxTests.closeDemux());
 }
 
@@ -159,9 +177,10 @@
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
     mDvrTests.setDemux(demux);
+    mFrontendTests.setDvrTests(mDvrTests);
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
-    ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
-    ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+    ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+    ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
@@ -170,17 +189,17 @@
     ASSERT_TRUE(filter != nullptr);
     mDvrTests.startRecordOutputThread(dvrConf.settings.record());
     ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
-    ASSERT_TRUE(mDvrTests.startDvr());
+    ASSERT_TRUE(mDvrTests.startDvrRecord());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
     mDvrTests.testRecordOutput();
     mDvrTests.stopRecordThread();
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
-    ASSERT_TRUE(mDvrTests.stopDvr());
+    ASSERT_TRUE(mDvrTests.stopDvrRecord());
     ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
-    mDvrTests.closeDvr();
+    mDvrTests.closeDvrRecord();
     ASSERT_TRUE(mDemuxTests.closeDemux());
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
@@ -223,8 +242,8 @@
     mFilterTests.setDemux(demux);
     mDvrTests.setDemux(demux);
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
-    ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
-    ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+    ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+    ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
@@ -232,13 +251,13 @@
     filter = mFilterTests.getFilterById(filterId);
     ASSERT_TRUE(filter != nullptr);
     ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
-    ASSERT_TRUE(mDvrTests.startDvr());
+    ASSERT_TRUE(mDvrTests.startDvrRecord());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
-    ASSERT_TRUE(mDvrTests.stopDvr());
+    ASSERT_TRUE(mDvrTests.stopDvrRecord());
     ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
-    mDvrTests.closeDvr();
+    mDvrTests.closeDvrRecord();
     ASSERT_TRUE(mDemuxTests.closeDemux());
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
@@ -266,6 +285,7 @@
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
+    mFrontendTests.setDemux(demux);
     for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
         ASSERT_TRUE(mFilterTests.openFilterInDemux((*config).type, (*config).bufferSize));
         ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -289,9 +309,9 @@
         ASSERT_TRUE(mFilterTests.startFilter(*id));
     }
     // tune test
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
     ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.stopFilter(*id));
     }
@@ -321,6 +341,17 @@
     mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_BLIND);
 }
 
+TEST_P(TunerLnbHidlTest, OpenLnbByName) {
+    description("Open and configure an Lnb with name then send a diseqc msg to it.");
+    ASSERT_TRUE(mLnbTests.openLnbByName(lnbArray[LNB_EXTERNAL].name));
+    ASSERT_TRUE(mLnbTests.setLnbCallback());
+    ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB_EXTERNAL].voltage));
+    ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB_EXTERNAL].tone));
+    ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB_EXTERNAL].position));
+    ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
+    ASSERT_TRUE(mLnbTests.closeLnb());
+}
+
 TEST_P(TunerLnbHidlTest, SendDiseqcMessageToLnb) {
     description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
     vector<uint32_t> ids;
@@ -424,6 +455,12 @@
     ASSERT_TRUE(mDemuxTests.closeDemux());
 }
 
+TEST_P(TunerFilterHidlTest, testTimeFilter) {
+    description("Open a timer filter in Demux and set time stamp.");
+    // TODO use paramterized tests
+    testTimeFilter(timeFilterArray[TIMER0]);
+}
+
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
     description("Test Video Filter functionality in Broadcast use case.");
     broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBT]);
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index d71222b..6804f3c 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -16,7 +16,6 @@
 
 #include "DemuxTests.h"
 #include "DescramblerTests.h"
-#include "DvrTests.h"
 #include "FrontendTests.h"
 #include "LnbTests.h"
 
@@ -34,6 +33,7 @@
     initFrontendScanConfig();
     initLnbConfig();
     initFilterConfig();
+    initTimeFilterConfig();
     initDvrConfig();
     initDescramblerConfig();
 }
@@ -127,6 +127,7 @@
     }
 
     void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
+    void testTimeFilter(TimeFilterConfig filterConf);
 
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
@@ -145,6 +146,7 @@
         mDemuxTests.setService(mService);
         mFilterTests.setService(mService);
         mLnbTests.setService(mService);
+        mDvrTests.setService(mService);
     }
 
   protected:
@@ -157,6 +159,7 @@
     DemuxTests mDemuxTests;
     FilterTests mFilterTests;
     LnbTests mLnbTests;
+    DvrTests mDvrTests;
 
     AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
 
@@ -245,6 +248,7 @@
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
+        mDvrTests.setService(mService);
         mDescramblerTests.setService(mService);
         mDescramblerTests.setCasService(mCasService);
     }
@@ -264,5 +268,6 @@
     DemuxTests mDemuxTests;
     FilterTests mFilterTests;
     DescramblerTests mDescramblerTests;
+    DvrTests mDvrTests;
 };
 }  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index d0f2b9c..6c68e35 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -86,6 +86,11 @@
 } Filter;
 
 typedef enum {
+    TIMER0,
+    TIMER_MAX,
+} TimeFilter;
+
+typedef enum {
     SOURCE,
     SINK,
     LINKAGE_DIR,
@@ -99,6 +104,7 @@
 
 typedef enum {
     LNB0,
+    LNB_EXTERNAL,
     LNB_MAX,
 } Lnb;
 
@@ -115,6 +121,7 @@
 typedef enum {
     DVR_RECORD0,
     DVR_PLAYBACK0,
+    DVR_SOFTWARE_FE,
     DVR_MAX,
 } Dvr;
 
@@ -131,7 +138,13 @@
     bool operator<(const FilterConfig& /*c*/) const { return false; }
 };
 
+struct TimeFilterConfig {
+    bool supportTimeFilter;
+    uint64_t timeStamp;
+};
+
 struct FrontendConfig {
+    bool isSoftwareFe;
     FrontendType type;
     FrontendSettings settings;
     vector<FrontendStatusType> tuneStatusTypes;
@@ -140,6 +153,7 @@
 
 struct LnbConfig {
     bool usingLnb;
+    string name;
     LnbVoltage voltage;
     LnbTone tone;
     LnbPosition position;
@@ -172,6 +186,7 @@
 static vector<uint8_t> diseqcMsgArray[DISEQC_MAX];
 static ChannelConfig channelArray[FRONTEND_MAX];
 static FilterConfig filterArray[FILTER_MAX];
+static TimeFilterConfig timeFilterArray[TIMER_MAX];
 static DemuxFilterType filterLinkageTypes[LINKAGE_DIR][FILTER_MAIN_TYPE_BIT_COUNT];
 static DvrConfig dvrArray[DVR_MAX];
 static DescramblerConfig descramblerArray[DESC_MAX];
@@ -200,7 +215,9 @@
     statuses.push_back(status);
     frontendArray[DVBT].tuneStatusTypes = types;
     frontendArray[DVBT].expectTuneStatuses = statuses;
+    frontendArray[DVBT].isSoftwareFe = true;
     frontendArray[DVBS].type = FrontendType::DVBS;
+    frontendArray[DVBS].isSoftwareFe = true;
 };
 
 /** Configuration array for the frontend scan test */
@@ -226,6 +243,11 @@
     lnbArray[LNB0].voltage = LnbVoltage::VOLTAGE_12V;
     lnbArray[LNB0].tone = LnbTone::NONE;
     lnbArray[LNB0].position = LnbPosition::UNDEFINED;
+    lnbArray[LNB_EXTERNAL].usingLnb = true;
+    lnbArray[LNB_EXTERNAL].name = "default_lnb_external";
+    lnbArray[LNB_EXTERNAL].voltage = LnbVoltage::VOLTAGE_5V;
+    lnbArray[LNB_EXTERNAL].tone = LnbTone::NONE;
+    lnbArray[LNB_EXTERNAL].position = LnbPosition::UNDEFINED;
 };
 
 /** Diseqc messages array for the Lnb test */
@@ -311,6 +333,12 @@
     filterLinkageTypes[SINK][4] = filterLinkageTypes[SOURCE][4];
 };
 
+/** Configuration array for the timer filter test */
+inline void initTimeFilterConfig() {
+    timeFilterArray[TIMER0].supportTimeFilter = true;
+    timeFilterArray[TIMER0].timeStamp = 1;
+}
+
 /** Configuration array for the dvr test */
 inline void initDvrConfig() {
     RecordSettings recordSettings{
@@ -331,9 +359,20 @@
             .packetSize = 188,
     };
     dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
-    dvrArray[DVR_PLAYBACK0].playbackInputFile = "/vendor/etc/segment000000.ts";
+    dvrArray[DVR_PLAYBACK0].playbackInputFile = "/data/local/tmp/segment000000.ts";
     dvrArray[DVR_PLAYBACK0].bufferSize = FMQ_SIZE_4M;
     dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
+    PlaybackSettings softwareFePlaybackSettings{
+            .statusMask = 0xf,
+            .lowThreshold = 0x1000,
+            .highThreshold = 0x07fff,
+            .dataFormat = DataFormat::TS,
+            .packetSize = 188,
+    };
+    dvrArray[DVR_SOFTWARE_FE].type = DvrType::PLAYBACK;
+    dvrArray[DVR_SOFTWARE_FE].playbackInputFile = "/data/local/tmp/segment000000.ts";
+    dvrArray[DVR_SOFTWARE_FE].bufferSize = FMQ_SIZE_4M;
+    dvrArray[DVR_SOFTWARE_FE].settings.playback(softwareFePlaybackSettings);
 };
 
 /** Configuration array for the descrambler test */