Create unique cdm engines per WVDrmPlugin instance
This change creates a unique id in the cdm identifier in order to force a one-to-one mapping between WVDrmPlugin instances and CDM Engines. This change simplifies some assumptions. This includes ensuring that the metrics for a given MediaDrm instance map to a given CdmEngine instance. Bug: 73724453 Test: Updated unit tests. GTS test pass. Shaka Player, Netflix and Google Play test. Change-Id: I7e041b6cdf3e272d067da49d25a297b4a4663f1f
This commit is contained in:
@@ -365,6 +365,10 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
const std::string& origin() const { return mCdmIdentifier.origin; }
|
||||
bool set_origin(const std::string& id);
|
||||
|
||||
// Indicates whether the builder can still be modified. This returns false
|
||||
// until a call to getCdmIdentifier.
|
||||
bool is_sealed() { return mIsIdentifierSealed; }
|
||||
|
||||
private:
|
||||
WVDRM_DISALLOW_COPY_AND_ASSIGN(CdmIdentifierBuilder);
|
||||
|
||||
@@ -382,6 +386,13 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
// outside this class should use getDeviceUniqueId() to get the
|
||||
// application-safe device-unique ID.
|
||||
Status getOemcryptoDeviceId(std::string* id);
|
||||
|
||||
// The unique identifier is meant to ensure that two clients with the
|
||||
// same spoid, origin and app package name still get different cdm engine
|
||||
// instances. This is a stepping stone to simplifying the implementation.
|
||||
// Note that we do not have a lock or mutex around this object. We assume
|
||||
// that locking is handled external to this object.
|
||||
uint32_t getNextUniqueId();
|
||||
} mCdmIdentifierBuilder;
|
||||
|
||||
sp<wvcdm::WvContentDecryptionModule> const mCDM;
|
||||
|
||||
@@ -392,7 +392,7 @@ status_t WVDrmPlugin::provideProvisionResponse(
|
||||
}
|
||||
CdmProvisioningResponse cdmResponse(response.begin(), response.end());
|
||||
if (cdmResponse == kSpecialUnprovisionResponse) {
|
||||
if (mCdmIdentifier == kDefaultCdmIdentifier) {
|
||||
if (mCdmIdentifier.IsEquivalentToDefault()) {
|
||||
return kErrorNoOriginSpecified;
|
||||
}
|
||||
return unprovision(mCdmIdentifier);
|
||||
@@ -540,9 +540,14 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
|
||||
} else if (name == "serviceCertificate") {
|
||||
value = ToVector(mPropertySet.service_certificate());
|
||||
} else if (name == "metrics") {
|
||||
std::string metrics_value;
|
||||
mCDM->GetSerializedMetrics(&metrics_value);
|
||||
value = ToVector(metrics_value);
|
||||
std::string serialized_metrics;
|
||||
drm_metrics::WvCdmMetrics metrics;
|
||||
mCDM->GetMetrics(mCdmIdentifier, &metrics);
|
||||
if (!metrics.SerializeToString(&serialized_metrics)) {
|
||||
return android::ERROR_DRM_UNKNOWN;
|
||||
} else {
|
||||
value = ToVector(serialized_metrics);
|
||||
}
|
||||
} else {
|
||||
ALOGE("App requested unknown byte array property %s", name.string());
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "mapErrors-inl.h"
|
||||
#include "media/stagefright/MediaErrors.h"
|
||||
#include "metrics.pb.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -139,6 +140,16 @@ WVDrmPlugin::~WVDrmPlugin() {
|
||||
}
|
||||
}
|
||||
mCryptoSessions.clear();
|
||||
CdmIdentifier identifier;
|
||||
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||
if (status != Status::OK) {
|
||||
ALOGE("Failed to get cdm identifier %d", status);
|
||||
} else {
|
||||
status = mapCdmResponseType(mCDM->CloseCdm(identifier));
|
||||
if (status != Status::OK) {
|
||||
ALOGE("Failed to get close cdm %d", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::openSessionCommon(std::vector<uint8_t>& sessionId) {
|
||||
@@ -616,7 +627,7 @@ Return<void> WVDrmPlugin::provideProvisionResponse(
|
||||
|
||||
CdmProvisioningResponse cdmResponse(resp.begin(), resp.end());
|
||||
if (cdmResponse == kSpecialUnprovisionResponse) {
|
||||
if (identifier == kDefaultCdmIdentifier) {
|
||||
if (identifier.IsEquivalentToDefault()) {
|
||||
ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified");
|
||||
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(certificate),
|
||||
toHidlVec(wrappedKey));
|
||||
@@ -984,9 +995,27 @@ Return<void> WVDrmPlugin::getPropertyByteArray(
|
||||
} else if (name == "serviceCertificate") {
|
||||
value = StrToVector(mPropertySet.service_certificate());
|
||||
} else if (name == "metrics") {
|
||||
std::string metrics_value;
|
||||
mCDM->GetSerializedMetrics(&metrics_value);
|
||||
value = StrToVector(metrics_value);
|
||||
drm_metrics::WvCdmMetrics metrics;
|
||||
// If the cdm identifier is not yet sealed, then there are no metrics
|
||||
// for that cdm engine. Avoid calling getCdmIdentifier and sealing
|
||||
// the identifier builder.
|
||||
if (mCdmIdentifierBuilder.is_sealed()) {
|
||||
CdmIdentifier identifier;
|
||||
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||
if (status != Status::OK) {
|
||||
ALOGE("Unexpected error retrieving cdm identifier: %d", status);
|
||||
} else {
|
||||
status = mapCdmResponseType(mCDM->GetMetrics(identifier, &metrics));
|
||||
}
|
||||
}
|
||||
if (status == Status::OK) {
|
||||
std::string serialized_metrics;
|
||||
if (!metrics.SerializeToString(&serialized_metrics)) {
|
||||
status = Status::ERROR_DRM_UNKNOWN;
|
||||
} else {
|
||||
value = StrToVector(serialized_metrics);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ALOGE("App requested unknown byte array property %s", name.c_str());
|
||||
status = Status::ERROR_DRM_CANNOT_HANDLE;
|
||||
@@ -1641,6 +1670,7 @@ WVDrmPlugin::CdmIdentifierBuilder::CdmIdentifierBuilder(
|
||||
mAppPackageName(appPackageName),
|
||||
mParent(parent) {
|
||||
mCdmIdentifier.app_package_name = mAppPackageName;
|
||||
mCdmIdentifier.unique_id = getNextUniqueId();
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
|
||||
@@ -1648,7 +1678,6 @@ Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
|
||||
if (!mIsIdentifierSealed) {
|
||||
Status res = calculateSpoid();
|
||||
if (res != Status::OK) return res;
|
||||
|
||||
mIsIdentifierSealed = true;
|
||||
}
|
||||
*identifier = mCdmIdentifier;
|
||||
@@ -1717,6 +1746,12 @@ Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
|
||||
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
|
||||
}
|
||||
|
||||
uint32_t WVDrmPlugin::CdmIdentifierBuilder::getNextUniqueId() {
|
||||
// Start with 1. 0 is reserved for the default cdm identifier.
|
||||
static uint32_t unique_id = 1;
|
||||
return ++unique_id;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
} // namespace V1_1
|
||||
} // namespace drm
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "media/stagefright/foundation/ABase.h"
|
||||
#include "media/stagefright/MediaErrors.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
#include "wv_content_decryption_module.h"
|
||||
@@ -120,15 +121,17 @@ const uint8_t* const kUnprovisionResponse =
|
||||
const size_t kUnprovisionResponseSize = 11;
|
||||
const std::string kDeviceId("0123456789\0ABCDEF", 17);
|
||||
|
||||
// This is a serialized MetricsGroup message containing a small amount of
|
||||
// This is a serialized WvCdmMetrics message containing a small amount of
|
||||
// sample data. This ensures we're able to extract it via a property.
|
||||
const char kSerializedMetrics[] = {
|
||||
0x0a, 0x0a, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, 0x02, 0x08, 0x00,
|
||||
0x0a, 0x12, 0x0a, 0x05, 0x74, 0x65, 0x73, 0x74, 0x32, 0x12, 0x09, 0x11,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x15, 0x0a, 0x05,
|
||||
0x74, 0x65, 0x73, 0x74, 0x33, 0x12, 0x0c, 0x1a, 0x0a, 0x74, 0x65, 0x73,
|
||||
0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65
|
||||
};
|
||||
const char kSerializedMetricsHex[] =
|
||||
"0a580a001a0210072202100d2a02100832182216636f6d2e676f6f676c652e616e64726f69"
|
||||
"642e676d734208220631342e302e304a06080112020800520610d5f3fad5056a0b1d00fd4c"
|
||||
"47280132020804a2010608011202080012cb010a0622047369643412b5010a021001520919"
|
||||
"1d5a643bdfff50405a0b1d00d01945280132021001620d1d00f8e84528013204080020006a"
|
||||
"0310b739820102100d8a01060801120248009a010310ff01da0106080112024800e2010e1d"
|
||||
"005243472801320528800248009a020d1d00b016452801320428404800a202060801120248"
|
||||
"19aa0206080212024800b2020b1d8098f047280132024800ba02021001ca020b1d00101945"
|
||||
"280132024800e202021004fa02021002a203021000b2030210021a09196891ed7c3f355040";
|
||||
|
||||
#define N_ELEM(a) (sizeof(a)/sizeof(a[0]))
|
||||
} // anonymous namespace
|
||||
@@ -198,7 +201,8 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
|
||||
MOCK_METHOD1(IsValidServiceCertificate, bool(const std::string&));
|
||||
|
||||
MOCK_METHOD1(GetSerializedMetrics, void(std::string*));
|
||||
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||
drm_metrics::WvCdmMetrics*));
|
||||
};
|
||||
|
||||
class MockCrypto : public WVGenericCryptoInterface {
|
||||
@@ -1126,8 +1130,9 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
static const std::string oemCryptoApiVersion = "13";
|
||||
static const std::string currentSRMVersion = "1";
|
||||
static const std::string cdmVersion = "Infinity Minus 1";
|
||||
std::string serializedMetrics(
|
||||
kSerializedMetrics, kSerializedMetrics + sizeof(kSerializedMetrics));
|
||||
drm_metrics::WvCdmMetrics expected_metrics;
|
||||
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
||||
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||
@@ -1171,8 +1176,9 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
.WillOnce(DoAll(SetArgPointee<2>(cdmVersion),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, GetSerializedMetrics(_))
|
||||
.WillOnce(SetArgPointee<0>(serializedMetrics));
|
||||
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
std::string stringResult;
|
||||
@@ -1275,14 +1281,51 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
EXPECT_STREQ(currentSRMVersion.c_str(), stringResult.c_str());
|
||||
});
|
||||
|
||||
// This call occurs before any open session or other call. This means
|
||||
// that the cdm identifer is not yet sealed, and metrics return empty
|
||||
// metrics data.
|
||||
plugin.getPropertyByteArray(
|
||||
hidl_string("metrics"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
std::vector<uint8_t> id(vectorResult);
|
||||
EXPECT_THAT(id, ElementsAreArray(serializedMetrics.data(),
|
||||
serializedMetrics.size()));
|
||||
const char empty[] = {};
|
||||
EXPECT_THAT(id, ElementsAreArray(empty, sizeof(empty)));
|
||||
});
|
||||
|
||||
// Set expectations for the OpenSession call and a CloseSession call.
|
||||
EXPECT_CALL(*cdm,
|
||||
OpenSession(StrEq("com.widevine"), _, HasOrigin(EMPTY_ORIGIN), _, _))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
// Provide expected behavior when plugin requests session control info
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(AtLeast(0));
|
||||
|
||||
// This call causes the cdm identifier to become sealed.
|
||||
std::vector<uint8_t> sessionId;
|
||||
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(),
|
||||
hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
// This call occurs after open session. The CDM identifer should be sealed.
|
||||
// And the call should populate the mock metrics data.
|
||||
plugin.getPropertyByteArray(
|
||||
hidl_string("metrics"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
std::vector<uint8_t> id(vectorResult);
|
||||
EXPECT_THAT(id, ElementsAreArray(serialized_metrics.data(),
|
||||
serialized_metrics.size()));
|
||||
});
|
||||
|
||||
ASSERT_EQ(Status::OK, plugin.closeSession(toHidlVec(sessionId)));
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "media/stagefright/foundation/ABase.h"
|
||||
#include "media/stagefright/foundation/AString.h"
|
||||
#include "media/stagefright/MediaErrors.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
#include "wv_content_decryption_module.h"
|
||||
@@ -33,14 +34,18 @@ const uint8_t* const kUnprovisionResponse =
|
||||
reinterpret_cast<const uint8_t*>("unprovision");
|
||||
const size_t kUnprovisionResponseSize = 11;
|
||||
|
||||
// This is a serialized MetricsGroup message containing a small amount of
|
||||
// This is a serialized WvCdmMetrics message containing a small amount of
|
||||
// sample data. This ensures we're able to extract it via a property.
|
||||
const char kSerializedMetrics[] = {
|
||||
0x0a, 0x0a, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, 0x02, 0x08, 0x00,
|
||||
0x0a, 0x12, 0x0a, 0x05, 0x74, 0x65, 0x73, 0x74, 0x32, 0x12, 0x09, 0x11,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x15, 0x0a, 0x05,
|
||||
0x74, 0x65, 0x73, 0x74, 0x33, 0x12, 0x0c, 0x1a, 0x0a, 0x74, 0x65, 0x73,
|
||||
0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65};
|
||||
const char kSerializedMetricsHex[] =
|
||||
"0a580a001a0210072202100d2a02100832182216636f6d2e676f6f676c652e616e64726f69"
|
||||
"642e676d734208220631342e302e304a06080112020800520610d5f3fad5056a0b1d00fd4c"
|
||||
"47280132020804a2010608011202080012cb010a0622047369643412b5010a021001520919"
|
||||
"1d5a643bdfff50405a0b1d00d01945280132021001620d1d00f8e84528013204080020006a"
|
||||
"0310b739820102100d8a01060801120248009a010310ff01da0106080112024800e2010e1d"
|
||||
"005243472801320528800248009a020d1d00b016452801320428404800a202060801120248"
|
||||
"19aa0206080212024800b2020b1d8098f047280132024800ba02021001ca020b1d00101945"
|
||||
"280132024800e202021004fa02021002a203021000b2030210021a09196891ed7c3f355040";
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class MockCDM : public WvContentDecryptionModule {
|
||||
@@ -108,7 +113,8 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
|
||||
MOCK_METHOD1(IsValidServiceCertificate, bool(const std::string&));
|
||||
|
||||
MOCK_METHOD1(GetSerializedMetrics, void(std::string*));
|
||||
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||
drm_metrics::WvCdmMetrics*));
|
||||
};
|
||||
|
||||
class MockCrypto : public WVGenericCryptoInterface {
|
||||
@@ -850,8 +856,10 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
static const string oemCryptoApiVersion = "10";
|
||||
static const string currentSRMVersion = "1";
|
||||
static const string cdmVersion = "Infinity Minus 1";
|
||||
string serializedMetrics(kSerializedMetrics,
|
||||
kSerializedMetrics + sizeof(kSerializedMetrics));
|
||||
|
||||
drm_metrics::WvCdmMetrics expected_metrics;
|
||||
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
||||
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||
@@ -895,8 +903,9 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
.WillOnce(DoAll(SetArgPointee<2>(cdmVersion),
|
||||
Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, GetSerializedMetrics(_))
|
||||
.WillOnce(SetArgPointee<0>(serializedMetrics));
|
||||
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
String8 stringResult;
|
||||
Vector<uint8_t> vectorResult;
|
||||
@@ -962,8 +971,8 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
vectorResult.clear();
|
||||
res = plugin.getPropertyByteArray(String8("metrics"), vectorResult);
|
||||
ASSERT_EQ(OK, res);
|
||||
EXPECT_THAT(vectorResult, ElementsAreArray(serializedMetrics.data(),
|
||||
serializedMetrics.size()));
|
||||
EXPECT_THAT(vectorResult, ElementsAreArray(serialized_metrics.data(),
|
||||
serialized_metrics.size()));
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
||||
|
||||
Reference in New Issue
Block a user