Merge "Add VTS tests for EVS HAL" into oc-dev
diff --git a/audio/2.0/default/Device.cpp b/audio/2.0/default/Device.cpp
index 8a51cd7..5ced0bc 100644
--- a/audio/2.0/default/Device.cpp
+++ b/audio/2.0/default/Device.cpp
@@ -59,6 +59,14 @@
     }
 }
 
+void Device::closeInputStream(audio_stream_in_t* stream) {
+    mDevice->close_input_stream(mDevice, stream);
+}
+
+void Device::closeOutputStream(audio_stream_out_t* stream) {
+    mDevice->close_output_stream(mDevice, stream);
+}
+
 char* Device::halGetParameters(const char* keys) {
     return mDevice->get_parameters(mDevice, keys);
 }
@@ -160,7 +168,7 @@
     ALOGV("open_output_stream status %d stream %p", status, halStream);
     sp<IStreamOut> streamOut;
     if (status == OK) {
-        streamOut = new StreamOut(mDevice, halStream);
+        streamOut = new StreamOut(this, halStream);
     }
     AudioConfig suggestedConfig;
     HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
@@ -196,7 +204,7 @@
     ALOGV("open_input_stream status %d stream %p", status, halStream);
     sp<IStreamIn> streamIn;
     if (status == OK) {
-        streamIn = new StreamIn(mDevice, halStream);
+        streamIn = new StreamIn(this, halStream);
     }
     AudioConfig suggestedConfig;
     HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
diff --git a/audio/2.0/default/Device.h b/audio/2.0/default/Device.h
index 46177fc..7738361 100644
--- a/audio/2.0/default/Device.h
+++ b/audio/2.0/default/Device.h
@@ -98,6 +98,8 @@
 
     // Utility methods for extending interfaces.
     Result analyzeStatus(const char* funcName, int status);
+    void closeInputStream(audio_stream_in_t* stream);
+    void closeOutputStream(audio_stream_out_t* stream);
     audio_hw_device_t* device() const { return mDevice; }
 
   private:
diff --git a/audio/2.0/default/StreamIn.cpp b/audio/2.0/default/StreamIn.cpp
index b641e82..2745607 100644
--- a/audio/2.0/default/StreamIn.cpp
+++ b/audio/2.0/default/StreamIn.cpp
@@ -135,7 +135,7 @@
 
 }  // namespace
 
-StreamIn::StreamIn(audio_hw_device_t* device, audio_stream_in_t* stream)
+StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
         : mIsClosed(false), mDevice(device), mStream(stream),
           mStreamCommon(new Stream(&stream->common)),
           mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
@@ -154,9 +154,8 @@
         status_t status = EventFlag::deleteEventFlag(&mEfGroup);
         ALOGE_IF(status, "read MQ event flag deletion error: %s", strerror(-status));
     }
-    mDevice->close_input_stream(mDevice, mStream);
+    mDevice->closeInputStream(mStream);
     mStream = nullptr;
-    mDevice = nullptr;
 }
 
 // Methods from ::android::hardware::audio::V2_0::IStream follow.
diff --git a/audio/2.0/default/StreamIn.h b/audio/2.0/default/StreamIn.h
index b867387..950d68f 100644
--- a/audio/2.0/default/StreamIn.h
+++ b/audio/2.0/default/StreamIn.h
@@ -27,6 +27,7 @@
 #include <hidl/Status.h>
 #include <utils/Thread.h>
 
+#include "Device.h"
 #include "Stream.h"
 
 namespace android {
@@ -55,7 +56,7 @@
     typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
     typedef MessageQueue<ReadStatus, kSynchronizedReadWrite> StatusMQ;
 
-    StreamIn(audio_hw_device_t* device, audio_stream_in_t* stream);
+    StreamIn(const sp<Device>& device, audio_stream_in_t* stream);
 
     // Methods from ::android::hardware::audio::V2_0::IStream follow.
     Return<uint64_t> getFrameSize()  override;
@@ -101,10 +102,10 @@
 
   private:
     bool mIsClosed;
-    audio_hw_device_t *mDevice;
+    const sp<Device> mDevice;
     audio_stream_in_t *mStream;
-    sp<Stream> mStreamCommon;
-    sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
+    const sp<Stream> mStreamCommon;
+    const sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
     std::unique_ptr<CommandMQ> mCommandMQ;
     std::unique_ptr<DataMQ> mDataMQ;
     std::unique_ptr<StatusMQ> mStatusMQ;
diff --git a/audio/2.0/default/StreamOut.cpp b/audio/2.0/default/StreamOut.cpp
index d820f3c..88045a0 100644
--- a/audio/2.0/default/StreamOut.cpp
+++ b/audio/2.0/default/StreamOut.cpp
@@ -135,7 +135,7 @@
 
 }  // namespace
 
-StreamOut::StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream)
+StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream)
         : mIsClosed(false), mDevice(device), mStream(stream),
           mStreamCommon(new Stream(&stream->common)),
           mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
@@ -155,9 +155,8 @@
         ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
     }
     mCallback.clear();
-    mDevice->close_output_stream(mDevice, mStream);
+    mDevice->closeOutputStream(mStream);
     mStream = nullptr;
-    mDevice = nullptr;
 }
 
 // Methods from ::android::hardware::audio::V2_0::IStream follow.
diff --git a/audio/2.0/default/StreamOut.h b/audio/2.0/default/StreamOut.h
index bbe64a1..99352bc 100644
--- a/audio/2.0/default/StreamOut.h
+++ b/audio/2.0/default/StreamOut.h
@@ -27,6 +27,7 @@
 #include <fmq/MessageQueue.h>
 #include <utils/Thread.h>
 
+#include "Device.h"
 #include "Stream.h"
 
 namespace android {
@@ -57,7 +58,7 @@
     typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
     typedef MessageQueue<WriteStatus, kSynchronizedReadWrite> StatusMQ;
 
-    StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream);
+    StreamOut(const sp<Device>& device, audio_stream_out_t* stream);
 
     // Methods from ::android::hardware::audio::V2_0::IStream follow.
     Return<uint64_t> getFrameSize()  override;
@@ -112,10 +113,10 @@
 
   private:
     bool mIsClosed;
-    audio_hw_device_t *mDevice;
+    const sp<Device> mDevice;
     audio_stream_out_t *mStream;
-    sp<Stream> mStreamCommon;
-    sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
+    const sp<Stream> mStreamCommon;
+    const sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
     sp<IStreamOutCallback> mCallback;
     std::unique_ptr<CommandMQ> mCommandMQ;
     std::unique_ptr<DataMQ> mDataMQ;
diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp
index 6f51e0e..c7428a5 100644
--- a/drm/1.0/default/DrmPlugin.cpp
+++ b/drm/1.0/default/DrmPlugin.cpp
@@ -77,7 +77,7 @@
             android::DrmPlugin::KeyRequestType legacyRequestType =
                     android::DrmPlugin::kKeyRequestType_Unknown;
 
-            status_t status = mLegacyPlugin->getKeyRequest(toVector(scope),
+            status = mLegacyPlugin->getKeyRequest(toVector(scope),
                     toVector(initData), String8(mimeType), legacyKeyType,
                     legacyOptionalParameters, legacyRequest, defaultUrl,
                     &legacyRequestType);
@@ -93,7 +93,7 @@
                 requestType = KeyRequestType::RELEASE;
                 break;
             case android::DrmPlugin::kKeyRequestType_Unknown:
-                status = android::BAD_VALUE;
+                requestType = KeyRequestType::UNKNOWN;
                 break;
             }
         }
diff --git a/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf b/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
new file mode 100644
index 0000000..1b44e4f
--- /dev/null
+++ b/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
Binary files differ
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..546aa12
--- /dev/null
+++ b/drm/1.0/vts/functional/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "drm_hidl_test",
+    srcs: [
+        "drm_hal_clearkey_test.cpp",
+        "drm_hal_vendor_test.cpp",
+        "shared_library.cpp",
+        "vendor_modules.cpp"
+        ],
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidlmemory",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libnativehelper",
+        "libutils",
+    ],
+    static_libs: [
+        "VtsHalHidlTargetTestBase"
+    ],
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+}
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
new file mode 100644
index 0000000..2296d2d
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -0,0 +1,904 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "drm_hal_clearkey_test@1.0"
+
+#include <android-base/logging.h>
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#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/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidlmemory/mapping.h>
+#include <memory>
+#include <random>
+
+#include "VtsHalHidlTargetTestBase.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+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::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::KeyRequestType;
+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::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using std::string;
+using std::unique_ptr;
+using std::random_device;
+using std::map;
+using std::mt19937;
+using std::vector;
+
+/**
+ * These clearkey tests use white box knowledge of the legacy clearkey
+ * plugin to verify that the HIDL HAL services and interfaces are working.
+ * It is not intended to verify any vendor's HAL implementation. If you
+ * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
+ */
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+static const uint8_t kClearKeyUUID[16] = {
+    0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
+    0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
+
+static const uint8_t kInvalidUUID[16] = {
+    0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+    0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+
+class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase {
+   public:
+    virtual void SetUp() override {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("Running test %s.%s", test_info->test_case_name(),
+              test_info->name());
+
+        drmFactory =
+                ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(
+                        "drm");
+        ASSERT_NE(drmFactory, nullptr);
+        cryptoFactory =
+                ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(
+                        "crypto");
+        ASSERT_NE(cryptoFactory, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+   protected:
+    sp<IDrmFactory> drmFactory;
+    sp<ICryptoFactory> cryptoFactory;
+};
+
+/**
+ * Ensure the factory supports the clearkey scheme UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, ClearKeyPluginSupported) {
+    EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(kClearKeyUUID));
+    EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID));
+}
+
+/**
+ * Ensure the factory doesn't support an invalid scheme UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, InvalidPluginNotSupported) {
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
+    EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
+}
+
+/**
+ * Ensure clearkey drm plugin can be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPlugin) {
+    hidl_string packageName("android.hardware.drm.test");
+    auto res = drmFactory->createPlugin(
+            kClearKeyUUID, packageName,
+            [&](Status status, const sp<IDrmPlugin>& plugin) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Ensure clearkey crypto plugin can be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPlugin) {
+    hidl_vec<uint8_t> initVec;
+    auto res = cryptoFactory->createPlugin(
+            kClearKeyUUID, initVec,
+            [&](Status status, const sp<ICryptoPlugin>& plugin) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid drm plugin can't be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidDrmPlugin) {
+    hidl_string packageName("android.hardware.drm.test");
+    auto res = drmFactory->createPlugin(
+            kInvalidUUID, packageName,
+            [&](Status status, const sp<IDrmPlugin>& plugin) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                EXPECT_EQ(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid crypto plugin can't be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidCryptoPlugin) {
+    hidl_vec<uint8_t> initVec;
+    auto res = cryptoFactory->createPlugin(
+            kInvalidUUID, initVec,
+            [&](Status status, const sp<ICryptoPlugin>& plugin) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                EXPECT_EQ(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest {
+   public:
+    virtual void SetUp() override {
+        // Create factories
+        DrmHalClearkeyFactoryTest::SetUp();
+
+        ASSERT_NE(drmFactory, nullptr);
+        hidl_string packageName("android.hardware.drm.test");
+        auto res = drmFactory->createPlugin(
+                kClearKeyUUID, packageName,
+                [this](Status status, const sp<IDrmPlugin>& plugin) {
+                    EXPECT_EQ(Status::OK, status);
+                    ASSERT_NE(plugin, nullptr);
+                    drmPlugin = plugin;
+                });
+        ASSERT_OK(res);
+
+        hidl_vec<uint8_t> initVec;
+        res = cryptoFactory->createPlugin(
+                kClearKeyUUID, initVec,
+                [this](Status status, const sp<ICryptoPlugin>& plugin) {
+                    EXPECT_EQ(Status::OK, status);
+                    ASSERT_NE(plugin, nullptr);
+                    cryptoPlugin = plugin;
+                });
+        ASSERT_OK(res);
+    }
+
+    virtual void TearDown() override {}
+
+    SessionId openSession();
+    void closeSession(const SessionId& sessionId);
+    sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+   protected:
+    sp<IDrmPlugin> drmPlugin;
+    sp<ICryptoPlugin> cryptoPlugin;
+};
+
+/**
+ *  DrmPlugin tests
+ */
+
+/**
+ * Test that the plugin can return a provision request.  Since
+ * the clearkey plugin doesn't support provisioning, it is
+ * expected to return Status::ERROR_DRM_CANNOT_HANDLE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetProvisionRequest) {
+    hidl_string certificateType;
+    hidl_string certificateAuthority;
+    auto res = drmPlugin->getProvisionRequest(
+            certificateType, certificateAuthority,
+            [&](Status status, const hidl_vec<uint8_t>&, const hidl_string&) {
+                // clearkey doesn't require provisioning
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * The DRM HAL should return BAD_VALUE if an empty provisioning
+ * response is provided.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideEmptyProvisionResponse) {
+    hidl_vec<uint8_t> response;
+    auto res = drmPlugin->provideProvisionResponse(
+            response, [&](Status status, const hidl_vec<uint8_t>&,
+                          const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalClearkeyPluginTest::openSession() {
+    SessionId sessionId;
+
+    auto res = drmPlugin->openSession(
+            [&sessionId](Status status, const SessionId& id) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(0u, id.size());
+                sessionId = id;
+            });
+    EXPECT_OK(res);
+    return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalClearkeyPluginTest::closeSession(const SessionId& sessionId) {
+    auto result = drmPlugin->closeSession(sessionId);
+    EXPECT_EQ(Status::OK, result);
+}
+
+/**
+ * Test that a session can be opened and closed
+ */
+TEST_F(DrmHalClearkeyPluginTest, OpenCloseSession) {
+    auto sessionId = openSession();
+    closeSession(sessionId);
+}
+
+/**
+ * Test that attempting to close an invalid (empty) sessionId
+ * is prohibited with the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, CloseInvalidSession) {
+    SessionId invalidSessionId;
+    Status result = drmPlugin->closeSession(invalidSessionId);
+    EXPECT_EQ(Status::BAD_VALUE, result);
+}
+
+/**
+ * Test that attempting to close a session that is already closed
+ * is prohibited with the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, CloseClosedSession) {
+    SessionId sessionId = openSession();
+    closeSession(sessionId);
+    Status result = drmPlugin->closeSession(sessionId);
+    EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, result);
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestNoSession) {
+    SessionId invalidSessionId;
+    hidl_vec<uint8_t> initData;
+    hidl_string mimeType = "video/mp4";
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest(
+            invalidSessionId, initData, mimeType, KeyType::STREAMING,
+            optionalParameters,
+            [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+                const hidl_string&) { EXPECT_EQ(Status::BAD_VALUE, status); });
+    EXPECT_OK(res);
+}
+
+/**
+ * The clearkey plugin doesn't support offline key requests.
+ * Test that the plugin returns the expected error code in
+ * this case.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestOfflineKeyTypeNotSupported) {
+    auto sessionId = openSession();
+    hidl_vec<uint8_t> initData;
+    hidl_string mimeType = "video/mp4";
+    KeyedVector optionalParameters;
+
+    auto res = drmPlugin->getKeyRequest(
+            sessionId, initData, mimeType, KeyType::OFFLINE, optionalParameters,
+            [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+                const hidl_string&) {
+                // Clearkey plugin doesn't support offline key type
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+    closeSession(sessionId);
+}
+
+/**
+ * Test that the plugin returns the documented error for the
+ * case of attempting to generate a key request using an
+ * invalid mime type
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestBadMime) {
+    auto sessionId = openSession();
+    hidl_vec<uint8_t> initData;
+    hidl_string mimeType = "video/unknown";
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest(
+            sessionId, initData, mimeType, KeyType::STREAMING,
+            optionalParameters, [&](Status status, const hidl_vec<uint8_t>&,
+                                    KeyRequestType, const hidl_string&) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+    closeSession(sessionId);
+}
+
+/**
+ * Test that a closed sessionID returns SESSION_NOT_OPENED
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseClosedSession) {
+    SessionId session = openSession();
+    closeSession(session);
+
+    hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+                                     0x79, 0x73, 0x22, 0x3a};
+    auto res = drmPlugin->provideKeyResponse(
+            session, keyResponse,
+            [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+                EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+                EXPECT_EQ(0u, keySetId.size());
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty sessionID returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseInvalidSessionId) {
+    SessionId session;
+
+    hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+                                     0x79, 0x73, 0x22, 0x3a};
+    auto res = drmPlugin->provideKeyResponse(
+            session, keyResponse,
+            [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+                EXPECT_EQ(0u, keySetId.size());
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty key response returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseEmptyResponse) {
+    SessionId session = openSession();
+    hidl_vec<uint8_t> emptyResponse;
+    auto res = drmPlugin->provideKeyResponse(
+            session, emptyResponse,
+            [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+                EXPECT_EQ(0u, keySetId.size());
+            });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+/**
+ * Test that the clearkey plugin doesn't support getting
+ * secure stops.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetSecureStops) {
+    auto res = drmPlugin->getSecureStops(
+            [&](Status status, const hidl_vec<SecureStop>&) {
+                // Clearkey plugin doesn't support secure stops
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that the clearkey plugin returns BAD_VALUE if
+ * an empty ssid is provided.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetSecureStopEmptySSID) {
+    SecureStopId ssid;
+    auto res = drmPlugin->getSecureStop(
+            ssid, [&](Status status, const SecureStop&) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that releasing all secure stops isn't handled by
+ * clearkey.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ReleaseAllSecureStops) {
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+              drmPlugin->releaseAllSecureStops());
+}
+
+/**
+ * Test that releasing a specific secure stop with an empty
+ * SSID returns BAD_VALUE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ReleaseSecureStopEmptySSID) {
+    SecureStopId ssid;
+    Status status = drmPlugin->releaseSecureStop(ssid);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * The following four tests verify that the properties
+ * defined in the MediaDrm API are supported by
+ * the plugin.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetVendorProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "vendor", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_EQ("Google", value);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetVersionProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "version", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_EQ("1.0", value);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetDescriptionProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "description", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_EQ("ClearKey CDM", value);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetAlgorithmsProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "algorithms", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_EQ("", value);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that attempting to read invalid string and byte array
+ * properties returns the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetInvalidStringProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "invalid", [&](Status status, const hidl_string&) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetByteArrayPropertyNotSupported) {
+    auto res = drmPlugin->getPropertyByteArray(
+            "deviceUniqueId", [&](Status status, const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Clearkey doesn't support setting string or byte array properties,
+ * particularly an undefined one.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetStringPropertyNotSupported) {
+    Status status = drmPlugin->setPropertyString("property", "value");
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, SetByteArrayPropertyNotSupported) {
+    hidl_vec<uint8_t> value;
+    Status status = drmPlugin->setPropertyByteArray("property", value);
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+}
+
+/**
+ * Clearkey doesn't support setting cipher algorithms, verify it
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNotSupported) {
+    SessionId session = openSession();
+    hidl_string algorithm = "AES/CBC/NoPadding";
+    Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+    closeSession(session);
+}
+
+/**
+ * Setting an empty algorithm should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherEmptyAlgorithm) {
+    SessionId session = openSession();
+    hidl_string algorithm;
+    Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+    closeSession(session);
+}
+
+/**
+ * Setting a cipher algorithm with no session returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNoSession) {
+    SessionId session;
+    hidl_string algorithm = "AES/CBC/NoPadding";
+    Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Clearkey doesn't support setting mac algorithms, verify it
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNotSupported) {
+    SessionId session = openSession();
+    hidl_string algorithm = "HmacSHA256";
+    Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+    closeSession(session);
+}
+
+/**
+ * Setting an empty algorithm should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacEmptyAlgorithm) {
+    SessionId session = openSession();
+    hidl_string algorithm;
+    Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+    closeSession(session);
+}
+
+/**
+ * Setting a mac algorithm with no session should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNoSession) {
+    SessionId session;
+    hidl_string algorithm = "HmacSHA256";
+    Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * The Generic* methods provide general purpose crypto operations
+ * that may be used for applications other than DRM. They leverage
+ * the hardware root of trust and secure key distribution mechanisms
+ * of a DRM system to enable app-specific crypto functionality where
+ * the crypto keys are not exposed outside of the trusted execution
+ * environment.
+ *
+ * Clearkey doesn't support generic encrypt/decrypt/sign/verify.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GenericEncryptNotSupported) {
+    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};
+    auto res = drmPlugin->encrypt(session, keyId, input, iv,
+                                  [&](Status status, const hidl_vec<uint8_t>&) {
+                                      EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+                                                status);
+                                  });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+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};
+    auto res = drmPlugin->decrypt(session, keyId, input, iv,
+                                  [&](Status status, const hidl_vec<uint8_t>&) {
+                                      EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+                                                status);
+                                  });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericSignNotSupported) {
+    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> message = {1, 2, 3, 4, 5};
+    auto res = drmPlugin->sign(session, keyId, message,
+                               [&](Status status, const hidl_vec<uint8_t>&) {
+                                   EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+                                             status);
+                               });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericVerifyNotSupported) {
+    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> message = {1, 2, 3, 4, 5};
+    hidl_vec<uint8_t> signature = {0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0};
+    auto res = drmPlugin->verify(
+            session, keyId, message, signature, [&](Status status, bool) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericSignRSANotSupported) {
+    SessionId session = openSession();
+    hidl_string algorithm = "RSASSA-PSS-SHA1";
+    hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+    hidl_vec<uint8_t> wrappedKey = {0, 0, 0, 0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, 0, 0, 0};
+    auto res = drmPlugin->signRSA(session, algorithm, message, wrappedKey,
+                                  [&](Status status, const hidl_vec<uint8_t>&) {
+                                      EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+                                                status);
+                                  });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+/**
+ *  CryptoPlugin tests
+ */
+
+/**
+ * Clearkey doesn't support secure decoder and is expected to
+ * return false.
+ */
+TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoder) {
+    EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("cenc"));
+}
+
+/**
+ * Verify that requiresSecureDecoderComponent handles empty mimetype
+ */
+TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoderEmptyMimeType) {
+    EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
+}
+
+/**
+ * Exercise the NotifyResolution API. There is no observable result,
+ * just call the method for coverage.
+ */
+TEST_F(DrmHalClearkeyPluginTest, NotifyResolution) {
+    cryptoPlugin->notifyResolution(1920, 1080);
+}
+
+/**
+ * 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> DrmHalClearkeyPluginTest::getDecryptMemory(size_t size,
+                                                       size_t index) {
+    sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+    EXPECT_NE(ashmemAllocator, nullptr);
+
+    hidl_memory hidlMemory;
+    auto res = ashmemAllocator->allocate(
+            size, [&](bool success, const hidl_memory& memory) {
+                EXPECT_EQ(true, success);
+                EXPECT_OK(cryptoPlugin->setSharedBufferBase(memory, index));
+                hidlMemory = memory;
+            });
+    EXPECT_OK(res);
+
+    sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+    EXPECT_OK(cryptoPlugin->setSharedBufferBase(hidlMemory, index));
+    return mappedMemory;
+}
+
+/**
+ * Exercise the setMediaDrmSession method. setMediaDrmSession
+ * is used to associate a drm session with a crypto session.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSession) {
+    auto sessionId = openSession();
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::OK, status);
+    closeSession(sessionId);
+}
+
+/**
+ * setMediaDrmSession with a closed session id
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionClosedSession) {
+    auto sessionId = openSession();
+    closeSession(sessionId);
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * Decrypt tests
+ */
+
+class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
+   public:
+    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);
+        return hidl_array<uint8_t, 16>(&vec[0]);
+    }
+};
+
+/**
+ * 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.
+ */
+void DrmHalClearkeyDecryptTest::loadKeys(const SessionId& sessionId) {
+    hidl_vec<uint8_t> 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};
+
+    hidl_vec<uint8_t> expectedKeyRequest = {
+            0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59,
+            0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2b,
+            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};
+
+    hidl_vec<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, 0x2b, 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, 0x0a};
+
+    hidl_string mimeType = "video/mp4";
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest(
+            sessionId, initData, mimeType, KeyType::STREAMING,
+            optionalParameters,
+            [&](Status status, const hidl_vec<uint8_t>& request,
+                KeyRequestType requestType, const hidl_string&) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_EQ(KeyRequestType::INITIAL, requestType);
+                EXPECT_EQ(request, expectedKeyRequest);
+            });
+    EXPECT_OK(res);
+
+    res = drmPlugin->provideKeyResponse(
+            sessionId, knownKeyResponse,
+            [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_EQ(0u, keySetId.size());
+            });
+    EXPECT_OK(res);
+}
+
+void DrmHalClearkeyDecryptTest::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();
+    }
+}
+
+/**
+ * Positive decrypt test.  "Decrypt" a single clear
+ * segment.  Verify data matches.
+ */
+TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
+    const size_t kSegmentSize = 1024;
+    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};
+
+    sp<IMemory> sharedMemory =
+            getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+
+    SharedBuffer sourceBuffer = {
+            .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+    fillRandom(sharedMemory);
+
+    DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+                                    {.bufferId = kSegmentIndex,
+                                     .offset = kSegmentSize,
+                                     .size = kSegmentSize},
+                                    .secureMemory = nullptr};
+
+    Pattern noPattern = {0, 0};
+    vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+                                     .numBytesOfEncryptedData = 0}};
+    uint64_t offset = 0;
+
+    auto sessionId = openSession();
+    loadKeys(sessionId);
+
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    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);
+
+    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";
+    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
new file mode 100644
index 0000000..db19719
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+#ifndef DRM_HAL_VENDOR_MODULE_API_H
+#define DRM_HAL_VENDOR_MODULE_API_H
+
+#include <stdint.h>
+#include <map>
+#include <string>
+#include <vector>
+
+/**
+ * The DRM and Crypto HALs interact with vendor-provided HAL implementations
+ * that have DRM-specific capabilities. Since the VTS tests cannot contain
+ * DRM-specific functionality, supporting modules are required to enable VTS
+ * to validate HAL implementations in a generic way.  If the vendor-specific
+ * VTS module is not provided for a given drm HAL implementation, only very
+ * small subset of functionality can be verified.
+ *
+ * As an example, a DRM HAL implementation interacts with a DRM-specific
+ * license server to obtain licenses for decrypting content.  The DRM HAL
+ * implementation generates a key request message, delivers it to the server
+ * and receives a key response message which is then loaded into the HAL. Once
+ * the keys are loaded, the Crypto HAL decryption functionality and performance
+ * and other associated APIs can be tested by the common VTS test suite.
+ *
+ * Vendor-specific VTS modules are shared libraries used by the DRM VTS test.
+ * They provide a set of functions to support VTS testing of the DRM HAL module.
+ *
+ * The modules are placed in a common location on the file system. The VTS test
+ * scans through all vendor-provided support libraries and runs the VTS test
+ * suite on each library that is found.
+ *
+ * The vendor-specific module exposes an extern “C” vendorModuleFactory()
+ * function that returns a DrmHalVTSVendorModule instance. DrmHalVTSVendorModule
+ * instances are versioned, where each version is represented by subclass of
+ * DrmHalVTSVendorModule that corresponds to the API version. For example, a
+ * vendor-specific module that implements version 1 of the API would return a
+ * DrmHalVTSVendorModule_V1 from the vendorModuleFactory() function.
+ */
+
+class DrmHalVTSVendorModule;
+
+extern "C" {
+/**
+ * The factory method for creating DrmHalVTSVendorModule instances. The returned
+ * instance will be a subclass of DrmHalVTSVendorModule that corresponds to the
+ * supported API version.
+ */
+DrmHalVTSVendorModule* vendorModuleFactory();
+};
+
+class DrmHalVTSVendorModule {
+   public:
+    DrmHalVTSVendorModule() {}
+    virtual ~DrmHalVTSVendorModule() {}
+
+    /**
+     * Return the vendor-specific module API version. The version is an integer
+     * value with initial version 1. The API version indicates which subclass
+     * version DrmHalVTSVendorModule this instance is.
+     */
+    virtual uint32_t getAPIVersion() = 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;
+
+    /**
+     * 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;
+
+   private:
+    DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete;
+    void operator=(const DrmHalVTSVendorModule&) = delete;
+};
+
+/**
+ * API Version 1.  This is the baseline version that supports a minimal set
+ * of VTS tests.
+ */
+class DrmHalVTSVendorModule_V1 : public DrmHalVTSVendorModule {
+   public:
+    DrmHalVTSVendorModule_V1() {}
+    virtual ~DrmHalVTSVendorModule_V1() {}
+
+    virtual uint32_t getAPIVersion() { return 1; }
+
+    /**
+     * Handle a provisioning request. This function will be called if the HAL
+     * module's getProvisionRequest returns a provision request.  The vendor
+     * module should process the provisioning request, either by sending it
+     * to a provisioning server, or generating a mock response.  The resulting
+     * provisioning response is returned to the VTS test.
+     *
+     * @param provisioningRequest the provisioning request recieved from
+     * the DRM HAL
+     * @param url the default url the HAL implementation provided with the
+     * provisioning request
+     * @return the generated provisioning response
+     */
+    virtual std::vector<uint8_t> handleProvisioningRequest(
+            const std::vector<uint8_t>& provisioningRequest,
+            const std::string& url) = 0;
+
+    /**
+     * Content configuration specifies content-specific parameters associated
+     * with a key request/response transaction. It allows the VTS test to
+     * request keys and use them to perform decryption.
+     */
+    struct ContentConfiguration {
+        /**
+         * Assign a name for this configuration that will be referred to
+         * in log messages.
+         */
+        const std::string name;
+
+        /**
+         * Server to use when requesting a key response.  This url will be
+         * passed as a parameter to the vendor vts module along with the
+         * key request to perform the key request transaction.
+         */
+        const std::string serverUrl;
+
+        /**
+         * Initialization data provided to getKeyRequest, e.g. PSSH for CENC
+         * content
+         */
+        const std::vector<uint8_t> initData;
+
+        /**
+         *  Mime type provided to getKeyRequest, e.g. "video/mp4", or "cenc"
+         */
+        const std::string mimeType;
+
+        /**
+         * Optional parameters to be associated with the key request
+         */
+        const std::map<std::string, std::string> optionalParameters;
+
+        /**
+         * The keys that will be available once the keys are loaded
+         */
+        struct Key {
+            /**
+             * Indicate if the key content is configured to require secure
+             * buffers,
+             * where the output buffers are protected and cannot be accessed.
+             * A vendor module should provide some content configurations where
+             * isSecure is false, to allow decrypt result verification tests to
+             * be
+             * run.
+             */
+            bool isSecure;
+
+            /**
+             * A key ID identifies a key to use for decryption
+             */
+            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.
+             */
+            const std::vector<uint8_t> keyValue;
+        };
+        std::vector<Key> keys;
+    };
+
+    /**
+     * Return a list of content configurations that can be exercised by the
+     * VTS test.
+     */
+    virtual std::vector<ContentConfiguration> getContentConfigurations() = 0;
+
+    /**
+     * Handle a key request. This function will be called if the HAL
+     * module's getKeyRequest returns a key request.  The vendor
+     * module should process the key request, either by sending it
+     * to a license server, or by generating a mock response.  The resulting
+     * key response is returned to the VTS test.
+     *
+     * @param keyRequest the key request recieved from the DRM HAL
+     * @param serverUrl the url of the key server that was supplied
+     * by the ContentConfiguration
+     * @return the generated key response
+     */
+    virtual std::vector<uint8_t> handleKeyRequest(
+            const std::vector<uint8_t>& keyRequest,
+            const std::string& serverUrl) = 0;
+};
+
+#endif  // DRM_HAL_VENDOR_MODULE_API_H
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
new file mode 100644
index 0000000..dcfee4e
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -0,0 +1,980 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "drm_hal_vendor_test@1.0"
+
+#include <android-base/logging.h>
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#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/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidlmemory/mapping.h>
+#include <memory>
+#include <random>
+
+#include "VtsHalHidlTargetTestBase.h"
+#include "drm_hal_vendor_module_api.h"
+#include "vendor_modules.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+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::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::KeyRequestType;
+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::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_0::SubSample;
+
+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::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using std::string;
+using std::unique_ptr;
+using std::random_device;
+using std::map;
+using std::mt19937;
+using std::vector;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+static const uint8_t kInvalidUUID[16] = {
+        0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+        0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+};
+
+static drm_vts::VendorModules* gVendorModules = nullptr;
+
+class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
+   public:
+    DrmHalVendorFactoryTest()
+        : vendorModule(gVendorModules ? static_cast<DrmHalVTSVendorModule_V1*>(
+                                                gVendorModules->getVendorModule(
+                                                        GetParam()))
+                                      : nullptr) {}
+
+    virtual ~DrmHalVendorFactoryTest() {}
+
+    virtual void 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());
+
+        ASSERT_NE(vendorModule, nullptr);
+        string name = vendorModule->getServiceName();
+        drmFactory =
+                ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(
+                        name != "default" ? name : "drm");
+        ASSERT_NE(drmFactory, nullptr);
+        cryptoFactory =
+                ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(
+                        name != "default" ? name : "crypto");
+        ASSERT_NE(cryptoFactory, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+   protected:
+    hidl_array<uint8_t, 16> getVendorUUID() {
+        vector<uint8_t> uuid = vendorModule->getUUID();
+        return hidl_array<uint8_t, 16>(&uuid[0]);
+    }
+
+    sp<IDrmFactory> drmFactory;
+    sp<ICryptoFactory> cryptoFactory;
+    unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+};
+
+/**
+ * Ensure the factory supports its scheme UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, VendorPluginSupported) {
+    EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
+    EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
+}
+
+/**
+ * Ensure the factory doesn't support an invalid scheme UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, InvalidPluginNotSupported) {
+    EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
+    EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
+}
+
+/**
+ * Ensure vendor drm plugin can be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateVendorDrmPlugin) {
+    hidl_string packageName("android.hardware.drm.test");
+    auto res = drmFactory->createPlugin(
+            getVendorUUID(), packageName,
+            [&](Status status, const sp<IDrmPlugin>& plugin) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Ensure vendor crypto plugin can be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateVendorCryptoPlugin) {
+    hidl_vec<uint8_t> initVec;
+    auto res = cryptoFactory->createPlugin(
+            getVendorUUID(), initVec,
+            [&](Status status, const sp<ICryptoPlugin>& plugin) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid drm plugin can't be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateInvalidDrmPlugin) {
+    hidl_string packageName("android.hardware.drm.test");
+    auto res = drmFactory->createPlugin(
+            kInvalidUUID, packageName,
+            [&](Status status, const sp<IDrmPlugin>& plugin) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                EXPECT_EQ(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid crypto plugin can't be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateInvalidCryptoPlugin) {
+    hidl_vec<uint8_t> initVec;
+    auto res = cryptoFactory->createPlugin(
+            kInvalidUUID, initVec,
+            [&](Status status, const sp<ICryptoPlugin>& plugin) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                EXPECT_EQ(plugin, nullptr);
+            });
+    EXPECT_OK(res);
+}
+
+class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest {
+   public:
+    virtual ~DrmHalVendorPluginTest() {}
+    virtual void SetUp() override {
+        // Create factories
+        DrmHalVendorFactoryTest::SetUp();
+
+        hidl_string packageName("android.hardware.drm.test");
+        auto res = drmFactory->createPlugin(
+                getVendorUUID(), packageName,
+                [this](Status status, const sp<IDrmPlugin>& plugin) {
+                    EXPECT_EQ(Status::OK, status);
+                    ASSERT_NE(plugin, nullptr);
+                    drmPlugin = plugin;
+                });
+        ASSERT_OK(res);
+
+        hidl_vec<uint8_t> initVec;
+        res = cryptoFactory->createPlugin(
+                getVendorUUID(), initVec,
+                [this](Status status, const sp<ICryptoPlugin>& plugin) {
+                    EXPECT_EQ(Status::OK, status);
+                    ASSERT_NE(plugin, nullptr);
+                    cryptoPlugin = plugin;
+                });
+        ASSERT_OK(res);
+    }
+
+    virtual void TearDown() override {}
+
+    SessionId openSession();
+    void closeSession(const SessionId& sessionId);
+    sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+   protected:
+    sp<IDrmPlugin> drmPlugin;
+    sp<ICryptoPlugin> cryptoPlugin;
+};
+
+/**
+ *  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(DrmHalVendorPluginTest, DoProvisioning) {
+    hidl_string certificateType;
+    hidl_string certificateAuthority;
+    hidl_vec<uint8_t> provisionRequest;
+    hidl_string defaultUrl;
+    auto res = drmPlugin->getProvisionRequest(
+            certificateType, certificateAuthority,
+            [&](Status status, const hidl_vec<uint8_t>& request,
+                const hidl_string& url) {
+                if (status == Status::OK) {
+                    EXPECT_NE(request.size(), 0u);
+                    provisionRequest = request;
+                    defaultUrl = url;
+                } else if (status == Status::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);
+    }
+}
+
+/**
+ * The DRM HAL should return BAD_VALUE if an empty provisioning
+ * response is provided.
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideEmptyProvisionResponse) {
+    hidl_vec<uint8_t> response;
+    auto res = drmPlugin->provideProvisionResponse(
+            response, [&](Status status, const hidl_vec<uint8_t>&,
+                          const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalVendorPluginTest::openSession() {
+    SessionId sessionId;
+
+    auto res = drmPlugin->openSession([&](Status status, const SessionId& id) {
+        EXPECT_EQ(Status::OK, status);
+        EXPECT_NE(id.size(), 0u);
+        sessionId = id;
+    });
+    EXPECT_OK(res);
+    return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalVendorPluginTest::closeSession(const SessionId& sessionId) {
+    Status status = drmPlugin->closeSession(sessionId);
+    EXPECT_EQ(Status::OK, status);
+}
+
+/**
+ * Test that a session can be opened and closed
+ */
+TEST_P(DrmHalVendorPluginTest, OpenCloseSession) {
+    auto sessionId = openSession();
+    closeSession(sessionId);
+}
+
+/**
+ * Test that attempting to close an invalid (empty) sessionId
+ * is prohibited with the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, CloseInvalidSession) {
+    SessionId invalidSessionId;
+    Status status = drmPlugin->closeSession(invalidSessionId);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that attempting to close a valid session twice
+ * is prohibited with the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, CloseClosedSession) {
+    auto sessionId = openSession();
+    closeSession(sessionId);
+    Status status = drmPlugin->closeSession(sessionId);
+    EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_P(DrmHalVendorPluginTest, GetKeyRequestNoSession) {
+    SessionId invalidSessionId;
+    hidl_vec<uint8_t> initData;
+    hidl_string mimeType = "video/mp4";
+    KeyedVector optionalParameters;
+    auto res = drmPlugin->getKeyRequest(
+            invalidSessionId, initData, mimeType, KeyType::STREAMING,
+            optionalParameters,
+            [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+                const hidl_string&) { EXPECT_EQ(Status::BAD_VALUE, status); });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty sessionID returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptySessionId) {
+    SessionId session;
+
+    hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+                                     0x79, 0x73, 0x22, 0x3a};
+    auto res = drmPlugin->provideKeyResponse(
+            session, keyResponse,
+            [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+                EXPECT_EQ(keySetId.size(), 0u);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty key response returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptyResponse) {
+    SessionId session = openSession();
+    hidl_vec<uint8_t> emptyResponse;
+    auto res = drmPlugin->provideKeyResponse(
+            session, emptyResponse,
+            [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+                EXPECT_EQ(keySetId.size(), 0u);
+            });
+    EXPECT_OK(res);
+    closeSession(session);
+}
+
+/**
+ * Test that the plugin either doesn't support getting
+ * secure stops, or has no secure stops available after
+ * clearing them.
+ */
+TEST_P(DrmHalVendorPluginTest, GetSecureStops) {
+    // There may be secure stops, depending on if there were keys
+    // loaded and unloaded previously. Clear them to get to a known
+    // state, then make sure there are none.
+    auto res = drmPlugin->getSecureStops(
+            [&](Status status, const hidl_vec<SecureStop>&) {
+                if (status != Status::OK) {
+                    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                }
+            });
+    EXPECT_OK(res);
+
+    res = drmPlugin->getSecureStops(
+            [&](Status status, const hidl_vec<SecureStop>& secureStops) {
+                if (status == Status::OK) {
+                    EXPECT_EQ(secureStops.size(), 0u);
+                } else {
+                    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                }
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that the clearkey plugin returns BAD_VALUE if
+ * an empty ssid is provided.
+ */
+TEST_P(DrmHalVendorPluginTest, GetSecureStopEmptySSID) {
+    SecureStopId ssid;
+    auto res = drmPlugin->getSecureStop(
+            ssid, [&](Status status, const SecureStop&) {
+                EXPECT_EQ(Status::BAD_VALUE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that releasing all secure stops either isn't supported
+ * or is completed successfully
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseAllSecureStops) {
+    Status status = drmPlugin->releaseAllSecureStops();
+    EXPECT_TRUE(status == Status::OK ||
+                status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Releasing a secure stop without first getting one and sending it to the
+ * server to get a valid SSID should return ERROR_DRM_INVALID_STATE.
+ * This is an optional API so it can also return CANNOT_HANDLE.
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopSequenceError) {
+    SecureStopId ssid = {1, 2, 3, 4};
+    Status status = drmPlugin->releaseSecureStop(ssid);
+    EXPECT_TRUE(status == Status::ERROR_DRM_INVALID_STATE ||
+                status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Test that releasing a specific secure stop with an empty ssid
+ * return BAD_VALUE. This is an optional API so it can also return
+ * CANNOT_HANDLE.
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopEmptySSID) {
+    SecureStopId ssid;
+    Status status = drmPlugin->releaseSecureStop(ssid);
+    EXPECT_TRUE(status == Status::BAD_VALUE ||
+                status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * The following five tests verify that the properties
+ * defined in the MediaDrm API are supported by
+ * the plugin.
+ */
+TEST_P(DrmHalVendorPluginTest, GetVendorProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "vendor", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(value.size(), 0u);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetVersionProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "version", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(value.size(), 0u);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetDescriptionProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "description", [&](Status status, const hidl_string& value) {
+                EXPECT_EQ(Status::OK, status);
+                EXPECT_NE(value.size(), 0u);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetAlgorithmsProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "algorithms", [&](Status status, const hidl_string& value) {
+                if (status == Status::OK) {
+                    EXPECT_NE(value.size(), 0u);
+                } else {
+                    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                }
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetPropertyUniqueDeviceID) {
+    auto res = drmPlugin->getPropertyByteArray(
+            "deviceUniqueId",
+            [&](Status status, const hidl_vec<uint8_t>& value) {
+                if (status == Status::OK) {
+                    EXPECT_NE(value.size(), 0u);
+                } else {
+                    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+                }
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that attempting to read invalid string and byte array
+ * properties returns the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, GetInvalidStringProperty) {
+    auto res = drmPlugin->getPropertyString(
+            "invalid", [&](Status status, const hidl_string&) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetInvalidByteArrayProperty) {
+    auto res = drmPlugin->getPropertyByteArray(
+            "invalid", [&](Status status, const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+            });
+    EXPECT_OK(res);
+}
+
+/**
+ * Test that setting invalid string and byte array properties returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetStringPropertyNotSupported) {
+    EXPECT_EQ(drmPlugin->setPropertyString("awefijaeflijwef", "value"),
+              Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+TEST_P(DrmHalVendorPluginTest, SetByteArrayPropertyNotSupported) {
+    hidl_vec<uint8_t> value;
+    EXPECT_EQ(drmPlugin->setPropertyByteArray("awefijaeflijwef", value),
+              Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Test that setting an invalid cipher algorithm returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherInvalidAlgorithm) {
+    SessionId session = openSession();
+    hidl_string algorithm;
+    Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+    closeSession(session);
+}
+
+/**
+ * Test that setting a cipher algorithm with no session returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithmNoSession) {
+    SessionId session;
+    hidl_string algorithm = "AES/CBC/NoPadding";
+    Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that setting a valid cipher algorithm returns
+ * the expected status value. It is not required that all
+ * vendor modules support this algorithm, but they must
+ * either accept it or return ERROR_DRM_CANNOT_HANDLE
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithm) {
+    SessionId session = openSession();
+    ;
+    hidl_string algorithm = "AES/CBC/NoPadding";
+    Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+    EXPECT_TRUE(status == Status::OK ||
+                status == Status::ERROR_DRM_CANNOT_HANDLE);
+    closeSession(session);
+}
+
+/**
+ * Test that setting an invalid mac algorithm returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacInvalidAlgorithm) {
+    SessionId session = openSession();
+    hidl_string algorithm;
+    Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+    closeSession(session);
+}
+
+/**
+ * Test that setting a mac algorithm with no session returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacNullAlgorithmNoSession) {
+    SessionId session;
+    hidl_string algorithm = "HmacSHA256";
+    Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that setting a valid mac algorithm returns
+ * the expected status value. It is not required that all
+ * vendor modules support this algorithm, but they must
+ * either accept it or return ERROR_DRM_CANNOT_HANDLE
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacAlgorithm) {
+    SessionId session = openSession();
+    hidl_string algorithm = "HmacSHA256";
+    Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+    EXPECT_TRUE(status == Status::OK ||
+                status == Status::ERROR_DRM_CANNOT_HANDLE);
+    closeSession(session);
+}
+
+/**
+ * The Generic* methods provide general purpose crypto operations
+ * that may be used for applications other than DRM. They leverage
+ * the hardware root of trust and secure key distribution mechanisms
+ * of a DRM system to enable app-specific crypto functionality where
+ * the crypto keys are not exposed outside of the trusted execution
+ * environment.
+ *
+ * Generic encrypt/decrypt/sign/verify should fail on invalid
+ * inputs, e.g. empty sessionId
+ */
+TEST_P(DrmHalVendorPluginTest, GenericEncryptNoSession) {
+    SessionId session;
+    hidl_vec<uint8_t> keyId, input, iv;
+    auto res = drmPlugin->encrypt(
+            session, keyId, input, iv,
+            [&](Status status, const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericDecryptNoSession) {
+    SessionId session;
+    hidl_vec<uint8_t> keyId, input, iv;
+    auto res = drmPlugin->decrypt(
+            session, keyId, input, iv,
+            [&](Status status, const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericSignNoSession) {
+    SessionId session;
+    hidl_vec<uint8_t> keyId, message;
+    auto res = drmPlugin->sign(
+            session, keyId, message,
+            [&](Status status, const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericVerifyNoSession) {
+    SessionId session;
+    hidl_vec<uint8_t> keyId, message, signature;
+    auto res = drmPlugin->verify(
+            session, keyId, message, signature, [&](Status status, bool) {
+                EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+            });
+    EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericSignRSANoSession) {
+    SessionId session;
+    hidl_string algorithm;
+    hidl_vec<uint8_t> message, wrappedKey;
+    auto res = drmPlugin->signRSA(session, algorithm, message, wrappedKey,
+                                  [&](Status status, const hidl_vec<uint8_t>&) {
+                                      EXPECT_EQ(Status::BAD_VALUE, status);
+                                  });
+    EXPECT_OK(res);
+}
+
+/**
+ * Exercise the requiresSecureDecoderComponent method. Additional tests
+ * will verify positive cases with specific vendor content configurations.
+ * Below we just test the negative cases.
+ */
+
+/**
+ * Verify that requiresSecureDecoderComponent handles empty mimetype.
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderEmptyMimeType) {
+    EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
+}
+
+/**
+ * Verify that requiresSecureDecoderComponent handles invalid mimetype.
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderInvalidMimeType) {
+    EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("bad"));
+}
+
+/**
+ *  CryptoPlugin tests
+ */
+
+/**
+ * Exercise the NotifyResolution API. There is no observable result,
+ * just call the method for coverage.
+ */
+TEST_P(DrmHalVendorPluginTest, NotifyResolution) {
+    cryptoPlugin->notifyResolution(1920, 1080);
+}
+
+/**
+ * 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> DrmHalVendorPluginTest::getDecryptMemory(size_t size,
+                                                     size_t index) {
+    sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+    EXPECT_NE(ashmemAllocator, nullptr);
+
+    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(mappedMemory, nullptr);
+    res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
+    EXPECT_OK(res);
+    return mappedMemory;
+}
+
+/**
+ * Exercise the setMediaDrmSession method. setMediaDrmSession
+ * is used to associate a drm session with a crypto session.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSession) {
+    auto sessionId = openSession();
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::OK, status);
+    closeSession(sessionId);
+}
+
+/**
+ * setMediaDrmSession with a closed session id
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionClosedSession) {
+    auto sessionId = openSession();
+    closeSession(sessionId);
+    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+    EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * Decrypt tests
+ */
+
+class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest {
+   public:
+    DrmHalVendorDecryptTest() = default;
+    virtual ~DrmHalVendorDecryptTest() {}
+
+   protected:
+    void loadKeys(const SessionId& sessionId,
+                  const DrmHalVTSVendorModule_V1::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]);
+    }
+};
+
+KeyedVector DrmHalVendorDecryptTest::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);
+}
+
+/**
+ * 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.
+ */
+void DrmHalVendorDecryptTest::loadKeys(
+        const SessionId& sessionId,
+        const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration) {
+    hidl_vec<uint8_t> keyRequest;
+    auto res = drmPlugin->getKeyRequest(
+            sessionId, configuration.initData, configuration.mimeType,
+            KeyType::STREAMING,
+            toHidlKeyedVector(configuration.optionalParameters),
+            [&](Status status, const hidl_vec<uint8_t>& request,
+                KeyRequestType type, const hidl_string&) {
+                EXPECT_EQ(Status::OK, status)
+                        << "Failed to get "
+                           "key request for configuration "
+                        << configuration.name;
+                EXPECT_EQ(type, KeyRequestType::INITIAL);
+                EXPECT_NE(request.size(), 0u) << "Expected key request size"
+                                                 " to have length > 0 bytes";
+                keyRequest = request;
+            });
+    EXPECT_OK(res);
+
+    /**
+     * 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";
+
+    res = drmPlugin->provideKeyResponse(
+            sessionId, keyResponse,
+            [&](Status status, const hidl_vec<uint8_t>&) {
+                EXPECT_EQ(Status::OK, status)
+                        << "Failure providing "
+                           "key response for configuration "
+                        << configuration.name;
+            });
+    EXPECT_OK(res);
+}
+
+void DrmHalVendorDecryptTest::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();
+    }
+}
+
+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";
+        }
+    }
+}
+
+/**
+ * Positive decrypt test.  "Decrypt" a single clear
+ * segment.  Verify data matches.
+ */
+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};
+
+        sp<IMemory> sharedMemory =
+                getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+
+        SharedBuffer sourceBuffer = {
+                .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+        fillRandom(sharedMemory);
+
+        DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+                                        {.bufferId = kSegmentIndex,
+                                         .offset = kSegmentSize,
+                                         .size = kSegmentSize},
+                                        .secureMemory = nullptr};
+
+        Pattern noPattern = {0, 0};
+        vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+                                         .numBytesOfEncryptedData = 0}};
+        uint64_t offset = 0;
+
+        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";
+        closeSession(sessionId);
+    }
+}
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+INSTANTIATE_TEST_CASE_P(
+        DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
+        testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+INSTANTIATE_TEST_CASE_P(
+        DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
+        testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+INSTANTIATE_TEST_CASE_P(
+        DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
+        testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+int main(int argc, char** argv) {
+    gVendorModules =
+            new drm_vts::VendorModules("/data/nativetest/drm_hidl_test/vendor");
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/drm/1.0/vts/functional/shared_library.cpp b/drm/1.0/vts/functional/shared_library.cpp
new file mode 100644
index 0000000..6658150
--- /dev/null
+++ b/drm/1.0/vts/functional/shared_library.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "drm-vts-shared-library"
+
+#include <dlfcn.h>
+#include <shared_library.h>
+
+using std::string;
+
+namespace drm_vts {
+
+SharedLibrary::SharedLibrary(const string& path) {
+    mLibHandle = dlopen(path.c_str(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+    if (mLibHandle != NULL) {
+        dlclose(mLibHandle);
+        mLibHandle = NULL;
+    }
+}
+
+bool SharedLibrary::operator!() const {
+    return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+    if (!mLibHandle) {
+        return NULL;
+    }
+
+    // Clear last error before we load the symbol again,
+    // in case the caller didn't retrieve it.
+    (void)dlerror();
+    return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+    const char* error = dlerror();
+    return error ? error : "No errors or unknown error";
+}
+};
diff --git a/drm/1.0/vts/functional/shared_library.h b/drm/1.0/vts/functional/shared_library.h
new file mode 100644
index 0000000..1f32243
--- /dev/null
+++ b/drm/1.0/vts/functional/shared_library.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef SHARED_LIBRARY_H_
+#define SHARED_LIBRARY_H_
+
+#include <string>
+#include <vector>
+
+namespace drm_vts {
+class SharedLibrary {
+   public:
+    explicit SharedLibrary(const std::string& path);
+    ~SharedLibrary();
+
+    bool operator!() const;
+    void* lookup(const char* symbol) const;
+    const char* lastError() const;
+
+   private:
+    void* mLibHandle;
+
+    SharedLibrary(const SharedLibrary&) = delete;
+    void operator=(const SharedLibrary&) = delete;
+};
+};
+
+#endif  // SHARED_LIBRARY_H_
diff --git a/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so b/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
new file mode 100755
index 0000000..d365b34
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
Binary files differ
diff --git a/drm/1.0/vts/functional/vendor_modules.cpp b/drm/1.0/vts/functional/vendor_modules.cpp
new file mode 100644
index 0000000..34af6f8
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor_modules.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "drm-vts-vendor-modules"
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <utils/Log.h>
+#include <memory>
+
+#include "shared_library.h"
+#include "vendor_modules.h"
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+
+namespace drm_vts {
+vector<string> VendorModules::getVendorModulePaths() {
+    if (mModuleList.size() > 0) {
+        return mModuleList;
+    }
+
+    DIR* dir = opendir(mModulesPath.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);
+        }
+    }
+
+    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));
+        if (!library) {
+            ALOGE("failed to map shared library %s", path.c_str());
+            return NULL;
+        }
+    }
+    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.0/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/vendor_modules.h
new file mode 100644
index 0000000..5371a0d
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor_modules.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef VENDOR_MODULES_H
+#define VENDOR_MODULES_H
+
+#include <map>
+
+#include "shared_library.h"
+
+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& path) : mModulesPath(path) {}
+    ~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);
+
+   private:
+    std::string mModulesPath;
+    std::vector<std::string> mModuleList;
+    std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
+
+    inline bool endsWith(const std::string& str, const std::string& suffix) {
+        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/drm/Android.bp b/drm/Android.bp
index bbb3e4b..33f70eb 100644
--- a/drm/Android.bp
+++ b/drm/Android.bp
@@ -1,4 +1,5 @@
 // This is an autogenerated file, do not edit.
 subdirs = [
     "1.0",
+    "1.0/vts/functional",
 ]
diff --git a/dumpstate/1.0/default/service.cpp b/dumpstate/1.0/default/service.cpp
index 85bea12..4f276b7 100644
--- a/dumpstate/1.0/default/service.cpp
+++ b/dumpstate/1.0/default/service.cpp
@@ -24,11 +24,18 @@
 using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
 using ::android::hardware::dumpstate::V1_0::implementation::DumpstateDevice;
 using ::android::hardware::joinRpcThreadpool;
+using ::android::OK;
 using ::android::sp;
 
-int main (int /* argc */, char * /* argv */ []) {
+int main(int /* argc */, char* /* argv */ []) {
     sp<IDumpstateDevice> dumpstate = new DumpstateDevice;
-    configureRpcThreadpool(1, true);
-    dumpstate->registerAsService();
+    configureRpcThreadpool(1, true /* will join */);
+    if (dumpstate->registerAsService() != OK) {
+        ALOGE("Could not register service.");
+        return 1;
+    }
     joinRpcThreadpool();
+
+    ALOGE("Service exited!");
+    return 1;
 }
diff --git a/gnss/1.0/IGnssDebug.hal b/gnss/1.0/IGnssDebug.hal
index 9c1038f..716ba88 100644
--- a/gnss/1.0/IGnssDebug.hal
+++ b/gnss/1.0/IGnssDebug.hal
@@ -3,30 +3,51 @@
 /** Extended interface for DEBUG support. */
 interface IGnssDebug {
     enum SatelliteEphemerisType : uint8_t {
-        /** no information is known to the gnss hardware, about this satellite */
-        UNKNOWN,
-        /**  this satellite is known to exist */
-        KNOWN,
-        /**  this satellite is not known to exist */
-        NONEXISTENT,
-        /** Only Almanac (approximate) location known for this satellite */
+        /** Ephemeris is known for this satellite. */
+        EPHEMERIS,
+        /**
+         * Ephemeris is not known, but Almanac (approximate location) is known.
+         */
         ALMANAC_ONLY,
-        /** Ephemeris is known from demodulating the signal on device */
+        /**
+         * Both ephemeris & almanac are not known (e.g. during a cold start
+         * blind search.)
+         */
+        NOT_AVAILABLE
+    };
+
+    enum SatelliteEphemerisSource : uint8_t {
+        /**
+         * The ephemeris (or almanac only) information was demodulated from the
+         * signal received on the device
+         */
         DEMODULATED,
-        /** Ephemeris has been provided by SUPL */
+        /**
+         * The ephemeris (or almanac only) information was received from a SUPL
+         * server.
+         */
         SUPL_PROVIDED,
-        /** Ephemeris has been provided by another server */
+        /**
+         * The ephemeris (or almanac only) information was provided by another
+         * server.
+         */
         OTHER_SERVER_PROVIDED,
         /**
-         * Predicted ephemeris has been provided by a server
-         * (e.g. Xtra, Extended Ephemeris, etc...)
+         * The ephemeris (or almanac only) information was provided by another
+         * method, e.g. injected via a local debug tool, from build defaults
+         * (e.g. almanac), or is from a satellite
+         * with SatelliteEphemerisType::NOT_AVAILABLE.
          */
-        SERVER_PREDICTED,
-        /**
-         * Predicted ephemeris in use, generated locally on the device (e.g. from prior
-         * ephemeris)
-         */
-        LOCALLY_PREDICTED
+        OTHER
+    };
+
+    enum SatelliteEphemerisHealth : uint8_t {
+        /** The ephemeris is known good. */
+        GOOD,
+        /** The ephemeris is known bad. */
+        BAD,
+        /** The ephemeris is unknown to be good or bad. */
+        UNKNOWN
     };
 
     /**
@@ -45,22 +66,22 @@
         double longitudeDegrees;
         /** Altitude above ellipsoid expressed in meters */
         float altitudeMeters;
-        /** Represents speed in meters per second. */
+        /** Represents horizontal speed in meters per second. */
         float speedMetersPerSec;
         /** Represents heading in degrees. */
         float bearingDegrees;
         /**
-         * estimated horizontal accuracy of position expressed in meters, radial,
+         * Estimated horizontal accuracy of position expressed in meters, radial,
          * 68% confidence.
          */
         double horizontalAccuracyMeters;
         /**
-         * estimated vertical accuracy of position expressed in meters, with
+         * Estimated vertical accuracy of position expressed in meters, with
          * 68% confidence.
          */
         double verticalAccuracyMeters;
         /**
-         * estimated speed accuracy in meters per second with 68% confidence.
+         * Estimated speed accuracy in meters per second with 68% confidence.
          */
         double speedAccuracyMetersPerSecond;
         /**
@@ -69,26 +90,31 @@
         double bearingAccuracyDegrees;
         /**
          * Time duration before this report that this position information was
-         * valid.
+         * valid.  This can, for example, be a previous injected location with
+         * an age potentially thousands of seconds old, or
+         * extrapolated to the current time (with appropriately increased
+         * accuracy estimates), with a (near) zero age.
          */
         float ageSeconds;
     };
 
     /**
      * Provides the current best known UTC time estimate.
+     * If no fresh information is available, e.g. after a delete all,
+     * then whatever the effective defaults are on the device must be
+     * provided (e.g. Jan. 1, 2017, with an uncertainty of 5 years) expressed
+     * in the specified units.
      */
     struct TimeDebug {
-        /**
-         * Validity of the data in the struct.
-         * False if current time is unknown.
-         */
-        bool valid;
-        /**
-         * UTC time estimate.
-         */
+        /** UTC time estimate. */
         GnssUtcTime timeEstimate;
         /** 68% error estimate in time. */
         float timeUncertaintyNs;
+        /**
+         * 68% error estimate in local clock drift,
+         * in nanoseconds per second (also known as parts per billion - ppb.)
+         */
+        float frequencyUncertaintyNsPerSec;
     };
 
     /**
@@ -99,14 +125,34 @@
         int16_t svid;
         /** Defines the constellation type of the given SV. */
         GnssConstellationType constellation;
+
         /** Defines the ephemeris type of the satellite. */
         SatelliteEphemerisType ephemerisType;
+        /** Defines the ephemeris source of the satellite. */
+        SatelliteEphemerisSource ephemerisSource;
         /**
-         * Time duration before this report, that the ephemeris source was last
-         * updated, e.g. latest demodulation, or latest server download.
-         * Set to 0 when ephemerisType is UNKNOWN.
+         * Defines whether the satellite is known healthy
+         * (safe for use in location calculation.)
+         */
+        SatelliteEphemerisHealth ephemerisHealth;
+        /**
+         * Time duration from this report (current time), minus the
+         * effective time of the ephemeris source (e.g. TOE, TOA.)
+         * Set to 0 when ephemerisType is NOT_AVAILABLE.
          */
         float ephemerisAgeSeconds;
+
+        /**
+         * True if a server has provided a predicted orbit (& clock) for
+         * this satellite.
+         */
+        bool serverPredictionIsAvailable;
+        /**
+         * Time duration from this report (current time) minus the time of the
+         * start of the server predicted information.  For example, a 1 day
+         * old prediction would be reported as 86400 seconds here.
+         */
+        float serverPredictionAgeSeconds;
     };
 
     /**
@@ -119,12 +165,11 @@
         /** Current best know time estimate */
         TimeDebug time;
         /**
-         * Provides a list of the decoded satellite ephemeris.
-         * Must provide a complete list for all constellations device can track,
+         * Provides a list of the available satellite data, for all
+         * satellites and constellations the device can track,
          * including GnssConstellationType UNKNOWN.
          */
         vec<SatelliteData> satelliteDataArray;
-
     };
 
     /**
diff --git a/radio/1.0/vts/functional/Android.bp b/radio/1.0/vts/functional/Android.bp
index 24e3926..7808de1 100644
--- a/radio/1.0/vts/functional/Android.bp
+++ b/radio/1.0/vts/functional/Android.bp
@@ -44,3 +44,27 @@
         "-g",
     ],
 }
+
+cc_test {
+    name: "VtsHalSapV1_0TargetTest",
+    defaults: ["hidl_defaults"],
+    srcs: ["sap_callback.cpp",
+           "sap_hidl_hal_api.cpp",
+           "sap_hidl_hal_test.cpp",
+           "VtsHalSapV1_0TargetTest.cpp"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libnativehelper",
+        "libutils",
+        "android.hardware.radio@1.0",
+    ],
+    static_libs: ["VtsHalHidlTargetTestBase"],
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+}
diff --git a/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
new file mode 100644
index 0000000..f902588
--- /dev/null
+++ b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+int main(int argc, char** argv) {
+    // Add Sim-access Profile Hidl Environment
+    ::testing::AddGlobalTestEnvironment(new SapHidlEnvironment);
+    ::testing::InitGoogleTest(&argc, argv);
+
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+
+    return status;
+}
diff --git a/radio/1.0/vts/functional/sap_callback.cpp b/radio/1.0/vts/functional/sap_callback.cpp
new file mode 100644
index 0000000..563d066
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_callback.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+SapCallback::SapCallback(SapHidlTest& parent) : parent(parent) {
+}
+
+Return<void> SapCallback::connectResponse(int32_t token, SapConnectRsp /*sapConnectRsp*/,
+        int32_t /*maxMsgSize*/) {
+    sapResponseToken = token;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::disconnectResponse(int32_t token) {
+    sapResponseToken = token;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::disconnectIndication(int32_t /*token*/,
+        SapDisconnectType /*disconnectType*/) {
+    return Void();
+}
+
+Return<void> SapCallback::apduResponse(int32_t token, SapResultCode resultCode,
+        const ::android::hardware::hidl_vec<uint8_t>& /*apduRsp*/) {
+    sapResponseToken = token;
+    sapResultCode = resultCode;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::transferAtrResponse(int32_t token, SapResultCode resultCode,
+        const ::android::hardware::hidl_vec<uint8_t>& /*atr*/) {
+    sapResponseToken = token;
+    sapResultCode = resultCode;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::powerResponse(int32_t token, SapResultCode resultCode) {
+    sapResponseToken = token;
+    sapResultCode = resultCode;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::resetSimResponse(int32_t token, SapResultCode resultCode) {
+    sapResponseToken = token;
+    sapResultCode = resultCode;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::statusIndication(int32_t /*token*/, SapStatus /*status*/) {
+    return Void();
+}
+
+Return<void> SapCallback::transferCardReaderStatusResponse(int32_t token,
+            SapResultCode resultCode, int32_t /*cardReaderStatus*/) {
+    sapResponseToken = token;
+    sapResultCode = resultCode;
+    parent.notify();
+    return Void();
+}
+
+Return<void> SapCallback::errorResponse(int32_t /*token*/) {
+    return Void();
+}
+
+Return<void> SapCallback::transferProtocolResponse(int32_t token,
+            SapResultCode resultCode) {
+    sapResponseToken = token;
+    sapResultCode = resultCode;
+    parent.notify();
+    return Void();
+}
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_api.cpp b/radio/1.0/vts/functional/sap_hidl_hal_api.cpp
new file mode 100644
index 0000000..e806bd7
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_api.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+/*
+ * Test ISap.connectReq() for the response returned.
+ */
+TEST_F(SapHidlTest, connectReq) {
+    int32_t token = 0;
+    int32_t maxMsgSize = 100;
+
+    sap->connectReq(++token, maxMsgSize);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+}
+
+/*
+ * Test IRadio.disconnectReq() for the response returned
+ */
+TEST_F(SapHidlTest, disconnectReq) {
+    int32_t token = 0;
+
+    sap->disconnectReq(++token);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+}
+
+/*
+ * Test IRadio.apduReq() for the response returned.
+ */
+TEST_F(SapHidlTest, apduReq) {
+    int32_t token = 0;
+    SapApduType sapApduType = SapApduType::APDU;
+    android::hardware::hidl_vec<uint8_t> command = {};
+
+    sap->apduReq(++token, sapApduType, command);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+
+    ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+              SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+              SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.transferAtrReq() for the response returned.
+ */
+TEST_F(SapHidlTest, transferAtrReq) {
+    int32_t token = 0;
+
+    sap->transferAtrReq(++token);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+
+    ASSERT_TRUE(SapResultCode::DATA_NOT_AVAILABLE == sapCb->sapResultCode ||
+              SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+              SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.powerReq() for the response returned.
+ */
+TEST_F(SapHidlTest, powerReq) {
+    int32_t token = 0;
+    bool state = true;
+
+    sap->powerReq(++token, state);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+
+    ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+              SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+              SapResultCode::CARD_REMOVED == sapCb->sapResultCode ||
+              SapResultCode::CARD_ALREADY_POWERED_ON == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.resetSimReq() for the response returned.
+ */
+TEST_F(SapHidlTest, resetSimReq) {
+    int32_t token = 0;
+
+    sap->resetSimReq(++token);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+
+    ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+              SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+              SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.transferCardReaderStatusReq() for the response returned.
+ */
+TEST_F(SapHidlTest, transferCardReaderStatusReq) {
+    int32_t token = 0;
+
+    sap->transferCardReaderStatusReq(++token);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+
+    EXPECT_EQ(SapResultCode::DATA_NOT_AVAILABLE, sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.setTransferProtocolReq() for the response returned.
+ */
+TEST_F(SapHidlTest, setTransferProtocolReq) {
+    int32_t token = 0;
+    SapTransferProtocol sapTransferProtocol = SapTransferProtocol::T0;
+
+    sap->setTransferProtocolReq(++token, sapTransferProtocol);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sapCb->sapResponseToken, token);
+
+    EXPECT_EQ(SapResultCode::NOT_SUPPORTED, sapCb->sapResultCode);
+}
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_test.cpp b/radio/1.0/vts/functional/sap_hidl_hal_test.cpp
new file mode 100644
index 0000000..a67c5b6
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+void SapHidlTest::SetUp() {
+    sap = ::testing::VtsHalHidlTargetTestBase::getService<ISap>(hidl_string("sap_uim_socket1"));
+    ASSERT_NE(sap, nullptr);
+
+    sapCb = new SapCallback(*this);
+    ASSERT_NE(sapCb, nullptr);
+
+    count = 0;
+
+    sap->setCallback(sapCb);
+}
+
+void SapHidlTest::TearDown() {
+}
+
+void SapHidlTest::notify() {
+    std::unique_lock<std::mutex> lock(mtx);
+    count++;
+    cv.notify_one();
+}
+
+std::cv_status SapHidlTest::wait() {
+    std::unique_lock<std::mutex> lock(mtx);
+
+    std::cv_status status = std::cv_status::no_timeout;
+    auto now = std::chrono::system_clock::now();
+    while (count == 0) {
+        status = cv.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+        if (status == std::cv_status::timeout) {
+            return status;
+        }
+    }
+    count--;
+    return status;
+}
+
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_utils.h b/radio/1.0/vts/functional/sap_hidl_hal_utils.h
new file mode 100644
index 0000000..e3efa50
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_utils.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#include <android-base/logging.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/1.0/ISap.h>
+#include <android/hardware/radio/1.0/ISapCallback.h>
+#include <android/hardware/radio/1.0/types.h>
+
+using namespace ::android::hardware::radio::V1_0;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+#define TIMEOUT_PERIOD 40
+
+class SapHidlTest;
+
+/* Callback class for sap response */
+class SapCallback : public ISapCallback {
+private:
+    SapHidlTest& parent;
+
+public:
+    SapResultCode sapResultCode;
+    int32_t sapResponseToken;
+
+    SapCallback(SapHidlTest& parent);
+
+    virtual ~SapCallback() = default;
+
+    Return<void> connectResponse(int32_t token, SapConnectRsp sapConnectRsp, int32_t maxMsgSize);
+
+    Return<void> disconnectResponse(int32_t token);
+
+    Return<void> disconnectIndication(int32_t token, SapDisconnectType disconnectType);
+
+    Return<void> apduResponse(int32_t token, SapResultCode resultCode,
+            const ::android::hardware::hidl_vec<uint8_t>& apduRsp);
+
+    Return<void> transferAtrResponse(int32_t token, SapResultCode resultCode,
+            const ::android::hardware::hidl_vec<uint8_t>& atr);
+
+    Return<void> powerResponse(int32_t token, SapResultCode resultCode);
+
+    Return<void> resetSimResponse(int32_t token, SapResultCode resultCode);
+
+    Return<void> statusIndication(int32_t token, SapStatus status);
+
+    Return<void> transferCardReaderStatusResponse(int32_t token, SapResultCode resultCode,
+            int32_t cardReaderStatus);
+
+    Return<void> errorResponse(int32_t token);
+
+    Return<void> transferProtocolResponse(int32_t token, SapResultCode resultCode);
+};
+
+// The main test class for Sap HIDL.
+class SapHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+private:
+    std::mutex mtx;
+    std::condition_variable cv;
+    int count;
+
+public:
+    virtual void SetUp() override;
+
+    virtual void TearDown() override;
+
+    /* Used as a mechanism to inform the test about data/event callback */
+    void notify();
+
+    /* Test code calls this function to wait for response */
+    std::cv_status wait();
+
+    /* Sap service */
+    sp<ISap> sap;
+
+    /* Sap Callback object */
+    sp<SapCallback> sapCb;
+};
+
+// A class for test environment setup
+class SapHidlEnvironment : public ::testing::Environment {
+public:
+    virtual void SetUp() {}
+    virtual void TearDown() {}
+};
diff --git a/wifi/supplicant/1.0/vts/Android.mk b/wifi/supplicant/1.0/vts/Android.mk
new file mode 100644
index 0000000..6361f9b
--- /dev/null
+++ b/wifi/supplicant/1.0/vts/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp
index 1fcfc8c..df4bfa9 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test_utils.cpp
@@ -136,14 +136,14 @@
 };
 
 void stopWifiFramework() {
-    ASSERT_EQ(std::system("svc wifi disable"), 0);
+    ASSERT_EQ(std::system("stop"), 0);
     // TODO: Use some other mechanism to wait for the framework to
     // finish disabling.
     sleep(5);
 }
 
 void startWifiFramework() {
-    ASSERT_EQ(std::system("svc wifi enable"), 0);
+    ASSERT_EQ(std::system("start"), 0);
     // These tests don't care whether the framework
     // finished enabling or not.
 }
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
index c2a58b6..5abf4e0 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -104,11 +104,13 @@
     }
     Return<void> onDisconnected(const hidl_array<uint8_t, 6>& /* bssid */,
                                 bool /* locallyGenerated */,
-                                uint32_t /* reasonCode */) override {
+                                ISupplicantStaIfaceCallback::ReasonCode
+                                /* reasonCode */) override {
         return Void();
     }
     Return<void> onAssociationRejected(
-        const hidl_array<uint8_t, 6>& /* bssid */, uint32_t /* statusCode */,
+        const hidl_array<uint8_t, 6>& /* bssid */,
+        ISupplicantStaIfaceCallback::StatusCode /* statusCode */,
         bool /*timedOut */) override {
         return Void();
     }
@@ -116,6 +118,11 @@
         const hidl_array<uint8_t, 6>& /* bssid */) override {
         return Void();
     }
+    Return<void> onBssidChanged(
+        ISupplicantStaIfaceCallback::BssidChangeReason /* reason */,
+        const hidl_array<uint8_t, 6>& /* bssid */) override {
+        return Void();
+    }
     Return<void> onEapFailure() override { return Void(); }
     Return<void> onWpsEventSuccess() override { return Void(); }
     Return<void> onWpsEventFail(