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