Merge "Use fmq for camera capture result." into oc-dev
diff --git a/biometrics/fingerprint/2.1/default/BiometricsFingerprint.cpp b/biometrics/fingerprint/2.1/default/BiometricsFingerprint.cpp
index 7ba84bd..16197d7 100644
--- a/biometrics/fingerprint/2.1/default/BiometricsFingerprint.cpp
+++ b/biometrics/fingerprint/2.1/default/BiometricsFingerprint.cpp
@@ -23,6 +23,7 @@
 #include "BiometricsFingerprint.h"
 
 #include <inttypes.h>
+#include <unistd.h>
 
 namespace android {
 namespace hardware {
@@ -187,7 +188,12 @@
         const hidl_string& storePath) {
     if (storePath.size() >= PATH_MAX || storePath.size() <= 0) {
         ALOGE("Bad path length: %zd", storePath.size());
+        return RequestStatus::SYS_EINVAL;
     }
+    if (access(storePath.c_str(), W_OK)) {
+        return RequestStatus::SYS_EINVAL;
+    }
+
     return ErrorFilter(mDevice->set_active_group(mDevice, gid,
                                                     storePath.c_str()));
 }
diff --git a/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp b/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
index d3f6612..29776b4 100644
--- a/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
+++ b/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
@@ -43,7 +43,7 @@
 static const uint32_t kTimeout = 3;
 static const std::chrono::seconds kTimeoutInSeconds = std::chrono::seconds(kTimeout);
 static const uint32_t kGroupId = 99;
-static const std::string kTmpDir = "/data/local/tmp/";
+static const std::string kTmpDir = "/data/system/";
 static const uint32_t kIterations = 1000;
 
 // Wait for a callback to occur (signaled by the given future) up to the
@@ -186,6 +186,8 @@
     ASSERT_FALSE(mService == nullptr);
 
     // Create an active group
+    // FP service can only write to /data/system due to
+    // SELinux Policy and Linux Dir Permissions
     Return<RequestStatus> res = mService->setActiveGroup(kGroupId, kTmpDir);
     ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
   }
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
index 4dd98ce..71b893a 100644
--- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -245,6 +245,7 @@
 
     SessionId openSession();
     void closeSession(const SessionId& sessionId);
+    hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type);
     sp<IMemory> getDecryptMemory(size_t size, size_t index);
 
    protected:
@@ -313,6 +314,70 @@
 }
 
 /**
+ * Helper method to load keys for subsequent decrypt tests.
+ * These tests use predetermined key request/response to
+ * avoid requiring a round trip to a license server.
+ */
+hidl_vec<uint8_t> DrmHalClearkeyPluginTest::loadKeys(
+    const SessionId& sessionId, const KeyType& type = KeyType::STREAMING) {
+    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, type, 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);
+
+    hidl_vec<uint8_t> keySetId;
+    res = drmPlugin->provideKeyResponse(
+        sessionId, knownKeyResponse,
+        [&](Status status, const hidl_vec<uint8_t>& myKeySetId) {
+            EXPECT_EQ(Status::OK, status);
+            EXPECT_EQ(0u, myKeySetId.size());
+            keySetId = myKeySetId;
+        });
+    EXPECT_OK(res);
+    return keySetId;
+}
+
+/**
  * Test that a session can be opened and closed
  */
 TEST_F(DrmHalClearkeyPluginTest, OpenCloseSession) {
@@ -471,6 +536,30 @@
 }
 
 /**
+ * Test that ClearKey cannot handle key restoring.
+ * Expected message is Status::ERROR_DRM_CANNOT_HANDLE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, RestoreKeysCannotHandle) {
+    hidl_vec<uint8_t> keySetId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+    SessionId sessionId = openSession();
+    Status status = drmPlugin->restoreKeys(sessionId, keySetId);
+    EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+    closeSession(sessionId);
+}
+
+/**
+ * Test that restoreKeys fails with a null key set ID.
+ * Error message is expected to be Status::BAD_VALUE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, RestoreKeysNull) {
+    SessionId sessionId = openSession();
+    hidl_vec<uint8_t> nullKeySetId;
+    Status status = drmPlugin->restoreKeys(sessionId, nullKeySetId);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+    closeSession(sessionId);
+}
+
+/**
  * Test that the clearkey plugin doesn't support getting
  * secure stops.
  */
@@ -831,7 +920,6 @@
 
 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(16u, vec.size());
@@ -845,67 +933,6 @@
             const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
 };
 
-/**
- * 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());
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
index 73e0cfe..b8b2052 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
+++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
@@ -65,7 +65,7 @@
 
 class DrmHalVTSVendorModule {
    public:
-    DrmHalVTSVendorModule() {}
+    DrmHalVTSVendorModule() : installed(true) {}
     virtual ~DrmHalVTSVendorModule() {}
 
     /**
@@ -89,7 +89,15 @@
      */
     virtual std::string getServiceName() const = 0;
 
+    /**
+     * Set a flag in the vendor module to indicate whether or not the drm
+     * scheme corresponding to this module is installed on the device.
+     */
+    void setInstalled(bool flag) {installed = flag;}
+    bool isInstalled() const {return installed;}
+
    private:
+    bool installed;
     DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete;
     void operator=(const DrmHalVTSVendorModule&) = delete;
 };
@@ -158,17 +166,30 @@
         const std::map<std::string, std::string> optionalParameters;
 
         /**
+         *  Define license policy attributes for the content configuration.
+         *  These attributes can affect which tests are able to be applied.
+         */
+        struct Policy {
+            /**
+             * Indicate if the license policy allows offline playback.
+             * Content configurated with this policy supports KeyType::OFFLINE
+             * key requests/responses. A vendor module should provide at least
+             * one content configuration where allowOffline is true if the drm
+             * scheme supports offline content.
+             */
+            bool allowOffline;
+        } policy;
+
+        /**
          * 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.
+             * buffers, where the output buffers are protected and cannot be
+             * accessed by the non-secure cpu. A vendor module should provide
+             * at least one content configurations where isSecure is false, to
+             * allow decrypt result verification tests to be run.
              */
             bool isSecure;
 
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
index fec43cf..14e4914 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -83,6 +83,14 @@
 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
 #define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
 
+#define RETURN_IF_SKIPPED \
+    if (!vendorModule->isInstalled()) { \
+        std::cout << "[  SKIPPED ] This drm scheme not supported." << \
+                " library:" << GetParam() << " service-name:" << \
+                vendorModule->getServiceName() << std::endl; \
+        return; \
+    }
+
 static const uint8_t kInvalidUUID[16] = {
         0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
         0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
@@ -124,6 +132,12 @@
             VtsTestBase::getService<ICryptoFactory>();
         }
         ASSERT_NE(cryptoFactory, nullptr);
+
+        // If drm scheme not installed skip subsequent tests
+        if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
+            vendorModule->setInstalled(false);
+            return;
+        }
     }
 
     virtual void TearDown() override {}
@@ -142,6 +156,7 @@
 
 TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) {
     const char* kVendorStr = "Vendor module ";
+    size_t count = 0;
     for (auto config : contentConfigurations) {
         ASSERT_TRUE(config.name.size() > 0) << kVendorStr << "has no name";
         ASSERT_TRUE(config.serverUrl.size() > 0) << kVendorStr
@@ -157,7 +172,9 @@
             ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
                                               << " has zero length key value";
         }
+        count++;
     }
+    EXPECT_NE(0u, count);
 }
 
 /**
@@ -178,9 +195,10 @@
 }
 
 /**
- * Ensure the factory supports the scheme uuid in the config
+ * Check if the factory supports the scheme uuid in the config.
  */
-TEST_P(DrmHalVendorFactoryTest, EmptyPluginConfigUUIDSupported) {
+TEST_P(DrmHalVendorFactoryTest, PluginConfigUUIDSupported) {
+    RETURN_IF_SKIPPED;
     EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
     EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
 }
@@ -205,6 +223,7 @@
  * Ensure valid content types in the configs are supported
  */
 TEST_P(DrmHalVendorFactoryTest, ValidContentTypeSupported) {
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         EXPECT_TRUE(drmFactory->isContentTypeSupported(config.mimeType));
     }
@@ -214,6 +233,7 @@
  * Ensure vendor drm plugin can be created
  */
 TEST_P(DrmHalVendorFactoryTest, CreateVendorDrmPlugin) {
+    RETURN_IF_SKIPPED;
     hidl_string packageName("android.hardware.drm.test");
     auto res = drmFactory->createPlugin(
             getVendorUUID(), packageName,
@@ -228,6 +248,7 @@
  * Ensure vendor crypto plugin can be created
  */
 TEST_P(DrmHalVendorFactoryTest, CreateVendorCryptoPlugin) {
+    RETURN_IF_SKIPPED;
     hidl_vec<uint8_t> initVec;
     auto res = cryptoFactory->createPlugin(
             getVendorUUID(), initVec,
@@ -242,6 +263,7 @@
  * Ensure invalid drm plugin can't be created
  */
 TEST_P(DrmHalVendorFactoryTest, CreateInvalidDrmPlugin) {
+    RETURN_IF_SKIPPED;
     hidl_string packageName("android.hardware.drm.test");
     auto res = drmFactory->createPlugin(
             kInvalidUUID, packageName,
@@ -256,6 +278,7 @@
  * Ensure invalid crypto plugin can't be created
  */
 TEST_P(DrmHalVendorFactoryTest, CreateInvalidCryptoPlugin) {
+    RETURN_IF_SKIPPED;
     hidl_vec<uint8_t> initVec;
     auto res = cryptoFactory->createPlugin(
             kInvalidUUID, initVec,
@@ -272,6 +295,7 @@
     virtual void SetUp() override {
         // Create factories
         DrmHalVendorFactoryTest::SetUp();
+        RETURN_IF_SKIPPED;
 
         hidl_string packageName("android.hardware.drm.test");
         auto res = drmFactory->createPlugin(
@@ -299,6 +323,10 @@
     SessionId openSession();
     void closeSession(const SessionId& sessionId);
     sp<IMemory> getDecryptMemory(size_t size, size_t index);
+    KeyedVector toHidlKeyedVector(const map<string, string>& params);
+    hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
+                               const ContentConfiguration& configuration,
+                               const KeyType& type);
 
    protected:
     sp<IDrmPlugin> drmPlugin;
@@ -319,6 +347,7 @@
  */
 
 TEST_P(DrmHalVendorPluginTest, DoProvisioning) {
+    RETURN_IF_SKIPPED;
     hidl_string certificateType;
     hidl_string certificateAuthority;
     hidl_vec<uint8_t> provisionRequest;
@@ -356,6 +385,7 @@
  * response is provided.
  */
 TEST_P(DrmHalVendorPluginTest, ProvideEmptyProvisionResponse) {
+    RETURN_IF_SKIPPED;
     hidl_vec<uint8_t> response;
     auto res = drmPlugin->provideProvisionResponse(
             response, [&](Status status, const hidl_vec<uint8_t>&,
@@ -389,10 +419,69 @@
     EXPECT_EQ(Status::OK, status);
 }
 
+KeyedVector DrmHalVendorPluginTest::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.
+ */
+hidl_vec<uint8_t> DrmHalVendorPluginTest::loadKeys(
+    const SessionId& sessionId, const ContentConfiguration& configuration,
+    const KeyType& type = KeyType::STREAMING) {
+    hidl_vec<uint8_t> keyRequest;
+    auto res = drmPlugin->getKeyRequest(
+        sessionId, configuration.initData, configuration.mimeType, type,
+        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";
+
+    hidl_vec<uint8_t> keySetId;
+    res = drmPlugin->provideKeyResponse(
+        sessionId, keyResponse,
+        [&](Status status, const hidl_vec<uint8_t>& myKeySetId) {
+            EXPECT_EQ(Status::OK, status) << "Failure providing "
+                                             "key response for configuration "
+                                          << configuration.name;
+            keySetId = myKeySetId;
+        });
+    EXPECT_OK(res);
+    return keySetId;
+}
+
 /**
  * Test that a session can be opened and closed
  */
 TEST_P(DrmHalVendorPluginTest, OpenCloseSession) {
+    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     closeSession(sessionId);
 }
@@ -402,6 +491,7 @@
  * is prohibited with the documented error code.
  */
 TEST_P(DrmHalVendorPluginTest, CloseInvalidSession) {
+    RETURN_IF_SKIPPED;
     SessionId invalidSessionId;
     Status status = drmPlugin->closeSession(invalidSessionId);
     EXPECT_EQ(Status::BAD_VALUE, status);
@@ -412,6 +502,7 @@
  * is prohibited with the documented error code.
  */
 TEST_P(DrmHalVendorPluginTest, CloseClosedSession) {
+    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     closeSession(sessionId);
     Status status = drmPlugin->closeSession(sessionId);
@@ -422,6 +513,7 @@
  * A get key request should fail if no sessionId is provided
  */
 TEST_P(DrmHalVendorPluginTest, GetKeyRequestNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId invalidSessionId;
     hidl_vec<uint8_t> initData;
     hidl_string mimeType = "video/mp4";
@@ -438,6 +530,7 @@
  * Test that an empty sessionID returns BAD_VALUE
  */
 TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptySessionId) {
+    RETURN_IF_SKIPPED;
     SessionId session;
 
     hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
@@ -455,6 +548,7 @@
  * Test that an empty key response returns BAD_VALUE
  */
 TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptyResponse) {
+    RETURN_IF_SKIPPED;
     SessionId session = openSession();
     hidl_vec<uint8_t> emptyResponse;
     auto res = drmPlugin->provideKeyResponse(
@@ -471,6 +565,7 @@
  * Test that a removeKeys on an empty sessionID returns BAD_VALUE
  */
 TEST_P(DrmHalVendorPluginTest, RemoveKeysEmptySessionId) {
+    RETURN_IF_SKIPPED;
     SessionId sessionId;
     Status status = drmPlugin->removeKeys(sessionId);
     EXPECT_TRUE(status == Status::BAD_VALUE);
@@ -481,6 +576,7 @@
  * that has no keys.
  */
 TEST_P(DrmHalVendorPluginTest, RemoveKeysNewSession) {
+    RETURN_IF_SKIPPED;
     SessionId sessionId = openSession();
     Status status = drmPlugin->removeKeys(sessionId);
     EXPECT_TRUE(status == Status::OK);
@@ -488,11 +584,65 @@
 }
 
 /**
+ * Test that keys are successfully restored to a new session
+ * for all content having a policy that allows offline use.
+ */
+TEST_P(DrmHalVendorPluginTest, RestoreKeys) {
+    RETURN_IF_SKIPPED;
+    for (auto config : contentConfigurations) {
+        if (config.policy.allowOffline) {
+            auto sessionId = openSession();
+            hidl_vec<uint8_t> keySetId =
+                    loadKeys(sessionId, config, KeyType::OFFLINE);
+            closeSession(sessionId);
+            sessionId = openSession();
+            EXPECT_NE(0u, keySetId.size());
+            Status status = drmPlugin->restoreKeys(sessionId, keySetId);
+            EXPECT_EQ(Status::OK, status);
+            closeSession(sessionId);
+        }
+    }
+}
+
+/**
+ * Test that restoreKeys fails with a null key set ID.
+ * Error message is expected to be Status::BAD_VALUE.
+ */
+TEST_P(DrmHalVendorPluginTest, RestoreKeysNull) {
+    RETURN_IF_SKIPPED;
+    SessionId sessionId = openSession();
+    hidl_vec<uint8_t> nullKeySetId;
+    Status status = drmPlugin->restoreKeys(sessionId, nullKeySetId);
+    EXPECT_EQ(Status::BAD_VALUE, status);
+    closeSession(sessionId);
+}
+
+/**
+ * Test that restoreKeys fails to restore keys to a closed
+ * session. Error message is expected to be
+ * Status::ERROR_DRM_SESSION_NOT_OPENED.
+ */
+TEST_P(DrmHalVendorPluginTest, RestoreKeysClosedSession) {
+    RETURN_IF_SKIPPED;
+    for (auto config : contentConfigurations) {
+        auto sessionId = openSession();
+        hidl_vec<uint8_t> keySetId = loadKeys(sessionId, config);
+        EXPECT_NE(0u, keySetId.size());
+        closeSession(sessionId);
+        sessionId = openSession();
+        closeSession(sessionId);
+        Status status = drmPlugin->restoreKeys(sessionId, keySetId);
+        EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+    }
+}
+
+/**
  * Test that the plugin either doesn't support getting
  * secure stops, or has no secure stops available after
  * clearing them.
  */
 TEST_P(DrmHalVendorPluginTest, GetSecureStops) {
+    RETURN_IF_SKIPPED;
     // 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.
@@ -520,6 +670,7 @@
  * an empty ssid is provided.
  */
 TEST_P(DrmHalVendorPluginTest, GetSecureStopEmptySSID) {
+    RETURN_IF_SKIPPED;
     SecureStopId ssid;
     auto res = drmPlugin->getSecureStop(
             ssid, [&](Status status, const SecureStop&) {
@@ -533,6 +684,7 @@
  * or is completed successfully
  */
 TEST_P(DrmHalVendorPluginTest, ReleaseAllSecureStops) {
+    RETURN_IF_SKIPPED;
     Status status = drmPlugin->releaseAllSecureStops();
     EXPECT_TRUE(status == Status::OK ||
                 status == Status::ERROR_DRM_CANNOT_HANDLE);
@@ -544,6 +696,7 @@
  * This is an optional API so it can also return CANNOT_HANDLE.
  */
 TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopSequenceError) {
+    RETURN_IF_SKIPPED;
     SecureStopId ssid = {1, 2, 3, 4};
     Status status = drmPlugin->releaseSecureStop(ssid);
     EXPECT_TRUE(status == Status::ERROR_DRM_INVALID_STATE ||
@@ -556,6 +709,7 @@
  * CANNOT_HANDLE.
  */
 TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopEmptySSID) {
+    RETURN_IF_SKIPPED;
     SecureStopId ssid;
     Status status = drmPlugin->releaseSecureStop(ssid);
     EXPECT_TRUE(status == Status::BAD_VALUE ||
@@ -568,6 +722,7 @@
  * the plugin.
  */
 TEST_P(DrmHalVendorPluginTest, GetVendorProperty) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyString(
             "vendor", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -577,6 +732,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GetVersionProperty) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyString(
             "version", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -586,6 +742,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GetDescriptionProperty) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyString(
             "description", [&](Status status, const hidl_string& value) {
                 EXPECT_EQ(Status::OK, status);
@@ -595,6 +752,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GetAlgorithmsProperty) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyString(
             "algorithms", [&](Status status, const hidl_string& value) {
                 if (status == Status::OK) {
@@ -607,6 +765,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GetPropertyUniqueDeviceID) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyByteArray(
             "deviceUniqueId",
             [&](Status status, const hidl_vec<uint8_t>& value) {
@@ -624,6 +783,7 @@
  * properties returns the documented error code.
  */
 TEST_P(DrmHalVendorPluginTest, GetInvalidStringProperty) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyString(
             "invalid", [&](Status status, const hidl_string&) {
                 EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
@@ -632,6 +792,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GetInvalidByteArrayProperty) {
+    RETURN_IF_SKIPPED;
     auto res = drmPlugin->getPropertyByteArray(
             "invalid", [&](Status status, const hidl_vec<uint8_t>&) {
                 EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
@@ -644,11 +805,13 @@
  * the expected status value.
  */
 TEST_P(DrmHalVendorPluginTest, SetStringPropertyNotSupported) {
+    RETURN_IF_SKIPPED;
     EXPECT_EQ(drmPlugin->setPropertyString("awefijaeflijwef", "value"),
               Status::ERROR_DRM_CANNOT_HANDLE);
 }
 
 TEST_P(DrmHalVendorPluginTest, SetByteArrayPropertyNotSupported) {
+    RETURN_IF_SKIPPED;
     hidl_vec<uint8_t> value;
     EXPECT_EQ(drmPlugin->setPropertyByteArray("awefijaeflijwef", value),
               Status::ERROR_DRM_CANNOT_HANDLE);
@@ -659,6 +822,7 @@
  * the expected status value.
  */
 TEST_P(DrmHalVendorPluginTest, SetCipherInvalidAlgorithm) {
+    RETURN_IF_SKIPPED;
     SessionId session = openSession();
     hidl_string algorithm;
     Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
@@ -671,6 +835,7 @@
  * the expected status value.
  */
 TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithmNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_string algorithm = "AES/CBC/NoPadding";
     Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
@@ -684,6 +849,7 @@
  * either accept it or return ERROR_DRM_CANNOT_HANDLE
  */
 TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithm) {
+    RETURN_IF_SKIPPED;
     SessionId session = openSession();
     ;
     hidl_string algorithm = "AES/CBC/NoPadding";
@@ -698,6 +864,7 @@
  * the expected status value.
  */
 TEST_P(DrmHalVendorPluginTest, SetMacInvalidAlgorithm) {
+    RETURN_IF_SKIPPED;
     SessionId session = openSession();
     hidl_string algorithm;
     Status status = drmPlugin->setMacAlgorithm(session, algorithm);
@@ -710,6 +877,7 @@
  * the expected status value.
  */
 TEST_P(DrmHalVendorPluginTest, SetMacNullAlgorithmNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_string algorithm = "HmacSHA256";
     Status status = drmPlugin->setMacAlgorithm(session, algorithm);
@@ -723,6 +891,7 @@
  * either accept it or return ERROR_DRM_CANNOT_HANDLE
  */
 TEST_P(DrmHalVendorPluginTest, SetMacAlgorithm) {
+    RETURN_IF_SKIPPED;
     SessionId session = openSession();
     hidl_string algorithm = "HmacSHA256";
     Status status = drmPlugin->setMacAlgorithm(session, algorithm);
@@ -743,6 +912,7 @@
  * inputs, e.g. empty sessionId
  */
 TEST_P(DrmHalVendorPluginTest, GenericEncryptNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_vec<uint8_t> keyId, input, iv;
     auto res = drmPlugin->encrypt(
@@ -754,6 +924,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GenericDecryptNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_vec<uint8_t> keyId, input, iv;
     auto res = drmPlugin->decrypt(
@@ -765,6 +936,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GenericSignNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_vec<uint8_t> keyId, message;
     auto res = drmPlugin->sign(
@@ -776,6 +948,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GenericVerifyNoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_vec<uint8_t> keyId, message, signature;
     auto res = drmPlugin->verify(
@@ -786,6 +959,7 @@
 }
 
 TEST_P(DrmHalVendorPluginTest, GenericSignRSANoSession) {
+    RETURN_IF_SKIPPED;
     SessionId session;
     hidl_string algorithm;
     hidl_vec<uint8_t> message, wrappedKey;
@@ -806,6 +980,7 @@
  * Verify that requiresSecureDecoderComponent handles empty mimetype.
  */
 TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderEmptyMimeType) {
+    RETURN_IF_SKIPPED;
     EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
 }
 
@@ -813,6 +988,7 @@
  * Verify that requiresSecureDecoderComponent handles invalid mimetype.
  */
 TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderInvalidMimeType) {
+    RETURN_IF_SKIPPED;
     EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("bad"));
 }
 
@@ -821,7 +997,7 @@
  * configurations
  */
 TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderConfig) {
-    const char* kVendorStr = "Vendor module ";
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         for (auto key : config.keys) {
             if (key.isSecure) {
@@ -893,6 +1069,7 @@
  * gets them.
  */
 TEST_P(DrmHalVendorPluginTest, ListenerEvents) {
+    RETURN_IF_SKIPPED;
     sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
     drmPlugin->setListener(listener);
     auto sessionId = openSession();
@@ -919,6 +1096,7 @@
  * the listener gets them.
  */
 TEST_P(DrmHalVendorPluginTest, ListenerExpirationUpdate) {
+    RETURN_IF_SKIPPED;
     sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
     drmPlugin->setListener(listener);
     auto sessionId = openSession();
@@ -936,6 +1114,7 @@
  * the listener gets them.
  */
 TEST_P(DrmHalVendorPluginTest, ListenerKeysChange) {
+    RETURN_IF_SKIPPED;
     sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
     drmPlugin->setListener(listener);
     auto sessionId = openSession();
@@ -961,6 +1140,7 @@
  * listener set.
  */
 TEST_P(DrmHalVendorPluginTest, NotListening) {
+    RETURN_IF_SKIPPED;
     sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
     drmPlugin->setListener(listener);
     drmPlugin->setListener(nullptr);
@@ -986,6 +1166,7 @@
  * just call the method for coverage.
  */
 TEST_P(DrmHalVendorPluginTest, NotifyResolution) {
+    RETURN_IF_SKIPPED;
     cryptoPlugin->notifyResolution(1920, 1080);
 }
 
@@ -1025,6 +1206,7 @@
  * is used to associate a drm session with a crypto session.
  */
 TEST_P(DrmHalVendorPluginTest, SetMediaDrmSession) {
+    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     Status status = cryptoPlugin->setMediaDrmSession(sessionId);
     EXPECT_EQ(Status::OK, status);
@@ -1035,6 +1217,7 @@
  * setMediaDrmSession with a closed session id
  */
 TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionClosedSession) {
+    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     closeSession(sessionId);
     Status status = cryptoPlugin->setMediaDrmSession(sessionId);
@@ -1045,6 +1228,7 @@
  * setMediaDrmSession with a empty session id: BAD_VALUE
  */
 TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionEmptySession) {
+    RETURN_IF_SKIPPED;
     SessionId sessionId;
     Status status = cryptoPlugin->setMediaDrmSession(sessionId);
     EXPECT_EQ(Status::BAD_VALUE, status);
@@ -1060,10 +1244,7 @@
     virtual ~DrmHalVendorDecryptTest() {}
 
    protected:
-    void loadKeys(const SessionId& sessionId,
-                  const ContentConfiguration& configuration);
     void fillRandom(const sp<IMemory>& memory);
-    KeyedVector toHidlKeyedVector(const map<string, string>& params);
     hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
         EXPECT_EQ(vec.size(), 16u);
         return hidl_array<uint8_t, 16>(&vec[0]);
@@ -1080,63 +1261,6 @@
             const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
 };
 
-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 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());
@@ -1298,6 +1422,7 @@
  * Test key status with empty session id, should return BAD_VALUE
  */
 TEST_P(DrmHalVendorDecryptTest, QueryKeyStatusInvalidSession) {
+    RETURN_IF_SKIPPED;
     SessionId sessionId;
     auto res = drmPlugin->queryKeyStatus(sessionId,
             [&](Status status, KeyedVector /* info */) {
@@ -1311,6 +1436,7 @@
  * Test key status.  There should be no key status prior to loading keys
  */
 TEST_P(DrmHalVendorDecryptTest, QueryKeyStatusWithNoKeys) {
+    RETURN_IF_SKIPPED;
     auto sessionId = openSession();
     auto keyStatus = queryKeyStatus(sessionId);
     EXPECT_EQ(0u, keyStatus.size());
@@ -1322,6 +1448,7 @@
  * Test key status.  There should be key status after loading keys.
  */
 TEST_P(DrmHalVendorDecryptTest, QueryKeyStatus) {
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         auto sessionId = openSession();
         loadKeys(sessionId, config);
@@ -1335,6 +1462,7 @@
  * Positive decrypt test. "Decrypt" a single clear segment and verify.
  */
 TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         for (auto key : config.keys) {
             const size_t kSegmentSize = 1024;
@@ -1362,6 +1490,7 @@
  * Verify data matches.
  */
 TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTest) {
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         for (auto key : config.keys) {
             const size_t kSegmentSize = 1024;
@@ -1388,6 +1517,7 @@
  * Negative decrypt test. Decrypt without loading keys.
  */
 TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         for (auto key : config.keys) {
             vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
@@ -1414,6 +1544,7 @@
  * decryption can't be performed.
  */
 TEST_P(DrmHalVendorDecryptTest, AttemptDecryptWithKeysRemoved) {
+    RETURN_IF_SKIPPED;
     for (auto config : contentConfigurations) {
         for (auto key : config.keys) {
             vector<uint8_t> iv(AES_BLOCK_SIZE, 0);