Add Privacy Mode and Serivce Certificate Support

This merges the following changes from the Widevine CDM repository:

da001b6  Add Privacy mode and service certificate
  This adds support to the CDM for privacy mode and service certificates.

92bf200  Add support for using Youtube Content Protection server for testing
  Enables testing with Youtube Content Protection server. Google Play license
  server is still the default. Select YTCP server by using the flag -icp
    e.g. adb shell '/system/bin/request_license_test -icp'

85dcd60  Fixes to enable privacy mode
  These includes changes to use PKCS7 padding, corrected root CA formatting
  and changes to integration test. Also refactored service certificate
  handling.

989971c  Correction to request license test
  Corrected PropertySetTest to provision when needed. Also added disabled
  privacy tests to run against YTCP staging server until GooglePlay
  integration is complete.

Bug: 10109249
Change-Id: If81d68c65d743d77a485406f48d1be41a74de0af
This commit is contained in:
Rahul Frias
2013-08-15 11:36:20 -07:00
parent f6c2a60485
commit a2e15186e5
12 changed files with 290 additions and 222 deletions

View File

@@ -19,11 +19,8 @@ class PolicyEngine;
class CdmLicense { class CdmLicense {
public: public:
CdmLicense() CdmLicense() : session_(NULL), initialized_(false) {}
: session_(NULL), ~CdmLicense() {}
initialized_(false),
service_certificate_response_pending_(false) {};
~CdmLicense() {};
bool Init(const std::string& token, CryptoSession* session, bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine); PolicyEngine* policy_engine);
@@ -49,7 +46,7 @@ class CdmLicense {
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url); std::string* server_url);
CdmResponseType HandleServiceCertificateResponse( CdmResponseType HandleServiceCertificateResponse(
const CdmKeyResponse& service_certificate_response); const video_widevine_server::sdk::SignedMessage& signed_message);
CdmResponseType HandleKeyErrorResponse( CdmResponseType HandleKeyErrorResponse(
const video_widevine_server::sdk::SignedMessage& signed_message); const video_widevine_server::sdk::SignedMessage& signed_message);
@@ -61,7 +58,6 @@ class CdmLicense {
std::string service_certificate_; std::string service_certificate_;
std::string init_data_; std::string init_data_;
bool initialized_; bool initialized_;
bool service_certificate_response_pending_;
// Used for certificate based licensing // Used for certificate based licensing
CdmKeyMessage key_request_; CdmKeyMessage key_request_;

View File

@@ -27,7 +27,7 @@
#include <string> #include <string>
#include "openssl/aes.h" #include "openssl/evp.h"
#include "openssl/rsa.h" #include "openssl/rsa.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
@@ -42,7 +42,7 @@ class AesCbcKey {
bool Encrypt(const std::string& in, std::string* out, std::string* iv); bool Encrypt(const std::string& in, std::string* out, std::string* iv);
private: private:
AES_KEY key_; EVP_CIPHER_CTX ctx_;
bool initialized_; bool initialized_;
CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey); CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey);
@@ -58,8 +58,8 @@ class RsaPublicKey {
// Encrypt a message using RSA-OAEP. Caller retains ownership of all // Encrypt a message using RSA-OAEP. Caller retains ownership of all
// parameters. Returns true if successful, false otherwise. // parameters. Returns true if successful, false otherwise.
bool Encrypt(const std::string& clear_message, bool Encrypt(const std::string& plaintext,
std::string* encrypted_message); std::string* ciphertext);
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. // Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if validation succeeds, false otherwise. // Returns true if validation succeeds, false otherwise.

View File

@@ -443,9 +443,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return KEY_ERROR; return KEY_ERROR;
} }
if (service_certificate_response_pending_)
return CdmLicense::HandleServiceCertificateResponse(license_response);
SignedMessage signed_response; SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) { if (!signed_response.ParseFromString(license_response)) {
LOGE( LOGE(
@@ -454,8 +451,17 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return KEY_ERROR; return KEY_ERROR;
} }
if (signed_response.type() == SignedMessage::ERROR) { switch (signed_response.type()) {
return HandleKeyErrorResponse(signed_response); case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE:
return CdmLicense::HandleServiceCertificateResponse(signed_response);
case SignedMessage::ERROR:
return HandleKeyErrorResponse(signed_response);
default:
LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d"
, signed_response.type());
return KEY_ERROR;
} }
if (!signed_response.has_signature()) { if (!signed_response.has_signature()) {
@@ -652,40 +658,12 @@ bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
signed_message.SerializeToString(signed_request); signed_message.SerializeToString(signed_request);
*server_url = server_url_; *server_url = server_url_;
service_certificate_response_pending_ = true;
return true; return true;
} }
CdmResponseType CdmLicense::HandleServiceCertificateResponse( CdmResponseType CdmLicense::HandleServiceCertificateResponse(
const CdmKeyResponse& service_certificate_response) { const video_widevine_server::sdk::SignedMessage& signed_response) {
if (!initialized_) {
LOGE("CdmLicense::HandleServiceCertificateResponse: not initialized");
return KEY_ERROR;
}
if (service_certificate_response.empty()) {
LOGE(
"CdmLicense::HandleServiceCertificateResponse: empty service "
"certificate response");
return KEY_ERROR;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(service_certificate_response))
return KEY_ERROR;
switch (signed_response.type()) {
case SignedMessage::ERROR:
return HandleKeyErrorResponse(signed_response);
case SignedMessage::SERVICE_CERTIFICATE:
break; // expected message type
default:
LOGE(
"CdmLicense::HandleServiceCertificateResponse: unexpected signed"
"message type: %d",
signed_response.type());
return KEY_ERROR;
}
SignedDeviceCertificate signed_service_certificate; SignedDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(signed_response.msg())) { if (!signed_service_certificate.ParseFromString(signed_response.msg())) {
@@ -696,12 +674,12 @@ CdmResponseType CdmLicense::HandleServiceCertificateResponse(
} }
RsaPublicKey root_ca_key; RsaPublicKey root_ca_key;
std::vector<uint8_t> ca_public_key( std::string ca_public_key(
&kServiceCertificateCAPublicKey[0], &kServiceCertificateCAPublicKey[0],
&kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]); &kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]);
if (!root_ca_key.Init(b2a_hex(ca_public_key))) { if (!root_ca_key.Init(ca_public_key)) {
LOGE( LOGE(
"CdmLicense::HandleServiceCertificateResponse: public key" "CdmLicense::HandleServiceCertificateResponse: public key "
"initialization failed"); "initialization failed");
return KEY_ERROR; return KEY_ERROR;
} }

View File

@@ -11,17 +11,15 @@
#include "privacy_crypto.h" #include "privacy_crypto.h"
#include "log.h" #include "log.h"
#include "openssl/aes.h"
#include "openssl/bio.h" #include "openssl/bio.h"
#include "openssl/err.h" #include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/pem.h" #include "openssl/pem.h"
#include "openssl/rsa.h"
#include "openssl/sha.h" #include "openssl/sha.h"
namespace { namespace {
const int kPssSaltLength = 20; const int kPssSaltLength = 20;
const int kRsaPkcs1OaepPaddingLength = 41; const int kRsaPkcs1OaepPaddingLength = 41;
const int kBitsInAByte = 8;
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -35,8 +33,10 @@ bool AesCbcKey::Init(const std::string& key) {
LOGE("AesCbcKey::Init: unexpected key size: %d", key.size()); LOGE("AesCbcKey::Init: unexpected key size: %d", key.size());
return false; return false;
} }
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
key.size() * kBitsInAByte, &key_) != 0) { EVP_CIPHER_CTX_init(&ctx_);
if (EVP_EncryptInit(&ctx_, EVP_aes_128_cbc(),
reinterpret_cast<const uint8_t*>(&key[0]), NULL) == 0) {
LOGE("AesCbcKey::Init: AES CBC key setup failure: %s", LOGE("AesCbcKey::Init: AES CBC key setup failure: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
return false; return false;
@@ -51,10 +51,6 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
LOGE("AesCbcKey::Encrypt: no cleartext provided"); LOGE("AesCbcKey::Encrypt: no cleartext provided");
return false; return false;
} }
if (in.size() % AES_BLOCK_SIZE) {
LOGE("AesCbcKey::Encrypt: cleartext not a block multiple: %d", in.size());
return false;
}
if (iv == NULL) { if (iv == NULL) {
LOGE("AesCbcKey::Encrypt: initialization vector destination not provided"); LOGE("AesCbcKey::Encrypt: initialization vector destination not provided");
return false; return false;
@@ -72,11 +68,33 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
return false; return false;
} }
out->resize(in.size()); if (EVP_EncryptInit(&ctx_, NULL, NULL,
reinterpret_cast<const uint8_t*>(iv->data())) == 0) {
LOGE("AesCbcKey::Encrypt: AES CBC iv setup failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(&in[0]), out->resize(in.size() + AES_BLOCK_SIZE);
reinterpret_cast<uint8_t*>(&out[0]), in.size(), &key_, int out_length = out->size();
reinterpret_cast<uint8_t*>(&iv[0]), AES_ENCRYPT); if (EVP_EncryptUpdate(
&ctx_, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(in.data())),
in.size()) == 0) {
LOGE("AesCbcKey::Encrypt: encryption failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
int padding = 0;
if (EVP_EncryptFinal(&ctx_, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) {
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
out->resize(out_length + padding);
return true; return true;
} }
@@ -103,7 +121,8 @@ bool RsaPublicKey::Init(const std::string& serialized_key) {
BIO_free(bio); BIO_free(bio);
if (key_ == NULL) { if (key_ == NULL) {
LOGE("RsaPublicKey::Init: RSA key deserialization failure"); LOGE("RsaPublicKey::Init: RSA key deserialization failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false; return false;
} }

View File

@@ -231,7 +231,7 @@ int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
wvcdm::InitLogging(argc, argv); wvcdm::InitLogging(argc, argv);
wvcdm::ConfigTestEnv config; wvcdm::ConfigTestEnv config(wvcdm::kGooglePlayServer);
g_client_auth.assign(config.client_auth()); g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system()); g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id()); g_wrong_key_id.assign(config.wrong_key_id());

View File

@@ -3,59 +3,43 @@
#include "config_test_env.h" #include "config_test_env.h"
namespace { namespace {
// Youtube Content Protection license server data
// choice of YT, GP, GP_Huahui, SDK_Hali const std::string kYtCpLicenseServer =
#define USE_SERVER_YT 1 "http://kir03wwwg185.widevine.net/drm";
#define USE_SERVER_GP 2 const std::string kYtCpClientAuth = "";
#define USE_SERVER_SDK_Hali 3 const std::string kYtCpKeyId =
// select which server to use for testing
#define USE_SERVER USE_SERVER_GP
#if (USE_SERVER == USE_SERVER_SDK_Hali)
static const std::string kLicenseServer =
"http://hamid.kir.corp.google.com:8888/drm";
static const std::string kClientAuth = "";
static const std::string kKeyId =
"000000347073736800000000" // blob size and pssh "000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121030313233343536373839616263646566"; // key - for gHali "0801121030313233343536373839616263646566"; // pssh data
#elif (USE_SERVER == USE_SERVER_YT) // Youtube license server data
const std::string kYtLicenseServer =
static const std::string kLicenseServer =
"https://www.youtube.com/api/drm/" "https://www.youtube.com/api/drm/"
"widevine?video_id=03681262dc412c06&source=YOUTUBE"; "widevine?video_id=03681262dc412c06&source=YOUTUBE";
static const std::string kClientAuth = ""; const std::string kYtClientAuth = "";
static const std::string kKeyId = const std::string kYtKeyId =
"000000347073736800000000" // blob size and pssh "000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121093789920E8D6520098577DF8F2DD5546"; // pssh data "0801121093789920E8D6520098577DF8F2DD5546"; // pssh data
#elif (USE_SERVER == USE_SERVER_GP) // Google Play license server data
const std::string kGpLicenseServer =
static const std::string kLicenseServer =
"https://jmt17.google.com/video-dev/license/GetCencLicense"; "https://jmt17.google.com/video-dev/license/GetCencLicense";
// NOTE: Append a userdata attribute to place a unique marker that the // NOTE: Append a userdata attribute to place a unique marker that the
// server team can use to track down specific requests during debugging // server team can use to track down specific requests during debugging
// e.g., "<existing-client-auth-string>&userdata=<your-ldap>.<your-tag>" // e.g., "<existing-client-auth-string>&userdata=<your-ldap>.<your-tag>"
// "<existing-client-auth-string>&userdata=jbmr2.dev" // "<existing-client-auth-string>&userdata=jbmr2.dev"
static const std::string kClientAuth = const std::string kGpClientAuth =
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
static const std::string kKeyId = const std::string kGpKeyId =
"000000347073736800000000" // blob size and pssh "000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data
#else
#error "Must define USE_SERVER"
#endif
// An invalid key id, expected to fail // An invalid key id, expected to fail
static const std::string kWrongKeyId = const std::string kWrongKeyId =
"000000347073736800000000" // blob size and pssh "000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
@@ -75,19 +59,28 @@ const std::string kProductionTestProvisioningServerUrl =
const std::string kServerSdkLicenseServer = const std::string kServerSdkLicenseServer =
"http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi"; "http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi";
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{ wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kDefaultHttpsPort, true, true },
{ wvcdm::kYouTubeContentProtectionServer, kYtCpLicenseServer,
kYtCpClientAuth, kYtCpKeyId, kDefaultHttpPort, false, false }
};
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
ConfigTestEnv::ConfigTestEnv() ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id)
: client_auth_(kClientAuth), : client_auth_(license_servers[server_id].client_tag),
key_id_(kKeyId), key_id_(license_servers[server_id].key_id),
key_system_("com.widevine.alpha"), key_system_("com.widevine.alpha"),
license_server_(kLicenseServer), license_server_(license_servers[server_id].url),
port_(kDefaultHttpsPort), port_(license_servers[server_id].port),
provisioning_server_url_(kProductionProvisioningServerUrl), provisioning_server_url_(kProductionProvisioningServerUrl),
provisioning_test_server_url_(kProductionTestProvisioningServerUrl), provisioning_test_server_url_(kProductionTestProvisioningServerUrl),
server_sdk_license_server_(kServerSdkLicenseServer), server_sdk_license_server_(kServerSdkLicenseServer),
use_chunked_transfer_(license_servers[server_id].use_chunked_transfer),
use_secure_transfer_(license_servers[server_id].use_secure_transfer),
wrong_key_id_(kWrongKeyId) {} wrong_key_id_(kWrongKeyId) {}
} // namespace wvcdm } // namespace wvcdm

View File

@@ -8,14 +8,29 @@
namespace { namespace {
const std::string kDefaultHttpsPort = "443"; const std::string kDefaultHttpsPort = "443";
const std::string kDefaultHttpPort = "80";
} }
namespace wvcdm { namespace wvcdm {
typedef enum {
kGooglePlayServer,
kYouTubeContentProtectionServer
} LicenseServerId;
// Configures default test environment. // Configures default test environment.
class ConfigTestEnv { class ConfigTestEnv {
public: public:
ConfigTestEnv(); typedef struct {
LicenseServerId id;
std::string url;
std::string client_tag;
std::string key_id;
std::string port;
bool use_chunked_transfer;
bool use_secure_transfer;
} LicenseServerConfiguration;
explicit ConfigTestEnv(LicenseServerId server_id);
~ConfigTestEnv() {}; ~ConfigTestEnv() {};
const std::string& client_auth() const { return client_auth_; } const std::string& client_auth() const { return client_auth_; }
@@ -32,6 +47,8 @@ class ConfigTestEnv {
const std::string& server_sdk_license_server() const { const std::string& server_sdk_license_server() const {
return server_sdk_license_server_; return server_sdk_license_server_;
} }
bool use_chunked_transfer() { return use_chunked_transfer_; }
bool use_secure_transfer() { return use_secure_transfer_; }
const KeyId& wrong_key_id() const { return wrong_key_id_; } const KeyId& wrong_key_id() const { return wrong_key_id_; }
void set_key_id(KeyId& key_id) { key_id_.assign(key_id); } void set_key_id(KeyId& key_id) { key_id_.assign(key_id); }
@@ -52,6 +69,8 @@ class ConfigTestEnv {
std::string provisioning_server_url_; std::string provisioning_server_url_;
std::string provisioning_test_server_url_; std::string provisioning_test_server_url_;
std::string server_sdk_license_server_; std::string server_sdk_license_server_;
bool use_chunked_transfer_;
bool use_secure_transfer_;
KeyId wrong_key_id_; KeyId wrong_key_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv); CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);

View File

@@ -12,10 +12,10 @@ size_t LicenseRequest::FindHeaderEndPosition(
return(response.find(kTwoBlankLines)); return(response.find(kTwoBlankLines));
} }
// Returns drm message in drm_msg. // This routine parses the license server's response message and
// The drm message is at the end of the response message. // extracts the drm message from the response header.
void LicenseRequest::GetDrmMessage(const std::string& response, void LicenseRequest::GetDrmMessage(const std::string& response,
std::string& drm_msg) { std::string& drm_msg) {
if (response.empty()) { if (response.empty()) {
drm_msg.clear(); drm_msg.clear();
return; return;

View File

@@ -22,7 +22,6 @@ UrlRequest::UrlRequest(const std::string& url, const std::string& port,
port_.assign(port); port_.assign(port);
} }
if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) { if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) {
LOGD("connected to %s", socket_.domain_name().c_str());
is_connected_ = true; is_connected_ = true;
} else { } else {
LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(), LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(),
@@ -99,26 +98,6 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
} }
} }
void UrlRequest::DumpMessage(const std::string& description,
const std::string& message) {
if (description.empty()) return;
LOGD("%s (%d bytes):", description.c_str(), message.size());
size_t remaining = message.size();
size_t portion = 0;
size_t start = 0;
while (remaining > 0) {
// LOGX may not get to empty its buffer if it is too large,
// pick an arbitrary small size to be safe
portion = (remaining < 512) ? remaining : 512;
LOGD("%s", message.substr(start, portion).c_str());
start += portion;
remaining -= portion;
}
LOGD("total bytes dumped(%d)", start);
}
int UrlRequest::GetResponse(std::string* message) { int UrlRequest::GetResponse(std::string* message) {
message->clear(); message->clear();
@@ -137,7 +116,7 @@ int UrlRequest::GetResponse(std::string* message) {
} while (bytes > 0); } while (bytes > 0);
ConcatenateChunkedResponse(response, message); ConcatenateChunkedResponse(response, message);
LOGD("%d bytes returned", message->size()); LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
return message->size(); return message->size();
} }
@@ -202,6 +181,8 @@ bool UrlRequest::PostRequest(const std::string& data) {
request_.append("\r\n\r\n"); request_.append("\r\n\r\n");
socket_.Write(request_.c_str(), request_.size()); socket_.Write(request_.c_str(), request_.size());
LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str());
LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str());
return true; return true;
} }
@@ -221,6 +202,8 @@ bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
request_.append("\r\n"); // terminates the request request_.append("\r\n"); // terminates the request
socket_.Write(request_.c_str(), request_.size()); socket_.Write(request_.c_str(), request_.size());
LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str());
LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str());
return true; return true;
} }

View File

@@ -20,7 +20,6 @@ class UrlRequest {
void AppendChunkToUpload(const std::string& data); void AppendChunkToUpload(const std::string& data);
void ConcatenateChunkedResponse(const std::string http_response, void ConcatenateChunkedResponse(const std::string http_response,
std::string* modified_response); std::string* modified_response);
void DumpMessage(const std::string& description, const std::string& message);
int GetResponse(std::string* message); int GetResponse(std::string* message);
int GetStatusCode(const std::string& response); int GetStatusCode(const std::string& response);
bool is_connected() const { return is_connected_; } bool is_connected() const { return is_connected_; }

View File

@@ -23,15 +23,28 @@ namespace {
// Default license server, can be configured using --server command line option // Default license server, can be configured using --server command line option
// Default key id (pssh), can be configured using --keyid command line option // Default key id (pssh), can be configured using --keyid command line option
std::string g_client_auth; std::string g_client_auth;
wvcdm::ConfigTestEnv* g_config = NULL;
wvcdm::KeyId g_key_id; wvcdm::KeyId g_key_id;
wvcdm::CdmKeySystem g_key_system; wvcdm::CdmKeySystem g_key_system;
std::string g_license_server; std::string g_license_server;
std::string g_port; std::string g_port;
wvcdm::KeyId g_wrong_key_id; wvcdm::KeyId g_wrong_key_id;
int g_use_full_path = 0; // cannot use boolean in getopt_long bool g_use_chunked_transfer = false;
} // namespace bool g_use_full_path = false;
bool g_use_secure_transfer = false;
wvcdm::LicenseServerId g_license_server_id = wvcdm::kGooglePlayServer;
namespace wvcdm { std::string kServiceCertificate =
"0803120F736572766963655F636572745F736E187B228E023082010A028201010"
"0A700366065DCBD545A2A40B4E1159458114F9458DDDEA71F3C2CE08809296157"
"675E567EEE278F59349A2AAA9DB44EFAA76AD4C97A53C14E9FE334F73DB7C9104"
"74F28DA3FCE317BFD0610EBF7BE92F9AFFB3E68DAEE1A644CF329F2739E39D8F6"
"6FD8B28082718EB5A4F2C23ECD0ACAB604CD9A138B54735425548CBE987A67ADD"
"AB34EB3FA82A84A679856575471CD127FEDA301C06A8B24039688BE97662ABC53"
"C98306515A88651318E43AED6BF1615B4CC81EF4C2AE085E2D5FF8127FA2FCBB2"
"11830DAFE40FB01CA2E370ECEDD768782460B3A778FC072072C7F9D1E865BED27"
"29DF039762EF44D35B3DDB9C5E1B7B39B40B6D046BBBBB2C5FCFB37A050203010"
"0013A0D6D79736572766963652E636F6D";
// TODO(rfrias): refactor to print out the decryption test names // TODO(rfrias): refactor to print out the decryption test names
struct SubSampleInfo { struct SubSampleInfo {
@@ -192,9 +205,7 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = {
namespace wvcdm { namespace wvcdm {
class TestWvCdmClientPropertySet : public CdmClientPropertySet { class TestWvCdmClientPropertySet : public CdmClientPropertySet {
public: public:
TestWvCdmClientPropertySet() TestWvCdmClientPropertySet() : use_privacy_mode_(false) {}
: service_certificate_(std::vector<uint8_t>()),
use_privacy_mode_(false) {}
virtual ~TestWvCdmClientPropertySet() {} virtual ~TestWvCdmClientPropertySet() {}
virtual std::string security_level() const { return security_level_; } virtual std::string security_level() const { return security_level_; }
@@ -289,32 +300,27 @@ class WvCdmRequestLicenseTest : public testing::Test {
const std::string& client_auth, const std::string& client_auth,
int expected_response) { int expected_response) {
// Use secure connection and chunk transfer coding. // Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth, g_port, true, true); UrlRequest url_request(server_url + client_auth, g_port,
g_use_secure_transfer, g_use_chunked_transfer);
if (!url_request.is_connected()) { if (!url_request.is_connected()) {
return ""; return "";
} }
url_request.PostRequest(key_msg_); url_request.PostRequest(key_msg_);
std::string message; std::string message;
int resp_bytes = url_request.GetResponse(&message); int resp_bytes = url_request.GetResponse(&message);
LOGD("end %d bytes response dump", resp_bytes);
LOGD("end %s ", message.c_str());
// Youtube server returns 400 for invalid message while play server returns // Youtube server returns 400 for invalid message while play server returns
// 500, so just test inequity here for invalid message // 500, so just test inequity here for invalid message
int status_code = url_request.GetStatusCode(message); int status_code = url_request.GetStatusCode(message);
if (expected_response == 200) { if (expected_response == 200) {
EXPECT_EQ(200, status_code); EXPECT_EQ(200, status_code);
} else {
EXPECT_NE(200, status_code);
} }
std::string drm_msg; std::string drm_msg;
if (200 == status_code) { if (200 == status_code) {
LicenseRequest lic_request; LicenseRequest lic_request;
lic_request.GetDrmMessage(message, drm_msg); lic_request.GetDrmMessage(message, drm_msg);
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), LOGV("HTTP response body: (%u bytes)", drm_msg.size());
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
} }
return drm_msg; return drm_msg;
} }
@@ -324,7 +330,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
std::string GetCertRequestResponse(const std::string& server_url, std::string GetCertRequestResponse(const std::string& server_url,
int expected_response) { int expected_response) {
// Use secure connection and chunk transfer coding. // Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url, g_port, true, true); UrlRequest url_request(server_url, kDefaultHttpsPort, true, true);
if (!url_request.is_connected()) { if (!url_request.is_connected()) {
return ""; return "";
} }
@@ -361,7 +367,6 @@ class WvCdmRequestLicenseTest : public testing::Test {
} }
} }
wvcdm::ConfigTestEnv config_;
wvcdm::WvContentDecryptionModule decryptor_; wvcdm::WvContentDecryptionModule decryptor_;
CdmKeyMessage key_msg_; CdmKeyMessage key_msg_;
CdmSessionId session_id_; CdmSessionId session_id_;
@@ -378,10 +383,10 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url)); &key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
@@ -393,19 +398,19 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url)); &key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest( EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
&key_msg_, &provisioning_server_url)); &key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
response = response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::UNKNOWN_ERROR, EXPECT_EQ(wvcdm::UNKNOWN_ERROR,
decryptor_.HandleProvisioningResponse(response)); decryptor_.HandleProvisioningResponse(response));
@@ -425,7 +430,26 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
decryptor_.OpenSession(g_key_system, &property_set_L1, &session_id_L1); decryptor_.OpenSession(g_key_system, &property_set_L1, &session_id_L1);
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
property_set_L3.set_use_privacy_mode(false); property_set_L3.set_use_privacy_mode(false);
decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_L3);
CdmResponseType sts = decryptor_.OpenSession(g_key_system, &property_set_L3,
&session_id_L3);
if (NEED_PROVISIONING == sts) {
std::string provisioning_server_url;
EXPECT_EQ(
NO_ERROR,
decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response =
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set_L3,
&session_id_L3));
} else {
EXPECT_EQ(NO_ERROR, sts);
}
property_set_Ln.set_security_level(""); property_set_Ln.set_security_level("");
decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln); decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln);
@@ -459,9 +483,9 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
decryptor_.GetProvisioningRequest(&key_msg_, decryptor_.GetProvisioningRequest(&key_msg_,
&provisioning_server_url)); &provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url()); EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url());
std::string response = std::string response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200); GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size())); EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response)); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
@@ -472,33 +496,27 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_F(WvCdmRequestLicenseTest, UsePrivacyModeTest) { TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeTest) {
TestWvCdmClientPropertySet property_set; TestWvCdmClientPropertySet property_set;
property_set.set_use_privacy_mode(true); property_set.set_use_privacy_mode(true);
decryptor_.OpenSession(g_key_system, &property_set, &session_id_); decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
if (property_set.service_certificate().empty()) { GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); std::string resp = GetKeyRequestResponse(g_license_server,
std::string resp = GetKeyRequestResponse(g_license_server, g_client_auth, 200);
g_client_auth, 200); EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), wvcdm::NEED_KEY);
wvcdm::NEED_KEY);
std::vector<uint8_t> service_certificate(key_msg_.begin(), key_msg_.end());
property_set.set_service_certificate(service_certificate);
}
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
}
// property has service certificate set from previous request TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest) {
EXPECT_FALSE(property_set.service_certificate().empty()); TestWvCdmClientPropertySet property_set;
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
property_set.set_use_privacy_mode(false); property_set.set_use_privacy_mode(true);
property_set.set_service_certificate(a2b_hex(kServiceCertificate));
decryptor_.OpenSession(g_key_system, &property_set, &session_id_); decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -521,7 +539,7 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
decryptor_.CloseSession(session_id_); decryptor_.CloseSession(session_id_);
} }
TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) { TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -913,32 +931,92 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
*/ */
} // namespace wvcdm } // namespace wvcdm
void show_menu(char* prog_name) {
std::cout << std::endl;
std::cout << "usage: " << prog_name << " [options]" << std::endl << std::endl;
std::cout << " enclose multiple arguments in '' when using adb shell"
<< std::endl;
std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'"
<< std::endl;
std::cout << " or adb shell '" << prog_name << " -u\"url\"'"
<< std::endl << std::endl;
std::cout << std::setw(35) << std::left << " -c/--chunked_transfer";
std::cout << "specifies chunked transfer encoding in request"
<< std::endl << std::endl;
std::cout << std::setw(35) << std::left << " -f/--use_full_path";
std::cout << "specify server url is not a proxy server" << std::endl;
std::cout << std::endl;
std::cout << std::setw(35) << std::left
<< " -i/--license_server_id=<gp/cp>";
std::cout << "specifies which default server settings to use: " << std::endl;
std::cout << std::setw(35) << std::left << " ";
std::cout << "gp (case sensitive) for GooglePlay server" << std::endl;
std::cout << std::setw(35) << std::left << " ";
std::cout << "cp (case sensitive) for Youtube Content Protection server"
<< std::endl << std::endl;
std::cout << std::setw(35) << std::left << " -k/--keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format"
<< std::endl << std::endl;
std::cout << std::setw(35) << std::left
<< " -p/--port=<port>";
std::cout << "specifies the connection port" << std::endl << std::endl;
std::cout << std::setw(35) << std::left
<< " -s/--secure_transfer";
std::cout << "use https transfer protocol" << std::endl << std::endl;
std::cout << std::setw(35) << std::left
<< " -u/--server=<server_url>";
std::cout
<< "configure the license server url, please include http[s] in the url"
<< std::endl << std::endl;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
wvcdm::ConfigTestEnv config; bool show_usage = false;
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
// The following variables are configurable through command line options.
g_license_server.assign(config.license_server());
g_key_id.assign(config.key_id());
g_port.assign(config.port());
std::string license_server(g_license_server);
int show_usage = 0;
static const struct option long_options[] = { static const struct option long_options[] = {
{"use_full_path", no_argument, &g_use_full_path, 0}, { "chunked_transfer", no_argument, NULL, 'c' },
{"keyid", required_argument, NULL, 'k'}, { "keyid", required_argument, NULL, 'k' },
{"port", required_argument, NULL, 'p'}, { "license_server_id", required_argument, NULL, 'i' },
{"server", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'}}; { "license_server_url", required_argument, NULL, 'u' },
{ "port", required_argument, NULL, 'p' },
{ "secure_transfer", no_argument, NULL, 's' },
{ "use_full_path", no_argument, NULL, 'f' },
{ NULL, 0, NULL, '\0' }
};
int option_index = 0; int option_index = 0;
int opt = 0; int opt = 0;
while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, while ((opt = getopt_long(argc, argv, "cfi:k:p:su:", long_options,
&option_index)) != -1) { &option_index)) != -1) {
switch (opt) { switch (opt) {
case 'c': {
g_use_chunked_transfer = true;
break;
}
case 'f': {
g_use_full_path = true;
break;
}
case 'i': {
std::string license_id(optarg);
if (!license_id.compare("gp")) {
g_license_server_id = wvcdm::kGooglePlayServer;
} else if (!license_id.compare("cp")) {
g_license_server_id = wvcdm::kYouTubeContentProtectionServer;
} else {
std::cout << "Invalid license server id" << optarg << std::endl;
show_usage = true;
}
break;
}
case 'k': { case 'k': {
g_key_id.clear(); g_key_id.clear();
g_key_id.assign(optarg); g_key_id.assign(optarg);
@@ -950,61 +1028,62 @@ int main(int argc, char** argv) {
break; break;
} }
case 's': { case 's': {
g_use_secure_transfer = true;
break;
}
case 'u': {
g_license_server.clear(); g_license_server.clear();
g_license_server.assign(optarg); g_license_server.assign(optarg);
break; break;
} }
case 'u': {
g_use_full_path = 1;
break;
}
case '?': { case '?': {
show_usage = 1; show_usage = true;
break; break;
} }
} }
} }
if (show_usage) { if (show_usage) {
std::cout << std::endl; show_menu(argv[0]);
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
std::cout << " enclose multiple arguments in '' when using adb shell"
<< std::endl;
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'"
<< std::endl << std::endl;
std::cout << std::setw(30) << std::left << " --port=<connection port>";
std::cout << "specifies the port number, in decimal format" << std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << g_port << std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout
<< "configure the license server url, please include http[s] in the url"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << license_server << std::endl;
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format" << std::endl;
std::cout << std::setw(30) << std::left << " default keyid:";
std::cout << g_key_id << std::endl;
std::cout << std::setw(30) << std::left << " --use_full_path";
std::cout << "specify server url is not a proxy server" << std::endl;
std::cout << std::endl;
return 0; return 0;
} }
g_config = new wvcdm::ConfigTestEnv(g_license_server_id);
g_client_auth.assign(g_config->client_auth());
g_key_system.assign(g_config->key_system());
g_wrong_key_id.assign(g_config->wrong_key_id());
// The following variables are configurable through command line
// options. If the command line arguments are absent, use the settings
// in license_servers[] pointed to by g_config.
if (g_key_id.empty()) {
g_key_id.assign(g_config->key_id());
}
if (g_license_server.empty()) {
g_license_server.assign(g_config->license_server());
}
if (g_port.empty()) {
g_port.assign(g_config->port());
}
if (!g_use_chunked_transfer) {
g_use_chunked_transfer = g_config->use_chunked_transfer();
}
if (!g_use_secure_transfer) {
g_use_secure_transfer = g_config->use_secure_transfer();
}
// Displays server url, port and key Id being used
std::cout << std::endl; std::cout << std::endl;
std::cout << "Server: " << g_license_server << std::endl; std::cout << "Server: " << g_license_server << std::endl;
std::cout << "Port: " << g_port << std::endl; std::cout << "Port: " << g_port << std::endl;
std::cout << "KeyID: " << g_key_id << std::endl << std::endl; std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
g_key_id = wvcdm::a2bs_hex(g_key_id); g_key_id = wvcdm::a2bs_hex(g_key_id);
config.set_license_server(g_license_server); g_config->set_license_server(g_license_server);
config.set_port(g_port); g_config->set_port(g_port);
config.set_key_id(g_key_id); g_config->set_key_id(g_key_id);
return RUN_ALL_TESTS(); int status = RUN_ALL_TESTS();
delete g_config;
return status;
} }

View File

@@ -10,6 +10,8 @@ adb root && adb wait-for-device remount && adb sync
adb shell /system/bin/oemcrypto_test adb shell /system/bin/oemcrypto_test
adb shell /system/bin/request_license_test adb shell /system/bin/request_license_test
adb shell /system/bin/request_license_test -icp --gtest_filter=WvCdmRequestLicenseTest.DISABLED_PrivacyModeTest --gtest_also_run_disabled_tests
adb shell /system/bin/request_license_test -icp --gtest_filter=WvCdmRequestLicenseTest.DISABLED_PrivacyModeWithServiceCertificateTest --gtest_also_run_disabled_tests
adb shell /system/bin/policy_engine_unittest adb shell /system/bin/policy_engine_unittest
adb shell /system/bin/libwvdrmmediacrypto_test adb shell /system/bin/libwvdrmmediacrypto_test
adb shell /system/bin/libwvdrmdrmplugin_test adb shell /system/bin/libwvdrmdrmplugin_test