Merge "Fix transitive includes."
diff --git a/automotive/evs/1.0/default/EvsEnumerator.cpp b/automotive/evs/1.0/default/EvsEnumerator.cpp
index 731e21b..b25d418 100644
--- a/automotive/evs/1.0/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.0/default/EvsEnumerator.cpp
@@ -99,7 +99,7 @@
     }
 
     // Construct a camera instance for the caller
-    pActiveCamera = new EvsCamera(cameraId);
+    pActiveCamera = new EvsCamera(cameraId.c_str());
     pRecord->activeInstance = pActiveCamera;
     if (pActiveCamera == nullptr) {
         ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
diff --git a/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp b/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp
index 9789ee6..f48a95d 100644
--- a/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp
+++ b/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp
@@ -30,6 +30,8 @@
 using ::android::hardware::hidl_string;
 using ::android::hardware::Return;
 using ::android::sp;
+using std::string;
+using std::vector;
 
 // The main test class for the Boot HIDL HAL.
 class BootHidlTest : public ::testing::VtsHalHidlTargetTestBase {
@@ -83,7 +85,7 @@
   {
     // Restore original flags to avoid problems on reboot
     CommandResult cr;
-    Return <void> result = boot->markBootSuccessful(generate_callback(&cr));
+    Return<void> result = boot->markBootSuccessful(generate_callback(&cr));
     EXPECT_TRUE(result.isOk());
     EXPECT_TRUE(cr.success);
   }
@@ -151,22 +153,21 @@
 
 // Sanity check Boot::getSuffix() on good and bad inputs.
 TEST_F(BootHidlTest, GetSuffix) {
-  const char *suffixPtr;
-  auto cb = [&](hidl_string suffix) { suffixPtr = suffix.c_str(); };
-  for (Slot i = 0; i < 2; i++) {
-    CommandResult cr;
-    Return<void> result = boot->getSuffix(i, cb);
-    EXPECT_TRUE(result.isOk());
-    char correctSuffix[3];
-    snprintf(correctSuffix, sizeof(correctSuffix), "_%c", 'a' + i);
-    ASSERT_EQ(0, strcmp(suffixPtr, correctSuffix));
-  }
-  {
-    char emptySuffix[] = "";
-    Return<void> result = boot->getSuffix(boot->getNumberSlots(), cb);
-    EXPECT_TRUE(result.isOk());
-    ASSERT_EQ(0, strcmp(emptySuffix, suffixPtr));
-  }
+    string suffixStr;
+    vector<string> correctSuffixes = {"_a", "_b"};
+    auto cb = [&](hidl_string suffix) { suffixStr = suffix.c_str(); };
+    for (Slot i = 0; i < 2; i++) {
+        CommandResult cr;
+        Return<void> result = boot->getSuffix(i, cb);
+        EXPECT_TRUE(result.isOk());
+        ASSERT_EQ(0, suffixStr.compare(correctSuffixes[i]));
+    }
+    {
+        string emptySuffix = "";
+        Return<void> result = boot->getSuffix(boot->getNumberSlots(), cb);
+        EXPECT_TRUE(result.isOk());
+        ASSERT_EQ(0, suffixStr.compare(emptySuffix));
+    }
 }
 
 int main(int argc, char **argv) {
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index bf7ac73..591861a 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -35,7 +35,7 @@
     // Methods from ::android::hardware::drm::V1_0::ICryptoPlugin follow
     Return<bool> CryptoPlugin::requiresSecureDecoderComponent(
             const hidl_string& mime) {
-        return mLegacyPlugin->requiresSecureDecoderComponent(mime);
+        return mLegacyPlugin->requiresSecureDecoderComponent(mime.c_str());
     }
 
     Return<void> CryptoPlugin::notifyResolution(uint32_t width,
diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp
index c7428a5..1695ef7 100644
--- a/drm/1.0/default/DrmPlugin.cpp
+++ b/drm/1.0/default/DrmPlugin.cpp
@@ -70,15 +70,15 @@
         if (status == android::OK) {
             android::KeyedVector<String8, String8> legacyOptionalParameters;
             for (size_t i = 0; i < optionalParameters.size(); i++) {
-                legacyOptionalParameters.add(String8(optionalParameters[i].key),
-                        String8(optionalParameters[i].value));
+                legacyOptionalParameters.add(String8(optionalParameters[i].key.c_str()),
+                        String8(optionalParameters[i].value.c_str()));
             }
 
             android::DrmPlugin::KeyRequestType legacyRequestType =
                     android::DrmPlugin::kKeyRequestType_Unknown;
 
             status = mLegacyPlugin->getKeyRequest(toVector(scope),
-                    toVector(initData), String8(mimeType), legacyKeyType,
+                    toVector(initData), String8(mimeType.c_str()), legacyKeyType,
                     legacyOptionalParameters, legacyRequest, defaultUrl,
                     &legacyRequestType);
 
@@ -149,7 +149,7 @@
         Vector<uint8_t> legacyRequest;
         String8 legacyDefaultUrl;
         status_t status = mLegacyPlugin->getProvisionRequest(
-                String8(certificateType), String8(certificateAuthority),
+                String8(certificateType.c_str()), String8(certificateAuthority.c_str()),
                 legacyRequest, legacyDefaultUrl);
 
         _hidl_cb(toStatus(status), toHidlVec(legacyRequest),
@@ -217,7 +217,7 @@
             getPropertyString_cb _hidl_cb) {
         String8 legacyValue;
         status_t status = mLegacyPlugin->getPropertyString(
-                String8(propertyName), legacyValue);
+                String8(propertyName.c_str()), legacyValue);
         _hidl_cb(toStatus(status), legacyValue.string());
         return Void();
     }
@@ -226,7 +226,7 @@
             getPropertyByteArray_cb _hidl_cb) {
         Vector<uint8_t> legacyValue;
         status_t status = mLegacyPlugin->getPropertyByteArray(
-                String8(propertyName), legacyValue);
+                String8(propertyName.c_str()), legacyValue);
         _hidl_cb(toStatus(status), toHidlVec(legacyValue));
         return Void();
     }
@@ -234,15 +234,15 @@
     Return<Status> DrmPlugin::setPropertyString(const hidl_string& propertyName,
             const hidl_string& value) {
         status_t legacyStatus =
-            mLegacyPlugin->setPropertyString(String8(propertyName),
-                    String8(value));
+            mLegacyPlugin->setPropertyString(String8(propertyName.c_str()),
+                    String8(value.c_str()));
         return toStatus(legacyStatus);
     }
 
     Return<Status> DrmPlugin::setPropertyByteArray(
             const hidl_string& propertyName, const hidl_vec<uint8_t>& value) {
         status_t legacyStatus =
-            mLegacyPlugin->setPropertyByteArray(String8(propertyName),
+            mLegacyPlugin->setPropertyByteArray(String8(propertyName.c_str()),
                     toVector(value));
         return toStatus(legacyStatus);
     }
@@ -251,7 +251,7 @@
             const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
         status_t legacyStatus =
             mLegacyPlugin->setCipherAlgorithm(toVector(sessionId),
-                String8(algorithm));
+                String8(algorithm.c_str()));
         return toStatus(legacyStatus);
     }
 
@@ -259,7 +259,7 @@
             const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
         status_t legacyStatus =
             mLegacyPlugin->setMacAlgorithm(toVector(sessionId),
-                String8(algorithm));
+                String8(algorithm.c_str()));
         return toStatus(legacyStatus);
     }
 
@@ -313,7 +313,7 @@
 
         Vector<uint8_t> legacySignature;
         status_t status = mLegacyPlugin->signRSA(toVector(sessionId),
-                String8(algorithm), toVector(message), toVector(wrappedKey),
+                String8(algorithm.c_str()), toVector(message), toVector(wrappedKey),
                 legacySignature);
         _hidl_cb(toStatus(status), toHidlVec(legacySignature));
         return Void();
@@ -327,19 +327,25 @@
 
     Return<void> DrmPlugin::sendEvent(EventType eventType,
             const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data) {
-        mListener->sendEvent(eventType, sessionId, data);
+        if (mListener != nullptr) {
+            mListener->sendEvent(eventType, sessionId, data);
+        }
         return Void();
     }
 
     Return<void> DrmPlugin::sendExpirationUpdate(
             const hidl_vec<uint8_t>& sessionId, int64_t expiryTimeInMS) {
-        mListener->sendExpirationUpdate(sessionId, expiryTimeInMS);
+        if (mListener != nullptr) {
+            mListener->sendExpirationUpdate(sessionId, expiryTimeInMS);
+        }
         return Void();
     }
 
     Return<void> DrmPlugin::sendKeysChange(const hidl_vec<uint8_t>& sessionId,
             const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
-        mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
+        if (mListener != nullptr) {
+            mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
+        }
         return Void();
     }
 
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index 36d7d1c..43ea372 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -34,6 +34,8 @@
         "libhwbinder",
         "liblog",
         "libnativehelper",
+        "libssl",
+        "libcrypto",
         "libutils",
     ],
     static_libs: [
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
index 01656f2..5a0d73a 100644
--- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -27,6 +27,7 @@
 #include <hidlmemory/mapping.h>
 #include <log/log.h>
 #include <memory>
+#include <openssl/aes.h>
 #include <random>
 
 #include "VtsHalHidlTargetTestBase.h"
@@ -125,6 +126,39 @@
 }
 
 /**
+ * Ensure the factory doesn't support an empty UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, EmptyPluginUUIDNotSupported) {
+    hidl_array<uint8_t, 16> emptyUUID;
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(emptyUUID));
+    EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(emptyUUID));
+}
+
+/**
+ * Ensure empty content type is not supported
+ */
+TEST_F(DrmHalClearkeyFactoryTest, EmptyContentTypeNotSupported) {
+    hidl_string empty;
+    EXPECT_FALSE(drmFactory->isContentTypeSupported(empty));
+}
+
+/**
+ * Ensure invalid content type is not supported
+ */
+TEST_F(DrmHalClearkeyFactoryTest, InvalidContentTypeNotSupported) {
+    hidl_string invalid("abcdabcd");
+    EXPECT_FALSE(drmFactory->isContentTypeSupported(invalid));
+}
+
+/**
+ * Ensure valid content type is supported
+ */
+TEST_F(DrmHalClearkeyFactoryTest, ValidContentTypeSupported) {
+    hidl_string cencType("cenc");
+    EXPECT_TRUE(drmFactory->isContentTypeSupported(cencType));
+}
+
+/**
  * Ensure clearkey drm plugin can be created
  */
 TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPlugin) {
@@ -418,6 +452,26 @@
 }
 
 /**
+ * Test that a removeKeys on an empty sessionID returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, RemoveKeysEmptySessionId) {
+    SessionId sessionId;
+    Status status = drmPlugin->removeKeys(sessionId);
+    EXPECT_TRUE(status == Status::BAD_VALUE);
+}
+
+/**
+ * Remove keys is not supported for clearkey.
+ */
+TEST_F(DrmHalClearkeyPluginTest, RemoveKeysNewSession) {
+    SessionId sessionId = openSession();
+    Status status = drmPlugin->removeKeys(sessionId);
+    // Clearkey plugin doesn't support remove keys
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+    closeSession(sessionId);
+}
+
+/**
  * Test that the clearkey plugin doesn't support getting
  * secure stops.
  */
@@ -617,7 +671,7 @@
     ;
     hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
-    hidl_vec<uint8_t> iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    hidl_vec<uint8_t> iv = std::vector<uint8_t>(AES_BLOCK_SIZE, 0);
     auto res = drmPlugin->encrypt(session, keyId, input, iv,
                                   [&](Status status, const hidl_vec<uint8_t>&) {
                                       EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
@@ -629,10 +683,9 @@
 
 TEST_F(DrmHalClearkeyPluginTest, GenericDecryptNotSupported) {
     SessionId session = openSession();
-    ;
     hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
-    hidl_vec<uint8_t> iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    hidl_vec<uint8_t> iv = std::vector<uint8_t>(AES_BLOCK_SIZE, 0);
     auto res = drmPlugin->decrypt(session, keyId, input, iv,
                                   [&](Status status, const hidl_vec<uint8_t>&) {
                                       EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
@@ -763,6 +816,17 @@
 }
 
 /**
+ * setMediaDrmSession with an empty session id: BAD_VALUE.  An
+ * empty session clears the previously set session and should
+ * return OK.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionEmptySession) {
+    SessionId sessionId;
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::OK, status);
+}
+
+/**
  * Decrypt tests
  */
 
@@ -771,9 +835,15 @@
     void loadKeys(const SessionId& sessionId);
     void fillRandom(const sp<IMemory>& memory);
     hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
-        EXPECT_EQ(vec.size(), 16u);
+        EXPECT_EQ(16u, vec.size());
         return hidl_array<uint8_t, 16>(&vec[0]);
     }
+    uint32_t decrypt(Mode mode, uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+            const Pattern& pattern, Status status);
+    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);
 };
 
 /**
@@ -847,36 +917,162 @@
     }
 }
 
-/**
- * Positive decrypt test.  "Decrypt" a single clear
- * segment.  Verify data matches.
- */
-TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
-    const size_t kSegmentSize = 1024;
+uint32_t DrmHalClearkeyDecryptTest::decrypt(Mode mode,
+        uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+        const Pattern& pattern, Status expectedStatus) {
     const size_t kSegmentIndex = 0;
     const vector<uint8_t> keyId = {0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47,
                                    0x7e, 0x87, 0x7e, 0x57, 0xd0, 0x0d,
                                    0x1e, 0xd0, 0x0d, 0x1e};
-    uint8_t iv[16] = {0};
+    const vector<uint8_t> contentKey = {0x1a, 0x8a, 0x20, 0x95, 0xe4,
+                                        0xde, 0xb2, 0xd2, 0x9e, 0xc8,
+                                        0x16, 0xac, 0x7b, 0xae, 0x20, 0x82};
+    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 is the decrypted output.
     sp<IMemory> sharedMemory =
-            getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+            getDecryptMemory(totalSize * 2, kSegmentIndex);
 
-    SharedBuffer sourceBuffer = {
-            .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+    const SharedBuffer sourceBuffer = {
+        .bufferId = kSegmentIndex, .offset = 0, .size = totalSize};
     fillRandom(sharedMemory);
 
-    DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
-                                    {.bufferId = kSegmentIndex,
-                                     .offset = kSegmentSize,
-                                     .size = kSegmentSize},
-                                    .secureMemory = nullptr};
+    const DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+                                          {.bufferId = kSegmentIndex,
+                                           .offset = totalSize,
+                                           .size = totalSize},
+                                          .secureMemory = nullptr};
+    const uint64_t offset = 0;
+    const bool kNotSecure = false;
+    uint32_t bytesWritten = 0;
+    auto res = cryptoPlugin->decrypt(kNotSecure, toHidlArray(keyId), localIv, mode,
+            pattern, subSamples, sourceBuffer, offset, destBuffer,
+            [&](Status status, uint32_t count, string detailedError) {
+                EXPECT_EQ(expectedStatus, status) << "Unexpected decrypt status " <<
+                detailedError;
+                bytesWritten = count;
+            });
+    EXPECT_OK(res);
 
-    Pattern noPattern = {0, 0};
-    vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
-                                     .numBytesOfEncryptedData = 0}};
-    uint64_t offset = 0;
+    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, contentKey);
+        break;
+    case Mode::AES_CBC:
+        aes_cbc_decrypt(&reference[0], base, localIv, subSamples, contentKey);
+        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 DrmHalClearkeyDecryptTest::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 DrmHalClearkeyDecryptTest::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;
+    size_t num = 0;
+    size_t ecount_buf = 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;
+    }
+}
+
+/**
+ * Test query key status
+ */
+TEST_F(DrmHalClearkeyDecryptTest, TestQueryKeyStatus) {
+    auto sessionId = openSession();
+    auto res = drmPlugin->queryKeyStatus(sessionId,
+            [&](Status status, KeyedVector /* info */) {
+                // clearkey doesn't support this method
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+
+/**
+ * Positive decrypt test.  "Decrypt" a single clear segment
+ */
+TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
+    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+    const Pattern noPattern = {0, 0};
+    const uint32_t kByteCount = 256;
+    const vector<SubSample> subSamples = {
+        {.numBytesOfClearData = kByteCount,
+         .numBytesOfEncryptedData = 0}};
     auto sessionId = openSession();
     loadKeys(sessionId);
 
@@ -884,21 +1080,57 @@
     EXPECT_EQ(Status::OK, status);
 
     const bool kNotSecure = false;
-    auto res = cryptoPlugin->decrypt(
-            kNotSecure, toHidlArray(keyId), iv, Mode::UNENCRYPTED, noPattern,
-            subSamples, sourceBuffer, offset, destBuffer,
-            [&](Status status, uint32_t bytesWritten, string detailedError) {
-                EXPECT_EQ(Status::OK, status) << "Failure in decryption:"
-                                              << detailedError;
-                EXPECT_EQ(bytesWritten, kSegmentSize);
-            });
-    EXPECT_OK(res);
+    uint32_t byteCount = decrypt(Mode::UNENCRYPTED, &iv[0], subSamples,
+            noPattern, Status::OK);
+    EXPECT_EQ(kByteCount, byteCount);
 
-    uint8_t* base = static_cast<uint8_t*>(
-            static_cast<void*>(sharedMemory->getPointer()));
+    closeSession(sessionId);
+}
 
-    EXPECT_EQ(0, memcmp(static_cast<void*>(base),
-                        static_cast<void*>(base + kSegmentSize), kSegmentSize))
-            << "decrypt data mismatch";
+/**
+ * Positive decrypt test.  Decrypt a single segment using AES_CTR.
+ * Verify data matches.
+ */
+TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) {
+    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
+        }};
+    auto sessionId = openSession();
+    loadKeys(sessionId);
+
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::OK, status);
+
+    const bool kNotSecure = false;
+    uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples,
+            noPattern, Status::OK);
+    EXPECT_EQ(kClearBytes + kEncryptedBytes, byteCount);
+
+    closeSession(sessionId);
+}
+/**
+ * Negative decrypt test. Decrypt without loading keys.
+ */
+TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
+    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);
+
+    const bool kNotSecure = false;
+    uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples,
+            noPattern, Status::ERROR_DRM_NO_LICENSE);
+    EXPECT_EQ(0u, byteCount);
+
     closeSession(sessionId);
 }
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
index db19719..73e0cfe 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
+++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
@@ -73,21 +73,21 @@
      * value with initial version 1. The API version indicates which subclass
      * version DrmHalVTSVendorModule this instance is.
      */
-    virtual uint32_t getAPIVersion() = 0;
+    virtual uint32_t getAPIVersion() const = 0;
 
     /**
      * Return the UUID for the DRM HAL implementation. Protection System
      * Specific
      * UUID (see http://dashif.org/identifiers/protection/)
      */
-    virtual std::vector<uint8_t> getUUID() = 0;
+    virtual std::vector<uint8_t> getUUID() const = 0;
 
     /**
      * Return the service name for the DRM HAL implementation. If the hal is a
      * legacy
      * drm plugin, i.e. not running as a HIDL service, return the empty string.
      */
-    virtual std::string getServiceName() = 0;
+    virtual std::string getServiceName() const = 0;
 
    private:
     DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete;
@@ -103,7 +103,7 @@
     DrmHalVTSVendorModule_V1() {}
     virtual ~DrmHalVTSVendorModule_V1() {}
 
-    virtual uint32_t getAPIVersion() { return 1; }
+    virtual uint32_t getAPIVersion() const { return 1; }
 
     /**
      * Handle a provisioning request. This function will be called if the HAL
@@ -178,11 +178,10 @@
             const std::vector<uint8_t> keyId;
 
             /**
-             * The key value is provided to generate expected values for
-             * validating
-             * decryption.  If isSecure is false, no key value is required.
+             * The clear content key is provided to generate expected values for
+             * validating decryption.
              */
-            const std::vector<uint8_t> keyValue;
+            const std::vector<uint8_t> clearContentKey;
         };
         std::vector<Key> keys;
     };
@@ -191,7 +190,8 @@
      * Return a list of content configurations that can be exercised by the
      * VTS test.
      */
-    virtual std::vector<ContentConfiguration> getContentConfigurations() = 0;
+    virtual std::vector<ContentConfiguration>
+            getContentConfigurations() const = 0;
 
     /**
      * Handle a key request. This function will be called if the HAL
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
index b44ace9..c2e942d 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -20,28 +20,34 @@
 #include <android/hardware/drm/1.0/ICryptoPlugin.h>
 #include <android/hardware/drm/1.0/IDrmFactory.h>
 #include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/IDrmPluginListener.h>
 #include <android/hardware/drm/1.0/types.h>
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <gtest/gtest.h>
 #include <hidlmemory/mapping.h>
 #include <log/log.h>
 #include <memory>
+#include <openssl/aes.h>
 #include <random>
 
-#include "VtsHalHidlTargetTestBase.h"
 #include "drm_hal_vendor_module_api.h"
 #include "vendor_modules.h"
+#include "VtsHalHidlTargetTestBase.h"
 
 using ::android::hardware::drm::V1_0::BufferType;
 using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::ICryptoFactory;
 using ::android::hardware::drm::V1_0::ICryptoPlugin;
 using ::android::hardware::drm::V1_0::IDrmFactory;
 using ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::IDrmPluginListener;
 using ::android::hardware::drm::V1_0::KeyedVector;
-using ::android::hardware::drm::V1_0::KeyValue;
 using ::android::hardware::drm::V1_0::KeyRequestType;
+using ::android::hardware::drm::V1_0::KeyStatus;
+using ::android::hardware::drm::V1_0::KeyStatusType;
 using ::android::hardware::drm::V1_0::KeyType;
+using ::android::hardware::drm::V1_0::KeyValue;
 using ::android::hardware::drm::V1_0::Mode;
 using ::android::hardware::drm::V1_0::Pattern;
 using ::android::hardware::drm::V1_0::SecureStop;
@@ -56,6 +62,7 @@
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
+using ::android::hardware::Void;
 using ::android::hidl::allocator::V1_0::IAllocator;
 using ::android::hidl::memory::V1_0::IMemory;
 using ::android::sp;
@@ -67,6 +74,9 @@
 using std::mt19937;
 using std::vector;
 
+using ContentConfiguration = ::DrmHalVTSVendorModule_V1::ContentConfiguration;
+using Key = ::DrmHalVTSVendorModule_V1::ContentConfiguration::Key;
+
 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
 #define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
 
@@ -80,10 +90,9 @@
 class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
    public:
     DrmHalVendorFactoryTest()
-        : vendorModule(gVendorModules ? static_cast<DrmHalVTSVendorModule_V1*>(
-                                                gVendorModules->getVendorModule(
-                                                        GetParam()))
-                                      : nullptr) {}
+        : vendorModule(static_cast<DrmHalVTSVendorModule_V1*>(
+                        gVendorModules->getModule(GetParam()))),
+          contentConfigurations(vendorModule->getContentConfigurations()) {}
 
     virtual ~DrmHalVendorFactoryTest() {}
 
@@ -117,14 +126,27 @@
     sp<IDrmFactory> drmFactory;
     sp<ICryptoFactory> cryptoFactory;
     unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+    const vector<ContentConfiguration> contentConfigurations;
 };
 
-/**
- * Ensure the factory supports its scheme UUID
- */
-TEST_P(DrmHalVendorFactoryTest, VendorPluginSupported) {
-    EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
-    EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
+TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) {
+    const char* kVendorStr = "Vendor module ";
+    for (auto config : contentConfigurations) {
+        ASSERT_TRUE(config.name.size() > 0) << kVendorStr << "has no name";
+        ASSERT_TRUE(config.serverUrl.size() > 0) << kVendorStr
+                                                 << "has no serverUrl";
+        ASSERT_TRUE(config.initData.size() > 0) << kVendorStr
+                                                << "has no init data";
+        ASSERT_TRUE(config.mimeType.size() > 0) << kVendorStr
+                                                << "has no mime type";
+        ASSERT_TRUE(config.keys.size() >= 1) << kVendorStr << "has no keys";
+        for (auto key : config.keys) {
+            ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
+                                              << " has zero length keyId";
+            ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
+                                              << " has zero length key value";
+        }
+    }
 }
 
 /**
@@ -136,6 +158,48 @@
 }
 
 /**
+ * Ensure the factory doesn't support an empty UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, EmptyPluginUUIDNotSupported) {
+    hidl_array<uint8_t, 16> emptyUUID;
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(emptyUUID));
+    EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(emptyUUID));
+}
+
+/**
+ * Ensure the factory supports the scheme uuid in the config
+ */
+TEST_P(DrmHalVendorFactoryTest, EmptyPluginConfigUUIDSupported) {
+    EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
+    EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
+}
+
+/**
+ * Ensure empty content type is not supported
+ */
+TEST_P(DrmHalVendorFactoryTest, EmptyContentTypeNotSupported) {
+    hidl_string empty;
+    EXPECT_FALSE(drmFactory->isContentTypeSupported(empty));
+}
+
+/**
+ * Ensure invalid content type is not supported
+ */
+TEST_P(DrmHalVendorFactoryTest, InvalidContentTypeNotSupported) {
+    hidl_string invalid("abcdabcd");
+    EXPECT_FALSE(drmFactory->isContentTypeSupported(invalid));
+}
+
+/**
+ * Ensure valid content types in the configs are supported
+ */
+TEST_P(DrmHalVendorFactoryTest, ValidContentTypeSupported) {
+    for (auto config : contentConfigurations) {
+        EXPECT_TRUE(drmFactory->isContentTypeSupported(config.mimeType));
+    }
+}
+
+/**
  * Ensure vendor drm plugin can be created
  */
 TEST_P(DrmHalVendorFactoryTest, CreateVendorDrmPlugin) {
@@ -393,6 +457,26 @@
 }
 
 /**
+ * Test that a removeKeys on an empty sessionID returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, RemoveKeysEmptySessionId) {
+    SessionId sessionId;
+    Status status = drmPlugin->removeKeys(sessionId);
+    EXPECT_TRUE(status == Status::BAD_VALUE);
+}
+
+/**
+ * Test that remove keys returns okay on an initialized session
+ * that has no keys.
+ */
+TEST_P(DrmHalVendorPluginTest, RemoveKeysNewSession) {
+    SessionId sessionId = openSession();
+    Status status = drmPlugin->removeKeys(sessionId);
+    EXPECT_TRUE(status == Status::OK);
+    closeSession(sessionId);
+}
+
+/**
  * Test that the plugin either doesn't support getting
  * secure stops, or has no secure stops available after
  * clearing them.
@@ -722,6 +806,175 @@
 }
 
 /**
+ * Verify that requiresSecureDecoderComponent returns true for secure
+ * configurations
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderConfig) {
+    const char* kVendorStr = "Vendor module ";
+    for (auto config : contentConfigurations) {
+        for (auto key : config.keys) {
+            if (key.isSecure) {
+                EXPECT_TRUE(cryptoPlugin->requiresSecureDecoderComponent(config.mimeType));
+                break;
+            }
+        }
+    }
+}
+
+/**
+ *  Event Handling tests
+ */
+
+class TestDrmPluginListener : public IDrmPluginListener {
+public:
+    TestDrmPluginListener() {reset();}
+    virtual ~TestDrmPluginListener() {}
+
+    virtual Return<void> sendEvent(EventType eventType, const hidl_vec<uint8_t>& sessionId,
+            const hidl_vec<uint8_t>& data) override {
+        eventType_ = eventType;
+        sessionId_ = sessionId;
+        data_ = data;
+        gotEvent_ = true;
+        return Void();
+    }
+
+    virtual Return<void> sendExpirationUpdate(const hidl_vec<uint8_t>& sessionId,
+            int64_t expiryTimeInMS) override {
+        sessionId_ = sessionId;
+        expiryTimeInMS_ = expiryTimeInMS;
+        gotExpirationUpdate_ = true;
+        return Void();
+    }
+
+    virtual Return<void> sendKeysChange(const hidl_vec<uint8_t>& sessionId,
+            const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) override {
+        sessionId_ = sessionId;
+        keyStatusList_ = keyStatusList;
+        hasNewUsableKey_ = hasNewUsableKey;
+        gotKeysChange_ = true;
+        return Void();
+    }
+
+    EventType getEventType() const {return eventType_;}
+    SessionId getSessionId() const {return sessionId_;}
+    vector<uint8_t> getData() const {return data_;}
+    int64_t getExpiryTimeInMS() const {return expiryTimeInMS_;}
+    hidl_vec<KeyStatus> getKeyStatusList() const {return keyStatusList_;}
+    bool hasNewUsableKey() {return hasNewUsableKey_;}
+    bool gotEvent() {return gotEvent_;}
+    bool gotExpirationUpdate() {return gotExpirationUpdate_;}
+    bool gotKeysChange() {return gotKeysChange_;}
+
+    void reset() {
+        gotEvent_ = gotExpirationUpdate_ = gotKeysChange_ = false;
+        eventType_ = EventType::PROVISION_REQUIRED;
+        sessionId_ = SessionId();
+        data_ = hidl_vec<uint8_t>();
+        expiryTimeInMS_ = 0;
+        keyStatusList_ = hidl_vec<KeyStatus>();
+        hasNewUsableKey_ = false;
+    }
+
+private:
+    bool gotEvent_;
+    bool gotExpirationUpdate_;
+    bool gotKeysChange_;
+
+    EventType eventType_;
+    SessionId sessionId_;
+    hidl_vec<uint8_t> data_;
+    int64_t expiryTimeInMS_;
+    hidl_vec<KeyStatus> keyStatusList_;
+    bool hasNewUsableKey_;
+};
+
+/**
+ * Simulate the plugin sending events. Make sure the listener
+ * gets them.
+ */
+TEST_P(DrmHalVendorPluginTest, ListenerEvents) {
+    sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
+    drmPlugin->setListener(listener);
+    auto sessionId = openSession();
+    vector<uint8_t> data = {0, 1, 2};
+    EventType eventTypes[] = {EventType::PROVISION_REQUIRED,
+                              EventType::KEY_NEEDED,
+                              EventType::KEY_EXPIRED,
+                              EventType::VENDOR_DEFINED,
+                              EventType::SESSION_RECLAIMED};
+    for (auto eventType : eventTypes) {
+        listener->reset();
+        drmPlugin->sendEvent(eventType, sessionId, data);
+        while (!listener->gotEvent()) {usleep(100);}
+        EXPECT_EQ(eventType, listener->getEventType());
+        EXPECT_EQ(sessionId, listener->getSessionId());
+        EXPECT_EQ(data, listener->getData());
+    }
+    closeSession(sessionId);
+}
+
+/**
+ * Simulate the plugin sending expiration updates and make sure
+ * the listener gets them.
+ */
+TEST_P(DrmHalVendorPluginTest, ListenerExpirationUpdate) {
+    sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
+    drmPlugin->setListener(listener);
+    auto sessionId = openSession();
+    drmPlugin->sendExpirationUpdate(sessionId, 100);
+    while (!listener->gotExpirationUpdate()) {usleep(100);}
+    EXPECT_EQ(sessionId, listener->getSessionId());
+    EXPECT_EQ(100, listener->getExpiryTimeInMS());
+    closeSession(sessionId);
+}
+
+/**
+ * Simulate the plugin sending keys change and make sure
+ * the listener gets them.
+ */
+TEST_P(DrmHalVendorPluginTest, ListenerKeysChange) {
+    sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
+    drmPlugin->setListener(listener);
+    auto sessionId = openSession();
+    const hidl_vec<KeyStatus> keyStatusList = {
+        {{1}, KeyStatusType::USABLE},
+        {{2}, KeyStatusType::EXPIRED},
+        {{3}, KeyStatusType::OUTPUTNOTALLOWED},
+        {{4}, KeyStatusType::STATUSPENDING},
+        {{5}, KeyStatusType::INTERNALERROR},
+    };
+
+    drmPlugin->sendKeysChange(sessionId, keyStatusList, true);
+    while (!listener->gotKeysChange()) {usleep(100);}
+    EXPECT_EQ(sessionId, listener->getSessionId());
+    EXPECT_EQ(keyStatusList, listener->getKeyStatusList());
+    EXPECT_EQ(true, listener->hasNewUsableKey());
+}
+
+/**
+ * Negative listener tests. Call send methods with no
+ * listener set.
+ */
+TEST_P(DrmHalVendorPluginTest, NotListening) {
+    sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
+    drmPlugin->setListener(listener);
+    drmPlugin->setListener(nullptr);
+
+    SessionId sessionId;
+    vector<uint8_t> data;
+    hidl_vec<KeyStatus> keyStatusList;
+    drmPlugin->sendEvent(EventType::PROVISION_REQUIRED, sessionId, data);
+    drmPlugin->sendExpirationUpdate(sessionId, 100);
+    drmPlugin->sendKeysChange(sessionId, keyStatusList, true);
+    usleep(1000); // can't wait for the event to be recieved, just wait a long time
+    EXPECT_EQ(false, listener->gotEvent());
+    EXPECT_EQ(false, listener->gotExpirationUpdate());
+    EXPECT_EQ(false, listener->gotKeysChange());
+}
+
+
+/**
  *  CryptoPlugin tests
  */
 
@@ -786,6 +1039,15 @@
 }
 
 /**
+ * setMediaDrmSession with a empty session id: BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionEmptySession) {
+    SessionId sessionId;
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
  * Decrypt tests
  */
 
@@ -796,14 +1058,23 @@
 
    protected:
     void loadKeys(const SessionId& sessionId,
-                  const DrmHalVTSVendorModule_V1::ContentConfiguration&
-                          configuration);
+                  const ContentConfiguration& configuration);
     void fillRandom(const sp<IMemory>& memory);
     KeyedVector toHidlKeyedVector(const map<string, string>& params);
     hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
         EXPECT_EQ(vec.size(), 16u);
         return hidl_array<uint8_t, 16>(&vec[0]);
     }
+    hidl_vec<KeyValue> queryKeyStatus(SessionId sessionId);
+    void removeKeys(SessionId sessionId);
+    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, Status 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);
 };
 
 KeyedVector DrmHalVendorDecryptTest::toHidlKeyedVector(
@@ -823,9 +1094,8 @@
  * These tests use predetermined key request/response to
  * avoid requiring a round trip to a license server.
  */
-void DrmHalVendorDecryptTest::loadKeys(
-        const SessionId& sessionId,
-        const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration) {
+void DrmHalVendorDecryptTest::loadKeys(const SessionId& sessionId,
+        const ContentConfiguration& configuration) {
     hidl_vec<uint8_t> keyRequest;
     auto res = drmPlugin->getKeyRequest(
             sessionId, configuration.initData, configuration.mimeType,
@@ -874,111 +1144,326 @@
     }
 }
 
-TEST_P(DrmHalVendorDecryptTest, ValidateConfigurations) {
-    vector<DrmHalVTSVendorModule_V1::ContentConfiguration> configurations =
-            vendorModule->getContentConfigurations();
-    const char* kVendorStr = "Vendor module ";
-    for (auto config : configurations) {
-        ASSERT_TRUE(config.name.size() > 0) << kVendorStr << "has no name";
-        ASSERT_TRUE(config.serverUrl.size() > 0) << kVendorStr
-                                                 << "has no serverUrl";
-        ASSERT_TRUE(config.initData.size() > 0) << kVendorStr
-                                                << "has no init data";
-        ASSERT_TRUE(config.mimeType.size() > 0) << kVendorStr
-                                                << "has no mime type";
-        ASSERT_TRUE(config.keys.size() >= 1) << kVendorStr << "has no keys";
-        for (auto key : config.keys) {
-            ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
-                                              << " has zero length keyId";
-            ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
-                                              << " has zero length key value";
+hidl_vec<KeyValue> DrmHalVendorDecryptTest::queryKeyStatus(SessionId sessionId) {
+    hidl_vec<KeyValue> keyStatus;
+    auto res = drmPlugin->queryKeyStatus(sessionId,
+            [&](Status status, KeyedVector info) {
+                EXPECT_EQ(Status::OK, status);
+                keyStatus = info;
+            });
+    EXPECT_OK(res);
+    return keyStatus;
+}
+
+void DrmHalVendorDecryptTest::removeKeys(SessionId sessionId) {
+    auto res = drmPlugin->removeKeys(sessionId);
+    EXPECT_OK(res);
+}
+
+uint32_t DrmHalVendorDecryptTest::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, Status 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 is the decrypted output.
+    sp<IMemory> sharedMemory =
+            getDecryptMemory(totalSize * 2, kSegmentIndex);
+
+    SharedBuffer sourceBuffer = {
+            .bufferId = kSegmentIndex, .offset = 0, .size = totalSize};
+    fillRandom(sharedMemory);
+
+    DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+                                    {.bufferId = kSegmentIndex,
+                                     .offset = totalSize,
+                                     .size = totalSize},
+                                    .secureMemory = nullptr};
+    uint64_t offset = 0;
+    uint32_t bytesWritten = 0;
+    auto res = cryptoPlugin->decrypt(isSecure, keyId, localIv, mode, pattern,
+            subSamples, sourceBuffer, offset, destBuffer,
+            [&](Status 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 DrmHalVendorDecryptTest::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 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;
         }
     }
 }
 
 /**
- * Positive decrypt test.  "Decrypt" a single clear
- * segment.  Verify data matches.
+ * Decrypt a list of clear+encrypted subsamples using the specified key
+ * in AES-CBC mode
  */
-TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
-    vector<DrmHalVTSVendorModule_V1::ContentConfiguration> configurations =
-            vendorModule->getContentConfigurations();
-    for (auto config : configurations) {
-        const size_t kSegmentSize = 1024;
-        const size_t kSegmentIndex = 0;
-        uint8_t iv[16] = {0};
+void DrmHalVendorDecryptTest::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);
 
-        sp<IMemory> sharedMemory =
-                getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+    size_t offset = 0;
+    size_t num = 0;
+    size_t ecount_buf = 0;
+    for (size_t i = 0; i < subSamples.size(); i++) {
+        const SubSample& subSample = subSamples[i];
 
-        SharedBuffer sourceBuffer = {
-                .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
-        fillRandom(sharedMemory);
+        memcpy(dest + offset, src + offset, subSample.numBytesOfClearData);
+        offset += subSample.numBytesOfClearData;
 
-        DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
-                                        {.bufferId = kSegmentIndex,
-                                         .offset = kSegmentSize,
-                                         .size = kSegmentSize},
-                                        .secureMemory = nullptr};
+        AES_cbc_encrypt(src + offset, dest + offset, subSample.numBytesOfEncryptedData,
+                &decryptionKey, iv, 0 /* decrypt */);
+        offset += subSample.numBytesOfEncryptedData;
+    }
+}
 
-        Pattern noPattern = {0, 0};
-        vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
-                                         .numBytesOfEncryptedData = 0}};
-        uint64_t offset = 0;
 
+/**
+ * Test key status with empty session id, should return BAD_VALUE
+ */
+TEST_P(DrmHalVendorDecryptTest, QueryKeyStatusInvalidSession) {
+    SessionId sessionId;
+    auto res = drmPlugin->queryKeyStatus(sessionId,
+            [&](Status status, KeyedVector /* info */) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+            });
+    EXPECT_OK(res);
+}
+
+
+/**
+ * Test key status.  There should be no key status prior to loading keys
+ */
+TEST_P(DrmHalVendorDecryptTest, QueryKeyStatusWithNoKeys) {
+    auto sessionId = openSession();
+    auto keyStatus = queryKeyStatus(sessionId);
+    EXPECT_EQ(0u, keyStatus.size());
+    closeSession(sessionId);
+}
+
+
+/**
+ * Test key status.  There should be key status after loading keys.
+ */
+TEST_P(DrmHalVendorDecryptTest, QueryKeyStatus) {
+    for (auto config : contentConfigurations) {
         auto sessionId = openSession();
         loadKeys(sessionId, config);
-
-        Status status = cryptoPlugin->setMediaDrmSession(sessionId);
-        EXPECT_EQ(Status::OK, status);
-
-        const bool kNotSecure = false;
-        auto res = cryptoPlugin->decrypt(
-                kNotSecure, toHidlArray(config.keys[0].keyId), iv,
-                Mode::UNENCRYPTED, noPattern, subSamples, sourceBuffer, offset,
-                destBuffer, [&](Status status, uint32_t bytesWritten,
-                                string detailedError) {
-                    EXPECT_EQ(Status::OK, status) << "Failure in decryption "
-                                                     "for configuration "
-                                                  << config.name << ": "
-                                                  << detailedError;
-                    EXPECT_EQ(bytesWritten, kSegmentSize);
-                });
-        EXPECT_OK(res);
-        uint8_t* base = static_cast<uint8_t*>(
-                static_cast<void*>(sharedMemory->getPointer()));
-
-        EXPECT_EQ(0,
-                  memcmp(static_cast<void*>(base),
-                         static_cast<void*>(base + kSegmentSize), kSegmentSize))
-                << "decrypt data mismatch";
+        auto keyStatus = queryKeyStatus(sessionId);
+        EXPECT_NE(0u, keyStatus.size());
         closeSession(sessionId);
     }
 }
 
 /**
+ * Positive decrypt test. "Decrypt" a single clear segment and verify.
+ */
+TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
+    for (auto config : contentConfigurations) {
+        for (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, Status::OK);
+            EXPECT_EQ(kSegmentSize, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Positive decrypt test.  Decrypt a single segment using aes_ctr.
+ * Verify data matches.
+ */
+TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTest) {
+    for (auto config : contentConfigurations) {
+        for (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, Status::OK);
+            EXPECT_EQ(kSegmentSize, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Negative decrypt test. Decrypt without loading keys.
+ */
+TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
+    for (auto config : contentConfigurations) {
+        for (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, Status::ERROR_DRM_NO_LICENSE);
+            EXPECT_EQ(0u, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Test key removal.  Load keys then remove them and verify that
+ * decryption can't be performed.
+ */
+TEST_P(DrmHalVendorDecryptTest, AttemptDecryptWithKeysRemoved) {
+    for (auto config : contentConfigurations) {
+        for (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);
+
+            loadKeys(sessionId, config);
+            removeKeys(sessionId);
+
+            uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
+                    toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
+                    key.clearContentKey, Status::ERROR_DRM_DECRYPT);
+            EXPECT_EQ(0u, byteCount);
+
+            closeSession(sessionId);
+        }
+    }
+}
+
+
+/**
  * Instantiate the set of test cases for each vendor module
  */
 
 INSTANTIATE_TEST_CASE_P(
         DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
-        testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+        testing::ValuesIn(gVendorModules->getPathList()));
 
 INSTANTIATE_TEST_CASE_P(
         DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
-        testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+        testing::ValuesIn(gVendorModules->getPathList()));
 
 INSTANTIATE_TEST_CASE_P(
         DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
-        testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+        testing::ValuesIn(gVendorModules->getPathList()));
 
 int main(int argc, char** argv) {
 #if defined(__LP64__)
-    const char *kModulePath = "/data/local/tmp/64/lib";
+    const char* kModulePath = "/data/local/tmp/64/lib";
 #else
-    const char *kModulePath = "/data/local/tmp/32/lib";
+    const char* kModulePath = "/data/local/tmp/32/lib";
 #endif
     gVendorModules = new drm_vts::VendorModules(kModulePath);
+    if (gVendorModules->getPathList().size() == 0) {
+        std::cerr << "No vendor modules found in " << kModulePath <<
+                ", exiting" << std::endl;
+        exit(-1);
+    }
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
 }
diff --git a/drm/1.0/vts/functional/vendor_modules.cpp b/drm/1.0/vts/functional/vendor_modules.cpp
index e81bbd4..2bf0b28 100644
--- a/drm/1.0/vts/functional/vendor_modules.cpp
+++ b/drm/1.0/vts/functional/vendor_modules.cpp
@@ -29,44 +29,37 @@
 using std::unique_ptr;
 
 namespace drm_vts {
-vector<string> VendorModules::getVendorModulePaths() {
-    if (mModuleList.size() > 0) {
-        return mModuleList;
-    }
-
-    DIR* dir = opendir(mModulesPath.c_str());
+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",
-              mModulesPath.c_str());
-        return mModuleList;
-    }
-
-    struct dirent* entry;
-    while ((entry = readdir(dir))) {
-        string fullpath = mModulesPath + "/" + entry->d_name;
-        if (endsWith(fullpath, ".so")) {
-            mModuleList.push_back(fullpath);
+        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);
     }
-
-    closedir(dir);
-    return mModuleList;
 }
 
-DrmHalVTSVendorModule* VendorModules::getVendorModule(const string& path) {
-    unique_ptr<SharedLibrary>& library = mOpenLibraries[path];
-    if (!library) {
-        library = unique_ptr<SharedLibrary>(new SharedLibrary(path));
+DrmHalVTSVendorModule* VendorModules::getModule(const string& path) {
+    if (mOpenLibraries.find(path) == mOpenLibraries.end()) {
+        auto library = std::make_unique<SharedLibrary>(path);
         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());
+              "%s", path.c_str(), library->lastError());
         return NULL;
     }
     typedef DrmHalVTSVendorModule* (*ModuleFactory)();
diff --git a/drm/1.0/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/vendor_modules.h
index 5371a0d..ca538f6 100644
--- a/drm/1.0/vts/functional/vendor_modules.h
+++ b/drm/1.0/vts/functional/vendor_modules.h
@@ -30,27 +30,33 @@
      * Initialize with a file system path where the shared libraries
      * are to be found.
      */
-    explicit VendorModules(const std::string& path) : mModulesPath(path) {}
+    explicit VendorModules(const std::string& dir) {
+        scanModules(dir);
+    }
     ~VendorModules() {}
 
     /**
-     * Return a list of paths to available vendor modules.
-     */
-    std::vector<std::string> getVendorModulePaths();
-
-    /**
      * Retrieve a DrmHalVTSVendorModule given its full path.  The
      * getAPIVersion method can be used to determine the versioned
      * subclass type.
      */
-    DrmHalVTSVendorModule* getVendorModule(const std::string& path);
+    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::string mModulesPath;
-    std::vector<std::string> mModuleList;
+    std::vector<std::string> mPathList;
     std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
 
-    inline bool endsWith(const std::string& str, const std::string& suffix) {
+    /**
+     * 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());
     }
diff --git a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
index b3ac874..f757a64 100644
--- a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
+++ b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
@@ -616,7 +616,7 @@
 
   switch (type) {
 #define CHECK_TYPE_STRING_FOR_SENSOR_TYPE(type) \
-    case SensorType::type: ASSERT_STREQ(SENSOR_STRING_TYPE_ ## type, stringType); break;
+    case SensorType::type: ASSERT_STREQ(SENSOR_STRING_TYPE_ ## type, stringType.c_str()); break;
     CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER);
     CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER_UNCALIBRATED);
     CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ADDITIONAL_INFO);
diff --git a/tests/baz/1.0/Android.bp b/tests/baz/1.0/Android.bp
index 8f327e3..1d5013b 100644
--- a/tests/baz/1.0/Android.bp
+++ b/tests/baz/1.0/Android.bp
@@ -7,6 +7,7 @@
         "IBase.hal",
         "IBaz.hal",
         "IBazCallback.hal",
+        "IQuux.hal",
     ],
 }
 
@@ -22,6 +23,7 @@
         "android/hardware/tests/baz/1.0/BaseAll.cpp",
         "android/hardware/tests/baz/1.0/BazAll.cpp",
         "android/hardware/tests/baz/1.0/BazCallbackAll.cpp",
+        "android/hardware/tests/baz/1.0/QuuxAll.cpp",
     ],
 }
 
@@ -50,6 +52,11 @@
         "android/hardware/tests/baz/1.0/BnHwBazCallback.h",
         "android/hardware/tests/baz/1.0/BpHwBazCallback.h",
         "android/hardware/tests/baz/1.0/BsBazCallback.h",
+        "android/hardware/tests/baz/1.0/IQuux.h",
+        "android/hardware/tests/baz/1.0/IHwQuux.h",
+        "android/hardware/tests/baz/1.0/BnHwQuux.h",
+        "android/hardware/tests/baz/1.0/BpHwQuux.h",
+        "android/hardware/tests/baz/1.0/BsQuux.h",
     ],
 }
 
diff --git a/tests/baz/1.0/Android.mk b/tests/baz/1.0/Android.mk
index 40026ec..9d4d6b6 100644
--- a/tests/baz/1.0/Android.mk
+++ b/tests/baz/1.0/Android.mk
@@ -76,6 +76,25 @@
 $(GEN): $(LOCAL_PATH)/IBazCallback.hal
 	$(transform-generated-source)
 LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IQuux.hal
+#
+GEN := $(intermediates)/android/hardware/tests/baz/V1_0/IQuux.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IQuux.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.tests.baz@1.0::IQuux
+
+$(GEN): $(LOCAL_PATH)/IQuux.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
 include $(BUILD_JAVA_LIBRARY)
 
 
@@ -153,6 +172,25 @@
 $(GEN): $(LOCAL_PATH)/IBazCallback.hal
 	$(transform-generated-source)
 LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IQuux.hal
+#
+GEN := $(intermediates)/android/hardware/tests/baz/V1_0/IQuux.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IQuux.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.tests.baz@1.0::IQuux
+
+$(GEN): $(LOCAL_PATH)/IQuux.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 
diff --git a/tests/baz/1.0/IQuux.hal b/tests/baz/1.0/IQuux.hal
new file mode 100644
index 0000000..ccf3212
--- /dev/null
+++ b/tests/baz/1.0/IQuux.hal
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tests.baz@1.0;
+
+interface IQuux {
+};
diff --git a/weaver/1.0/Android.bp b/weaver/1.0/Android.bp
new file mode 100644
index 0000000..738da4f
--- /dev/null
+++ b/weaver/1.0/Android.bp
@@ -0,0 +1,63 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+filegroup {
+    name: "android.hardware.weaver@1.0_hal",
+    srcs: [
+        "types.hal",
+        "IWeaver.hal",
+    ],
+}
+
+genrule {
+    name: "android.hardware.weaver@1.0_genc++",
+    tools: ["hidl-gen"],
+    cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.weaver@1.0",
+    srcs: [
+        ":android.hardware.weaver@1.0_hal",
+    ],
+    out: [
+        "android/hardware/weaver/1.0/types.cpp",
+        "android/hardware/weaver/1.0/WeaverAll.cpp",
+    ],
+}
+
+genrule {
+    name: "android.hardware.weaver@1.0_genc++_headers",
+    tools: ["hidl-gen"],
+    cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.weaver@1.0",
+    srcs: [
+        ":android.hardware.weaver@1.0_hal",
+    ],
+    out: [
+        "android/hardware/weaver/1.0/types.h",
+        "android/hardware/weaver/1.0/hwtypes.h",
+        "android/hardware/weaver/1.0/IWeaver.h",
+        "android/hardware/weaver/1.0/IHwWeaver.h",
+        "android/hardware/weaver/1.0/BnHwWeaver.h",
+        "android/hardware/weaver/1.0/BpHwWeaver.h",
+        "android/hardware/weaver/1.0/BsWeaver.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.weaver@1.0",
+    generated_sources: ["android.hardware.weaver@1.0_genc++"],
+    generated_headers: ["android.hardware.weaver@1.0_genc++_headers"],
+    export_generated_headers: ["android.hardware.weaver@1.0_genc++_headers"],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "libcutils",
+        "android.hidl.base@1.0",
+    ],
+    export_shared_lib_headers: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+        "android.hidl.base@1.0",
+    ],
+}
diff --git a/weaver/1.0/Android.mk b/weaver/1.0/Android.mk
new file mode 100644
index 0000000..f8b08ac
--- /dev/null
+++ b/weaver/1.0/Android.mk
@@ -0,0 +1,232 @@
+# This file is autogenerated by hidl-gen. Do not edit manually.
+
+LOCAL_PATH := $(call my-dir)
+
+################################################################################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.weaver@1.0-java
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+intermediates := $(call local-generated-sources-dir, COMMON)
+
+HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
+
+LOCAL_JAVA_LIBRARIES := \
+    android.hidl.base@1.0-java \
+
+
+#
+# Build types.hal (WeaverConfig)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverConfig.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverConfig
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (WeaverReadResponse)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverReadResponse.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverReadResponse
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (WeaverReadStatus)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverReadStatus.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverReadStatus
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (WeaverStatus)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverStatus.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverStatus
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IWeaver.hal
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/IWeaver.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IWeaver.hal
+$(GEN): PRIVATE_DEPS += $(LOCAL_PATH)/types.hal
+$(GEN): $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::IWeaver
+
+$(GEN): $(LOCAL_PATH)/IWeaver.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+include $(BUILD_JAVA_LIBRARY)
+
+
+################################################################################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.weaver@1.0-java-static
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+intermediates := $(call local-generated-sources-dir, COMMON)
+
+HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android.hidl.base@1.0-java-static \
+
+
+#
+# Build types.hal (WeaverConfig)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverConfig.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverConfig
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (WeaverReadResponse)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverReadResponse.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverReadResponse
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (WeaverReadStatus)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverReadStatus.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverReadStatus
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (WeaverStatus)
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/WeaverStatus.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::types.WeaverStatus
+
+$(GEN): $(LOCAL_PATH)/types.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IWeaver.hal
+#
+GEN := $(intermediates)/android/hardware/weaver/V1_0/IWeaver.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IWeaver.hal
+$(GEN): PRIVATE_DEPS += $(LOCAL_PATH)/types.hal
+$(GEN): $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+        $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+        -Ljava \
+        -randroid.hardware:hardware/interfaces \
+        -randroid.hidl:system/libhidl/transport \
+        android.hardware.weaver@1.0::IWeaver
+
+$(GEN): $(LOCAL_PATH)/IWeaver.hal
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/weaver/1.0/IWeaver.hal b/weaver/1.0/IWeaver.hal
new file mode 100644
index 0000000..2362c29
--- /dev/null
+++ b/weaver/1.0/IWeaver.hal
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.weaver@1.0;
+
+/**
+ * Weaver provides secure storage of secret values that can only be read if the
+ * corresponding key has been presented.
+ *
+ * The storage must be secure as the device's user authentication and encryption
+ * relies on the security of these values. The cardinality of the domains of the
+ * key and value must be suitably large such that they cannot be easily guessed.
+ *
+ * Weaver is structured as an array of slots, each containing a key-value pair.
+ * Slots are uniquely identified by an ID in the range [0, `getConfig().slots`).
+ */
+interface IWeaver {
+    /**
+     * Retrieves the config information for this implementation of Weaver.
+     *
+     * The config is static i.e. every invocation returns the same information.
+     *
+     * @return status is OK if the config was successfuly obtained.
+     * @return config data for this implementation of Weaver if status is OK,
+     *         otherwise undefined.
+     */
+    getConfig() generates (WeaverStatus status, WeaverConfig config);
+
+    /**
+     * Overwrites the identified slot with the provided key and value.
+     *
+     * The new values are written regardless of the current state of the slot in
+     * order to remain idempotent.
+     *
+     * @param slotId of the slot to write to.
+     * @param key to write to the slot.
+     * @param value to write to slot.
+     * @return status is OK if the write was successfully completed.
+     */
+    write(uint32_t slotId, vec<uint8_t> key, vec<uint8_t> value)
+                generates (WeaverStatus status);
+
+    /**
+     * Attempts to retrieve the value stored in the identified slot.
+     *
+     * The value is only returned if the provided key matches the key stored in
+     * the slot. The value is never returned if the wrong key is provided.
+     *
+     * Throttling is used to limit the frequency of failed read attempts. The
+     * value is only returned when throttling is not active, even if the correct
+     * key is provided. If called when throttling is active, the time until the
+     * next attempt can be made is returned.
+     *
+     * @param slotId of the slot to read from.
+     * @param key that is stored in the slot.
+     * @return status is OK if the value was successfully read, INCORRECT_KEY if
+     *         the key does not match the key in the slot or THROTTLE if
+     *         throttling is active.
+     * @return readResponse contains the value read and the timeout to wait
+     *         before making the next request. The value is undefined if the
+     *         status is not OK and the timeout is undefined if the status is
+     *         FAILED.
+     */
+    read(uint32_t slotId, vec<uint8_t> key)
+                generates (WeaverReadStatus status,
+                           WeaverReadResponse readResponse);
+};
diff --git a/weaver/1.0/types.hal b/weaver/1.0/types.hal
new file mode 100644
index 0000000..49e5c04
--- /dev/null
+++ b/weaver/1.0/types.hal
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.weaver@1.0;
+
+enum WeaverStatus : uint32_t {
+    OK,
+    FAILED,
+};
+
+struct WeaverConfig {
+    /** The number of slots available. */
+    uint32_t slots;
+    /** The number of bytes used for a key. */
+    uint32_t keySize;
+    /** The number of bytes used for a value. */
+    uint32_t valueSize;
+};
+
+enum WeaverReadStatus : WeaverStatus {
+    INCORRECT_KEY,
+    THROTTLE,
+};
+
+struct WeaverReadResponse {
+    /** The time to wait, in milliseconds, before making the next request. */
+    uint32_t timeout;
+    /** The value read from the slot or empty if the value was not read. */
+    vec<uint8_t> value;
+};
diff --git a/weaver/Android.bp b/weaver/Android.bp
new file mode 100644
index 0000000..bbb3e4b
--- /dev/null
+++ b/weaver/Android.bp
@@ -0,0 +1,4 @@
+// This is an autogenerated file, do not edit.
+subdirs = [
+    "1.0",
+]