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:
@@ -19,11 +19,8 @@ class PolicyEngine;
|
||||
class CdmLicense {
|
||||
public:
|
||||
|
||||
CdmLicense()
|
||||
: session_(NULL),
|
||||
initialized_(false),
|
||||
service_certificate_response_pending_(false) {};
|
||||
~CdmLicense() {};
|
||||
CdmLicense() : session_(NULL), initialized_(false) {}
|
||||
~CdmLicense() {}
|
||||
|
||||
bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
@@ -49,7 +46,7 @@ class CdmLicense {
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
CdmResponseType HandleServiceCertificateResponse(
|
||||
const CdmKeyResponse& service_certificate_response);
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
@@ -61,7 +58,6 @@ class CdmLicense {
|
||||
std::string service_certificate_;
|
||||
std::string init_data_;
|
||||
bool initialized_;
|
||||
bool service_certificate_response_pending_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
CdmKeyMessage key_request_;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
@@ -42,7 +42,7 @@ class AesCbcKey {
|
||||
bool Encrypt(const std::string& in, std::string* out, std::string* iv);
|
||||
|
||||
private:
|
||||
AES_KEY key_;
|
||||
EVP_CIPHER_CTX ctx_;
|
||||
bool initialized_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey);
|
||||
@@ -58,8 +58,8 @@ class RsaPublicKey {
|
||||
|
||||
// Encrypt a message using RSA-OAEP. Caller retains ownership of all
|
||||
// parameters. Returns true if successful, false otherwise.
|
||||
bool Encrypt(const std::string& clear_message,
|
||||
std::string* encrypted_message);
|
||||
bool Encrypt(const std::string& plaintext,
|
||||
std::string* ciphertext);
|
||||
|
||||
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// Returns true if validation succeeds, false otherwise.
|
||||
|
||||
@@ -443,9 +443,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (service_certificate_response_pending_)
|
||||
return CdmLicense::HandleServiceCertificateResponse(license_response);
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response)) {
|
||||
LOGE(
|
||||
@@ -454,8 +451,17 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (signed_response.type() == SignedMessage::ERROR) {
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
switch (signed_response.type()) {
|
||||
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()) {
|
||||
@@ -652,40 +658,12 @@ bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
signed_message.SerializeToString(signed_request);
|
||||
*server_url = server_url_;
|
||||
service_certificate_response_pending_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleServiceCertificateResponse(
|
||||
const CdmKeyResponse& service_certificate_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;
|
||||
}
|
||||
const video_widevine_server::sdk::SignedMessage& signed_response) {
|
||||
|
||||
SignedDeviceCertificate signed_service_certificate;
|
||||
if (!signed_service_certificate.ParseFromString(signed_response.msg())) {
|
||||
@@ -696,12 +674,12 @@ CdmResponseType CdmLicense::HandleServiceCertificateResponse(
|
||||
}
|
||||
|
||||
RsaPublicKey root_ca_key;
|
||||
std::vector<uint8_t> ca_public_key(
|
||||
std::string ca_public_key(
|
||||
&kServiceCertificateCAPublicKey[0],
|
||||
&kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]);
|
||||
if (!root_ca_key.Init(b2a_hex(ca_public_key))) {
|
||||
if (!root_ca_key.Init(ca_public_key)) {
|
||||
LOGE(
|
||||
"CdmLicense::HandleServiceCertificateResponse: public key"
|
||||
"CdmLicense::HandleServiceCertificateResponse: public key "
|
||||
"initialization failed");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -11,17 +11,15 @@
|
||||
#include "privacy_crypto.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
|
||||
namespace {
|
||||
const int kPssSaltLength = 20;
|
||||
const int kRsaPkcs1OaepPaddingLength = 41;
|
||||
const int kBitsInAByte = 8;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -35,8 +33,10 @@ bool AesCbcKey::Init(const std::string& key) {
|
||||
LOGE("AesCbcKey::Init: unexpected key size: %d", key.size());
|
||||
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",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
@@ -51,10 +51,6 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
LOGE("AesCbcKey::Encrypt: no cleartext provided");
|
||||
return false;
|
||||
}
|
||||
if (in.size() % AES_BLOCK_SIZE) {
|
||||
LOGE("AesCbcKey::Encrypt: cleartext not a block multiple: %d", in.size());
|
||||
return false;
|
||||
}
|
||||
if (iv == NULL) {
|
||||
LOGE("AesCbcKey::Encrypt: initialization vector destination not provided");
|
||||
return false;
|
||||
@@ -72,11 +68,33 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
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]),
|
||||
reinterpret_cast<uint8_t*>(&out[0]), in.size(), &key_,
|
||||
reinterpret_cast<uint8_t*>(&iv[0]), AES_ENCRYPT);
|
||||
out->resize(in.size() + AES_BLOCK_SIZE);
|
||||
int out_length = out->size();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -103,7 +121,8 @@ bool RsaPublicKey::Init(const std::string& serialized_key) {
|
||||
BIO_free(bio);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::InitLogging(argc, argv);
|
||||
|
||||
wvcdm::ConfigTestEnv config;
|
||||
wvcdm::ConfigTestEnv config(wvcdm::kGooglePlayServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_key_system.assign(config.key_system());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
|
||||
@@ -3,59 +3,43 @@
|
||||
#include "config_test_env.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// choice of YT, GP, GP_Huahui, SDK_Hali
|
||||
#define USE_SERVER_YT 1
|
||||
#define USE_SERVER_GP 2
|
||||
#define USE_SERVER_SDK_Hali 3
|
||||
|
||||
// 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 =
|
||||
// Youtube Content Protection license server data
|
||||
const std::string kYtCpLicenseServer =
|
||||
"http://kir03wwwg185.widevine.net/drm";
|
||||
const std::string kYtCpClientAuth = "";
|
||||
const std::string kYtCpKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
||||
"0801121030313233343536373839616263646566"; // key - for gHali
|
||||
"0801121030313233343536373839616263646566"; // pssh data
|
||||
|
||||
#elif (USE_SERVER == USE_SERVER_YT)
|
||||
|
||||
static const std::string kLicenseServer =
|
||||
// Youtube license server data
|
||||
const std::string kYtLicenseServer =
|
||||
"https://www.youtube.com/api/drm/"
|
||||
"widevine?video_id=03681262dc412c06&source=YOUTUBE";
|
||||
static const std::string kClientAuth = "";
|
||||
static const std::string kKeyId =
|
||||
const std::string kYtClientAuth = "";
|
||||
const std::string kYtKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
||||
"0801121093789920E8D6520098577DF8F2DD5546"; // pssh data
|
||||
|
||||
#elif (USE_SERVER == USE_SERVER_GP)
|
||||
|
||||
static const std::string kLicenseServer =
|
||||
// Google Play license server data
|
||||
const std::string kGpLicenseServer =
|
||||
"https://jmt17.google.com/video-dev/license/GetCencLicense";
|
||||
|
||||
// NOTE: Append a userdata attribute to place a unique marker that the
|
||||
// server team can use to track down specific requests during debugging
|
||||
// e.g., "<existing-client-auth-string>&userdata=<your-ldap>.<your-tag>"
|
||||
// "<existing-client-auth-string>&userdata=jbmr2.dev"
|
||||
static const std::string kClientAuth =
|
||||
const std::string kGpClientAuth =
|
||||
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
|
||||
|
||||
static const std::string kKeyId =
|
||||
const std::string kGpKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
||||
"08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data
|
||||
|
||||
#else
|
||||
#error "Must define USE_SERVER"
|
||||
#endif
|
||||
|
||||
// An invalid key id, expected to fail
|
||||
static const std::string kWrongKeyId =
|
||||
const std::string kWrongKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
||||
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
|
||||
@@ -75,19 +59,28 @@ const std::string kProductionTestProvisioningServerUrl =
|
||||
|
||||
const std::string kServerSdkLicenseServer =
|
||||
"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 wvcdm {
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv()
|
||||
: client_auth_(kClientAuth),
|
||||
key_id_(kKeyId),
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id)
|
||||
: client_auth_(license_servers[server_id].client_tag),
|
||||
key_id_(license_servers[server_id].key_id),
|
||||
key_system_("com.widevine.alpha"),
|
||||
license_server_(kLicenseServer),
|
||||
port_(kDefaultHttpsPort),
|
||||
license_server_(license_servers[server_id].url),
|
||||
port_(license_servers[server_id].port),
|
||||
provisioning_server_url_(kProductionProvisioningServerUrl),
|
||||
provisioning_test_server_url_(kProductionTestProvisioningServerUrl),
|
||||
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) {}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -8,14 +8,29 @@
|
||||
|
||||
namespace {
|
||||
const std::string kDefaultHttpsPort = "443";
|
||||
const std::string kDefaultHttpPort = "80";
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
typedef enum {
|
||||
kGooglePlayServer,
|
||||
kYouTubeContentProtectionServer
|
||||
} LicenseServerId;
|
||||
|
||||
// Configures default test environment.
|
||||
class ConfigTestEnv {
|
||||
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() {};
|
||||
|
||||
const std::string& client_auth() const { return client_auth_; }
|
||||
@@ -32,6 +47,8 @@ class ConfigTestEnv {
|
||||
const std::string& server_sdk_license_server() const {
|
||||
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_; }
|
||||
|
||||
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_test_server_url_;
|
||||
std::string server_sdk_license_server_;
|
||||
bool use_chunked_transfer_;
|
||||
bool use_secure_transfer_;
|
||||
KeyId wrong_key_id_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);
|
||||
|
||||
@@ -12,10 +12,10 @@ size_t LicenseRequest::FindHeaderEndPosition(
|
||||
return(response.find(kTwoBlankLines));
|
||||
}
|
||||
|
||||
// Returns drm message in drm_msg.
|
||||
// The drm message is at the end of the response message.
|
||||
// This routine parses the license server's response message and
|
||||
// extracts the drm message from the response header.
|
||||
void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
std::string& drm_msg) {
|
||||
std::string& drm_msg) {
|
||||
if (response.empty()) {
|
||||
drm_msg.clear();
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,6 @@ UrlRequest::UrlRequest(const std::string& url, const std::string& port,
|
||||
port_.assign(port);
|
||||
}
|
||||
if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) {
|
||||
LOGD("connected to %s", socket_.domain_name().c_str());
|
||||
is_connected_ = true;
|
||||
} else {
|
||||
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) {
|
||||
message->clear();
|
||||
|
||||
@@ -137,7 +116,7 @@ int UrlRequest::GetResponse(std::string* message) {
|
||||
} while (bytes > 0);
|
||||
|
||||
ConcatenateChunkedResponse(response, message);
|
||||
LOGD("%d bytes returned", message->size());
|
||||
LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
|
||||
return message->size();
|
||||
}
|
||||
|
||||
@@ -202,6 +181,8 @@ bool UrlRequest::PostRequest(const std::string& data) {
|
||||
request_.append("\r\n\r\n");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -221,6 +202,8 @@ bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
|
||||
request_.append("\r\n"); // terminates the request
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ class UrlRequest {
|
||||
void AppendChunkToUpload(const std::string& data);
|
||||
void ConcatenateChunkedResponse(const std::string http_response,
|
||||
std::string* modified_response);
|
||||
void DumpMessage(const std::string& description, const std::string& message);
|
||||
int GetResponse(std::string* message);
|
||||
int GetStatusCode(const std::string& response);
|
||||
bool is_connected() const { return is_connected_; }
|
||||
|
||||
@@ -23,15 +23,28 @@ namespace {
|
||||
// Default license server, can be configured using --server command line option
|
||||
// Default key id (pssh), can be configured using --keyid command line option
|
||||
std::string g_client_auth;
|
||||
wvcdm::ConfigTestEnv* g_config = NULL;
|
||||
wvcdm::KeyId g_key_id;
|
||||
wvcdm::CdmKeySystem g_key_system;
|
||||
std::string g_license_server;
|
||||
std::string g_port;
|
||||
wvcdm::KeyId g_wrong_key_id;
|
||||
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
||||
} // namespace
|
||||
bool g_use_chunked_transfer = false;
|
||||
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
|
||||
struct SubSampleInfo {
|
||||
@@ -192,9 +205,7 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = {
|
||||
namespace wvcdm {
|
||||
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
TestWvCdmClientPropertySet()
|
||||
: service_certificate_(std::vector<uint8_t>()),
|
||||
use_privacy_mode_(false) {}
|
||||
TestWvCdmClientPropertySet() : use_privacy_mode_(false) {}
|
||||
virtual ~TestWvCdmClientPropertySet() {}
|
||||
|
||||
virtual std::string security_level() const { return security_level_; }
|
||||
@@ -289,32 +300,27 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
const std::string& client_auth,
|
||||
int expected_response) {
|
||||
// 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()) {
|
||||
return "";
|
||||
}
|
||||
url_request.PostRequest(key_msg_);
|
||||
std::string 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
|
||||
// 500, so just test inequity here for invalid message
|
||||
int status_code = url_request.GetStatusCode(message);
|
||||
if (expected_response == 200) {
|
||||
EXPECT_EQ(200, status_code);
|
||||
} else {
|
||||
EXPECT_NE(200, status_code);
|
||||
}
|
||||
|
||||
std::string drm_msg;
|
||||
if (200 == status_code) {
|
||||
LicenseRequest lic_request;
|
||||
lic_request.GetDrmMessage(message, drm_msg);
|
||||
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
|
||||
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
||||
drm_msg.size()).c_str());
|
||||
LOGV("HTTP response body: (%u bytes)", drm_msg.size());
|
||||
}
|
||||
return drm_msg;
|
||||
}
|
||||
@@ -324,7 +330,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
std::string GetCertRequestResponse(const std::string& server_url,
|
||||
int expected_response) {
|
||||
// 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()) {
|
||||
return "";
|
||||
}
|
||||
@@ -361,7 +367,6 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
wvcdm::ConfigTestEnv config_;
|
||||
wvcdm::WvContentDecryptionModule decryptor_;
|
||||
CdmKeyMessage key_msg_;
|
||||
CdmSessionId session_id_;
|
||||
@@ -378,10 +383,10 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||
&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 =
|
||||
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
|
||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
@@ -393,19 +398,19 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
|
||||
&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(
|
||||
&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 =
|
||||
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
|
||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(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_EQ(wvcdm::UNKNOWN_ERROR,
|
||||
decryptor_.HandleProvisioningResponse(response));
|
||||
@@ -425,7 +430,26 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
|
||||
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_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("");
|
||||
decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln);
|
||||
|
||||
@@ -459,9 +483,9 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
decryptor_.GetProvisioningRequest(&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 =
|
||||
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
|
||||
GetCertRequestResponse(g_config->provisioning_test_server_url(), 200);
|
||||
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
||||
|
||||
@@ -472,33 +496,27 @@ TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UsePrivacyModeTest) {
|
||||
TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeTest) {
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
|
||||
property_set.set_use_privacy_mode(true);
|
||||
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
|
||||
|
||||
if (property_set.service_certificate().empty()) {
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
std::string resp = GetKeyRequestResponse(g_license_server,
|
||||
g_client_auth, 200);
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
|
||||
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);
|
||||
std::string resp = GetKeyRequestResponse(g_license_server,
|
||||
g_client_auth, 200);
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
|
||||
wvcdm::NEED_KEY);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
// property has service certificate set from previous request
|
||||
EXPECT_FALSE(property_set.service_certificate().empty());
|
||||
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_);
|
||||
TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest) {
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
|
||||
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_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
@@ -521,7 +539,7 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) {
|
||||
TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) {
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
|
||||
@@ -913,32 +931,92 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) {
|
||||
*/
|
||||
} // 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) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
wvcdm::ConfigTestEnv config;
|
||||
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;
|
||||
bool show_usage = false;
|
||||
static const struct option long_options[] = {
|
||||
{"use_full_path", no_argument, &g_use_full_path, 0},
|
||||
{"keyid", required_argument, NULL, 'k'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"server", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'}};
|
||||
{ "chunked_transfer", no_argument, NULL, 'c' },
|
||||
{ "keyid", required_argument, NULL, 'k' },
|
||||
{ "license_server_id", required_argument, NULL, 'i' },
|
||||
{ "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 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) {
|
||||
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': {
|
||||
g_key_id.clear();
|
||||
g_key_id.assign(optarg);
|
||||
@@ -950,61 +1028,62 @@ int main(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
g_use_secure_transfer = true;
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
g_license_server.clear();
|
||||
g_license_server.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
g_use_full_path = 1;
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
show_usage = 1;
|
||||
show_usage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage) {
|
||||
std::cout << std::endl;
|
||||
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;
|
||||
show_menu(argv[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 << "Server: " << g_license_server << std::endl;
|
||||
std::cout << "Port: " << g_port << std::endl;
|
||||
std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
|
||||
|
||||
g_key_id = wvcdm::a2bs_hex(g_key_id);
|
||||
config.set_license_server(g_license_server);
|
||||
config.set_port(g_port);
|
||||
config.set_key_id(g_key_id);
|
||||
g_config->set_license_server(g_license_server);
|
||||
g_config->set_port(g_port);
|
||||
g_config->set_key_id(g_key_id);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
int status = RUN_ALL_TESTS();
|
||||
delete g_config;
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ adb root && adb wait-for-device remount && adb sync
|
||||
|
||||
adb shell /system/bin/oemcrypto_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/libwvdrmmediacrypto_test
|
||||
adb shell /system/bin/libwvdrmdrmplugin_test
|
||||
|
||||
Reference in New Issue
Block a user