// Copyright 2013 Google Inc. All Rights Reserved. #include #include #include #include #include #include "cdm_identifier.h" #include "clock.h" #include "config_test_env.h" #include "device_files.h" #include "file_store.h" #include "license_protocol.pb.h" #include "license_request.h" #include "log.h" #include "oemcrypto_adapter.h" #include "OEMCryptoCENC.h" #include "properties.h" #include "pst_report.h" #include "string_conversions.h" #include "test_base.h" #include "url_request.h" #include "wv_cdm_constants.h" #include "wv_content_decryption_module.h" namespace { // HTTP response codes. const int kHttpOk = 200; const int kHttpBadRequest = 400; const int kHttpInternalServerError = 500; const uint32_t kMinute = 60; const uint32_t kClockTolerance = 10; const uint32_t kTwoMinutes = 120; const uint32_t kMaxUsageTableSize = 50; // 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; wvcdm::KeyId g_wrong_key_id; wvcdm::LicenseServerId g_license_server_id = wvcdm::kContentProtectionUatServer; // TODO(rfrias): refactor to print out the decryption test names struct SubSampleInfo { bool retrieve_key; size_t num_of_subsamples; bool validate_key_id; bool is_encrypted; bool is_secure; wvcdm::KeyId key_id; std::vector encrypt_data; std::vector decrypt_data; std::vector iv; size_t block_offset; uint8_t subsample_flags; }; SubSampleInfo kEncryptedStreamingNoPstSubSample = { // key SD, encrypted, 256b true, 1, true, true, false, wvcdm::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"), wvcdm::a2b_hex( "64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36" "17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d" "7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab" "ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c" "2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4" "9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4" "6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012" "c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"), wvcdm::a2b_hex( "217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c" "942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca" "595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747" "8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6" "ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91" "029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd" "4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed" "08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"), wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, 3}; SubSampleInfo kEncryptedStreamingClip1SubSample = { true, 1, true, true, false, wvcdm::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"), wvcdm::a2b_hex( "fe8670a01c86906c056b4bf85ad278464c4eb79c60de1da8480e66e78561350e" "a25ae19a001f834c43aaeadf900b3c5a6745e885a4d1d1ae5bafac08dc1d60e5" "f3465da303909ec4b09023490471f670b615d77db844192854fdab52b7806203" "89b374594bbb6a2f2fcc31036d7cb8a3f80c0e27637b58a7650028fbf2470d68" "1bbd77934af165d215ef325c74438c9d99a20fc628792db28c05ed5deff7d9d4" "dba02ddb6cf11dc6e78cb5200940af9a2321c3a7c4c79be67b54a744dae1209c" "fa02fc250ce18d30c7da9c3a4a6c9619bf8561a42ff1e55a7b14fa3c8de69196" "c2b8e3ff672fc37003b479da5d567b7199917dbe5aa402890ebb066bce140b33"), wvcdm::a2b_hex( "d08733bd0ef671f467906b50ff8322091400f86fd6f016fea2b86e33923775b3" "ebb4c8c6f3ba8b78dd200a74d3872a40264ab99e1d422e4f819abb7f249114aa" "b334420b37c86ce81938615ab9d3a6b2de8db545cd88e35091031e73016fb386" "1b754298329b52dbe483de3a532277815e659f3e05e89257333225b933d92e15" "ef2deff287a192d2c8fc942a29a5f3a1d54440ac6385de7b34bb650b889e4ae9" "58c957b5f5ff268f445c0a6b825fcad55290cb7b5c9814bc4c72984dcf4c8fd7" "5f511c173b2e0a3163b18a1eac58539e5c188aeb0751b946ad4dcd08ea777a7f" "37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"), wvcdm::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3}; SubSampleInfo kEncryptedStreamingClip5SubSample = { true, 1, true, true, false, wvcdm::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"), wvcdm::a2b_hex( "934997779aa1aeb45d6ba8845f13786575d0adf85a5e93674d9597f8d4286ed7" "dcce02f306e502bbd9f1cadf502f354038ca921276d158d911bdf3171d335b18" "0ae0f9abece16ff31ee263228354f724da2f3723b19caa38ea02bd6563b01208" "fb5bf57854ac0fe38d5883197ef90324b2721ff20fdcf9a53819515e6daa096e" "70f6f5c1d29a4a13dafd127e2e1f761ea0e28fd451607552ecbaef5da3c780bc" "aaf2667b4cc4f858f01d480cac9e32c3fbb5705e5d2adcceebefc2535c117208" "e65f604799fc3d7223e16908550f287a4bea687008cb0064cf14d3aeedb8c705" "09ebc5c2b8b5315f43c04d78d2f55f4b32c7d33e157114362106395cc0bb6d93"), wvcdm::a2b_hex( "2dd54eee1307753508e1f250d637044d6e8f5abf057dab73e9e95f83910e4efc" "191c9bac63950f13fd51833dd94a4d03f2b64fb5c721970c418fe53fa6f74ad5" "a6e16477a35c7aa6e28909b069cd25770ef80da20918fc30fe95fd5c87fd3522" "1649de17ca2c7b3dc31f936f0cbdf97c7b1c15de3a86b279dc4b4de64943914a" "99734556c4b7a1a0b022c1933cb0786068fc18d49fed2f2b49f3ac6d01c32d07" "92175ce2844eaf9064e6a3fcffade038d690cbed81659351163a22432f0d0545" "037e1c805d8e92a1272b4196ad0ce22f26bb80063137a8e454d3b97e2414283d" "ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"), wvcdm::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0, 3}; SubSampleInfo kEncryptedOfflineClip2SubSample = { true, 1, true, true, false, wvcdm::a2bs_hex("3260F39E12CCF653529990168A3583FF"), wvcdm::a2b_hex( "3b2cbde084973539329bd5656da22d20396249bf4a18a51c38c4743360cc9fea" "a1c78d53de1bd7e14dc5d256fd20a57178a98b83804258c239acd7aa38f2d7d2" "eca614965b3d22049e19e236fc1800e60965d8b36415677bf2f843d50a6943c4" "683c07c114a32f5e5fbc9939c483c3a1b2ecd3d82b554d649798866191724283" "f0ab082eba2da79aaca5c4eaf186f9ee9a0c568f621f705a578f30e4e2ef7b96" "5e14cc046ce6dbf272ee5558b098f332333e95fc879dea6c29bf34acdb649650" "f08201b9e649960f2493fd7677cc3abf5ae70e5445845c947ba544456b431646" "d95a133bff5f57614dda5e4446cd8837901d074149dadf4b775b5b07bb88ca20"), wvcdm::a2b_hex( "D3EE543581F16AB2EABFA13468133314D19CB6A14A42229BE83543828D801475" "FAE1C2C5D193DA8445B9C4B1598E8FCBDF42EFF1FBB54EBC6A4815E2836C2848" "15094DEDE76FE4658A2D6EA3E775A872CA71835CF274676C18556C665EC7F32A" "4DBB04C10BA988B42758E37DCEFD99D9CE3AFFB1E816C412B4013890E1A31E25" "64EBF2125BC54D66FECDF8A31956830121546DC183B3DAC103E223778875B590" "3961448C287B367C585E510DA43BF9242B8E9A27B9F6F3EC7E4B5A0A74A1742B" "F5CD65EA66D7D9E79C02C7E7D5CD02DB182DDD8EAC3525B0834F1F2822AD0006" "944B5080B998BB0FE6E566AAFAE2328B37FD189F1920A964434ECF18E11AC81E"), wvcdm::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3}; std::string kStreamingClip1PstInitData = wvcdm::a2bs_hex( "000000427073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id "08011a0d7769646576696e655f74657374220f73" // pssh data "747265616d696e675f636c697033"); std::string kOfflineClip2PstInitData = wvcdm::a2bs_hex( "000000427073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id "08011a0d7769646576696e655f74657374220d6f" // pssh data "66666c696e655f636c697032"); std::string kOfflineClip4 = wvcdm::a2bs_hex( "000000427073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id "08011a0d7769646576696e655f74657374220d6f" // pssh data "66666c696e655f636c697034"); std::string kUatLicenseServer = "https://proxy.uat.widevine.com/proxy"; bool StringToInt64(const std::string& input, int64_t* output) { std::istringstream ss(input); ss >> *output; return !ss.fail(); } } // namespace using ::testing::Contains; using ::testing::Pair; using ::testing::StrNe; namespace wvcdm { // Protobuf generated classes using video_widevine::LicenseIdentification; using video_widevine::LicenseRequest_ContentIdentification; using video_widevine::LicenseRequest_ContentIdentification_CencDeprecated; using video_widevine::ClientIdentification; using video_widevine::SignedMessage; class TestWvCdmClientPropertySet : public CdmClientPropertySet { public: TestWvCdmClientPropertySet() : use_privacy_mode_(false), is_session_sharing_enabled_(false), session_sharing_id_(0) {} virtual ~TestWvCdmClientPropertySet() {} virtual const std::string& app_id() const { return app_id_; } virtual const std::string& security_level() const { return security_level_; } virtual const std::string& service_certificate() const { return service_certificate_; } virtual void set_service_certificate(const std::string& cert) { service_certificate_ = cert; } virtual const std::string& device_provisioning_service_certificate() const { return device_provisioning_service_certificate_; } virtual void set_device_provisioning_service_certificate( const std::string& cert) { device_provisioning_service_certificate_ = cert; } virtual bool use_privacy_mode() const { return use_privacy_mode_; } virtual bool is_session_sharing_enabled() const { return is_session_sharing_enabled_; } virtual uint32_t session_sharing_id() const { return session_sharing_id_; } void set_app_id(const std::string& app_id) { app_id_ = app_id; } void set_security_level(const std::string& security_level) { if (security_level == QUERY_VALUE_SECURITY_LEVEL_L1 || security_level == QUERY_VALUE_SECURITY_LEVEL_L3) { security_level_ = security_level; } } void set_use_privacy_mode(bool use_privacy_mode) { use_privacy_mode_ = use_privacy_mode; } void set_session_sharing_mode(bool enable) { is_session_sharing_enabled_ = enable; } void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } private: std::string app_id_; std::string security_level_; std::string service_certificate_; std::string device_provisioning_service_certificate_; bool use_privacy_mode_; bool is_session_sharing_enabled_; uint32_t session_sharing_id_; }; class WvCdmExtendedDurationTest : public WvCdmTestBase { public: WvCdmExtendedDurationTest() {} ~WvCdmExtendedDurationTest() {} protected: void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) { ConfigTestEnv config(g_license_server_id, false); if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) key_id->assign(a2bs_hex(config.key_id())); else key_id->assign(g_key_id); if (g_client_auth.compare(g_config->client_auth()) == 0) client_auth->assign(config.client_auth()); else client_auth->assign(g_client_auth); } void GenerateKeyRequest(const std::string& init_data, CdmLicenseType license_type) { CdmAppParameterMap app_parameters; CdmKeyRequest key_request; EXPECT_EQ(KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id_, key_set_id_, "video/mp4", init_data, license_type, app_parameters, NULL, kDefaultCdmIdentifier, &key_request)); EXPECT_EQ(kKeyRequestTypeInitial, key_request.type); key_msg_ = key_request.message; EXPECT_EQ(0u, key_request.url.size()); } void GenerateRenewalRequest(CdmLicenseType license_type, std::string* server_url) { // TODO application makes a license request, CDM will renew the license // when appropriate. std::string init_data; CdmAppParameterMap app_parameters; CdmKeyRequest key_request; EXPECT_EQ(KEY_MESSAGE, decryptor_.GenerateKeyRequest( session_id_, key_set_id_, "video/mp4", init_data, license_type, app_parameters, NULL, kDefaultCdmIdentifier, &key_request)); *server_url = key_request.url; key_msg_ = key_request.message; EXPECT_EQ(kKeyRequestTypeRenewal, key_request.type); // TODO(edwinwong, rfrias): Add tests cases for when license server url // is empty on renewal. Need appropriate key id at the server. EXPECT_NE(0u, key_request.url.size()); } void GenerateKeyRelease(CdmKeySetId key_set_id, CdmResponseType expected_response) { CdmSessionId session_id; CdmInitData init_data; CdmAppParameterMap app_parameters; CdmKeyRequest key_request; EXPECT_EQ(expected_response, decryptor_.GenerateKeyRequest( session_id, key_set_id, "video/mp4", init_data, kLicenseTypeRelease, app_parameters, NULL, kDefaultCdmIdentifier, &key_request)); if (expected_response == KEY_MESSAGE) { key_msg_ = key_request.message; EXPECT_EQ(kKeyRequestTypeRelease, key_request.type); } } void GenerateKeyRelease(CdmKeySetId key_set_id) { GenerateKeyRelease(key_set_id, KEY_MESSAGE); } void LogResponseError(const std::string& message, int http_status_code) { LOGD("HTTP Status code = %d", http_status_code); LOGD("HTTP response(%d): %s", message.size(), b2a_hex(message).c_str()); } // Post a request and extract the drm message from the response std::string GetKeyRequestResponse(const std::string& server_url, const std::string& client_auth) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url + client_auth); EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " << server_url << client_auth; url_request.PostRequest(key_msg_); std::string message; EXPECT_TRUE(url_request.GetResponse(&message)); int http_status_code = url_request.GetStatusCode(message); if (kHttpOk != http_status_code) { LogResponseError(message, http_status_code); } EXPECT_EQ(kHttpOk, http_status_code); std::string drm_msg; if (kHttpOk == http_status_code) { LicenseRequest lic_request; lic_request.GetDrmMessage(message, drm_msg); LOGV("HTTP response body: (%u bytes)", drm_msg.size()); } key_response_ = drm_msg; return drm_msg; } // Post a request and extract the signed provisioning message from // the HTTP response. std::string GetCertRequestResponse(const std::string& server_url) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url); EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " << server_url; url_request.PostCertRequestInQueryString(key_msg_); std::string message; EXPECT_TRUE(url_request.GetResponse(&message)); int http_status_code = url_request.GetStatusCode(message); if (kHttpOk != http_status_code) { LogResponseError(message, http_status_code); } EXPECT_EQ(kHttpOk, http_status_code); return message; } // Post a request and extract the signed provisioning message from // the HTTP response. std::string GetUsageInfoResponse(const std::string& server_url, const std::string& client_auth, const std::string& usage_info_request) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url + client_auth); EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " << server_url << client_auth; url_request.PostRequest(usage_info_request); std::string message; EXPECT_TRUE(url_request.GetResponse(&message)); int http_status_code = url_request.GetStatusCode(message); if (kHttpOk != http_status_code) { LogResponseError(message, http_status_code); } EXPECT_EQ(kHttpOk, http_status_code); std::string usage_info; if (kHttpOk == http_status_code) { LicenseRequest license; license.GetDrmMessage(message, usage_info); LOGV("HTTP response body: (%u bytes)", usage_info.size()); } return usage_info; } void VerifyKeyRequestResponse(const std::string& server_url, const std::string& client_auth, bool is_renewal) { std::string resp = GetKeyRequestResponse(server_url, client_auth); if (is_renewal) { // TODO application makes a license request, CDM will renew the license // when appropriate EXPECT_EQ(KEY_ADDED, decryptor_.AddKey(session_id_, resp, &key_set_id_)); } else { EXPECT_EQ(KEY_ADDED, decryptor_.AddKey(session_id_, resp, &key_set_id_)); } } void Unprovision() { EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL1, kDefaultCdmIdentifier)); EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3, kDefaultCdmIdentifier)); } void Provision() { CdmResponseType status = decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); switch (status) { case NO_ERROR: decryptor_.CloseSession(session_id_); return; case NEED_PROVISIONING: break; default: EXPECT_EQ(NO_ERROR, status); return; } std::string provisioning_server_url; CdmCertificateType cert_type = kCertificateWidevine; std::string cert_authority, cert, wrapped_key; status = decryptor_.GetProvisioningRequest( cert_type, cert_authority, kDefaultCdmIdentifier, &key_msg_, &provisioning_server_url); EXPECT_EQ(NO_ERROR, status); if (NO_ERROR != status) return; EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); std::string response = GetCertRequestResponse(g_config->provisioning_server_url()); EXPECT_NE(0, static_cast(response.size())); EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(kDefaultCdmIdentifier, response, &cert, &wrapped_key)); EXPECT_EQ(0, static_cast(cert.size())); EXPECT_EQ(0, static_cast(wrapped_key.size())); decryptor_.CloseSession(session_id_); return; } void ValidateResponse(video_widevine::LicenseType license_type, bool has_provider_session_token) { // Validate signed response SignedMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(key_response_)); EXPECT_EQ(SignedMessage::LICENSE, signed_message.type()); EXPECT_TRUE(signed_message.has_signature()); EXPECT_TRUE(!signed_message.msg().empty()); // Verify license video_widevine::License license; EXPECT_TRUE(license.ParseFromString(signed_message.msg())); // Verify license identification license_id_ = license.id(); EXPECT_EQ(license_type, license_id_.type()); EXPECT_EQ(has_provider_session_token, license_id_.has_provider_session_token()); } void ValidateRenewalRequest(int64_t expected_seconds_since_started, int64_t expected_seconds_since_last_played, bool has_provider_session_token) { // Validate signed renewal request SignedMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(key_msg_)); EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); EXPECT_TRUE(signed_message.has_signature()); EXPECT_TRUE(!signed_message.msg().empty()); // Verify license request video_widevine::LicenseRequest license_renewal; EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())); // Verify Content Identification const LicenseRequest_ContentIdentification& content_id = license_renewal.content_id(); EXPECT_FALSE(content_id.has_cenc_id_deprecated()); EXPECT_FALSE(content_id.has_webm_id_deprecated()); EXPECT_TRUE(content_id.has_existing_license()); const LicenseRequest_ContentIdentification::ExistingLicense& existing_license = content_id.existing_license(); const LicenseIdentification& id = existing_license.license_id(); EXPECT_EQ(license_id_.request_id(), id.request_id()); EXPECT_EQ(license_id_.session_id(), id.session_id()); EXPECT_EQ(license_id_.purchase_id(), id.purchase_id()); EXPECT_EQ(license_id_.type(), id.type()); EXPECT_EQ(license_id_.version(), id.version()); EXPECT_EQ(has_provider_session_token, !id.provider_session_token().empty()); EXPECT_NEAR(existing_license.seconds_since_started(), expected_seconds_since_started, kClockTolerance); EXPECT_NEAR(existing_license.seconds_since_last_played(), expected_seconds_since_last_played, kClockTolerance); EXPECT_TRUE(existing_license.session_usage_table_entry().empty()); EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_RENEWAL, license_renewal.type()); EXPECT_LT(0, license_renewal.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version()); EXPECT_TRUE(license_renewal.has_key_control_nonce()); } void ValidateReleaseRequest(std::string& usage_msg, int64_t expected_seconds_since_license_received, int64_t expected_seconds_since_first_playback, int64_t expected_seconds_since_last_playback) { // Validate signed renewal request SignedMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(usage_msg)); EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); EXPECT_TRUE(signed_message.has_signature()); EXPECT_TRUE(!signed_message.msg().empty()); // Verify license request video_widevine::LicenseRequest license_renewal; EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())); // Verify Content Identification const LicenseRequest_ContentIdentification& content_id = license_renewal.content_id(); EXPECT_FALSE(content_id.has_cenc_id_deprecated()); EXPECT_FALSE(content_id.has_webm_id_deprecated()); EXPECT_TRUE(content_id.has_existing_license()); const LicenseRequest_ContentIdentification::ExistingLicense& existing_license = content_id.existing_license(); const LicenseIdentification& id = existing_license.license_id(); EXPECT_EQ(license_id_.request_id(), id.request_id()); EXPECT_EQ(license_id_.session_id(), id.session_id()); EXPECT_EQ(license_id_.purchase_id(), id.purchase_id()); EXPECT_EQ(license_id_.type(), id.type()); EXPECT_EQ(license_id_.version(), id.version()); EXPECT_TRUE(!id.provider_session_token().empty()); EXPECT_NEAR(existing_license.seconds_since_started(), expected_seconds_since_first_playback, kClockTolerance); EXPECT_NEAR(existing_license.seconds_since_last_played(), expected_seconds_since_last_playback, kClockTolerance); EXPECT_TRUE(!existing_license.session_usage_table_entry().empty()); // Verify usage report uint8_t *buffer = reinterpret_cast( const_cast(existing_license.session_usage_table_entry().data())); Unpacked_PST_Report usage_report(buffer); EXPECT_EQ(usage_report.report_size(), existing_license.session_usage_table_entry().size()); EXPECT_EQ(kInactiveUsed, usage_report.status()); EXPECT_EQ(id.provider_session_token().size(), usage_report.pst_length()); std::string pst(reinterpret_cast(usage_report.pst()), usage_report.pst_length()); EXPECT_EQ(id.provider_session_token(), pst); EXPECT_LE(kInsecureClock, usage_report.clock_security_level()); int64_t seconds_since_license_received = usage_report.seconds_since_license_received(); int64_t seconds_since_first_decrypt = usage_report.seconds_since_first_decrypt(); int64_t seconds_since_last_decrypt = usage_report.seconds_since_last_decrypt(); // Detect licenses that were never used if (seconds_since_first_decrypt < 0 || seconds_since_first_decrypt > seconds_since_license_received) { seconds_since_first_decrypt = 0; seconds_since_last_decrypt = 0; } EXPECT_NEAR(seconds_since_license_received, expected_seconds_since_license_received, kClockTolerance); EXPECT_NEAR(seconds_since_first_decrypt, expected_seconds_since_first_playback, kClockTolerance); EXPECT_NEAR(seconds_since_last_decrypt, expected_seconds_since_last_playback, kClockTolerance); EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_RELEASE, license_renewal.type()); EXPECT_LT(0, license_renewal.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version()); EXPECT_TRUE(license_renewal.has_key_control_nonce()); } void QueryKeyStatus(bool streaming, bool expect_renewal, int64_t* license_duration_remaining, int64_t* playback_duration_remaining) { CdmQueryMap query_info; EXPECT_EQ(NO_ERROR, decryptor_.QueryKeyStatus(session_id_, &query_info)); EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_LICENSE_TYPE, streaming ? QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE))); EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_PLAY_ALLOWED, QUERY_VALUE_TRUE))); EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_PERSIST_ALLOWED, streaming ? QUERY_VALUE_FALSE : QUERY_VALUE_TRUE))); if (expect_renewal) { EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_RENEW_ALLOWED, QUERY_VALUE_TRUE))); EXPECT_THAT(query_info, Contains(Pair(QUERY_KEY_RENEWAL_SERVER_URL, StrNe("")))); } std::string key = QUERY_KEY_LICENSE_DURATION_REMAINING; ASSERT_THAT(query_info, Contains(Pair(key,StrNe("")))); EXPECT_TRUE(StringToInt64(query_info[key], license_duration_remaining)); key = QUERY_KEY_PLAYBACK_DURATION_REMAINING; ASSERT_THAT(query_info, Contains(Pair(key,StrNe("")))); EXPECT_TRUE(StringToInt64(query_info[key], playback_duration_remaining)); } uint32_t QueryStatus(SecurityLevel security_level, const std::string& key) { std::string str; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QueryStatus(security_level, key, &str)); std::istringstream ss(str); uint32_t value; ss >> value; EXPECT_FALSE(ss.fail()); EXPECT_TRUE(ss.eof()); return value; } std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) { decryptor_.OpenSession(g_key_system, property_set, kDefaultCdmIdentifier, NULL, &session_id_); CdmQueryMap query_info; EXPECT_EQ(NO_ERROR, decryptor_.QuerySessionStatus(session_id_, &query_info)); CdmQueryMap::iterator itr = query_info.find(QUERY_KEY_SECURITY_LEVEL); EXPECT_TRUE(itr != query_info.end()); decryptor_.CloseSession(session_id_); return itr->second; } CdmSecurityLevel GetDefaultSecurityLevel() { std::string level = GetSecurityLevel(NULL); CdmSecurityLevel security_level = kSecurityLevelUninitialized; if (level == QUERY_VALUE_SECURITY_LEVEL_L1) { security_level = kSecurityLevelL1; } else if (level == QUERY_VALUE_SECURITY_LEVEL_L3) { security_level = kSecurityLevelL3; } else { EXPECT_TRUE(false); } return security_level; } WvContentDecryptionModule decryptor_; CdmKeyMessage key_msg_; CdmKeyResponse key_response_; CdmSessionId session_id_; CdmKeySetId key_set_id_; video_widevine::LicenseIdentification license_id_; }; TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRequestTest) { Provision(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); EXPECT_TRUE(!key_msg_.empty()); // Validate signed request SignedMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(key_msg_)); EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); EXPECT_TRUE(signed_message.has_signature()); EXPECT_TRUE(!signed_message.msg().empty()); // Verify license request video_widevine::LicenseRequest license_request; EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); // Verify Client Identification const ClientIdentification& client_id = license_request.client_id(); EXPECT_EQ(video_widevine:: ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE, client_id.type()); EXPECT_LT(0, client_id.client_info_size()); for (int i = 0; i < client_id.client_info_size(); ++i) { const ::video_widevine::ClientIdentification_NameValue& name_value = client_id.client_info(i); EXPECT_TRUE(!name_value.name().empty()); EXPECT_TRUE(!name_value.value().empty()); } EXPECT_FALSE(client_id.has_provider_client_token()); EXPECT_FALSE(client_id.has_license_counter()); const ClientIdentification::ClientCapabilities& client_capabilities = client_id.client_capabilities(); EXPECT_FALSE(client_capabilities.has_client_token()); EXPECT_TRUE(client_capabilities.has_session_token()); EXPECT_FALSE(client_capabilities.video_resolution_constraints()); EXPECT_TRUE(client_capabilities.has_max_hdcp_version()); EXPECT_TRUE(client_capabilities.has_oem_crypto_api_version()); // Verify Content Identification const LicenseRequest_ContentIdentification& content_id = license_request.content_id(); EXPECT_TRUE(content_id.has_cenc_id_deprecated()); EXPECT_FALSE(content_id.has_webm_id_deprecated()); EXPECT_FALSE(content_id.has_existing_license()); const LicenseRequest_ContentIdentification_CencDeprecated& cenc_id = content_id.cenc_id_deprecated(); EXPECT_TRUE(std::equal(cenc_id.pssh(0).begin(), cenc_id.pssh(0).end(), g_key_id.begin() + 32)); EXPECT_EQ(video_widevine::STREAMING, cenc_id.license_type()); EXPECT_TRUE(cenc_id.has_request_id()); // Verify other license request fields EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_NEW, license_request.type()); EXPECT_LT(0, license_request.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_request.protocol_version()); EXPECT_TRUE(license_request.has_key_control_nonce()); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) { Provision(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); // Validate signed response SignedMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(key_response_)); EXPECT_EQ(SignedMessage::LICENSE, signed_message.type()); EXPECT_TRUE(signed_message.has_signature()); EXPECT_TRUE(!signed_message.msg().empty()); // Verify license video_widevine::License license; EXPECT_TRUE(license.ParseFromString(signed_message.msg())); // Verify license identification video_widevine::LicenseIdentification license_id = license.id(); EXPECT_LT(0u, license_id.request_id().size()); EXPECT_LT(0u, license_id.session_id().size()); EXPECT_EQ(video_widevine::STREAMING, license_id.type()); EXPECT_FALSE(license_id.has_provider_session_token()); // Create renewal request std::string license_server; GenerateRenewalRequest(kLicenseTypeStreaming, &license_server); EXPECT_TRUE(!license_server.empty()); EXPECT_TRUE(!key_msg_.empty()); // Validate signed renewal request signed_message.Clear(); EXPECT_TRUE(signed_message.ParseFromString(key_msg_)); EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); EXPECT_TRUE(signed_message.has_signature()); EXPECT_TRUE(!signed_message.msg().empty()); // Verify license request video_widevine::LicenseRequest license_renewal; EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())); // Client Identification not filled in in renewal // Verify Content Identification const LicenseRequest_ContentIdentification& content_id = license_renewal.content_id(); EXPECT_FALSE(content_id.has_cenc_id_deprecated()); EXPECT_FALSE(content_id.has_webm_id_deprecated()); EXPECT_TRUE(content_id.has_existing_license()); const LicenseRequest_ContentIdentification::ExistingLicense& existing_license = content_id.existing_license(); const LicenseIdentification& id = existing_license.license_id(); EXPECT_EQ(license_id.request_id(), id.request_id()); EXPECT_EQ(license_id.session_id(), id.session_id()); EXPECT_EQ(license_id.purchase_id(), id.purchase_id()); EXPECT_EQ(license_id.type(), id.type()); EXPECT_EQ(license_id.version(), id.version()); EXPECT_TRUE(id.provider_session_token().empty()); EXPECT_EQ(0, existing_license.seconds_since_started()); EXPECT_EQ(0, existing_license.seconds_since_last_played()); EXPECT_TRUE(existing_license.session_usage_table_entry().empty()); EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_RENEWAL, license_renewal.type()); EXPECT_LT(0, license_renewal.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version()); EXPECT_TRUE(license_renewal.has_key_control_nonce()); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) { Provision(); SubSampleInfo* data = &kEncryptedStreamingClip5SubSample; TestWvCdmClientPropertySet client_property_set; TestWvCdmClientPropertySet* property_set = NULL; CdmSecurityLevel security_level = GetDefaultSecurityLevel(); FileSystem file_system; DeviceFiles handle(&file_system); EXPECT_TRUE(handle.Init(security_level)); std::vector provider_session_tokens; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( DeviceFiles::GetUsageInfoFileName(""), &provider_session_tokens)); for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) { decryptor_.OpenSession(g_key_system, property_set, kDefaultCdmIdentifier, NULL, &session_id_); std::string key_id = a2bs_hex( "000000427073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id "08011a0d7769646576696e655f74657374220f73" // pssh data "747265616d696e675f636c697035"); GenerateKeyRequest(key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); decryptor_.CloseSession(session_id_); } uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; CdmResponseType status = decryptor_.GetUsageInfo("", &usage_info); EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status); while (usage_info.size() > 0) { for (size_t i = 0; i < usage_info.size(); ++i) { release_msg = GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]); EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg)); } status = decryptor_.GetUsageInfo("", &usage_info); switch (status) { case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break; case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break; default: FAIL() << "GetUsageInfo failed with error " << static_cast(status); break; } } } // This test verifies that sessions allocated internally during // key release message generation are deallocated after their // time to live period expires. // TODO: Disabled till b/32617908 is addressed TEST_F(WvCdmExtendedDurationTest, DISABLED_AutomatedOfflineSessionReleaseTest) { Unprovision(); Provision(); // override default settings unless configured through the command line std::string key_id; std::string client_auth; GetOfflineConfiguration(&key_id, &client_auth); uint32_t initial_open_sessions = QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); uint32_t max_sessions = QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS); uint32_t num_key_set_ids = max_sessions - initial_open_sessions; if (num_key_set_ids > kMaxUsageTableSize) num_key_set_ids = kMaxUsageTableSize; std::set key_set_id_map; for (uint32_t i = 0; i < num_key_set_ids; ++i) { decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline); VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false); EXPECT_FALSE(key_set_id_.empty()); decryptor_.CloseSession(session_id_); key_set_id_map.insert(key_set_id_); } std::set::iterator iter; for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) { session_id_.clear(); key_set_id_.clear(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, *iter)); decryptor_.CloseSession(session_id_); } for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) { session_id_.clear(); GenerateKeyRelease(*iter); } uint32_t open_sessions = QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); EXPECT_EQ(open_sessions, key_set_id_map.size() + initial_open_sessions); sleep(kMinute + kClockTolerance); iter = key_set_id_map.begin(); session_id_.clear(); GenerateKeyRelease(*iter); open_sessions = QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); EXPECT_GE(open_sessions, initial_open_sessions); EXPECT_LE(open_sessions - initial_open_sessions, key_set_id_map.size()); for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) { session_id_.clear(); GenerateKeyRelease(*iter); key_set_id_ = *iter; VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false); } } class WvCdmStreamingNoPstTest : public WvCdmExtendedDurationTest, public ::testing::WithParamInterface {}; TEST_P(WvCdmStreamingNoPstTest, UsageTest) { Unprovision(); Provision(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); ValidateResponse(video_widevine::STREAMING, false); int64_t initial_license_duration_remaining = 0; int64_t initial_playback_duration_remaining = 0; QueryKeyStatus(true, true, &initial_license_duration_remaining, &initial_playback_duration_remaining); sleep(kMinute); int64_t expected_seconds_since_license_received = kMinute; int64_t expected_seconds_since_initial_playback = 0; int64_t expected_seconds_since_last_playback = 0; for (size_t i = 0; i < GetParam(); ++i) { // Decrypt data SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &(data + i)->key_id, &(data + i)->encrypt_data.front(), (data + i)->encrypt_data.size(), &(data + i)->iv, (data + i)->block_offset, &decrypt_buffer[0]); decryption_parameters.is_encrypted = (data + i)->is_encrypted; decryption_parameters.is_secure = (data + i)->is_secure; decryption_parameters.subsample_flags = (data + i)->subsample_flags; EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, decryption_parameters)); EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer); } sleep(kMinute); expected_seconds_since_license_received += kMinute; expected_seconds_since_initial_playback += kMinute; expected_seconds_since_last_playback = kMinute; } // Create renewal request and validate std::string license_server; GenerateRenewalRequest(kLicenseTypeStreaming, &license_server); EXPECT_TRUE(!license_server.empty()); EXPECT_TRUE(!key_msg_.empty()); ValidateRenewalRequest(expected_seconds_since_initial_playback, expected_seconds_since_last_playback, false); // Query and validate usage information int64_t license_duration_remaining = 0; int64_t playback_duration_remaining = 0; QueryKeyStatus(true, true, &license_duration_remaining, &playback_duration_remaining); EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, expected_seconds_since_license_received, kClockTolerance); EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining, expected_seconds_since_initial_playback, kClockTolerance); decryptor_.CloseSession(session_id_); } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingNoPstTest, ::testing::Values(0, 1, 2)); class WvCdmStreamingPstTest : public WvCdmExtendedDurationTest, public ::testing::WithParamInterface {}; TEST_P(WvCdmStreamingPstTest, UsageTest) { Unprovision(); Provision(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(kStreamingClip1PstInitData, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); ValidateResponse(video_widevine::STREAMING, true); int64_t initial_license_duration_remaining = 0; int64_t initial_playback_duration_remaining = 0; QueryKeyStatus(true, false, &initial_license_duration_remaining, &initial_playback_duration_remaining); sleep(kMinute); int64_t expected_seconds_since_license_received = kMinute; int64_t expected_seconds_since_initial_playback = 0; int64_t expected_seconds_since_last_playback = 0; for (size_t i = 0; i < GetParam(); ++i) { // Decrypt data SubSampleInfo* data = &kEncryptedStreamingClip1SubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &(data + i)->key_id, &(data + i)->encrypt_data.front(), (data + i)->encrypt_data.size(), &(data + i)->iv, (data + i)->block_offset, &decrypt_buffer[0]); decryption_parameters.is_encrypted = (data + i)->is_encrypted; decryption_parameters.is_secure = (data + i)->is_secure; decryption_parameters.subsample_flags = (data + i)->subsample_flags; EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, decryption_parameters)); EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer); } sleep(kMinute); expected_seconds_since_license_received += kMinute; expected_seconds_since_initial_playback += kMinute; expected_seconds_since_last_playback = kMinute; } // Query and validate usage information int64_t license_duration_remaining = 0; int64_t playback_duration_remaining = 0; QueryKeyStatus(true, false, &license_duration_remaining, &playback_duration_remaining); EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, expected_seconds_since_license_received, kClockTolerance); EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining, expected_seconds_since_initial_playback, kClockTolerance); decryptor_.CloseSession(session_id_); } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingPstTest, ::testing::Values(0, 1, 2)); class WvCdmStreamingUsageReportTest : public WvCdmExtendedDurationTest, public ::testing::WithParamInterface {}; TEST_P(WvCdmStreamingUsageReportTest, UsageTest) { Unprovision(); Provision(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(kStreamingClip1PstInitData, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); ValidateResponse(video_widevine::STREAMING, true); int64_t initial_license_duration_remaining = 0; int64_t initial_playback_duration_remaining = 0; QueryKeyStatus(true, false, &initial_license_duration_remaining, &initial_playback_duration_remaining); sleep(kMinute); int64_t expected_seconds_since_license_received = kMinute; int64_t expected_seconds_since_initial_playback = 0; int64_t expected_seconds_since_last_playback = 0; for (size_t i = 0; i < GetParam(); ++i) { // Decrypt data SubSampleInfo* data = &kEncryptedStreamingClip1SubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &(data + i)->key_id, &(data + i)->encrypt_data.front(), (data + i)->encrypt_data.size(), &(data + i)->iv, (data + i)->block_offset, &decrypt_buffer[0]); decryption_parameters.is_encrypted = (data + i)->is_encrypted; decryption_parameters.is_secure = (data + i)->is_secure; decryption_parameters.subsample_flags = (data + i)->subsample_flags; EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, decryption_parameters)); EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer); } sleep(kMinute); expected_seconds_since_license_received += kMinute; expected_seconds_since_initial_playback += kMinute; expected_seconds_since_last_playback = kMinute; } // Query and validate usage information int64_t license_duration_remaining = 0; int64_t playback_duration_remaining = 0; QueryKeyStatus(true, false, &license_duration_remaining, &playback_duration_remaining); EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, expected_seconds_since_license_received, kClockTolerance); EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining, expected_seconds_since_initial_playback, kClockTolerance); decryptor_.CloseSession(session_id_); // Create usage report and validate uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; CdmResponseType status = decryptor_.GetUsageInfo("", &usage_info); EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status); while (usage_info.size() > 0) { for (size_t i = 0; i < usage_info.size(); ++i) { ValidateReleaseRequest(usage_info[i], expected_seconds_since_license_received, expected_seconds_since_initial_playback, expected_seconds_since_last_playback); release_msg = GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]); EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg)); } status = decryptor_.GetUsageInfo("", &usage_info); switch (status) { case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break; case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break; default: FAIL() << "GetUsageInfo failed with error " << static_cast(status); break; } } } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest, ::testing::Values(0, 1, 2)); class WvCdmOfflineUsageReportTest : public WvCdmExtendedDurationTest, public ::testing::WithParamInterface {}; TEST_P(WvCdmOfflineUsageReportTest, UsageTest) { Unprovision(); Provision(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline); VerifyKeyRequestResponse(g_license_server, g_client_auth, false); ValidateResponse(video_widevine::OFFLINE, true); CdmKeySetId key_set_id = key_set_id_; EXPECT_FALSE(key_set_id_.empty()); int64_t initial_license_duration_remaining = 0; int64_t initial_playback_duration_remaining = 0; QueryKeyStatus(false, true, &initial_license_duration_remaining, &initial_playback_duration_remaining); decryptor_.CloseSession(session_id_); sleep(kMinute); int64_t expected_seconds_since_license_received = kMinute; int64_t expected_seconds_since_initial_playback = 0; int64_t expected_seconds_since_last_playback = 0; for (size_t i = 0; i < GetParam(); ++i) { session_id_.clear(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); EXPECT_EQ(KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); // Query and validate usage information int64_t license_duration_remaining = 0; int64_t playback_duration_remaining = 0; QueryKeyStatus(false, true, &license_duration_remaining, &playback_duration_remaining); EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, expected_seconds_since_license_received, kClockTolerance); EXPECT_NEAR( initial_playback_duration_remaining - playback_duration_remaining, expected_seconds_since_initial_playback, kClockTolerance); // Decrypt data SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &(data + i)->key_id, &(data + i)->encrypt_data.front(), (data + i)->encrypt_data.size(), &(data + i)->iv, (data + i)->block_offset, &decrypt_buffer[0]); decryption_parameters.is_encrypted = (data + i)->is_encrypted; decryption_parameters.is_secure = (data + i)->is_secure; decryption_parameters.subsample_flags = (data + i)->subsample_flags; EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, decryption_parameters)); EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer); } sleep(10); decryptor_.CloseSession(session_id_); sleep(kMinute - 10); expected_seconds_since_license_received += kMinute; expected_seconds_since_initial_playback += kMinute; expected_seconds_since_last_playback = kMinute; } session_id_.clear(); decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, &session_id_); EXPECT_EQ(KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); // Query and validate usage information int64_t license_duration_remaining = 0; int64_t playback_duration_remaining = 0; QueryKeyStatus(false, true, &license_duration_remaining, &playback_duration_remaining); EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, expected_seconds_since_license_received, kClockTolerance); EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining, expected_seconds_since_initial_playback, kClockTolerance); decryptor_.CloseSession(session_id_); session_id_.clear(); key_set_id_.clear(); GenerateKeyRelease(key_set_id); ValidateReleaseRequest(key_msg_, expected_seconds_since_license_received, expected_seconds_since_initial_playback, expected_seconds_since_last_playback); key_set_id_ = key_set_id; VerifyKeyRequestResponse(g_license_server, g_client_auth, false); } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest, ::testing::Values(0, 1, 2)); } // 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(37) << std::left << " -i/--license_server_id="; std::cout << "specifies which default server settings to use: " << std::endl; std::cout << std::setw(37) << std::left << " "; std::cout << "gp for GooglePlay server" << std::endl; std::cout << std::setw(37) << std::left << " "; std::cout << "cp for Content Protection UAT server" << std::endl; std::cout << std::setw(37) << std::left << " "; std::cout << "st for Content Protection Staging server" << std::endl << std::endl; std::cout << std::setw(37) << std::left << " -k/--keyid="; std::cout << "configure the key id or pssh, in hex format" << std::endl << std::endl; std::cout << std::setw(37) << std::left << " -u/--server="; 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); bool show_usage = false; static const struct option long_options[] = { {"keyid", required_argument, NULL, 'k'}, {"license_server_id", required_argument, NULL, 'i'}, {"license_server_url", required_argument, NULL, 'u'}, {NULL, 0, NULL, '\0'}}; int option_index = 0; int opt = 0; while ((opt = getopt_long(argc, argv, "i:k:u:", long_options, &option_index)) != -1) { switch (opt) { 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::kContentProtectionUatServer; } else if (!license_id.compare("st")) { g_license_server_id = wvcdm::kContentProtectionStagingServer; } 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); break; } case 'u': { g_license_server.clear(); g_license_server.assign(optarg); break; } case '?': { show_usage = true; break; } } } if (show_usage) { 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 kLicenseServers[] 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()); } // Displays server url, port and key Id being used std::cout << std::endl; std::cout << "Server: " << g_license_server << std::endl; std::cout << "KeyID: " << g_key_id << std::endl << std::endl; g_key_id = wvcdm::a2bs_hex(g_key_id); g_config->set_license_server(g_license_server); int status = RUN_ALL_TESTS(); delete g_config; return status; }