Merge "audio: Fix and enhance channel mask enums"
diff --git a/audio/5.0/config/audio_policy_configuration.xsd b/audio/5.0/config/audio_policy_configuration.xsd
index ee3a437..c580b25 100644
--- a/audio/5.0/config/audio_policy_configuration.xsd
+++ b/audio/5.0/config/audio_policy_configuration.xsd
@@ -42,7 +42,7 @@
                 <xs:element name="globalConfiguration" type="globalConfiguration"/>
                 <xs:element name="modules" type="modules" maxOccurs="unbounded"/>
                 <xs:element name="volumes" type="volumes" maxOccurs="unbounded"/>
-                <xs:element name="surroundSound" type="surroundSound" />
+                <xs:element name="surroundSound" type="surroundSound" minOccurs="0" />
             </xs:sequence>
             <xs:attribute name="version" type="version"/>
         </xs:complexType>
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 3efaaec..435e19c 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -989,6 +989,7 @@
      *
      * @change_mode VehiclePropertyChangeMode:ON_CHANGE
      * @access VehiclePropertyAccess:READ_WRITE
+     * @data_enum VehicleUnit
      */
     HVAC_TEMPERATURE_DISPLAY_UNITS = (
         0x050E
@@ -2433,7 +2434,7 @@
     ERROR = 4,
 };
 
-enum VehicleApPowerStateConfigFlag : int32_t /* NOTE: type is guessed */ {
+enum VehicleApPowerStateConfigFlag : int32_t {
     /**
      * AP can enter deep sleep state. If not set, AP will always shutdown from
      * VehicleApPowerState#SHUTDOWN_PREPARE power state.
diff --git a/biometrics/face/1.0/IBiometricsFace.hal b/biometrics/face/1.0/IBiometricsFace.hal
index 0499c5d..cd368fa 100644
--- a/biometrics/face/1.0/IBiometricsFace.hal
+++ b/biometrics/face/1.0/IBiometricsFace.hal
@@ -53,6 +53,10 @@
      * returning to the idle state. Calling this method with the same userId
      * should have no effect on the state machine.
      *
+     * Note that onLockoutChanged() MUST be invoked by the implementation in
+     * response to a user change in order to update the framework with the
+     * timeout of the new user (or 0 if the user is not locked out).
+     *
      * @param userId A non-negative user identifier that must be unique and
      *     persistent for a given user.
      * @param storePath filesystem path to the template storage directory.
@@ -61,19 +65,22 @@
     setActiveUser(int32_t userId, string storePath) generates (Status status);
 
     /**
-     * Begins a secure transaction request, e.g. enrollment.
+     * Begins a secure transaction request, e.g. enroll() or resetLockout().
      *
      * Generates a unique and cryptographically secure random token used to
      * indicate the start of a secure transaction. generateChallenge() and
-     * revokeChallenge() specify a pin/pattern/password cleared time window where
-     * the secure transaction is allowed.
+     * revokeChallenge() specify a window where the resulting HAT that is
+     * generated in response to checking the user's PIN/pattern/password
+     * can be used to verify/perform a secure transaction.
      *
-     * generateChallenge() generates a challenge which must then be wrapped by the
+     * generateChallenge() generates a challenge which must then be wrapped by
      * gatekeeper after verifying a successful strong authentication attempt,
      * which generates a Hardware Authentication Token. The challenge prevents
-     * spoofing and replay attacks and ensures that we only update a user’s face
-     * template if the operation was preceded by some kind of strong credential
-     * confirmation (e.g. device password).
+     * spoofing and replay attacks and ensures that only a transaction backed
+     * by a user authentication (PIN/pattern/password) can proceed.
+     *
+     * The implementation should be tolerant of revokeChallenge() being invoked
+     * after timeout has expired.
      *
      * @param challengeTimeoutSec A timeout in seconds, after which the driver
      *     must invalidate the challenge. This is to prevent bugs or crashes in
@@ -81,35 +88,35 @@
      * @return result, with its "value" parameter representing a "challenge": a
      *     unique and cryptographically secure random token.
      */
-    @callflow(next={"enroll", "revokeChallenge", "setFeatureDisabled"})
+    @callflow(next={"enroll", "revokeChallenge", "setFeature"})
     generateChallenge(uint32_t challengeTimeoutSec)
         generates (OptionalUint64 result);
 
     /**
      * Enrolls a user's face.
      *
-     * Note that this interface permits implementations where multiple faces can
-     * be enrolled for a single user. However, allowing multiple faces to be
-     * enrolled can be a severe security vulnerability and hence, most
-     * implementations must ensure that only a single face be enrolled at a
-     * given time. Multi-enrollment must only be used where there is a clear
-     * necessity for a shared use case, e.g. TVs or cars.
-     *
-     * Note that the Hardware Authentication Token must still be valid after
-     * this call, and must be explicitly invalidated by a call to
-     * revokeChallenge(). This allows clients to immediately reattempt
-     * enrollment (for example, if a user wasn’t satisfied with their enrollment)
-     * without having to go through another strong authentication flow.
+     * Note that the Hardware Authentication Token must be valid for the
+     * duration of enrollment and thus should be explicitly invalidated by a
+     * call to revokeChallenge() when enrollment is complete, to reduce the
+     * window of opportunity to re-use the challenge and HAT. For example,
+     * Settings calls generateChallenge() once to allow the user to enroll one
+     * or more faces or toggle secure settings without having to re-enter the
+     * PIN/pattern/password. Once the user completes the operation, Settings
+     * invokes revokeChallenge() to close the transaction. If the HAT is expired,
+     * the implementation must invoke onError with UNABLE_TO_PROCESS.
      *
      * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
      * method.
      *
      * @param hat A valid Hardware Authentication Token, generated as a result
      *     of a generateChallenge() challenge being wrapped by the gatekeeper
-     *     after a sucessful strong authentication request.
-     * @param timeoutSec A timeout in seconds, after which this enrollment
-     *     attempt is cancelled. Note that the client still needs to
-     *     call revokeChallenge() to terminate the enrollment session.
+     *     after a successful strong authentication request.
+     * @param timeoutSec A timeout in seconds, after which this enroll
+     *     attempt is cancelled. Note that the framework can continue
+     *     enrollment by calling this again with a valid HAT. This timeout is
+     *     expected to be used to limit power usage if the device becomes idle
+     *     during enrollment. The implementation is expected to send
+     *     ERROR_TIMEOUT if this happens.
      * @param disabledFeatures A list of features to be disabled during
      *     enrollment. Note that all features are enabled by default.
      * @return status The status of this method call.
@@ -122,8 +129,8 @@
      * Finishes the secure transaction by invalidating the challenge generated
      * by generateChallenge().
      *
-     * Clients must call this method once enrollment is complete, and the user's
-     * face template no longer needs to be updated.
+     * Clients must call this method once the secure transaction (e.g. enroll
+     * or setFeature) is completed. See generateChallenge().
      *
      * @return status The status of this method call.
      */
@@ -131,33 +138,43 @@
     revokeChallenge() generates (Status status);
 
     /**
-     * Requires all subsequent enroll/authenticate calls to use the feature.
-     * This method does not affect enroll, which has its own feature list.
-     *
      * Changes the state of previous enrollment setting. Because this may
      * decrease security, the user must enter their password before this method
      * is invoked (see @param HAT). The driver must verify the HAT before
-     * changing any feature state.
+     * changing any feature state. This method must return ILLEGAL_ARGUMENT if
+     * the HAT or faceId is invalid. This must only be invoked after
+     * setActiveUser() is called.
+     *
      * Note: In some cases it may not be possible to change the state of this
      * flag without re-enrolling. For example, if the user didn't provide
      * attention during the original enrollment. This flag reflects the same
      * persistent state as the one passed to enroll().
      *
+     * Note: This call may block for a short amount of time (few hundred
+     * milliseconds). Clients are expected to invoke this asynchronously if it
+     * takes much longer than the above limit. Also note that the result is
+     * returned solely through Status (and not onError).
+     *
      * @param feature The feature to be enabled or disabled.
      * @param enabled True to enable the feature, false to disable.
      * @param hat A valid Hardware Authentication Token, generated as a result
      *     of getChallenge().
+     * @param faceId the ID of the enrollment returned by enroll() for the
+     *     feature to update.
      * @return status The status of this method call.
      */
-    setFeature(Feature feature, bool enabled, vec<uint8_t> hat)
+    setFeature(Feature feature, bool enabled, vec<uint8_t> hat, uint32_t faceId)
         generates(Status status);
 
     /**
-     * Retrieves the current state of the feature.
+     * Retrieves the current state of the feature. If the faceId is invalid,
+     * the implementation must return ILLEGAL_ARGUMENT.
      *
-     * @return enabled True if the feature is enabled, false if disabled.
+     * @param faceId the ID of the enrollment returned by enroll().
+     * @return result with the value set to true if the feature is enabled,
+     *     false if disabled.
      */
-    getFeature(Feature feature) generates (bool enabled);
+    getFeature(Feature feature, uint32_t faceId) generates (OptionalBool result);
 
     /**
      * Returns an identifier associated with the current face set.
@@ -176,7 +193,7 @@
     getAuthenticatorId() generates (OptionalUint64 result);
 
     /**
-     * Cancels a pending enrollment or authentication request.
+     * Cancels the current enroll, authenticate, remove, or enumerate operation.
      *
      * @return status The status of this method call.
      */
@@ -241,8 +258,12 @@
     /**
      * Reset lockout for the current user.
      *
+     * Note: This call may block for a short amount of time (few hundred
+     * milliseconds). Clients are expected to invoke this asynchronously if it
+     * takes much longer than the above limit.
+     *
      * @param hat A valid Hardware Authentication Token, generated when the
-     *     user authenticates with Pin/Pattern/Pass. When the Hardware
+     *     user authenticates with PIN/pattern/pass. When the Hardware
      *     Authentication Token is verified, lockout must be reset and
      *     onLockoutChanged must be called with duration 0.
      * @return status The status of this method call.
diff --git a/biometrics/face/1.0/IBiometricsFaceClientCallback.hal b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
index c9dd0e2..969bc68 100644
--- a/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
+++ b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
@@ -39,7 +39,7 @@
      * A callback invoked when a face has been successfully authenticated.
      *
      * @param deviceId A unique id associated with the HAL implementation
-     *     service that processed this autentication attempt.
+     *     service that processed this authentication attempt.
      * @param faceId The id of the face template that passed the authentication
      *     challenge.
      * @param userId The active user id for the authenticated face.
diff --git a/biometrics/face/1.0/types.hal b/biometrics/face/1.0/types.hal
index b5db966..8c4a4e9 100644
--- a/biometrics/face/1.0/types.hal
+++ b/biometrics/face/1.0/types.hal
@@ -82,13 +82,14 @@
 enum FaceError : int32_t {
 
     /**
-     * A hardware error has occured that cannot be resolved. Try again later.
+     * A hardware error has occurred that cannot be resolved. Try again later.
      */
     HW_UNAVAILABLE = 1,
 
     /**
-     * The current enroll or authenticate operation could not be completed;
-     * the sensor was unable to process the current image.
+     * The current enroll or authenticate operation could not be completed,
+     * e.g. the sensor was unable to process the current image or the HAT was
+     * invalid.
      */
     UNABLE_TO_PROCESS = 2,
 
@@ -97,6 +98,11 @@
      * prevent programs from blocking the face HAL indefinitely. The timeout is
      * framework and sensor-specific, but is generally on the order of 30
      * seconds.
+
+     * The timeout is a device-specific time meant to optimize power. For
+     * example after 30 seconds of searching for a face it can be use to
+     * indicate that the implementation is no longer looking and the framework
+     * should restart the operation on the next user interaction.
      */
     TIMEOUT = 3,
 
@@ -108,8 +114,8 @@
 
     /**
      * The current operation has been cancelled. This may happen if a new
-     * request (authenticate, remove) is initiated while an on-going operation
-     * is in progress, or if cancel() was called.
+     * request (authenticate, remove, enumerate, enroll) is initiated while
+     * an on-going operation is in progress, or if cancel() was called.
      */
     CANCELED = 5,
 
@@ -357,7 +363,7 @@
 
 /**
  * Result structure with an addition bool field. See documentation in
- * getRequireAttention() for usage of the value.
+ * getFeature() for usage of the value.
  */
 struct OptionalBool {
     /**
diff --git a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
index f496bbe..795a1ae 100644
--- a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
+++ b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
@@ -42,6 +42,7 @@
 using android::hardware::biometrics::face::V1_0::Feature;
 using android::hardware::biometrics::face::V1_0::IBiometricsFace;
 using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
+using android::hardware::biometrics::face::V1_0::OptionalBool;
 using android::hardware::biometrics::face::V1_0::OptionalUint64;
 using android::hardware::biometrics::face::V1_0::Status;
 
@@ -167,6 +168,19 @@
     std::promise<void> promise;
 };
 
+class LockoutChangedCallback : public FaceCallbackBase {
+  public:
+    Return<void> onLockoutChanged(uint64_t duration) override {
+        this->hasDuration = true;
+        this->duration = duration;
+        promise.set_value();
+        return Return<void>();
+    }
+    bool hasDuration;
+    uint64_t duration;
+    std::promise<void> promise;
+};
+
 // Test environment for Face HIDL HAL.
 class FaceHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
   public:
@@ -266,12 +280,8 @@
         token[i] = 0;
     }
 
-    Return<Status> res = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token);
-    ASSERT_EQ(Status::OK, static_cast<Status>(res));
-
-    // At least one call to onError should occur
-    ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
-    ASSERT_TRUE(cb->hasError);
+    Return<Status> res = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token, 0);
+    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, static_cast<Status>(res));
 }
 
 // setFeature with an invalid HAT should fail.
@@ -285,24 +295,27 @@
         token[i] = i;
     }
 
-    Return<Status> res = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token);
-    ASSERT_EQ(Status::OK, static_cast<Status>(res));
-
-    // At least one call to onError should occur
-    ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
-    ASSERT_TRUE(cb->hasError);
+    Return<Status> res = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token, 0);
+    ASSERT_EQ(Status::ILLEGAL_ARGUMENT, static_cast<Status>(res));
 }
 
-// getFeature by default should return true for REQUIRE_ATTENTION.
+void assertGetFeatureFails(sp<IBiometricsFace> service, int faceId, Feature feature) {
+    std::promise<void> promise;
+
+    // Features cannot be retrieved for invalid faces.
+    Return<void> res = service->getFeature(feature, faceId, [&promise](const OptionalBool& result) {
+        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, result.status);
+        promise.set_value();
+    });
+    ASSERT_TRUE(waitForCallback(promise.get_future()));
+}
+
 TEST_F(FaceHidlTest, GetFeatureRequireAttentionTest) {
-    Return<bool> res = mService->getFeature(Feature::REQUIRE_ATTENTION);
-    ASSERT_EQ(true, static_cast<bool>(res));
+    assertGetFeatureFails(mService, 0 /* faceId */, Feature::REQUIRE_ATTENTION);
 }
 
-// getFeature by default should return true for REQUIRE_DIVERSITY.
 TEST_F(FaceHidlTest, GetFeatureRequireDiversityTest) {
-    Return<bool> res = mService->getFeature(Feature::REQUIRE_DIVERSITY);
-    ASSERT_EQ(true, static_cast<bool>(res));
+    assertGetFeatureFails(mService, 0 /* faceId */, Feature::REQUIRE_DIVERSITY);
 }
 
 // revokeChallenge should always return within the timeout
@@ -400,6 +413,20 @@
     ASSERT_EQ(FaceError::CANCELED, cb->error);
 }
 
+TEST_F(FaceHidlTest, OnLockoutChangedTest) {
+    sp<LockoutChangedCallback> cb = new LockoutChangedCallback();
+    mService->setCallback(cb, kAssertCallbackIsSet);
+
+    // Update active user and ensure lockout duration 0 is received
+    mService->setActiveUser(5, kTmpDir);
+
+    // Make sure callback was invoked
+    ASSERT_TRUE(waitForCallback(cb->promise.get_future()));
+
+    // Check that duration 0 was received
+    ASSERT_EQ(0, cb->duration);
+}
+
 }  // anonymous namespace
 
 int main(int argc, char** argv) {
diff --git a/camera/device/3.5/default/CameraDeviceSession.cpp b/camera/device/3.5/default/CameraDeviceSession.cpp
index bea1be6..e812e50 100644
--- a/camera/device/3.5/default/CameraDeviceSession.cpp
+++ b/camera/device/3.5/default/CameraDeviceSession.cpp
@@ -273,6 +273,7 @@
         int streamId = bufRets[i].streamId;
         const hidl_vec<StreamBuffer>& hBufs = bufRets[i].val.buffers();
         camera3_stream_buffer_t* outBufs = returned_buf_reqs[i].output_buffers;
+        returned_buf_reqs[i].num_output_buffers = hBufs.size();
         for (size_t b = 0; b < hBufs.size(); b++) {
             const StreamBuffer& hBuf = hBufs[b];
             camera3_stream_buffer_t& outBuf = outBufs[b];
@@ -281,27 +282,19 @@
                     hBuf.bufferId, hBuf.buffer.getNativeHandle(),
                     /*out*/&(outBuf.buffer),
                     /*allowEmptyBuf*/false);
-            if (s != Status::OK) {
-                ALOGE("%s: import stream %d bufferId %" PRIu64 " failed!",
-                        __FUNCTION__, streamId, hBuf.bufferId);
-                cleanupInflightBufferFences(importedFences, importedBuffers);
-                // Buffer import should never fail - restart HAL since something is very
-                // wrong.
-                assert(false);
-                return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
-            }
+            // Buffer import should never fail - restart HAL since something is very wrong.
+            LOG_ALWAYS_FATAL_IF(s != Status::OK,
+                    "%s: import stream %d bufferId %" PRIu64 " failed!",
+                    __FUNCTION__, streamId, hBuf.bufferId);
 
             pushBufferId(*(outBuf.buffer), hBuf.bufferId, streamId);
             importedBuffers.push_back(std::make_pair(*(outBuf.buffer), streamId));
 
-            if (!sHandleImporter.importFence(
-                    hBuf.acquireFence,
-                    outBuf.acquire_fence)) {
-                ALOGE("%s: stream %d bufferId %" PRIu64 "acquire fence is invalid",
+            bool succ = sHandleImporter.importFence(hBuf.acquireFence, outBuf.acquire_fence);
+            // Fence import should never fail - restart HAL since something is very wrong.
+            LOG_ALWAYS_FATAL_IF(!succ,
+                        "%s: stream %d bufferId %" PRIu64 "acquire fence is invalid",
                         __FUNCTION__, streamId, hBuf.bufferId);
-                cleanupInflightBufferFences(importedFences, importedBuffers);
-                return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
-            }
             importedFences.push_back(outBuf.acquire_fence);
             outBuf.stream = returned_buf_reqs[i].stream;
             outBuf.status = CAMERA3_BUFFER_STATUS_OK;
diff --git a/camera/metadata/3.3/types.hal b/camera/metadata/3.3/types.hal
index 27d82b9..1ed8ed8 100644
--- a/camera/metadata/3.3/types.hal
+++ b/camera/metadata/3.3/types.hal
@@ -98,7 +98,7 @@
      */
     ANDROID_REQUEST_AVAILABLE_SESSION_KEYS = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_REQUEST_END,
 
-    /** android.request.availablePhysicalCameraRequestKeys [static, int32[], hidden]
+    /** android.request.availablePhysicalCameraRequestKeys [static, int32[], ndk_public]
      *
      * <p>A subset of the available request keys that can be overridden for
      * physical devices backing a logical multi-camera.</p>
diff --git a/current.txt b/current.txt
index 4852f55..8184523 100644
--- a/current.txt
+++ b/current.txt
@@ -390,7 +390,7 @@
 684702a60deef03a1e8093961dc0a18c555c857ad5a77ba7340b0635ae01eb70 android.hardware.camera.device@3.4::ICameraDeviceSession
 f8a19622cb0cc890913b1ef3e32b675ffb26089a09e02fef4056ebad324d2b5d android.hardware.camera.device@3.4::types
 291638a1b6d4e63283e9e722ab5049d9351717ffa2b66162124f84d1aa7c2835 android.hardware.camera.metadata@3.2::types
-8a075cf3a17fe99c6d23415a3e9a65612f1fee73ee052a3a8a0ca5b8877395a4 android.hardware.camera.metadata@3.3::types
+a31142da4cc87ad50e4e981d12291d4decbb432fcb00f175f0ca904d0fcdbe5b android.hardware.camera.metadata@3.3::types
 da33234403ff5d60f3473711917b9948e6484a4260b5247acdafb111193a9de2 android.hardware.configstore@1.0::ISurfaceFlingerConfigs
 21165b8e30c4b2d52980e4728f661420adc16e38bbe73476c06b2085be908f4c android.hardware.gnss@1.0::IGnssCallback
 d702fb01dc2a0733aa820b7eb65435ee3334f75632ef880bafd2fb8803a20a58 android.hardware.gnss@1.0::IGnssMeasurementCallback
@@ -435,9 +435,9 @@
 443659bb9e27221e5da0d16c7a0ecb2dc3a9a03acc8a0b2196b47c50735e2d2e android.hardware.audio.effect@5.0::IVirtualizerEffect
 78fed26a781cdca1b3bcb37520bff705d7764ee81db9cfd37014953c7ad2596e android.hardware.audio.effect@5.0::IVisualizerEffect
 6385b6accab8a544e2ee54ba7bf5aa55dff6153bcedd80fdaae16fe9e0be7050 android.hardware.audio.effect@5.0::types
-2f11e4c10ebe2b600426e0695f3c720d21663501c1c9449537055f13f37600d3 android.hardware.biometrics.face@1.0::IBiometricsFace
-dfb0666af59eb306c82a6f576c65a160e6829d3324211a10429fd63768df70df android.hardware.biometrics.face@1.0::IBiometricsFaceClientCallback
-cc40d308f38b6a218fcf99f264ebb49544fce670a6abdf294c617357a3d83dad android.hardware.biometrics.face@1.0::types
+baf5a0cbf357035394be02d87334890228338fae715f7ab06e2f0e7d05abe656 android.hardware.biometrics.face@1.0::IBiometricsFace
+6cbf288d6e6a9f6f0c09d1cde66318aa5b6a8c525778a61ccaedddb090f5e9cb android.hardware.biometrics.face@1.0::IBiometricsFaceClientCallback
+ef5d339413d0c94a5b58baafbe3e4bccfd9ed2e7328fbbb5c25471c79923ed2f android.hardware.biometrics.face@1.0::types
 ecedc58dbcdb13503c19c0ab160ac1dd0530bb1471164149282dd1463c684185 android.hardware.bluetooth.audio@2.0::IBluetoothAudioPort
 fb9c40e4deab40be5476477078fe3d8a4a4495fd9deef4321878d169d675c633 android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvider
 f7431f3e3e4e3387fc6f27a6cf423eddcd824a395dc4349d302c995ab44a9895 android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory
@@ -479,7 +479,7 @@
 01c6398c90fc6be0640810e2c5d8a4863b457280132bb3f97dd5682e19632b62 android.hardware.graphics.bufferqueue@2.0::types
 7a2d64095252f85781b2d521f4f11d04ce774544feececcec2088c568656e93c android.hardware.graphics.common@1.2::types
 3dff04a36b86660b5807414587e530bb0c294ed56fdff06f8915ba0a9b73f974 android.hardware.graphics.composer@2.3::IComposer
-ed0cf91532e88dff073cf7f45b9c65ee1c88553ebcb97892f7b7072abb9b822a android.hardware.graphics.composer@2.3::IComposerClient
+daa44e83d7709bf1c9e0bd9a6b552feff496fd14574a9461ee93c21980fc5b15 android.hardware.graphics.composer@2.3::IComposerClient
 5c8bf8e1af9efe225a4661db8c08ff1b7e13fdc8ed49f35291bd0b6c9436b8f2 android.hardware.graphics.mapper@3.0::IMapper
 7183d9d9acfa41a61a64bdfed548e98299265a7bb1821a3ed204173b5c2cfd4a android.hardware.graphics.mapper@3.0::types
 c3f831a66d5815baf74f5b82fe79cf099542ddae4dfab3f388e1d41828e794fc android.hardware.health.storage@1.0::IGarbageCollectCallback
@@ -507,7 +507,7 @@
 92714960d1a53fc2ec557302b41c7cc93d2636d8364a44bd0f85be0c92927ff8 android.hardware.neuralnetworks@1.2::IExecutionCallback
 83885d366f22ada42c00d8854f0b7e7ba4cf73ddf80bb0d8e168ce132cec57ea android.hardware.neuralnetworks@1.2::IPreparedModel
 e1c734d1545e1a4ae749ff1dd9704a8e594c59aea7c8363159dc258e93e0df3b android.hardware.neuralnetworks@1.2::IPreparedModelCallback
-c752cff336d86762c26dc82e7e037f4962b815b1a068d2319d40a3d068e26f68 android.hardware.neuralnetworks@1.2::types
+896d1827541d620996720a79c6476edb902a58d515bf908f67a5bdef4d2c318c android.hardware.neuralnetworks@1.2::types
 cf7a4ba516a638f9b82a249c91fb603042c2d9ca43fd5aad9cf6c0401ed2a5d7 android.hardware.nfc@1.2::INfc
 abf98c2ae08bf765db54edc8068e36d52eb558cff6706b6fd7c18c65a1f3fc18 android.hardware.nfc@1.2::types
 4cb252dc6372a874aef666b92a6e9529915aa187521a700f0789065c3c702ead android.hardware.power.stats@1.0::IPowerStats
@@ -516,7 +516,7 @@
 e9d0f11a52715f5a29d89e2d8e2e21db1e16a43174af6b9d51a62d705cda1455 android.hardware.radio@1.3::IRadioIndication
 d233f0da44f55fdef0a95db5229231412787bb67695cd1ea197ce89a3c2908b9 android.hardware.radio@1.3::IRadioResponse
 750a363c8cec70baa1aac19e275c15233c5898e93c6bb5155fa2ca7f365490dc android.hardware.radio@1.3::types
-4ac73ec1e4cfa535209e5e22547f08beb20ef812b4a29d0824780f52cbe2324d android.hardware.radio@1.4::IRadio
+ef4ab741f7e7762fb45e2e24ca83871f72006ce05f57aa9addc574893dd29872 android.hardware.radio@1.4::IRadio
 33d9e6895cca98aa56296bb01720d18b8acd0e4de4960beb712e63ad147438a5 android.hardware.radio@1.4::IRadioIndication
 0cc0dd87c634aad36d7df22b2832839ef7ded71909dbcde11cfdd69dc0dc52b8 android.hardware.radio@1.4::IRadioResponse
 22091ad1f6cb6da1c7c1467e6412c9c1ae577b3ecc0c3e5047fc4b50cdd60c69 android.hardware.radio@1.4::types
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..6b4a4c0
--- /dev/null
+++ b/drm/1.2/vts/functional/Android.bp
@@ -0,0 +1,40 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalDrmV1_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "drm_hal_clearkey_module.cpp",
+        "drm_hal_common.cpp",
+        "drm_hal_test.cpp",
+        "vendor_modules.cpp",
+    ],
+    include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
+    static_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
+        "android.hardware.drm@1.0-helper",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libhidlmemory",
+        "libnativehelper",
+        "libssl",
+        "libcrypto",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/drm/1.2/vts/functional/drm_hal_clearkey_module.cpp b/drm/1.2/vts/functional/drm_hal_clearkey_module.cpp
new file mode 100644
index 0000000..a0dc001
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_clearkey_module.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "drm_hal_clearkey_module@1.2"
+
+#include <gtest/gtest.h>
+#include "drm_hal_clearkey_module.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+std::vector<uint8_t> DrmHalVTSClearkeyModule::handleProvisioningRequest(
+        const std::vector<uint8_t>& /*provisioningRequest*/,
+        const std::string& /*url*/) {
+    EXPECT_TRUE(false) << "Clearkey doesn't support provisioning";
+    return {};
+}
+
+std::vector<DrmHalVTSClearkeyModule::ContentConfiguration>
+        DrmHalVTSClearkeyModule::getContentConfigurations() const {
+    DrmHalVTSClearkeyModule::ContentConfiguration conf = {
+        .name = "DrmHalVTSClearkeyModule", // name
+        .serverUrl = "", // serverUrl
+        .initData = { // initData
+            // BMFF box header (4 bytes size + 'pssh')
+            0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+            // full box header (version = 1 flags = 0)
+            0x01, 0x00, 0x00, 0x00,
+            // system id
+            0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c,
+            0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+            // number of key ids
+            0x00, 0x00, 0x00, 0x01,
+            // key id
+            0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87, 0x7e, 0x57, 0xd0,
+            0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+            // size of data, must be zero
+            0x00, 0x00, 0x00, 0x00
+        },
+        .mimeType = "video/mp4", // mimeType
+        .optionalParameters = {}, // optionalParameters
+        .policy = { .allowOffline = true }, // allowOffline
+        .keys = { // keys
+            {
+                .isSecure = false, // isSecure
+                .keyId = { // keyId
+                    0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
+                    0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e
+                },
+                .clearContentKey = { // clearContentKey
+                    0x1a, 0x8a, 0x20, 0x95, 0xe4, 0xde, 0xb2, 0xd2,
+                    0x9e, 0xc8, 0x16, 0xac, 0x7b, 0xae, 0x20, 0x82
+                }
+            }
+        }
+    };
+    return { conf };
+}
+
+std::vector<uint8_t> DrmHalVTSClearkeyModule::handleKeyRequest(
+        const std::vector<uint8_t>& keyRequest,
+        const std::string& /*serverUrl*/) {
+
+    // {"kids":["YAYeAX5Hfod-V9ANHtANHg"],"type":"temporary"}
+    std::vector<uint8_t> expectedKeyRequest = {
+        0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59, 0x41, 0x59, 0x65,
+        0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e, 0x48, 0x74,
+        0x41, 0x4e, 0x48, 0x67, 0x22, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a,
+        0x22, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x22, 0x7d};
+
+    // {"kids":["YAYeAX5Hfod-V9ANHtANHg"],"type":"persistent-license"}
+    std::vector<uint8_t> expectedKeyRequestPersistent = {
+        0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59, 0x41, 0x59, 0x65,
+        0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e, 0x48, 0x74,
+        0x41, 0x4e, 0x48, 0x67, 0x22, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a,
+        0x22, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x69,
+        0x63, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x7d};
+
+    // {"keys":[{"kty":"oct","kid":"YAYeAX5Hfod-V9ANHtANHg","k":"GoogleTestKeyBase64ggg"}]}
+    std::vector<uint8_t> knownKeyResponse = {
+        0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6b, 0x74, 0x79, 0x22,
+        0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59,
+        0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e,
+        0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b, 0x22, 0x3a, 0x22, 0x47, 0x6f,
+        0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65,
+        0x36, 0x34, 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x7d};
+
+    // {"keys":[{"kty":"oct","kid":"YAYeAX5Hfod-V9ANHtANHg","k":"GoogleTestKeyBase64ggg"}],"type":"persistent-license"}
+    std::vector<uint8_t> knownKeyResponsePersistent = {
+        0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6b, 0x74, 0x79, 0x22,
+        0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59,
+        0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e,
+        0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b, 0x22, 0x3a, 0x22, 0x47, 0x6f,
+        0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65,
+        0x36, 0x34, 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22,
+        0x3a, 0x22, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x69,
+        0x63, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x7d};
+
+    std::string req(keyRequest.begin(), keyRequest.end());
+    if (req.find("persistent-license") != std::string::npos) {
+        EXPECT_EQ(expectedKeyRequestPersistent, keyRequest);
+        return knownKeyResponsePersistent;
+    } else {
+        EXPECT_EQ(expectedKeyRequest, keyRequest);
+        return knownKeyResponse;
+    }
+
+}
+
+}  // namespace vts
+}  // namespace V1_2
+}  // namespace drm
+}  // namespace hardware
+}  // namespace android
diff --git a/drm/1.2/vts/functional/drm_hal_clearkey_module.h b/drm/1.2/vts/functional/drm_hal_clearkey_module.h
new file mode 100644
index 0000000..7250cf2
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_clearkey_module.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef DRM_HAL_CLEARKEY_MODULE_H
+#define DRM_HAL_CLEARKEY_MODULE_H
+
+#include "drm_hal_vendor_module_api.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+class DrmHalVTSClearkeyModule : public DrmHalVTSVendorModule_V1 {
+   public:
+    DrmHalVTSClearkeyModule() {}
+    virtual ~DrmHalVTSClearkeyModule() {}
+
+    virtual uint32_t getAPIVersion() const override { return 1; }
+
+    virtual std::string getServiceName() const override { return "clearkey"; }
+
+    virtual std::vector<uint8_t> getUUID() const override {
+        return {0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
+                0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E };
+    }
+
+
+    virtual std::vector<uint8_t> handleProvisioningRequest(
+            const std::vector<uint8_t>& provisioningRequest,
+            const std::string& url) override;
+
+    virtual std::vector<DrmHalVTSClearkeyModule::ContentConfiguration>
+            getContentConfigurations() const override;
+
+    virtual std::vector<uint8_t> handleKeyRequest(
+            const std::vector<uint8_t>& keyRequest,
+            const std::string& serverUrl) override;
+};
+
+}  // namespace vts
+}  // namespace V1_2
+}  // namespace drm
+}  // namespace hardware
+}  // namespace android
+
+#endif  // DRM_HAL_CLEARKEY_MODULE_H
diff --git a/drm/1.2/vts/functional/drm_hal_common.cpp b/drm/1.2/vts/functional/drm_hal_common.cpp
new file mode 100644
index 0000000..b9a8425
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_common.cpp
@@ -0,0 +1,483 @@
+/*
+ * 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 "drm_hal_common@1.2"
+
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+#include <openssl/aes.h>
+#include <random>
+
+#include "drm_hal_clearkey_module.h"
+#include "drm_hal_common.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ICryptoPluginV1_0 = ::android::hardware::drm::V1_0::ICryptoPlugin;
+using IDrmPluginV1_0 = ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using StatusV1_0 = ::android::hardware::drm::V1_0::Status;
+
+using ::android::hardware::drm::V1_1::KeyRequestType;
+
+using ::android::hardware::drm::V1_2::KeySetId;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
+using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_memory;
+
+using ::android::hidl::allocator::V1_0::IAllocator;
+
+using std::random_device;
+using std::mt19937;
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+const char *kCallbackLostState = "LostState";
+
+drm_vts::VendorModules *DrmHalTest::gVendorModules = nullptr;
+
+/**
+ * DrmHalPluginListener
+ */
+
+Return<void> DrmHalPluginListener::sendSessionLostState(const hidl_vec<uint8_t>& sessionId) {
+    NotifyFromCallback(kCallbackLostState, sessionId);
+    return Void();
+}
+
+/**
+ * DrmHalTest
+ */
+
+DrmHalTest::DrmHalTest()
+    : vendorModule(GetParam() == "clearkey"
+            ? new DrmHalVTSClearkeyModule()
+            : static_cast<DrmHalVTSVendorModule_V1*>(gVendorModules->getModule(GetParam()))),
+      contentConfigurations(vendorModule->getContentConfigurations()) {
+}
+
+void DrmHalTest::SetUp() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+
+    ALOGD("Running test %s.%s from (vendor) module %s",
+          test_info->test_case_name(), test_info->name(),
+          GetParam().c_str());
+
+    string name = vendorModule->getServiceName();
+    drmFactory = VtsHalHidlTargetTestBase::getService<IDrmFactory>(name);
+    if (drmFactory == nullptr) {
+        drmFactory = VtsHalHidlTargetTestBase::getService<IDrmFactory>();
+    }
+    if (drmFactory != nullptr) {
+        drmPlugin = createDrmPlugin();
+    }
+
+    cryptoFactory = VtsHalHidlTargetTestBase::getService<ICryptoFactory>(name);
+    if (cryptoFactory == nullptr) {
+        cryptoFactory = VtsHalHidlTargetTestBase::getService<ICryptoFactory>();
+    }
+    if (cryptoFactory != nullptr) {
+        cryptoPlugin = createCryptoPlugin();
+    }
+
+    // If drm scheme not installed skip subsequent tests
+    if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
+        vendorModule->setInstalled(false);
+        return;
+    }
+
+    ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() <<  " drm@1.2 plugin";
+    ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't find " << vendorModule->getServiceName() <<  " crypto@1.2 plugin";
+
+}
+
+sp<IDrmPlugin> DrmHalTest::createDrmPlugin() {
+    if (drmFactory == nullptr) {
+        return nullptr;
+    }
+    sp<IDrmPlugin> plugin = nullptr;
+    hidl_string packageName("android.hardware.drm.test");
+    auto res = drmFactory->createPlugin(
+            getVendorUUID(), packageName,
+                    [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
+                EXPECT_EQ(StatusV1_0::OK, status);
+                plugin = IDrmPlugin::castFrom(pluginV1_0);
+            });
+
+    if (!res.isOk()) {
+        ALOGE("createDrmPlugin remote call failed");
+    }
+    return plugin;
+}
+
+sp<ICryptoPlugin> DrmHalTest::createCryptoPlugin() {
+    if (cryptoFactory == nullptr) {
+        return nullptr;
+    }
+    sp<ICryptoPlugin> plugin = nullptr;
+    hidl_vec<uint8_t> initVec;
+    auto res = cryptoFactory->createPlugin(
+            getVendorUUID(), initVec,
+                    [&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
+                EXPECT_EQ(StatusV1_0::OK, status);
+                plugin = ICryptoPlugin::castFrom(pluginV1_0);
+            });
+    if (!res.isOk()) {
+        ALOGE("createCryptoPlugin remote call failed");
+    }
+    return plugin;
+}
+
+hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() {
+    vector<uint8_t> uuid = vendorModule->getUUID();
+    return hidl_array<uint8_t, 16>(&uuid[0]);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalTest::openSession() {
+    SessionId sessionId;
+
+    auto res = drmPlugin->openSession([&](StatusV1_0 status, const hidl_vec<unsigned char> &id) {
+        EXPECT_EQ(StatusV1_0::OK, status);
+        EXPECT_NE(id.size(), 0u);
+        sessionId = id;
+    });
+    EXPECT_OK(res);
+    return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalTest::closeSession(const SessionId& sessionId) {
+    StatusV1_0 status = drmPlugin->closeSession(sessionId);
+    EXPECT_EQ(StatusV1_0::OK, status);
+}
+
+hidl_vec<uint8_t> DrmHalTest::getKeyRequest(
+    const SessionId& sessionId,
+    const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
+    const KeyType& type = KeyType::STREAMING) {
+    hidl_vec<uint8_t> keyRequest;
+    auto res = drmPlugin->getKeyRequest_1_2(
+        sessionId, configuration.initData, configuration.mimeType, type,
+        toHidlKeyedVector(configuration.optionalParameters),
+        [&](Status status, const hidl_vec<uint8_t>& request,
+            KeyRequestType requestType, const hidl_string&) {
+            EXPECT_EQ(Status::OK, status) << "Failed to get "
+                                             "key request for configuration "
+                                          << configuration.name;
+            if (type == KeyType::RELEASE) {
+                EXPECT_EQ(KeyRequestType::RELEASE, requestType);
+            } else {
+                EXPECT_EQ(KeyRequestType::INITIAL, requestType);
+            }
+            EXPECT_NE(request.size(), 0u) << "Expected key request size"
+                                             " to have length > 0 bytes";
+            keyRequest = request;
+        });
+    EXPECT_OK(res);
+    return keyRequest;
+}
+
+DrmHalVTSVendorModule_V1::ContentConfiguration DrmHalTest::getContent(const KeyType& type) const {
+    for (const auto& config : contentConfigurations) {
+        if (type != KeyType::OFFLINE || config.policy.allowOffline) {
+            return config;
+        }
+    }
+    EXPECT_TRUE(false) << "no content configurations found";
+    return {};
+}
+
+hidl_vec<uint8_t> DrmHalTest::provideKeyResponse(
+    const SessionId& sessionId,
+    const hidl_vec<uint8_t>& keyResponse) {
+    hidl_vec<uint8_t> keySetId;
+    auto res = drmPlugin->provideKeyResponse(
+        sessionId, keyResponse,
+        [&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
+            EXPECT_EQ(StatusV1_0::OK, status) << "Failure providing "
+                                                 "key response for configuration ";
+            keySetId = myKeySetId;
+        });
+    EXPECT_OK(res);
+    return keySetId;
+}
+
+/**
+ * Helper method to load keys for subsequent decrypt tests.
+ * These tests use predetermined key request/response to
+ * avoid requiring a round trip to a license server.
+ */
+hidl_vec<uint8_t> DrmHalTest::loadKeys(
+    const SessionId& sessionId,
+    const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
+    const KeyType& type) {
+    hidl_vec<uint8_t> keyRequest = getKeyRequest(sessionId, configuration, type);
+
+    /**
+     * Get key response from vendor module
+     */
+    hidl_vec<uint8_t> keyResponse =
+        vendorModule->handleKeyRequest(keyRequest, configuration.serverUrl);
+    EXPECT_NE(keyResponse.size(), 0u) << "Expected key response size "
+                                         "to have length > 0 bytes";
+
+    return provideKeyResponse(sessionId, keyResponse);
+}
+
+hidl_vec<uint8_t> DrmHalTest::loadKeys(
+        const SessionId& sessionId,
+        const KeyType& type) {
+    return loadKeys(sessionId, getContent(type), type);
+}
+
+KeyedVector DrmHalTest::toHidlKeyedVector(
+    const map<string, string>& params) {
+    std::vector<KeyValue> stdKeyedVector;
+    for (auto it = params.begin(); it != params.end(); ++it) {
+        KeyValue keyValue;
+        keyValue.key = it->first;
+        keyValue.value = it->second;
+        stdKeyedVector.push_back(keyValue);
+    }
+    return KeyedVector(stdKeyedVector);
+}
+
+hidl_array<uint8_t, 16> DrmHalTest::toHidlArray(const vector<uint8_t>& vec) {
+    EXPECT_EQ(16u, vec.size());
+    return hidl_array<uint8_t, 16>(&vec[0]);
+}
+
+/**
+ * getDecryptMemory allocates memory for decryption, then sets it
+ * as a shared buffer base in the crypto hal.  The allocated and
+ * mapped IMemory is returned.
+ *
+ * @param size the size of the memory segment to allocate
+ * @param the index of the memory segment which will be used
+ * to refer to it for decryption.
+ */
+sp<IMemory> DrmHalTest::getDecryptMemory(size_t size, size_t index) {
+    sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+    EXPECT_NE(nullptr, ashmemAllocator.get());
+
+    hidl_memory hidlMemory;
+    auto res = ashmemAllocator->allocate(
+            size, [&](bool success, const hidl_memory& memory) {
+                EXPECT_EQ(success, true);
+                EXPECT_EQ(memory.size(), size);
+                hidlMemory = memory;
+            });
+
+    EXPECT_OK(res);
+
+    sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+    EXPECT_NE(nullptr, mappedMemory.get());
+    res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
+    EXPECT_OK(res);
+    return mappedMemory;
+}
+
+void DrmHalTest::fillRandom(const sp<IMemory>& memory) {
+    random_device rd;
+    mt19937 rand(rd());
+    for (size_t i = 0; i < memory->getSize() / sizeof(uint32_t); i++) {
+        auto p = static_cast<uint32_t*>(
+                static_cast<void*>(memory->getPointer()));
+        p[i] = rand();
+    }
+}
+
+uint32_t DrmHalTest::decrypt(Mode mode, bool isSecure,
+        const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
+        const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
+        const vector<uint8_t>& key, StatusV1_2 expectedStatus) {
+    const size_t kSegmentIndex = 0;
+
+    uint8_t localIv[AES_BLOCK_SIZE];
+    memcpy(localIv, iv, AES_BLOCK_SIZE);
+
+    size_t totalSize = 0;
+    for (size_t i = 0; i < subSamples.size(); i++) {
+        totalSize += subSamples[i].numBytesOfClearData;
+        totalSize += subSamples[i].numBytesOfEncryptedData;
+    }
+
+    // The first totalSize bytes of shared memory is the encrypted
+    // input, the second totalSize bytes (if exists) is the decrypted output.
+    size_t factor = expectedStatus == StatusV1_2::ERROR_DRM_FRAME_TOO_LARGE ? 1 : 2;
+    sp<IMemory> sharedMemory =
+            getDecryptMemory(totalSize * factor, kSegmentIndex);
+
+    const SharedBuffer sourceBuffer = {
+        .bufferId = kSegmentIndex, .offset = 0, .size = totalSize};
+    fillRandom(sharedMemory);
+
+    const DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+                                          {.bufferId = kSegmentIndex,
+                                           .offset = totalSize,
+                                           .size = totalSize},
+                                          .secureMemory = nullptr};
+    const uint64_t offset = 0;
+    uint32_t bytesWritten = 0;
+    auto res = cryptoPlugin->decrypt_1_2(isSecure, keyId, localIv, mode, pattern,
+            subSamples, sourceBuffer, offset, destBuffer,
+            [&](StatusV1_2 status, uint32_t count, string detailedError) {
+                EXPECT_EQ(expectedStatus, status) << "Unexpected decrypt status " <<
+                detailedError;
+                bytesWritten = count;
+            });
+    EXPECT_OK(res);
+
+    if (bytesWritten != totalSize) {
+        return bytesWritten;
+    }
+    uint8_t* base = static_cast<uint8_t*>(
+            static_cast<void*>(sharedMemory->getPointer()));
+
+    // generate reference vector
+    vector<uint8_t> reference(totalSize);
+
+    memcpy(localIv, iv, AES_BLOCK_SIZE);
+    switch (mode) {
+    case Mode::UNENCRYPTED:
+        memcpy(&reference[0], base, totalSize);
+        break;
+    case Mode::AES_CTR:
+        aes_ctr_decrypt(&reference[0], base, localIv, subSamples, key);
+        break;
+    case Mode::AES_CBC:
+        aes_cbc_decrypt(&reference[0], base, localIv, subSamples, key);
+        break;
+    case Mode::AES_CBC_CTS:
+        EXPECT_TRUE(false) << "AES_CBC_CTS mode not supported";
+        break;
+    }
+
+    // compare reference to decrypted data which is at base + total size
+    EXPECT_EQ(0, memcmp(static_cast<void *>(&reference[0]),
+                        static_cast<void*>(base + totalSize), totalSize))
+            << "decrypt data mismatch";
+    return totalSize;
+}
+
+/**
+ * Decrypt a list of clear+encrypted subsamples using the specified key
+ * in AES-CTR mode
+ */
+void DrmHalTest::aes_ctr_decrypt(uint8_t* dest, uint8_t* src,
+        uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+        const vector<uint8_t>& key) {
+    AES_KEY decryptionKey;
+    AES_set_encrypt_key(&key[0], 128, &decryptionKey);
+
+    size_t offset = 0;
+    unsigned int blockOffset = 0;
+    uint8_t previousEncryptedCounter[AES_BLOCK_SIZE];
+    memset(previousEncryptedCounter, 0, AES_BLOCK_SIZE);
+
+    for (size_t i = 0; i < subSamples.size(); i++) {
+        const SubSample& subSample = subSamples[i];
+
+        if (subSample.numBytesOfClearData > 0) {
+            memcpy(dest + offset, src + offset, subSample.numBytesOfClearData);
+            offset += subSample.numBytesOfClearData;
+        }
+
+        if (subSample.numBytesOfEncryptedData > 0) {
+            AES_ctr128_encrypt(src + offset, dest + offset,
+                    subSample.numBytesOfEncryptedData, &decryptionKey,
+                    iv, previousEncryptedCounter, &blockOffset);
+            offset += subSample.numBytesOfEncryptedData;
+        }
+    }
+}
+
+/**
+ * Decrypt a list of clear+encrypted subsamples using the specified key
+ * in AES-CBC mode
+ */
+void DrmHalTest::aes_cbc_decrypt(uint8_t* dest, uint8_t* src,
+        uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+        const vector<uint8_t>& key) {
+    AES_KEY decryptionKey;
+    AES_set_encrypt_key(&key[0], 128, &decryptionKey);
+
+    size_t offset = 0;
+    for (size_t i = 0; i < subSamples.size(); i++) {
+        memcpy(dest + offset, src + offset, subSamples[i].numBytesOfClearData);
+        offset += subSamples[i].numBytesOfClearData;
+
+        AES_cbc_encrypt(src + offset, dest + offset, subSamples[i].numBytesOfEncryptedData,
+                &decryptionKey, iv, 0 /* decrypt */);
+        offset += subSamples[i].numBytesOfEncryptedData;
+    }
+}
+
+/**
+ * Helper method to test decryption with invalid keys is returned
+ */
+void DrmHalClearkeyTest::decryptWithInvalidKeys(
+        hidl_vec<uint8_t>& invalidResponse,
+        vector<uint8_t>& iv,
+        const Pattern& noPattern,
+        const vector<SubSample>& subSamples) {
+    DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent();
+    if (content.keys.empty()) {
+        FAIL() << "no keys";
+    }
+
+    const auto& key = content.keys[0];
+    auto sessionId = openSession();
+    auto res = drmPlugin->provideKeyResponse(
+        sessionId, invalidResponse,
+        [&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
+            EXPECT_EQ(StatusV1_0::OK, status);
+            EXPECT_EQ(0u, myKeySetId.size());
+        });
+    EXPECT_OK(res);
+
+    EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk());
+
+    uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
+            toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
+            key.clearContentKey, Status::ERROR_DRM_NO_LICENSE);
+    EXPECT_EQ(0u, byteCount);
+
+    closeSession(sessionId);
+}
+
+}  // namespace vts
+}  // namespace V1_2
+}  // namespace drm
+}  // namespace hardware
+}  // namespace android
diff --git a/drm/1.2/vts/functional/drm_hal_common.h b/drm/1.2/vts/functional/drm_hal_common.h
new file mode 100644
index 0000000..1b95dde
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_common.h
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+#ifndef DRM_HAL_COMMON_H
+#define DRM_HAL_COMMON_H
+
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/ICryptoPlugin.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmPluginListener.h>
+#include <android/hardware/drm/1.2/types.h>
+
+#include <chrono>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "drm_hal_vendor_module_api.h"
+#include "vendor_modules.h"
+#include "VtsHalHidlTargetCallbackBase.h"
+#include "VtsHalHidlTargetTestBase.h"
+
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyStatus;
+using ::android::hardware::drm::V1_0::KeyType;
+using ::android::hardware::drm::V1_0::Mode;
+using ::android::hardware::drm::V1_0::Pattern;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::drm::V1_1::ICryptoFactory;
+
+using ::android::hardware::drm::V1_2::ICryptoPlugin;
+using ::android::hardware::drm::V1_2::IDrmFactory;
+using ::android::hardware::drm::V1_2::IDrmPlugin;
+using ::android::hardware::drm::V1_2::IDrmPluginListener;
+using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using ::testing::VtsHalHidlTargetTestBase;
+
+using std::map;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+#define RETURN_IF_SKIPPED \
+    if (!vendorModule->isInstalled()) { \
+        std::cout << "[  SKIPPED ] This drm scheme not supported." << \
+                " library:" << GetParam() << " service-name:" << \
+                vendorModule->getServiceName() << std::endl; \
+        return; \
+    }
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static DrmHidlEnvironment* Instance() {
+        static DrmHidlEnvironment* instance = new DrmHidlEnvironment;
+        return instance;
+    }
+
+    void registerTestServices() override {
+        registerTestService<ICryptoFactory>();
+        registerTestService<IDrmFactory>();
+        setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION);
+    }
+
+   private:
+    DrmHidlEnvironment() {}
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment);
+};
+
+class DrmHalTest : public ::testing::TestWithParam<std::string> {
+   public:
+    static drm_vts::VendorModules* gVendorModules;
+    DrmHalTest();
+    virtual void SetUp() override;
+    virtual void TearDown() override {}
+
+   protected:
+    hidl_array<uint8_t, 16> getVendorUUID();
+    SessionId openSession();
+    void closeSession(const SessionId& sessionId);
+    hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
+                               const KeyType& type = KeyType::STREAMING);
+    hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
+                               const DrmHalVTSVendorModule_V1::ContentConfiguration&,
+                               const KeyType& type = KeyType::STREAMING);
+    hidl_vec<uint8_t> getKeyRequest(const SessionId& sessionId,
+                               const DrmHalVTSVendorModule_V1::ContentConfiguration&,
+                               const KeyType& type);
+    hidl_vec<uint8_t> provideKeyResponse(const SessionId& sessionId,
+                               const hidl_vec<uint8_t>& keyResponse);
+    DrmHalVTSVendorModule_V1::ContentConfiguration getContent(
+            const KeyType& type = KeyType::STREAMING) const;
+
+    KeyedVector toHidlKeyedVector(const map<string, string>& params);
+    hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec);
+
+    void fillRandom(const sp<IMemory>& memory);
+    sp<IMemory> getDecryptMemory(size_t size, size_t index);
+    uint32_t decrypt(Mode mode, bool isSecure,
+            const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
+            const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
+            const vector<uint8_t>& key, StatusV1_2 expectedStatus);
+    void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+            const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+    void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+            const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+
+    sp<IDrmFactory> drmFactory;
+    sp<ICryptoFactory> cryptoFactory;
+    sp<IDrmPlugin> drmPlugin;
+    sp<ICryptoPlugin> cryptoPlugin;
+    unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+    const vector<DrmHalVTSVendorModule_V1::ContentConfiguration> contentConfigurations;
+
+   private:
+    sp<IDrmPlugin> createDrmPlugin();
+    sp<ICryptoPlugin> createCryptoPlugin();
+
+};
+
+class DrmHalClearkeyTest : public DrmHalTest {
+   public:
+    virtual void SetUp() override { DrmHalTest::SetUp(); }
+    virtual void TearDown() override {}
+    void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse,
+            vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples);
+};
+
+/**
+ *  Event Handling tests
+ */
+extern const char *kCallbackLostState;
+
+class DrmHalPluginListener
+    : public ::testing::VtsHalHidlTargetCallbackBase<SessionId>,
+      public IDrmPluginListener {
+public:
+    DrmHalPluginListener() {
+        SetWaitTimeoutDefault(std::chrono::milliseconds(500));
+    }
+    virtual ~DrmHalPluginListener() {}
+
+    virtual Return<void> sendEvent(EventType, const hidl_vec<uint8_t>&,
+            const hidl_vec<uint8_t>& ) override { return Void(); }
+
+    virtual Return<void> sendExpirationUpdate(const hidl_vec<uint8_t>&,
+            int64_t) override { return Void(); }
+
+    virtual Return<void> sendKeysChange(const hidl_vec<uint8_t>&,
+            const hidl_vec<KeyStatus>&, bool) override { return Void(); }
+
+    virtual Return<void> sendSessionLostState(const hidl_vec<uint8_t>& sessionId) override;
+
+};
+
+}  // namespace vts
+}  // namespace V1_2
+}  // namespace drm
+}  // namespace hardware
+}  // namespace android
+
+#endif  // DRM_HAL_COMMON_H
diff --git a/drm/1.2/vts/functional/drm_hal_test.cpp b/drm/1.2/vts/functional/drm_hal_test.cpp
new file mode 100644
index 0000000..067b5e4
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_test.cpp
@@ -0,0 +1,542 @@
+/*
+ * 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 "drm_hal_clearkey_test@1.2"
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+#include <openssl/aes.h>
+
+#include "drm_hal_common.h"
+
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_1::KeyRequestType;
+using ::android::hardware::drm::V1_1::SecurityLevel;
+using ::android::hardware::drm::V1_2::HdcpLevel;
+using ::android::hardware::drm::V1_2::KeySetId;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
+
+using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTest;
+using ::android::hardware::drm::V1_2::vts::DrmHalPluginListener;
+using ::android::hardware::drm::V1_2::vts::DrmHalTest;
+using ::android::hardware::drm::V1_2::vts::DrmHidlEnvironment;
+using ::android::hardware::drm::V1_2::vts::kCallbackLostState;
+
+using ::android::hardware::hidl_string;
+
+static const char* const kVideoMp4 = "video/mp4";
+static const char* const kBadMime = "video/unknown";
+static const char* const kDrmErrorTestKey = "drmErrorTest";
+static const char* const kDrmErrorGeneric = "";
+static const char* const kResourceContentionValue = "resourceContention";
+static const SecurityLevel kSwSecureCrypto = SecurityLevel::SW_SECURE_CRYPTO;
+
+/**
+ * Ensure drm factory supports module UUID Scheme
+ */
+TEST_P(DrmHalTest, VendorUuidSupported) {
+    auto res = drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kSwSecureCrypto);
+    ALOGI("kVideoMp4 = %s res %d", kVideoMp4, (bool)res);
+    EXPECT_TRUE(res);
+}
+
+/**
+ * Ensure drm factory doesn't support an invalid scheme UUID
+ */
+TEST_P(DrmHalTest, InvalidPluginNotSupported) {
+    const uint8_t kInvalidUUID[16] = {
+        0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+        0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(kInvalidUUID, kVideoMp4, kSwSecureCrypto));
+}
+
+/**
+ * Ensure drm factory doesn't support an empty UUID
+ */
+TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) {
+    hidl_array<uint8_t, 16> emptyUUID;
+    memset(emptyUUID.data(), 0, 16);
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(emptyUUID, kVideoMp4, kSwSecureCrypto));
+}
+
+/**
+ * Ensure drm factory doesn't support an invalid mime type
+ */
+TEST_P(DrmHalTest, BadMimeNotSupported) {
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto));
+}
+
+/**
+ *  DrmPlugin tests
+ */
+
+/**
+ * Test that a DRM plugin can handle provisioning.  While
+ * it is not required that a DRM scheme require provisioning,
+ * it should at least return appropriate status values. If
+ * a provisioning request is returned, it is passed to the
+ * vendor module which should provide a provisioning response
+ * that is delivered back to the HAL.
+ */
+TEST_P(DrmHalTest, DoProvisioning) {
+    RETURN_IF_SKIPPED;
+    hidl_string certificateType;
+    hidl_string certificateAuthority;
+    hidl_vec<uint8_t> provisionRequest;
+    hidl_string defaultUrl;
+    auto res = drmPlugin->getProvisionRequest_1_2(
+            certificateType, certificateAuthority,
+            [&](StatusV1_2 status, const hidl_vec<uint8_t>& request,
+                const hidl_string& url) {
+                if (status == StatusV1_2::OK) {
+                    EXPECT_NE(request.size(), 0u);
+                    provisionRequest = request;
+                    defaultUrl = url;
+                } else if (status == StatusV1_2::ERROR_DRM_CANNOT_HANDLE) {
+                    EXPECT_EQ(0u, request.size());
+                }
+            });
+    EXPECT_OK(res);
+
+    if (provisionRequest.size() > 0) {
+        vector<uint8_t> response = vendorModule->handleProvisioningRequest(
+                provisionRequest, defaultUrl);
+        ASSERT_NE(0u, response.size());
+
+        auto res = drmPlugin->provideProvisionResponse(
+                response, [&](Status status, const hidl_vec<uint8_t>&,
+                              const hidl_vec<uint8_t>&) {
+                    EXPECT_EQ(Status::OK, status);
+                });
+        EXPECT_OK(res);
+    }
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_P(DrmHalTest, GetKeyRequestNoSession) {
+    SessionId invalidSessionId;
+    hidl_vec<uint8_t> initData;
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest_1_2(
+            invalidSessionId, initData, kVideoMp4, KeyType::STREAMING,
+            optionalParameters,
+            [&](StatusV1_2 status, const hidl_vec<uint8_t>&, KeyRequestType,
+                const hidl_string&) { EXPECT_EQ(StatusV1_2::BAD_VALUE, status); });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that the plugin returns the documented error for the
+ * case of attempting to generate a key request using an
+ * invalid mime type
+ */
+TEST_P(DrmHalTest, GetKeyRequestBadMime) {
+    auto sessionId = openSession();
+    hidl_vec<uint8_t> initData;
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest_1_2(
+            sessionId, initData, kBadMime, KeyType::STREAMING,
+            optionalParameters, [&](StatusV1_2 status, const hidl_vec<uint8_t>&,
+                                    KeyRequestType, const hidl_string&) {
+                EXPECT_NE(StatusV1_2::OK, status);
+            });
+    EXPECT_OK(res);
+    closeSession(sessionId);
+}
+
+template <Status S, size_t N>
+void checkKeySetIds(Status status, const hidl_vec<KeySetId>& keySetIds) {
+    EXPECT_EQ(S, status);
+    if (S == Status::OK) {
+        EXPECT_EQ(N, keySetIds.size());
+    }
+}
+
+template <Status S, OfflineLicenseState T>
+void checkKeySetIdState(Status status, OfflineLicenseState state) {
+    if (S == Status::OK) {
+        EXPECT_TRUE(T == OfflineLicenseState::USABLE || T == OfflineLicenseState::INACTIVE);
+    } else {
+        EXPECT_TRUE(T == OfflineLicenseState::UNKNOWN);
+    }
+    EXPECT_EQ(S, status);
+    EXPECT_EQ(T, state);
+}
+
+/**
+ * Test clearkey plugin offline key support
+ */
+TEST_P(DrmHalTest, OfflineLicenseTest) {
+    auto sessionId = openSession();
+    hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
+
+    auto res = drmPlugin->getOfflineLicenseKeySetIds(checkKeySetIds<Status::OK, 1u>);
+    EXPECT_OK(res);
+
+    Status err = drmPlugin->removeOfflineLicense(keySetId);
+    EXPECT_EQ(Status::OK, err);
+
+    res = drmPlugin->getOfflineLicenseKeySetIds(checkKeySetIds<Status::OK, 0u>);
+    EXPECT_OK(res);
+
+    err = drmPlugin->removeOfflineLicense(keySetId);
+    EXPECT_EQ(Status::BAD_VALUE, err);
+
+    closeSession(sessionId);
+}
+
+/**
+ * Test clearkey plugin offline key state
+ */
+TEST_P(DrmHalTest, OfflineLicenseStateTest) {
+    auto sessionId = openSession();
+    DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE);
+    hidl_vec<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE);
+    drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<Status::OK, OfflineLicenseState::USABLE>);
+
+    hidl_vec<uint8_t> keyRequest = getKeyRequest(keySetId, content, KeyType::RELEASE);
+    drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<Status::OK, OfflineLicenseState::INACTIVE>);
+
+    /**
+     * Get key response from vendor module
+     */
+    hidl_vec<uint8_t> keyResponse =
+        vendorModule->handleKeyRequest(keyRequest, content.serverUrl);
+    EXPECT_GT(keyResponse.size(), 0u);
+
+    provideKeyResponse(keySetId, keyResponse);
+    drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>);
+    closeSession(sessionId);
+}
+
+/**
+ * Negative offline license test. Remove empty keySetId
+ */
+TEST_P(DrmHalTest, RemoveEmptyKeySetId) {
+    KeySetId emptyKeySetId;
+    Status err = drmPlugin->removeOfflineLicense(emptyKeySetId);
+    EXPECT_EQ(Status::BAD_VALUE, err);
+}
+
+/**
+ * Negative offline license test. Get empty keySetId state
+ */
+TEST_P(DrmHalTest, GetEmptyKeySetIdState) {
+    KeySetId emptyKeySetId;
+    auto res = drmPlugin->getOfflineLicenseState(emptyKeySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>);
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that the plugin returns valid connected and max HDCP levels
+ */
+TEST_P(DrmHalTest, GetHdcpLevels) {
+    auto res = drmPlugin->getHdcpLevels_1_2(
+            [&](StatusV1_2 status, const HdcpLevel &connectedLevel,
+                const HdcpLevel &maxLevel) {
+                EXPECT_EQ(StatusV1_2::OK, status);
+                EXPECT_GE(connectedLevel, HdcpLevel::HDCP_NONE);
+                EXPECT_LE(maxLevel, HdcpLevel::HDCP_V2_3);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ *  CryptoPlugin Decrypt tests
+ */
+
+/**
+ * Positive decrypt test.  "Decrypt" a single clear segment
+ */
+TEST_P(DrmHalTest, ClearSegmentTest) {
+    RETURN_IF_SKIPPED;
+    for (const auto& config : contentConfigurations) {
+        for (const auto& key : config.keys) {
+            const size_t kSegmentSize = 1024;
+            vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+            const Pattern noPattern = {0, 0};
+            const vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+                                                   .numBytesOfEncryptedData = 0}};
+            auto sessionId = openSession();
+            loadKeys(sessionId, config);
+
+            Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+            EXPECT_EQ(Status::OK, status);
+
+            uint32_t byteCount = decrypt(Mode::UNENCRYPTED, key.isSecure, toHidlArray(key.keyId),
+                    &iv[0], subSamples, noPattern, key.clearContentKey, StatusV1_2::OK);
+            EXPECT_EQ(kSegmentSize, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Positive decrypt test.  Decrypt a single segment using aes_ctr.
+ * Verify data matches.
+ */
+TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) {
+    RETURN_IF_SKIPPED;
+    for (const auto& config : contentConfigurations) {
+        for (const auto& key : config.keys) {
+            const size_t kSegmentSize = 1024;
+            vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+            const Pattern noPattern = {0, 0};
+            const vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+                                                   .numBytesOfEncryptedData = 0}};
+            auto sessionId = openSession();
+            loadKeys(sessionId, config);
+
+            Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+            EXPECT_EQ(Status::OK, status);
+
+            uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure, toHidlArray(key.keyId),
+                    &iv[0], subSamples, noPattern, key.clearContentKey, StatusV1_2::OK);
+            EXPECT_EQ(kSegmentSize, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Negative decrypt test.  Decrypted frame too large to fit in output buffer
+ */
+TEST_P(DrmHalTest, ErrorFrameTooLarge) {
+    RETURN_IF_SKIPPED;
+    for (const auto& config : contentConfigurations) {
+        for (const auto& key : config.keys) {
+            const size_t kSegmentSize = 1024;
+            vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+            const Pattern noPattern = {0, 0};
+            const vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+                                                   .numBytesOfEncryptedData = 0}};
+            auto sessionId = openSession();
+            loadKeys(sessionId, config);
+
+            Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+            EXPECT_EQ(Status::OK, status);
+
+            decrypt(Mode::UNENCRYPTED, key.isSecure, toHidlArray(key.keyId),
+                    &iv[0], subSamples, noPattern, key.clearContentKey, StatusV1_2::ERROR_DRM_FRAME_TOO_LARGE);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Negative decrypt test. Decrypt without loading keys.
+ */
+TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) {
+    RETURN_IF_SKIPPED;
+    for (const auto& config : contentConfigurations) {
+        for (const auto& key : config.keys) {
+            vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+            const Pattern noPattern = {0, 0};
+            const vector<SubSample> subSamples = {{.numBytesOfClearData = 256,
+                                                   .numBytesOfEncryptedData = 256}};
+            auto sessionId = openSession();
+
+            Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+            EXPECT_EQ(Status::OK, status);
+
+            uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
+                    toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
+                    key.clearContentKey, StatusV1_2::ERROR_DRM_NO_LICENSE);
+            EXPECT_EQ(0u, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Ensure clearkey drm factory doesn't support security level higher than supported
+ */
+TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
+    const SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL;
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kHwSecureAll));
+}
+
+/**
+ * Test resource contention during attempt to generate key request
+ */
+TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
+    Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kResourceContentionValue);
+    EXPECT_EQ(Status::OK, status);
+    auto sessionId = openSession();
+    hidl_vec<uint8_t> initData;
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest_1_2(
+            sessionId, initData, kVideoMp4, KeyType::STREAMING,
+            optionalParameters, [&](StatusV1_2 status, const hidl_vec<uint8_t>&,
+                                    KeyRequestType, const hidl_string&) {
+                EXPECT_EQ(StatusV1_2::ERROR_DRM_RESOURCE_CONTENTION, status);
+            });
+    EXPECT_OK(res);
+
+    status = drmPlugin->closeSession(sessionId);
+    EXPECT_NE(Status::OK, status);
+}
+
+/**
+ * Test clearkey plugin offline key with mock error
+ */
+TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
+    auto sessionId = openSession();
+    hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
+    Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorGeneric);
+    EXPECT_EQ(Status::OK, status);
+
+    // everything should start failing
+    const Status kInvalidState = Status::ERROR_DRM_INVALID_STATE;
+    const OfflineLicenseState kUnknownState = OfflineLicenseState::UNKNOWN;
+    auto res = drmPlugin->getOfflineLicenseKeySetIds(checkKeySetIds<kInvalidState, 0u>);
+    EXPECT_OK(res);
+    res = drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<kInvalidState, kUnknownState>);
+    EXPECT_OK(res);
+    Status err = drmPlugin->removeOfflineLicense(keySetId);
+    EXPECT_EQ(kInvalidState, err);
+    closeSession(sessionId);
+}
+
+/**
+ * Test SessionLostState is triggered on error
+ */
+TEST_P(DrmHalClearkeyTest, SessionLostState) {
+    sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
+    auto res = drmPlugin->setListener(listener);
+    EXPECT_OK(res);
+
+    Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorGeneric);
+    EXPECT_EQ(Status::OK, status);
+
+    auto sessionId = openSession();
+    drmPlugin->closeSession(sessionId);
+
+    auto result = listener->WaitForCallback(kCallbackLostState);
+    EXPECT_TRUE(result.no_timeout);
+    EXPECT_TRUE(result.args);
+    EXPECT_EQ(sessionId, *(result.args));
+}
+
+/**
+ * Negative decrypt test. Decrypt with invalid key.
+ */
+TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
+    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+    const Pattern noPattern = {0, 0};
+    const uint32_t kClearBytes = 512;
+    const uint32_t kEncryptedBytes = 512;
+    const vector<SubSample> subSamples = {
+        {.numBytesOfClearData = kClearBytes,
+         .numBytesOfEncryptedData = kEncryptedBytes}};
+
+    // base 64 encoded JSON response string, must not contain padding character '='
+    const hidl_string emptyKeyResponse =
+            "{\"keys\":[" \
+                "{" \
+                    "\"kty\":\"oct\"" \
+                    "\"alg\":\"A128KW2\"" \
+                    "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" \
+                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAyAy\"" \
+                "}" \
+                "{" \
+                    "\"kty\":\"oct\"," \
+                    "\"alg\":\"A128KW2\"" \
+                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," \
+                    // empty key follows
+                    "\"k\":\"R\"" \
+                "}]" \
+            "}";
+    const size_t kEmptyKeyResponseSize = emptyKeyResponse.size();
+
+    hidl_vec<uint8_t> invalidResponse;
+    invalidResponse.resize(kEmptyKeyResponseSize);
+    memcpy(invalidResponse.data(), emptyKeyResponse.c_str(), kEmptyKeyResponseSize);
+    decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
+}
+
+/**
+ * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
+ */
+TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
+    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+    const Pattern noPattern = {0, 0};
+    const uint32_t kClearBytes = 512;
+    const uint32_t kEncryptedBytes = 512;
+    const vector<SubSample> subSamples = {
+        {.numBytesOfClearData = kClearBytes,
+         .numBytesOfEncryptedData = kEncryptedBytes}};
+
+    // base 64 encoded JSON response string, must not contain padding character '='
+    const hidl_string keyTooLongResponse =
+            "{\"keys\":[" \
+                "{" \
+                    "\"kty\":\"oct\"," \
+                    "\"alg\":\"A128KW2\"" \
+                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," \
+                    // key too long
+                    "\"k\":\"V2lubmllIHRoZSBwb29oIVdpbm5pZSB0aGUgcG9vaCE=\"" \
+                "}]" \
+            "}";
+    const size_t kKeyTooLongResponseSize = keyTooLongResponse.size();
+
+    hidl_vec<uint8_t> invalidResponse;
+    invalidResponse.resize(kKeyTooLongResponseSize);
+    memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
+    decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
+}
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+INSTANTIATE_TEST_CASE_P(
+        DrmHalTestClearkey, DrmHalTest,
+        testing::Values("clearkey"));
+
+INSTANTIATE_TEST_CASE_P(
+        DrmHalTestClearkeyExtended, DrmHalClearkeyTest,
+        testing::Values("clearkey"));
+
+INSTANTIATE_TEST_CASE_P(
+        DrmHalTestVendor, DrmHalTest,
+        testing::ValuesIn(DrmHalTest::gVendorModules->getPathList()));
+
+int main(int argc, char** argv) {
+#if defined(__LP64__)
+    const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+    const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+    DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath);
+    if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
+        std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+                ", all vendor tests will be skipped" << std::endl;
+    }
+    ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    DrmHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGI("Test result = %d", status);
+    return status;
+}
diff --git a/drm/1.2/vts/functional/vendor_modules.cpp b/drm/1.2/vts/functional/vendor_modules.cpp
new file mode 100644
index 0000000..efcb90a
--- /dev/null
+++ b/drm/1.2/vts/functional/vendor_modules.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "drm-vts-vendor-modules"
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <log/log.h>
+#include <memory>
+#include <utils/String8.h>
+#include <SharedLibrary.h>
+
+#include "vendor_modules.h"
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+using ::android::String8;
+using ::android::hardware::drm::V1_0::helper::SharedLibrary;
+
+namespace drm_vts {
+void VendorModules::scanModules(const std::string &directory) {
+    DIR* dir = opendir(directory.c_str());
+    if (dir == NULL) {
+        ALOGE("Unable to open drm VTS vendor directory %s", directory.c_str());
+    } else {
+        struct dirent* entry;
+        while ((entry = readdir(dir))) {
+            ALOGD("checking file %s", entry->d_name);
+            string fullpath = directory + "/" + entry->d_name;
+            if (endsWith(fullpath, ".so")) {
+                mPathList.push_back(fullpath);
+            }
+        }
+        closedir(dir);
+    }
+}
+
+DrmHalVTSVendorModule* VendorModules::getModule(const string& path) {
+    if (mOpenLibraries.find(path) == mOpenLibraries.end()) {
+        auto library = std::make_unique<SharedLibrary>(String8(path.c_str()));
+        if (!library) {
+            ALOGE("failed to map shared library %s", path.c_str());
+            return NULL;
+        }
+        mOpenLibraries[path] = std::move(library);
+    }
+    const unique_ptr<SharedLibrary>& library = mOpenLibraries[path];
+    void* symbol = library->lookup("vendorModuleFactory");
+    if (symbol == NULL) {
+        ALOGE("getVendorModule failed to lookup 'vendorModuleFactory' in %s: "
+              "%s", path.c_str(), library->lastError());
+        return NULL;
+    }
+    typedef DrmHalVTSVendorModule* (*ModuleFactory)();
+    ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
+    return (*moduleFactory)();
+}
+};
diff --git a/drm/1.2/vts/functional/vendor_modules.h b/drm/1.2/vts/functional/vendor_modules.h
new file mode 100644
index 0000000..9b730ad
--- /dev/null
+++ b/drm/1.2/vts/functional/vendor_modules.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef VENDOR_MODULES_H
+#define VENDOR_MODULES_H
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include <SharedLibrary.h>
+
+using ::android::hardware::drm::V1_0::helper::SharedLibrary;
+
+class DrmHalVTSVendorModule;
+
+namespace drm_vts {
+class VendorModules {
+   public:
+    /**
+     * Initialize with a file system path where the shared libraries
+     * are to be found.
+     */
+    explicit VendorModules(const std::string& dir) {
+        scanModules(dir);
+    }
+    ~VendorModules() {}
+
+    /**
+     * Retrieve a DrmHalVTSVendorModule given its full path.  The
+     * getAPIVersion method can be used to determine the versioned
+     * subclass type.
+     */
+    DrmHalVTSVendorModule* getModule(const std::string& path);
+
+    /**
+     * Return the list of paths to available vendor modules.
+     */
+    std::vector<std::string> getPathList() const {return mPathList;}
+
+   private:
+    std::vector<std::string> mPathList;
+    std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
+
+    /**
+     * Scan the list of paths to available vendor modules.
+     */
+    void scanModules(const std::string& dir);
+
+    inline bool endsWith(const std::string& str, const std::string& suffix) const {
+        if (suffix.size() > str.size()) return false;
+        return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
+    }
+
+    VendorModules(const VendorModules&) = delete;
+    void operator=(const VendorModules&) = delete;
+};
+};
+
+#endif  // VENDOR_MODULES_H
diff --git a/fastboot/1.0/default/Android.bp b/fastboot/1.0/default/Android.bp
new file mode 100644
index 0000000..fde7efa
--- /dev/null
+++ b/fastboot/1.0/default/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+
+cc_library {
+    name: "android.hardware.fastboot@1.0-impl-mock",
+    recovery: true,
+    srcs: [
+        "Fastboot.cpp",
+    ],
+    relative_install_path: "hw",
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "libutils",
+        "libcutils",
+        "android.hardware.fastboot@1.0",
+    ],
+}
diff --git a/fastboot/1.0/default/Fastboot.cpp b/fastboot/1.0/default/Fastboot.cpp
new file mode 100644
index 0000000..a12233d
--- /dev/null
+++ b/fastboot/1.0/default/Fastboot.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "Fastboot.h"
+
+namespace android {
+namespace hardware {
+namespace fastboot {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::fastboot::V1_0::IFastboot follow.
+Return<void> Fastboot::getPartitionType(const hidl_string& /* partitionName */,
+                                        getPartitionType_cb _hidl_cb) {
+    _hidl_cb(FileSystemType::RAW, {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::doOemCommand(const hidl_string& /* oemCmd */, doOemCommand_cb _hidl_cb) {
+    _hidl_cb({Status::FAILURE_UNKNOWN, "Command not supported in default implementation"});
+    return Void();
+}
+
+Return<void> Fastboot::getVariant(getVariant_cb _hidl_cb) {
+    _hidl_cb("NA", {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::getOffModeChargeState(getOffModeChargeState_cb _hidl_cb) {
+    _hidl_cb(false, {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::getBatteryVoltageFlashingThreshold(
+        getBatteryVoltageFlashingThreshold_cb _hidl_cb) {
+    _hidl_cb(0, {Status::SUCCESS, ""});
+    return Void();
+}
+
+extern "C" IFastboot* HIDL_FETCH_IFastboot(const char* /* name */) {
+    return new Fastboot();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace fastboot
+}  // namespace hardware
+}  // namespace android
diff --git a/fastboot/1.0/default/Fastboot.h b/fastboot/1.0/default/Fastboot.h
new file mode 100644
index 0000000..3127059
--- /dev/null
+++ b/fastboot/1.0/default/Fastboot.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace fastboot {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct Fastboot : public IFastboot {
+    // Methods from ::android::hardware::fastboot::V1_0::IFastboot follow.
+    Return<void> getPartitionType(const hidl_string& partitionName,
+                                  getPartitionType_cb _hidl_cb) override;
+    Return<void> doOemCommand(const hidl_string& oemCmd, doOemCommand_cb _hidl_cb) override;
+    Return<void> getVariant(getVariant_cb _hidl_cb) override;
+    Return<void> getOffModeChargeState(getOffModeChargeState_cb _hidl_cb) override;
+    Return<void> getBatteryVoltageFlashingThreshold(
+            getBatteryVoltageFlashingThreshold_cb _hidl_cb) override;
+};
+
+extern "C" IFastboot* HIDL_FETCH_IFastboot(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace fastboot
+}  // namespace hardware
+}  // namespace android
diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp
index 147a470..cc34290 100644
--- a/gnss/1.1/vts/functional/Android.bp
+++ b/gnss/1.1/vts/functional/Android.bp
@@ -27,5 +27,8 @@
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@common-vts-lib",
     ],
+    shared_libs: [
+        "android.hardware.gnss.measurement_corrections@1.0",
+    ],
     test_suites: ["general-tests"],
 }
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index f327197..64187e2 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -26,6 +26,7 @@
         "AGnssRil.cpp",
         "Gnss.cpp",
         "GnssMeasurement.cpp",
+        "GnssMeasurementCorrections.cpp",
         "GnssVisibilityControl.cpp",
         "service.cpp"
     ],
diff --git a/gnss/2.0/default/Gnss.cpp b/gnss/2.0/default/Gnss.cpp
index 33edbd5..ee6da53 100644
--- a/gnss/2.0/default/Gnss.cpp
+++ b/gnss/2.0/default/Gnss.cpp
@@ -25,11 +25,14 @@
 #include "AGnssRil.h"
 #include "GnssConfiguration.h"
 #include "GnssMeasurement.h"
+#include "GnssMeasurementCorrections.h"
 #include "GnssVisibilityControl.h"
 #include "Utils.h"
 
 using ::android::hardware::Status;
 using ::android::hardware::gnss::common::Utils;
+using ::android::hardware::gnss::measurement_corrections::V1_0::implementation::
+        GnssMeasurementCorrections;
 using ::android::hardware::gnss::visibility_control::V1_0::implementation::GnssVisibilityControl;
 
 namespace android {
@@ -248,8 +251,8 @@
 
 Return<sp<measurement_corrections::V1_0::IMeasurementCorrections>>
 Gnss::getExtensionMeasurementCorrections() {
-    // TODO(b/124012850): Implement function.
-    return sp<measurement_corrections::V1_0::IMeasurementCorrections>{};
+    ALOGD("Gnss::getExtensionMeasurementCorrections");
+    return new GnssMeasurementCorrections();
 }
 
 Return<sp<visibility_control::V1_0::IGnssVisibilityControl>> Gnss::getExtensionVisibilityControl() {
diff --git a/gnss/2.0/default/GnssMeasurementCorrections.cpp b/gnss/2.0/default/GnssMeasurementCorrections.cpp
new file mode 100644
index 0000000..cbf34ba
--- /dev/null
+++ b/gnss/2.0/default/GnssMeasurementCorrections.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "GnssMeasurementCorrections"
+
+#include "GnssMeasurementCorrections.h"
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace measurement_corrections {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from V1_0::IMeasurementCorrections follow.
+Return<bool> GnssMeasurementCorrections::setCorrections(const MeasurementCorrections& corrections) {
+    ALOGD("setCorrections");
+    ALOGD("corrections = lat: %f, lng: %f, alt: %f, hUnc: %f, vUnc: %f, toa: %llu, "
+          "satCorrections.size: %d",
+          corrections.latitudeDegrees, corrections.longitudeDegrees, corrections.altitudeMeters,
+          corrections.horizontalPositionUncertaintyMeters,
+          corrections.verticalPositionUncertaintyMeters,
+          static_cast<unsigned long long>(corrections.toaGpsNanosecondsOfWeek),
+          static_cast<int>(corrections.satCorrections.size()));
+    for (auto singleSatCorrection : corrections.satCorrections) {
+        ALOGD("singleSatCorrection = flags: %d, constellation: %d, svid: %d, cfHz: %f, probLos: %f,"
+              " epl: %f, eplUnc: %f",
+              static_cast<int>(singleSatCorrection.singleSatCorrectionFlags),
+              static_cast<int>(singleSatCorrection.constellation),
+              static_cast<int>(singleSatCorrection.svid), singleSatCorrection.carrierFrequencyHz,
+              singleSatCorrection.probSatIsLos, singleSatCorrection.excessPathLengthMeters,
+              singleSatCorrection.excessPathLengthUncertaintyMeters);
+        ALOGD("reflecting plane = lat: %f, lng: %f, alt: %f, azm: %f",
+              singleSatCorrection.reflectingPlane.latitudeDegrees,
+              singleSatCorrection.reflectingPlane.longitudeDegrees,
+              singleSatCorrection.reflectingPlane.altitudeMeters,
+              singleSatCorrection.reflectingPlane.azimuthDegrees);
+    }
+
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace measurement_corrections
+}  // namespace gnss
+}  // namespace hardware
+}  // namespace android
diff --git a/gnss/2.0/default/GnssMeasurementCorrections.h b/gnss/2.0/default/GnssMeasurementCorrections.h
new file mode 100644
index 0000000..f758bc8
--- /dev/null
+++ b/gnss/2.0/default/GnssMeasurementCorrections.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace measurement_corrections {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct GnssMeasurementCorrections : public IMeasurementCorrections {
+    // Methods from V1_0::IMeasurementCorrections follow.
+    Return<bool> setCorrections(const MeasurementCorrections& corrections) override;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace measurement_corrections
+}  // namespace gnss
+}  // namespace hardware
+}  // namespace android
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 b135dba..3703eba 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -18,6 +18,7 @@
 
 #include <VtsHalHidlTargetTestBase.h>
 #include <gnss_hal_test.h>
+#include "Utils.h"
 
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
@@ -32,6 +33,9 @@
 using IAGnss_1_0 = android::hardware::gnss::V1_0::IAGnss;
 using IAGnssCallback_2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
 
+using android::hardware::gnss::common::Utils;
+using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
+using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
 using android::hardware::gnss::V1_0::IGnssNi;
 using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
@@ -273,6 +277,24 @@
 }
 
 /*
+ * TestGnssMeasurementCorrections:
+ * Gets the GnssMeasurementCorrectionsExtension and verifies that it supports the
+ * gnss.measurement_corrections@1.0::IMeasurementCorrections interface by invoking a method.
+ */
+TEST_F(GnssHalTest, TestGnssMeasurementCorrections) {
+    // Verify IMeasurementCorrections is supported.
+    auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections();
+    ASSERT_TRUE(measurementCorrections.isOk());
+    sp<IMeasurementCorrections> iMeasurementCorrections = measurementCorrections;
+    ASSERT_NE(iMeasurementCorrections, nullptr);
+
+    // Set a mock MeasurementCorrections.
+    auto result = iMeasurementCorrections->setCorrections(Utils::getMockMeasurementCorrections());
+    ASSERT_TRUE(result.isOk());
+    EXPECT_TRUE(result);
+}
+
+/*
  * TestGnssDataElapsedRealtimeFlags:
  * Sets a GnssMeasurementCallback, waits for a GnssData object, and verifies the flags in member
  * elapsedRealitme are valid.
diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp
index 99d8cf9..1988171 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.measurement_corrections@1.0",
     ],
     static_libs: [
         "libgtest",
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index 24d6883..51d3ea1 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -22,6 +22,7 @@
 namespace gnss {
 namespace common {
 
+using V1_0::GnssConstellationType;
 using V1_0::GnssLocationFlags;
 
 void Utils::checkLocation(const GnssLocation& location, bool check_speed,
@@ -91,6 +92,53 @@
     EXPECT_GT(location.timestamp, 1.48e12);
 }
 
+const MeasurementCorrections Utils::getMockMeasurementCorrections() {
+    ReflectingPlane reflectingPlane = {
+            .latitudeDegrees = 37.4220039,
+            .longitudeDegrees = -122.0840991,
+            .altitudeMeters = 250.35,
+            .azimuthDegrees = 203.0,
+    };
+
+    SingleSatCorrection singleSatCorrection1 = {
+            .singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
+                                        GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
+                                        GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC |
+                                        GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE,
+            .constellation = GnssConstellationType::GPS,
+            .svid = 12,
+            .carrierFrequencyHz = 1.59975e+09,
+            .probSatIsLos = 0.50001,
+            .excessPathLengthMeters = 137.4802,
+            .excessPathLengthUncertaintyMeters = 25.5,
+            .reflectingPlane = reflectingPlane,
+    };
+    SingleSatCorrection singleSatCorrection2 = {
+            .singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
+                                        GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
+                                        GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC,
+            .constellation = GnssConstellationType::GPS,
+            .svid = 9,
+            .carrierFrequencyHz = 1.59975e+09,
+            .probSatIsLos = 0.873,
+            .excessPathLengthMeters = 26.294,
+            .excessPathLengthUncertaintyMeters = 10.0,
+    };
+
+    hidl_vec<SingleSatCorrection> singleSatCorrections = {singleSatCorrection1,
+                                                          singleSatCorrection2};
+    MeasurementCorrections mockCorrections = {
+            .latitudeDegrees = 37.4219999,
+            .longitudeDegrees = -122.0840575,
+            .altitudeMeters = 30.60062531,
+            .horizontalPositionUncertaintyMeters = 9.23542,
+            .verticalPositionUncertaintyMeters = 15.02341,
+            .toaGpsNanosecondsOfWeek = 2935633453L,
+            .satCorrections = singleSatCorrections,
+    };
+    return mockCorrections;
+}
+
 }  // 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 f8eeff6..dce4c7b 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -18,8 +18,10 @@
 #define android_hardware_gnss_common_vts_Utils_H_
 
 #include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
 
 using GnssLocation = ::android::hardware::gnss::V1_0::GnssLocation;
+using namespace android::hardware::gnss::measurement_corrections::V1_0;
 
 namespace android {
 namespace hardware {
@@ -29,6 +31,7 @@
 struct Utils {
     static void checkLocation(const GnssLocation& location, bool check_speed,
                               bool check_more_accuracies);
+    static const MeasurementCorrections getMockMeasurementCorrections();
 };
 
 }  // namespace common
diff --git a/graphics/composer/2.3/IComposerClient.hal b/graphics/composer/2.3/IComposerClient.hal
index a3b7792..5fcc0e6 100644
--- a/graphics/composer/2.3/IComposerClient.hal
+++ b/graphics/composer/2.3/IComposerClient.hal
@@ -28,7 +28,6 @@
 
 interface IComposerClient extends @2.2::IComposerClient {
 
-    // Bug: Move this enum to LLNDK after we decide where to put graphic types.
     /**
      * Required capabilities which are supported by the display. The
      * particular set of supported capabilities for a given display may be
@@ -64,7 +63,6 @@
         DOZE = 2,
     };
 
-    //Bug: Move this enum to LLNDK after we decide where to put graphic types.
     /**
      * PerFrameMetadataKey
      *
diff --git a/health/2.0/vts/functional/Android.bp b/health/2.0/vts/functional/Android.bp
index 3544e5f..b090548 100644
--- a/health/2.0/vts/functional/Android.bp
+++ b/health/2.0/vts/functional/Android.bp
@@ -19,6 +19,7 @@
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: ["VtsHalHealthV2_0TargetTest.cpp"],
     static_libs: [
+        "libgflags",
         "android.hardware.health@1.0",
         "android.hardware.health@2.0",
     ],
diff --git a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
index f895aec..74fe4fb 100644
--- a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
+++ b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
@@ -17,11 +17,14 @@
 #define LOG_TAG "health_hidl_hal_test"
 
 #include <mutex>
+#include <set>
+#include <string>
 
 #include <VtsHalHidlTargetTestBase.h>
 #include <android-base/logging.h>
 #include <android/hardware/health/2.0/IHealth.h>
 #include <android/hardware/health/2.0/types.h>
+#include <gflags/gflags.h>
 
 using ::testing::AssertionFailure;
 using ::testing::AssertionResult;
@@ -29,6 +32,41 @@
 using ::testing::VtsHalHidlTargetTestBase;
 using ::testing::VtsHalHidlTargetTestEnvBase;
 
+DEFINE_bool(force, false, "Force test healthd even when the default instance is present.");
+
+// If GTEST_SKIP is not implemented, use our own skipping mechanism
+#ifndef GTEST_SKIP
+static std::mutex gSkippedTestsMutex;
+static std::set<std::string> gSkippedTests;
+static std::string GetCurrentTestName() {
+    const auto& info = ::testing::UnitTest::GetInstance()->current_test_info();
+#ifdef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+    std::string test_suite = info->test_suite_name();
+#else
+    std::string test_suite = info->test_case_name();
+#endif
+    return test_suite + "." + info->name();
+}
+
+#define GTEST_SKIP()                                           \
+    do {                                                       \
+        std::unique_lock<std::mutex> lock(gSkippedTestsMutex); \
+        gSkippedTests.insert(GetCurrentTestName());            \
+        return;                                                \
+    } while (0)
+
+#define SKIP_IF_SKIPPED()                                                      \
+    do {                                                                       \
+        std::unique_lock<std::mutex> lock(gSkippedTestsMutex);                 \
+        if (gSkippedTests.find(GetCurrentTestName()) != gSkippedTests.end()) { \
+            std::cerr << "[  SKIPPED ] " << GetCurrentTestName() << std::endl; \
+            return;                                                            \
+        }                                                                      \
+    } while (0)
+#else
+#define SKIP_IF_SKIPPED()
+#endif
+
 namespace android {
 namespace hardware {
 namespace health {
@@ -57,6 +95,14 @@
    public:
     virtual void SetUp() override {
         std::string serviceName = HealthHidlEnvironment::Instance()->getServiceName<IHealth>();
+
+        if (serviceName == "backup" && !FLAGS_force &&
+            ::testing::VtsHalHidlTargetTestBase::getService<IHealth>() != nullptr) {
+            LOG(INFO) << "Skipping tests on healthd because the default instance is present. "
+                      << "Use --force if you really want to test healthd.";
+            GTEST_SKIP();
+        }
+
         LOG(INFO) << "get service with name:" << serviceName;
         ASSERT_FALSE(serviceName.empty());
         mHealth = ::testing::VtsHalHidlTargetTestBase::getService<IHealth>(serviceName);
@@ -111,6 +157,7 @@
  * unregisterCallback, and update.
  */
 TEST_F(HealthHidlTest, Callbacks) {
+    SKIP_IF_SKIPPED();
     using namespace std::chrono_literals;
     sp<Callback> firstCallback = new Callback();
     sp<Callback> secondCallback = new Callback();
@@ -147,6 +194,7 @@
 }
 
 TEST_F(HealthHidlTest, UnregisterNonExistentCallback) {
+    SKIP_IF_SKIPPED();
     sp<Callback> callback = new Callback();
     auto ret = mHealth->unregisterCallback(callback);
     ASSERT_OK(ret);
@@ -225,6 +273,7 @@
  * Tests the values returned by getChargeCounter() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getChargeCounter) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getChargeCounter([](auto result, auto value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, std::to_string(value), value > 0);
     }));
@@ -234,6 +283,7 @@
  * Tests the values returned by getCurrentNow() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getCurrentNow) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getCurrentNow([](auto result, auto value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, std::to_string(value), value != INT32_MIN);
     }));
@@ -243,6 +293,7 @@
  * Tests the values returned by getCurrentAverage() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getCurrentAverage) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getCurrentAverage([](auto result, auto value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, std::to_string(value), value != INT32_MIN);
     }));
@@ -252,6 +303,7 @@
  * Tests the values returned by getCapacity() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getCapacity) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getCapacity([](auto result, auto value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, std::to_string(value), 0 <= value && value <= 100);
     }));
@@ -261,6 +313,7 @@
  * Tests the values returned by getEnergyCounter() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getEnergyCounter) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getEnergyCounter([](auto result, auto value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, std::to_string(value), value != INT64_MIN);
     }));
@@ -270,6 +323,7 @@
  * Tests the values returned by getChargeStatus() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getChargeStatus) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getChargeStatus([](auto result, auto value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(
             result, toString(value),
@@ -281,6 +335,7 @@
  * Tests the values returned by getStorageInfo() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getStorageInfo) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getStorageInfo([](auto result, auto& value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, toString(value), verifyStorageInfo(value));
     }));
@@ -290,6 +345,7 @@
  * Tests the values returned by getDiskStats() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getDiskStats) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getDiskStats([](auto result, auto& value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, toString(value), true);
     }));
@@ -299,6 +355,7 @@
  * Tests the values returned by getHealthInfo() from interface IHealth.
  */
 TEST_F(HealthHidlTest, getHealthInfo) {
+    SKIP_IF_SKIPPED();
     EXPECT_OK(mHealth->getHealthInfo([](auto result, auto& value) {
         EXPECT_VALID_OR_UNSUPPORTED_PROP(result, toString(value), verifyHealthInfo(value));
     }));
@@ -314,6 +371,7 @@
     ::testing::AddGlobalTestEnvironment(HealthHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
     HealthHidlEnvironment::Instance()->init(&argc, argv);
+    gflags::ParseCommandLineFlags(&argc, &argv, true /* remove flags */);
     int status = RUN_ALL_TESTS();
     LOG(INFO) << "Test result = " << status;
     return status;
diff --git a/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp b/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
index 88a9e26..f299e36 100644
--- a/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
+++ b/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
@@ -387,17 +387,28 @@
                                   OMX_StateIdle);
     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
 
+    OMX_PARAM_PORTDEFINITIONTYPE portDefInput;
+    OMX_PARAM_PORTDEFINITIONTYPE portDefOutput;
+    status = getPortParam(omxNode, OMX_IndexParamPortDefinition, kPortIndexInput, &portDefInput);
+    EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
+    status = getPortParam(omxNode, OMX_IndexParamPortDefinition, kPortIndexOutput, &portDefOutput);
+    EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
+
     // Dont switch states until the ports are populated
-    status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
-    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    if (portDefInput.nBufferCountActual || portDefOutput.nBufferCountActual) {
+        status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
+        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    }
 
     // allocate buffers on input port
     ASSERT_NO_FATAL_FAILURE(allocatePortBuffers(
         omxNode, iBuffer, kPortIndexInput, pm[0], allocGrap));
 
     // Dont switch states until the ports are populated
-    status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
-    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    if (portDefOutput.nBufferCountActual) {
+        status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
+        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    }
 
     // allocate buffers on output port
     ASSERT_NO_FATAL_FAILURE(allocatePortBuffers(
@@ -430,9 +441,18 @@
                                   OMX_StateLoaded);
     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
 
+    OMX_PARAM_PORTDEFINITIONTYPE portDefInput;
+    OMX_PARAM_PORTDEFINITIONTYPE portDefOutput;
+    status = getPortParam(omxNode, OMX_IndexParamPortDefinition, kPortIndexInput, &portDefInput);
+    EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
+    status = getPortParam(omxNode, OMX_IndexParamPortDefinition, kPortIndexOutput, &portDefOutput);
+    EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
+
     // dont change state until all buffers are freed
-    status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
-    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    if (portDefInput.nBufferCountActual || portDefOutput.nBufferCountActual) {
+        status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
+        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    }
 
     for (size_t i = 0; i < iBuffer->size(); ++i) {
         status = omxNode->freeBuffer(kPortIndexInput, (*iBuffer)[i].id);
@@ -440,8 +460,10 @@
     }
 
     // dont change state until all buffers are freed
-    status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
-    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    if (portDefOutput.nBufferCountActual) {
+        status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT, iBuffer, oBuffer);
+        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::TIMED_OUT);
+    }
 
     for (size_t i = 0; i < oBuffer->size(); ++i) {
         status = omxNode->freeBuffer(kPortIndexOutput, (*oBuffer)[i].id);
diff --git a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
index 4ddc833..1c1d39b 100644
--- a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
+++ b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
@@ -115,6 +115,7 @@
         }
         if (compClass == unknown_class) disableTest = true;
         isSecure = false;
+        mTunnel = false;
         size_t suffixLen = strlen(".secure");
         if (strlen(gEnv->getComponent().c_str()) >= suffixLen) {
             isSecure =
@@ -122,6 +123,18 @@
                             strlen(gEnv->getComponent().c_str()) - suffixLen,
                         ".secure");
         }
+        if (compClass == video_decoder) {
+            omxNode->configureVideoTunnelMode(
+                1, OMX_TRUE, 0,
+                [&](android::hardware::media::omx::V1_0::Status _s,
+                    const ::android::hardware::hidl_handle& sidebandHandle) {
+                    (void)sidebandHandle;
+                    if (_s == android::hardware::media::omx::V1_0::Status::OK)
+                        this->mTunnel = true;
+                });
+        }
+        // NOTES: secure components are not covered in these tests.
+        // we are disabling tests for them
         if (disableTest) std::cout << "[   WARN   ] Test Disabled \n";
     }
 
@@ -149,6 +162,7 @@
     sp<CodecObserver> observer;
     sp<IOmxNode> omxNode;
     standardCompClass compClass;
+    bool mTunnel;
     bool isSecure;
     bool disableTest;
 
@@ -991,7 +1005,8 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateLoadedtoIdle(omxNode, observer, &pBuffer[0], &pBuffer[1],
                                 kPortIndexInput, kPortIndexOutput, portMode));
-    for (size_t i = portBase; i < portBase + 2; i++) {
+    int range = mTunnel ? 1 : 2;
+    for (size_t i = portBase; i < portBase + range; i++) {
         status =
             omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), i);
         ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
@@ -1104,7 +1119,8 @@
             dispatchOutputBuffer(omxNode, &pBuffer[1], i, portMode[1]));
     }
 
-    for (size_t i = portBase; i < portBase + 2; i++) {
+    int range = mTunnel ? 1 : 2;
+    for (size_t i = portBase; i < portBase + range; i++) {
         status =
             omxNode->sendCommand(toRawCommandType(OMX_CommandPortDisable), i);
         ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
diff --git a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp
index 1db9f75..df048c6 100644
--- a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp
+++ b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp
@@ -153,7 +153,17 @@
                         ".secure");
         }
         if (isSecure) disableTest = true;
+        omxNode->configureVideoTunnelMode(
+            1, OMX_TRUE, 0,
+            [&](android::hardware::media::omx::V1_0::Status _s,
+                const ::android::hardware::hidl_handle& sidebandHandle) {
+                (void)sidebandHandle;
+                if (_s == android::hardware::media::omx::V1_0::Status::OK)
+                    this->disableTest = true;
+            });
         if (disableTest) std::cout << "[   WARN   ] Test Disabled \n";
+        // NOTES: secure and tunneled components are not covered in these tests.
+        // we are disabling tests for them
     }
 
     virtual void TearDown() override {
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 4a8664f..bb14dec 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -4223,7 +4223,7 @@
     TOPK_V2 = 90,
 
     /**
-     * Performs the tranpose of 2-D convolution operation.
+     * Performs the transpose of 2-D convolution operation.
      *
      * This operation is sometimes called "deconvolution" after Deconvolutional
      * Networks, but is actually the transpose (gradient) of
@@ -4844,7 +4844,7 @@
         /**
          * The extension name.
          *
-         * See {@link Extension::name}.
+         * See {@link Extension::name} for the format specification.
          */
         string name;
 
@@ -5128,7 +5128,11 @@
     /**
      * The extension name.
      *
+     * The name must consist of lowercase latin letters, numbers, periods, and
+     * underscore signs. The name must contain at least one period.
+     *
      * The name must start with the reverse domain name of the vendor.
+     *
      * Example: com.google.test_extension
      */
     string name;
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 2b88edd..365a750 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -64,7 +64,12 @@
                 for (auto& extension : extensions) {
                     std::string extensionName = extension.name;
                     EXPECT_FALSE(extensionName.empty());
-                    EXPECT_NE(extensionName.find("."), std::string::npos)
+                    for (char c : extensionName) {
+                        EXPECT_TRUE(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_' ||
+                                    c == '.')
+                                << "Extension name contains an illegal character: " << c;
+                    }
+                    EXPECT_NE(extensionName.find('.'), std::string::npos)
                             << "Extension name must start with the reverse domain name of the "
                                "vendor";
                 }
diff --git a/radio/1.4/IRadio.hal b/radio/1.4/IRadio.hal
index 046f074..3f28ebb 100644
--- a/radio/1.4/IRadio.hal
+++ b/radio/1.4/IRadio.hal
@@ -207,7 +207,7 @@
      *        as defined in types.hal
      * @param multiSimPolicy Policy to be used for devices with multiple SIMs.
      *
-     * Response callback is IRadioResponse.setAllowedCarriersResponse()
+     * Response callback is IRadioResponse.setAllowedCarriersResponse_1_4()
      */
     oneway setAllowedCarriers_1_4(int32_t serial, CarrierRestrictionsWithPriority carriers,
             SimLockMultiSimPolicy multiSimPolicy);
@@ -217,7 +217,7 @@
      *
      * @param serial Serial number of request.
      *
-     * Response callback is IRadioResponse.getAllowedCarriersResponse_1_3()
+     * Response callback is IRadioResponse.getAllowedCarriersResponse_1_4()
      */
     oneway getAllowedCarriers_1_4(int32_t serial);
 
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 8e1dc3e..76d8758 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -522,8 +522,8 @@
     memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
     dataProfileInfo.profileId = DataProfileId::DEFAULT;
     dataProfileInfo.apn = hidl_string("internet");
-    dataProfileInfo.protocol = PdpProtocolType::IPV4V6;
-    dataProfileInfo.roamingProtocol = PdpProtocolType::IPV4V6;
+    dataProfileInfo.protocol = PdpProtocolType::IP;
+    dataProfileInfo.roamingProtocol = PdpProtocolType::IP;
     dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
     dataProfileInfo.user = hidl_string("username");
     dataProfileInfo.password = hidl_string("password");
diff --git a/radio/1.4/vts/functional/radio_response.cpp b/radio/1.4/vts/functional/radio_response.cpp
index e7842a9..eac0c68 100644
--- a/radio/1.4/vts/functional/radio_response.cpp
+++ b/radio/1.4/vts/functional/radio_response.cpp
@@ -533,7 +533,9 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_4::setInitialAttachApnResponse(const RadioResponseInfo& /*info*/) {
+Return<void> RadioResponse_v1_4::setInitialAttachApnResponse(const RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_4.notify(info.serial);
     return Void();
 }
 
@@ -604,7 +606,9 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_4::setDataProfileResponse(const RadioResponseInfo& /*info*/) {
+Return<void> RadioResponse_v1_4::setDataProfileResponse(const RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_4.notify(info.serial);
     return Void();
 }
 
diff --git a/wifi/1.3/default/wifi_chip.cpp b/wifi/1.3/default/wifi_chip.cpp
index 727aac5..d4c0329 100644
--- a/wifi/1.3/default/wifi_chip.cpp
+++ b/wifi/1.3/default/wifi_chip.cpp
@@ -114,7 +114,7 @@
     std::unique_ptr<DIR, decltype(&closedir)> dir_dump(
         opendir(kTombstoneFolderPath), closedir);
     if (!dir_dump) {
-        LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+        PLOG(ERROR) << "Failed to open directory";
         return false;
     }
     struct dirent* dp;
@@ -128,8 +128,7 @@
         struct stat cur_file_stat;
         std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
         if (stat(cur_file_path.c_str(), &cur_file_stat) == -1) {
-            LOG(ERROR) << "Failed to get file stat for " << cur_file_path
-                       << ": " << strerror(errno);
+            PLOG(ERROR) << "Failed to get file stat for " << cur_file_path;
             success = false;
             continue;
         }
@@ -144,7 +143,7 @@
         if (cur_file_count > kMaxRingBufferFileNum ||
             cur_file.first < delete_files_before) {
             if (unlink(cur_file.second.c_str()) != 0) {
-                LOG(ERROR) << "Error deleting file " << strerror(errno);
+                PLOG(ERROR) << "Error deleting file";
                 success = false;
             }
             cur_file_count--;
@@ -168,13 +167,11 @@
                 major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
                 minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
     if (write(out_fd, read_buf.data(), llen) == -1) {
-        LOG(ERROR) << "Error writing cpio header to file " << file_name << " "
-                   << strerror(errno);
+        PLOG(ERROR) << "Error writing cpio header to file " << file_name;
         return false;
     }
     if (write(out_fd, file_name, file_name_len) == -1) {
-        LOG(ERROR) << "Error writing filename to file " << file_name << " "
-                   << strerror(errno);
+        PLOG(ERROR) << "Error writing filename to file " << file_name;
         return false;
     }
 
@@ -183,8 +180,7 @@
     if (llen != 0) {
         const uint32_t zero = 0;
         if (write(out_fd, &zero, 4 - llen) == -1) {
-            LOG(ERROR) << "Error padding 0s to file " << file_name << " "
-                       << strerror(errno);
+            PLOG(ERROR) << "Error padding 0s to file " << file_name;
             return false;
         }
     }
@@ -200,17 +196,17 @@
     while (llen > 0) {
         ssize_t bytes_read = read(fd_read, read_buf.data(), read_buf.size());
         if (bytes_read == -1) {
-            LOG(ERROR) << "Error reading file " << strerror(errno);
+            PLOG(ERROR) << "Error reading file";
             return ++n_error;
         }
         llen -= bytes_read;
         if (write(out_fd, read_buf.data(), bytes_read) == -1) {
-            LOG(ERROR) << "Error writing data to file " << strerror(errno);
+            PLOG(ERROR) << "Error writing data to file";
             return ++n_error;
         }
         if (bytes_read == 0) {  // this should never happen, but just in case
                                 // to unstuck from while loop
-            LOG(ERROR) << "Unexpected read result for " << strerror(errno);
+            PLOG(ERROR) << "Unexpected read result";
             n_error++;
             break;
         }
@@ -219,7 +215,7 @@
     if (llen != 0) {
         const uint32_t zero = 0;
         if (write(out_fd, &zero, 4 - llen) == -1) {
-            LOG(ERROR) << "Error padding 0s to file " << strerror(errno);
+            PLOG(ERROR) << "Error padding 0s to file";
             return ++n_error;
         }
     }
@@ -234,7 +230,7 @@
               sprintf(read_buf.data(), "070701%040X%056X%08XTRAILER!!!", 1,
                       0x0b, 0) +
                   4) == -1) {
-        LOG(ERROR) << "Error writing trailing bytes " << strerror(errno);
+        PLOG(ERROR) << "Error writing trailing bytes";
         return false;
     }
     return true;
@@ -249,7 +245,7 @@
     std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(input_dir),
                                                        closedir);
     if (!dir_dump) {
-        LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+        PLOG(ERROR) << "Failed to open directory";
         return ++n_error;
     }
     while ((dp = readdir(dir_dump.get()))) {
@@ -263,15 +259,13 @@
         struct stat st;
         const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
         if (stat(cur_file_path.c_str(), &st) == -1) {
-            LOG(ERROR) << "Failed to get file stat for " << cur_file_path
-                       << ": " << strerror(errno);
+            PLOG(ERROR) << "Failed to get file stat for " << cur_file_path;
             n_error++;
             continue;
         }
         const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
         if (fd_read == -1) {
-            LOG(ERROR) << "Failed to open file " << cur_file_path << " "
-                       << strerror(errno);
+            PLOG(ERROR) << "Failed to open file " << cur_file_path;
             n_error++;
             continue;
         }
@@ -1373,14 +1367,14 @@
             kTombstoneFolderPath + item.first + "XXXXXXXXXX";
         const int dump_fd = mkstemp(makeCharVec(file_path_raw).data());
         if (dump_fd == -1) {
-            LOG(ERROR) << "create file failed: " << strerror(errno);
+            PLOG(ERROR) << "create file failed";
             return false;
         }
         unique_fd file_auto_closer(dump_fd);
         for (const auto& cur_block : cur_buffer.getData()) {
             if (write(dump_fd, cur_block.data(),
                       sizeof(cur_block[0]) * cur_block.size()) == -1) {
-                LOG(ERROR) << "Error writing to file " << strerror(errno);
+                PLOG(ERROR) << "Error writing to file";
             }
         }
     }