diff --git a/cdm/cdm.gyp b/cdm/cdm.gyp index dc59140d..c58c94e6 100644 --- a/cdm/cdm.gyp +++ b/cdm/cdm.gyp @@ -91,12 +91,14 @@ '../core/src/privacy_crypto_dummy.cpp', ], }], - ], - 'direct_dependencies': [ - 'device_files', - 'license_protocol', - ], - 'conditions': [ + ['oemcrypto_v8=="true"', { + 'sources!': [ # exclude + '../core/src/oemcrypto_adapter_static.cpp', + ], + 'sources': [ # include + '../core/src/oemcrypto_adapter_static_v8.cpp', + ], + }], ['use_system_protobuf=="true"', { 'direct_dependent_settings': { 'libraries': [ diff --git a/cdm/include/cdm_host_clock.h b/cdm/include/cdm_host_clock.h index 7a074200..6935e364 100644 --- a/cdm/include/cdm_host_clock.h +++ b/cdm/include/cdm_host_clock.h @@ -21,6 +21,7 @@ class HostClock { private: static IClock* impl_; }; + } // namspace wvcdm #endif // WVCDM_CDM_CDM_HOST_CLOCK_H_ diff --git a/cdm/include/cdm_host_file.h b/cdm/include/cdm_host_file.h index cb603f5a..93311ae9 100644 --- a/cdm/include/cdm_host_file.h +++ b/cdm/include/cdm_host_file.h @@ -13,31 +13,37 @@ class IFileFactory; class File::Impl { public: explicit Impl(cdm::Host* const host) : host_(host) {} - FILE* file_; + static void RegisterFileFactory(IFileFactory* factory) { factory_ = factory; } - static IFileFactory* factory_; virtual bool Exists(const std::string& name); virtual bool Open(const std::string& name); virtual bool Close(); virtual bool Remove(const std::string& name); - virtual size_t Read(char* buffer, size_t bytes); - virtual size_t Write(const char* buffer, size_t bytes); - virtual size_t FileSize(const std::string& name); + virtual ssize_t Read(char* buffer, size_t bytes); + virtual ssize_t Write(const char* buffer, size_t bytes); + virtual ssize_t FileSize(const std::string& name); private: + static IFileFactory* factory_; + friend class File; + cdm::Host* const host_; std::string fname_; }; class IFileFactory { protected: - IFileFactory(){File::Impl::RegisterFileFactory(this);} - virtual ~IFileFactory(){} + IFileFactory() { + File::Impl::RegisterFileFactory(this); + } + + virtual ~IFileFactory() {} + public: - virtual File::Impl* NewFileImpl () = 0; + virtual File::Impl* NewFileImpl() = 0; }; } // namespace wvcdm diff --git a/cdm/include/content_decryption_module.h b/cdm/include/content_decryption_module.h index c5dc42fa..537a3c07 100644 --- a/cdm/include/content_decryption_module.h +++ b/cdm/include/content_decryption_module.h @@ -269,6 +269,9 @@ class ContentDecryptionModule_1 { const uint8_t* key, int key_size, const uint8_t* key_id, int key_id_size) = 0; + // Tests whether |key_id| is known to any current session. + virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) = 0; + // Cancels any pending key request made to the CDM for |session_id|. // // Returns kSuccess if all pending key requests for |session_id| were @@ -293,80 +296,6 @@ class ContentDecryptionModule_1 { virtual Status Decrypt(const InputBuffer& encrypted_buffer, DecryptedBlock* decrypted_buffer) = 0; - // Initializes the CDM audio decoder with |audio_decoder_config|. - // This function is only needed if DecryptAndDecodeSamples() or - // DecryptDecodeAndRenderSamples() is used. It must be called before either - // of these routines is called. - // - // Returns kSuccess if the |audio_decoder_config| is supported and the CDM - // audio decoder is successfully initialized. - // Returns kSessionError if |audio_decoder_config| is not supported. The CDM - // may still be able to do Decrypt(). - // - virtual Status InitializeAudioDecoder( - const AudioDecoderConfig& audio_decoder_config) = 0; - - // Initializes the CDM video decoder with |video_decoder_config|. - // This function is only needed if DecryptAndDecodeFrame() or - // DecryptDecodeAndRenderFrame() is used. It must be called before either - // of these routines is called. - // - // Returns kSuccess if the |video_decoder_config| is supported and the CDM - // video decoder is successfully initialized. - // Returns kSessionError if |video_decoder_config| is not supported. The CDM - // may still be able to do Decrypt(). - // - virtual Status InitializeVideoDecoder( - const VideoDecoderConfig& video_decoder_config) = 0; - - // De-initializes the CDM decoder and sets it to an uninitialized state. The - // caller can initialize the decoder again after this call to re-initialize - // it. This can be used to reconfigure the decoder if the configuration - // changes. - virtual void DeinitializeDecoder(StreamType decoder_type) = 0; - - // Resets the CDM decoder to an initialized clean state. All internal buffers - // MUST be flushed. - virtual void ResetDecoder(StreamType decoder_type) = 0; - - // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a - // |video_frame|. Upon end-of-stream, the caller should call this function - // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty - // |video_frame| (|format| == kEmptyVideoFrame) is produced. - // - // Returns kSuccess if decryption and decoding both succeeded, in which case - // the callee will have filled the |video_frame| and passed the ownership of - // |frame_buffer| in |video_frame| to the caller. - // Returns kNoKey if the CDM did not have the necessary decryption key - // to decrypt. - // Returns kNeedMoreData if more data was needed by the decoder to generate - // a decoded frame (e.g. during initialization and end-of-stream). - // Returns kDecryptError if any decryption error happened. - // Returns kDecodeError if any decoding error happened. - // If the return value is not kSuccess, |video_frame| should be ignored by - // the caller. - virtual Status DecryptAndDecodeFrame(const InputBuffer& encrypted_buffer, - VideoFrame* video_frame) = 0; - - // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into - // |audio_frames|. Upon end-of-stream, the caller should call this function - // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty - // |audio_frames| is produced. - // - // Returns kSuccess if decryption and decoding both succeeded, in which case - // the callee will have filled |audio_frames| and passed the ownership of - // |data| in |audio_frames| to the caller. - // Returns kNoKey if the CDM did not have the necessary decryption key - // to decrypt. - // Returns kNeedMoreData if more data was needed by the decoder to generate - // audio samples (e.g. during initialization and end-of-stream). - // Returns kDecryptError if any decryption error happened. - // Returns kDecodeError if any decoding error happened. - // If the return value is not kSuccess, |audio_frames| should be ignored by - // the caller. - virtual Status DecryptAndDecodeSamples(const InputBuffer& encrypted_buffer, - AudioFrames* audio_frames) = 0; - // Decrypts the |encrypted_buffer|, decodes the decrypted buffer into a // video frame, and passes the frame to the rendering FW/HW. No data // is returned. @@ -471,28 +400,18 @@ class Host_1 { MediaKeyError error_code, uint32_t system_code) = 0; - // Get private data from the host. This function is limited to internal use. - typedef const void* (*GetPrivateInterface)(const char* interface_name); - virtual void GetPrivateData(int32_t* instance, - GetPrivateInterface* get_interface) = 0; + // Version 1.3: + // These virtual member functions extend the cdm::Host interface to allow + // the CDM to query the host for various information. -//Version 1.3 - // These Virtual member functions extend the cdm::Host interface to allow - // the sharing for platform specific information, between the cdm::Host and - // the CDM. + // Asks the host to persist a name-value pair. + virtual void SetPlatformString(const std::string& name, + const std::string& value) = 0; - virtual int GetPlatformString(const std::string& name, - std::string* value) = 0; - virtual int SetPlatformString(const std::string& name, - const std::string& value) = 0; - virtual int PersistPlatformString(const std::string& name, - const std::string& value) = 0; - virtual int GetPlatformByteArray(const std::string& name, - std::vector* value) = 0; - virtual int SetPlatformByteArray(const std::string& name, - const std::vector& value) = 0; - virtual int PersistPlatformByteArray(const std::string& name, - const std::vector& value) = 0; + // Retrieves a value by name. If there is no such value, the Host should + // set value to an empty string. + virtual void GetPlatformString(const std::string& name, + std::string* value) = 0; protected: Host_1() {} diff --git a/cdm/include/wv_cdm_version.h b/cdm/include/wv_cdm_version.h index cc7ad56a..b67e2860 100644 --- a/cdm/include/wv_cdm_version.h +++ b/cdm/include/wv_cdm_version.h @@ -1,3 +1,3 @@ // Widevine CDM Kit Version - #define WV_CDM_VERSION "v2.1.2-0-773" + #define WV_CDM_VERSION "v2.1.3-2-789" diff --git a/cdm/include/wv_content_decryption_module.h b/cdm/include/wv_content_decryption_module.h index 0d1abbd0..247ee4e2 100644 --- a/cdm/include/wv_content_decryption_module.h +++ b/cdm/include/wv_content_decryption_module.h @@ -28,7 +28,7 @@ class WVClientPropertySet : public wvcdm::CdmClientPropertySet { security_level_ = securityLevel; } - virtual std::string security_level() const { + virtual const std::string& security_level() const { return security_level_; } @@ -40,11 +40,11 @@ class WVClientPropertySet : public wvcdm::CdmClientPropertySet { return use_privacy_mode_; } - void set_service_certificate(const std::vector& serviceCertificate) { + void set_service_certificate(const std::string& serviceCertificate) { service_certificate_ = serviceCertificate; } - virtual std::vector service_certificate() const { + virtual const std::string& service_certificate() const { return service_certificate_; } @@ -67,24 +67,23 @@ class WVClientPropertySet : public wvcdm::CdmClientPropertySet { return; // This is unused by common cdm but we need a // definition for the pure virtual methods. } + private: CORE_DISALLOW_COPY_AND_ASSIGN(WVClientPropertySet); std::string security_level_; bool use_privacy_mode_; - std::vector service_certificate_; + std::string service_certificate_; }; class WvContentDecryptionModule : public cdm::ContentDecryptionModule, public IFileFactory, public IClock { - File::Impl* NewFileImpl() { return new File::Impl(host_); } public: - explicit WvContentDecryptionModule(cdm::Host* host) - : host_(host), host_event_listener_(host, &cdm_engine_) { - HostClock::SetClockInterface(this); - } + explicit WvContentDecryptionModule(cdm::Host* host); + virtual ~WvContentDecryptionModule(); + // cdm::ContentDecryptionModule implementation. virtual cdm::Status GenerateKeyRequest(const char* type, int type_size, const uint8_t* init_data, @@ -94,6 +93,8 @@ class WvContentDecryptionModule : public cdm::ContentDecryptionModule, const uint8_t* key, int key_size, const uint8_t* key_id, int key_id_size) OVERRIDE; + virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) OVERRIDE; + virtual cdm::Status CancelKeyRequest(const char* session_id, int session_id_size) OVERRIDE; @@ -102,24 +103,6 @@ class WvContentDecryptionModule : public cdm::ContentDecryptionModule, virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer, cdm::DecryptedBlock* decrypted_buffer) OVERRIDE; - virtual cdm::Status InitializeAudioDecoder( - const cdm::AudioDecoderConfig& audio_decoder_config) OVERRIDE; - - virtual cdm::Status InitializeVideoDecoder( - const cdm::VideoDecoderConfig& video_decoder_config) OVERRIDE; - - virtual void DeinitializeDecoder(cdm::StreamType decoder_type) OVERRIDE; - - virtual void ResetDecoder(cdm::StreamType decoder_type) OVERRIDE; - - virtual cdm::Status DecryptAndDecodeFrame( - const cdm::InputBuffer& encrypted_buffer, - cdm::VideoFrame* video_frame) OVERRIDE; - - virtual cdm::Status DecryptAndDecodeSamples( - const cdm::InputBuffer& encrypted_buffer, - cdm::AudioFrames* audio_frames) OVERRIDE; - virtual cdm::Status DecryptDecodeAndRenderFrame( const cdm::InputBuffer& encrypted_buffer) OVERRIDE; @@ -128,29 +111,30 @@ class WvContentDecryptionModule : public cdm::ContentDecryptionModule, virtual void Destroy() OVERRIDE; - // Provisioning related methods virtual cdm::Status GetProvisioningRequest( - std::string* request, std::string* default_url); + std::string* request, std::string* default_url) OVERRIDE; virtual cdm::Status HandleProvisioningResponse( - std::string& response); - - void EnablePolicyTimer(); - void DisablePolicyTimer(); - void OnTimerEvent(); + std::string& response) OVERRIDE; private: - virtual int64_t GetCurrentTimeInSeconds() { - return host_->GetCurrentWallTimeInSeconds(); - } + void EnablePolicyTimer(); + void DisablePolicyTimer(); + + virtual File::Impl* NewFileImpl() OVERRIDE; + + virtual int64_t GetCurrentTimeInSeconds() OVERRIDE; + cdm::Status DoSubsampleDecrypt(CdmDecryptionParameters& parameters, std::vector& iv, const cdm::InputBuffer& encrypted_buffer, cdm::DecryptedBlock* decrypted_block); + CdmEngine cdm_engine_; cdm::Host* const host_; HostEventListener host_event_listener_; WVClientPropertySet property_set_; + bool timer_enabled_; CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule); }; diff --git a/cdm/src/file_store.cpp b/cdm/src/file_store.cpp index b2c4336f..58a43f1a 100644 --- a/cdm/src/file_store.cpp +++ b/cdm/src/file_store.cpp @@ -6,7 +6,7 @@ namespace wvcdm { -IFileFactory* File::Impl::factory_ = NULL; +IFileFactory* File::Impl::factory_ = NULL; // File::Impl() Section // The file handler for cert.bin, aka DeviceCertificate is all we're @@ -14,68 +14,60 @@ IFileFactory* File::Impl::factory_ = NULL; bool File::Impl::Exists(const std::string& name) { if (name == "cert.bin") { - std::vector value; - if (host_->GetPlatformByteArray("DeviceCertificate", &value)) { - return true; - } + std::string value; + host_->GetPlatformString("DeviceCertificate", &value); + return !value.empty(); } return false; } bool File::Impl::Open(const std::string& name) { if (name == "cert.bin") { - fname_= name; + fname_ = name; return true; } return false; } bool File::Impl::Close() { - fname_ = ""; + fname_.clear(); return true; } bool File::Impl::Remove(const std::string& name) { - if (Exists(name)) { - std::vector value(0); - if (host_->SetPlatformByteArray("DeviceCertificate", value)) { - return true; - } + if (name == "cert.bin") { + host_->SetPlatformString("DeviceCertificate", ""); + return true; } return false; } -size_t File::Impl::Read(char* buffer, size_t bytes) { - if (fname_ == "cert.bin") { - std::vector value; - if (host_->GetPlatformByteArray("DeviceCertificate", &value)) { - memcpy(buffer, &value[0], bytes); - return bytes; - } - } - return 0; -} - -size_t File::Impl::Write(const char* buffer, size_t bytes) { +ssize_t File::Impl::Read(char* buffer, size_t bytes) { if (fname_ == "cert.bin") { - std::vector value; - value.resize(bytes); - memcpy(&value[0], buffer, bytes); - if (host_->PersistPlatformByteArray("DeviceCertificate", value)) { - return bytes; - } + std::string value; + host_->GetPlatformString("DeviceCertificate", &value); + memcpy(buffer, value.data(), std::min(bytes, value.size())); + return value.size() ? value.size() : -1; } - return 0; + return -1; } -size_t File::Impl::FileSize(const std::string& name) { - if (name == "cert.bin") { - std::vector value; - if (host_->GetPlatformByteArray("DeviceCertificate", &value)) { - return value.size(); - } +ssize_t File::Impl::Write(const char* buffer, size_t bytes) { + if (fname_ == "cert.bin") { + std::string value(buffer, bytes); + host_->SetPlatformString("DeviceCertificate", value); + return bytes; } - return 0; + return -1; +} + +ssize_t File::Impl::FileSize(const std::string& name) { + if (name == "cert.bin") { + std::string value; + host_->GetPlatformString("DeviceCertificate", &value); + return value.size() ? value.size() : -1; + } + return -1; } File::File() : impl_(File::Impl::factory_->NewFileImpl()) {} @@ -91,7 +83,6 @@ bool File::Open(const std::string& name, int flags) { void File::Close() { impl_->Close(); - return; } ssize_t File::Read(char* buffer, size_t bytes) { @@ -136,11 +127,7 @@ bool File::IsRegularFile(const std::string& path) { } ssize_t File::FileSize(const std::string& path) { - size_t size = impl_->FileSize(path); - if (size > 0) { - return size; - } - return -1; + return impl_->FileSize(path); } } // namespace wvcdm diff --git a/cdm/src/properties_common.cpp b/cdm/src/properties_common.cpp index 539e28f8..8589c6ab 100644 --- a/cdm/src/properties_common.cpp +++ b/cdm/src/properties_common.cpp @@ -3,7 +3,6 @@ #include "properties.h" #include -#include #include "log.h" diff --git a/cdm/src/timer.cpp b/cdm/src/timer.cpp deleted file mode 100644 index 50aa3cdb..00000000 --- a/cdm/src/timer.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Timer class - provides a simple timer implementation -// -#include "cdm_host_timer.h" -#include "scoped_ptr.h" -#include "timer.h" - -#include -#include -#include -#include -#include - -namespace wvcdm { - -ITimerFactory* Timer::Impl::factory_ = NULL; - -Timer::Impl::Impl(cdm::Host* const host) - : host_(host), handler_(NULL), delay_ms_(0), - state_(kIdle) { -} - -void Timer::Impl::RegisterTimerFactory(ITimerFactory* factory) { - factory_ = factory; -} - -void Timer::Impl::Start(TimerHandler* handler, uint32_t time_in_secs) { - handler_ = handler; - delay_ms_ = time_in_secs * 1000; - state_ = kRunning; - host_->SetTimer(delay_ms_, this); -} - -void Timer::Impl::OnTimerEvent() { - if (kRunning == state_) { - handler_->OnTimerEvent(); - host_->SetTimer(delay_ms_, this); - } -} - -void Timer::Impl::Stop() { - state_ = kIdle; -} - -Timer::Timer() : impl_(NULL) {} - -Timer::~Timer() { - if (impl_) - delete impl_; -} - -bool Timer::Start(TimerHandler* handler, uint32_t time_in_secs) { - if (!handler || 0 == time_in_secs) - return false; - if (!impl_ && Impl::factory_) - impl_ = Impl::factory_->NewTimerImpl(); - if(!impl_) - return false; - - impl_->Start(handler, time_in_secs); - return IsRunning(); -} - -void Timer::Stop() { - if (impl_) - impl_->Stop(); -} - -bool Timer::IsRunning() { - if (!impl_) - return false; - return impl_->IsRunning(); -} - -} // namespace wvcdm diff --git a/cdm/src/wv_content_decryption_module.cpp b/cdm/src/wv_content_decryption_module.cpp index dd5f1929..0afb7600 100644 --- a/cdm/src/wv_content_decryption_module.cpp +++ b/cdm/src/wv_content_decryption_module.cpp @@ -2,17 +2,14 @@ #include "wv_content_decryption_module.h" -#include #include -#include "cdm_client_property_set.h" -#include "content_decryption_module.h" -#include "initialization_data.h" +#include +#include "initialization_data.h" #include "log.h" #include "OEMCryptoCENC.h" #include "properties.h" - #include "wv_cdm_constants.h" #include "wv_cdm_types.h" #include "wv_cdm_version.h" @@ -38,10 +35,10 @@ void* CreateCdmInstance(int cdm_interface_version, const char* key_system, int GetCdmVersion() { return cdm::kCdmInterfaceVersion; } namespace { + static const std::string kWvCdmVersionString(WV_CDM_VERSION); -const int kCdmPolicyTimerDurationSeconds = 1; -const int kCdmPolicyTimerCancel = 0; +const int kCdmPolicyTimerDurationSeconds = 5; // The iso spec only uses the lower 8 bytes of the iv as // the counter. @@ -69,6 +66,7 @@ bool Ctr128Add(size_t block_count, uint8_t* counter) { } return true; } + } // namespace namespace wvcdm { @@ -80,6 +78,13 @@ bool IsBufferEncrypted(const cdm::InputBuffer& input_buffer) { // cdm::ContentDecryptionModule implementation. +WvContentDecryptionModule::WvContentDecryptionModule(cdm::Host* host) + : host_(host), + host_event_listener_(host, &cdm_engine_), + timer_enabled_(false) { + HostClock::SetClockInterface(this); +} + WvContentDecryptionModule::~WvContentDecryptionModule() { DisablePolicyTimer(); } @@ -87,7 +92,7 @@ WvContentDecryptionModule::~WvContentDecryptionModule() { cdm::Status WvContentDecryptionModule::GenerateKeyRequest( const char* type, int type_size, const uint8_t* init_data, int init_data_size) { - LOGI("Enter WvContentDecryptionModule::GenerateKeyRequest()"); + LOGI("WvContentDecryptionModule::GenerateKeyRequest()"); CdmInitData init_data_internal(reinterpret_cast(init_data), init_data_size); InitializationData initialization_data(ISO_BMFF_VIDEO_MIME_TYPE, @@ -97,14 +102,14 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest( std::string security_level; std::string privacy_mode; - VectorBytes service_certificate; + std::string service_certificate; host_->GetPlatformString("SecurityLevel", &security_level); host_->GetPlatformString("PrivacyOn", &privacy_mode); - host_->GetPlatformByteArray("ServiceCertificate", &service_certificate); + host_->GetPlatformString("ServiceCertificate", &service_certificate); property_set_.set_security_level(security_level); - property_set_.set_use_privacy_mode(privacy_mode == "True" ? 1 : 0 ); + property_set_.set_use_privacy_mode(privacy_mode == "True" ? 1 : 0); property_set_.set_service_certificate(service_certificate); CdmResponseType result = @@ -146,7 +151,7 @@ cdm::Status WvContentDecryptionModule::AddKey(const char* session_id, const uint8_t* key, int key_size, const uint8_t* key_id, int key_id_size) { - LOGI("Enter WvContentDecryptionModule::AddKey()\n"); + LOGI("WvContentDecryptionModule::AddKey()"); CdmSessionId session_id_internal(session_id, session_id_size); CdmKeyResponse key_data((const char*)key, key_size); CdmKeySetId key_set_id; @@ -160,12 +165,17 @@ cdm::Status WvContentDecryptionModule::AddKey(const char* session_id, } else { return cdm::kSessionError; } +} +bool WvContentDecryptionModule::IsKeyValid(const uint8_t* key_id, + int key_id_size) { + KeyId key(reinterpret_cast(key_id), key_id_size); + return cdm_engine_.IsKeyLoaded(key); } cdm::Status WvContentDecryptionModule::CancelKeyRequest(const char* session_id, int session_id_size) { - LOGI("Enter WvContentDecryptionModule::CancelKeyRequest()\n"); + LOGI("WvContentDecryptionModule::CancelKeyRequest()"); CdmSessionId session_id_internal(session_id, session_id_size); return cdm_engine_.CancelKeyRequest(session_id_internal) == NO_ERROR ? cdm::kSuccess @@ -173,18 +183,17 @@ cdm::Status WvContentDecryptionModule::CancelKeyRequest(const char* session_id, } void WvContentDecryptionModule::TimerExpired(void* context) { - LOGI("Timer expired, send cdm_engine OnTimerEvent"); - if (this != context) { - LOGD("Context should have been set, Timer Expired Error\n"); - return; + LOGI("WvContentDecryptionModule::TimerExpired()"); + if (timer_enabled_) { + cdm_engine_.OnTimerEvent(); + host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL); } - OnTimerEvent(); } cdm::Status WvContentDecryptionModule::Decrypt( const cdm::InputBuffer& encrypted_buffer, cdm::DecryptedBlock* decrypted_block) { - LOGI("=>Enter WvContentDecryptionModule::Decrypt()\n"); + LOGI("WvContentDecryptionModule::Decrypt()"); if (static_cast(encrypted_buffer.iv_size) != KEY_IV_SIZE) return cdm::kDecryptError; std::vector < uint8_t > iv(KEY_IV_SIZE); @@ -210,47 +219,13 @@ cdm::Status WvContentDecryptionModule::Decrypt( decrypted_block); } -cdm::Status WvContentDecryptionModule::InitializeAudioDecoder( - const cdm::AudioDecoderConfig& audio_decoder_config) { - LOGI("WvContentDecryptionModule::InitializeAudioDecoder() Not implemented\n"); - return cdm::kDecodeError; -} - -cdm::Status WvContentDecryptionModule::InitializeVideoDecoder( - const cdm::VideoDecoderConfig& video_decoder_config) { - LOGI("WvContentDecryptionModule::InitializeVideoDecoder() Not implemented\n"); - return cdm::kDecodeError; -} - -void WvContentDecryptionModule::DeinitializeDecoder( - cdm::StreamType decoder_type) { - LOGI("WvContentDecryptionModule::DeInitializeDecoder() Not implemented\n"); -} - -void WvContentDecryptionModule::ResetDecoder(cdm::StreamType decoder_type) { - LOGI("WvContentDecryptionModule::ResetDecoder() Not implemented\n"); -} - -cdm::Status WvContentDecryptionModule::DecryptAndDecodeFrame( - const cdm::InputBuffer& encrypted_buffer, cdm::VideoFrame* video_frame) { - LOGI("WvContentDecryptionModule::DecryptAndDecodeFrame() Not implemented\n"); - return cdm::kDecodeError; -} - -cdm::Status WvContentDecryptionModule::DecryptAndDecodeSamples( - const cdm::InputBuffer& encrypted_buffer, cdm::AudioFrames* audio_frames) { - LOGI( - "WvContentDecryptionModule::DecryptAndDecodeSamples() Not implemented\n"); - return cdm::kDecodeError; -} - // This is the Level 1 API. When the host application calls the CDM's // DecryptDecodeAndRenderFrame(), rather than the CDM's Decrypt(), // OEMCrypto_DecryptCTR() will be told to use direct rendering with no // cleartext in the return. cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderFrame( const cdm::InputBuffer& encrypted_buffer) { - LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderFrame()\n"); + LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderFrame()"); if (static_cast(encrypted_buffer.iv_size) != KEY_IV_SIZE) return cdm::kDecryptError; @@ -276,7 +251,7 @@ cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderFrame( // in the return. cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderSamples( const cdm::InputBuffer& encrypted_buffer) { - LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderSamples()\n"); + LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderSamples()"); if (static_cast(encrypted_buffer.iv_size) != KEY_IV_SIZE) return cdm::kDecryptError; @@ -324,19 +299,16 @@ cdm::Status WvContentDecryptionModule::HandleProvisioningResponse( } void WvContentDecryptionModule::EnablePolicyTimer() { - LOGI("WvContentDecryptionModule::EnablePolicyTimer()\n"); - host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, this); + LOGI("WvContentDecryptionModule::EnablePolicyTimer()"); + if (!timer_enabled_) { + timer_enabled_ = true; + host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL); + } } void WvContentDecryptionModule::DisablePolicyTimer() { - LOGI("WvContentDecryptionModule::DisablePolicyTimer()\n"); - host_->SetTimer(kCdmPolicyTimerCancel, NULL); -} - -void WvContentDecryptionModule::OnTimerEvent() { - - LOGI("WvContentDecryptionModule::OnTimerEvent()\n"); - cdm_engine_.OnTimerEvent(); + LOGI("WvContentDecryptionModule::DisablePolicyTimer()"); + timer_enabled_ = false; } cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt( @@ -385,7 +357,7 @@ cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt( continue; if (is_encrypted) { uint32_t counter = encrypted_offset / kIvSize; - ::Ctr128Add(counter - block_ctr, &iv[0]); + Ctr128Add(counter - block_ctr, &iv[0]); block_ctr = counter; } @@ -413,16 +385,22 @@ cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt( switch (status) { case wvcdm::NEED_KEY: return cdm::kNoKey; - break; case wvcdm::NO_ERROR: break; default: return cdm::kDecryptError; - break; } } } return cdm::kSuccess; } +int64_t WvContentDecryptionModule::GetCurrentTimeInSeconds() { + return host_->GetCurrentWallTimeInSeconds(); +} + +File::Impl* WvContentDecryptionModule::NewFileImpl() { + return new File::Impl(host_); +} + } // namespace wvcdm diff --git a/cdm/test/cdm_api_test.cpp b/cdm/test/cdm_api_test.cpp index 6f335c5f..1c6c4361 100644 --- a/cdm/test/cdm_api_test.cpp +++ b/cdm/test/cdm_api_test.cpp @@ -7,49 +7,37 @@ // Review the TestHost class below to observe how the CDM interfaces with // the host application. -// -#include + +#include #include #include -#include #include +#include + #include "clock.h" #include "config_test_env.h" #include "content_decryption_module.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include "license_request.h" #include "log.h" - #include "scoped_ptr.h" #include "string_conversions.h" #include "url_request.h" -#include "wv_cdm_types.h" - #include "wv_cdm_common.h" -using ::testing::_; -using ::testing::AtLeast; -using ::testing::Invoke; -using ::testing::Return; using wvcdm::scoped_ptr; static const int kTestPolicyRenewalDelaySeconds = 60; static const int kDelayWaitToForRenewalMessageSeconds = 2; -// Linux based get time for host. -// This can be tailored if required. -double GetCurrentTestTime() { + +static double GetCurrentTime() { struct timeval tv; tv.tv_sec = tv.tv_usec = 0; gettimeofday(&tv, NULL); return tv.tv_sec; } -using wvcdm::StringPairs; -using wvcdm::VectorPairs; - // These classes below are naive implementation of the abstract classes defined // in the CDM interface (content_decryptiom_module.h), which are used for tests // only. @@ -77,192 +65,11 @@ class TestBuffer : public cdm::Buffer { CORE_DISALLOW_COPY_AND_ASSIGN(TestBuffer); }; -class TestHostFile { - public: - TestHostFile() : fname_(""), file_(NULL) {} - virtual ~TestHostFile() { if (file_) fclose(file_); } - virtual bool Init(const std::string& name); - virtual bool Exists(); - virtual bool Remove(); - virtual size_t Size(); - virtual size_t Read(char* buffer, size_t bytes); - virtual size_t Write(const char* buffer, size_t bytes); - virtual std::string GetFileName() { return fname_;} - - private: - std::string fname_; - FILE* file_; - CORE_DISALLOW_COPY_AND_ASSIGN(TestHostFile); -}; - -bool TestHostFile::Init(const std::string& name) { - fname_ = name; - return true; -} - -bool TestHostFile::Exists() { - file_ = fopen(fname_.c_str(), "rb"); - bool exists = file_; - if (file_) { - fclose(file_); - file_ = NULL; - } - return (exists); -} - -bool TestHostFile::Remove() { - if (Exists()) { - if (remove(fname_.c_str())) { - return true; - } - } - return false; -} - -size_t TestHostFile::Size() { - file_ = fopen(fname_.c_str(), "rb"); - if (file_) { - fseek (file_, 0, SEEK_END); - size_t size = ftell (file_); - rewind (file_); - - if (file_) { - fclose(file_); - file_ = NULL; - } - return size; - } - return 0; -} - -size_t TestHostFile::Read(char* buffer, size_t bytes) { - file_ = fopen(fname_.c_str(), "rb"); - if (file_) { - fseek (file_, 0, SEEK_END); - size_t size = ftell (file_); - rewind (file_); - size_t check = fread (buffer,1,size,file_); - if (file_) { - fclose(file_); - file_ = NULL; - } - return check; - } - return 0; -} - -size_t TestHostFile::Write(const char* buffer, size_t bytes) { - FILE* file_ = fopen(fname_.c_str(), "w+"); - if (file_) { - size_t check = fwrite (buffer,1,bytes,file_); - if (file_) { - fclose(file_); - file_ = NULL; - } - return check; - } - return 0; -} - -class IHostTime { - public: - virtual double GetCurrentTestTime() = 0; -}; - -class SimplePThread { -public: - SimplePThread(); - ~SimplePThread(); - void Start(uint64_t periodicity, void* host_ptr, void* context); - void Stop(); - uint32_t GetTickCount(); - -private: - static bool is_running_; - pthread_t simple_timer_thread_; - - typedef void* (*TimeThreadFunc)(void *arg_list); - static void* Tthread_function(void *arg_list); - TimeThreadFunc time_thread_function_; - struct arg_list_struct { - uint64_t periodicity; - void* host_ptr; - void* context; - }; - static uint32_t tick_count_; -}; - -bool SimplePThread::is_running_ = false; -uint32_t SimplePThread::tick_count_ = 0; - -SimplePThread::SimplePThread() : simple_timer_thread_(0) { -} - -SimplePThread::~SimplePThread() { - LOGI("SimplePThread DTOR: calling Stop() to shutdown thread."); - Stop(); -} - -void SimplePThread::Stop() { - if (is_running_) { - void* result = NULL; - LOGI("SimplePThread is running and now it will be cancelled\n"); - pthread_cancel(simple_timer_thread_); - pthread_join(simple_timer_thread_, &result); - is_running_ = false; - tick_count_ = 0; - } -} - -void SimplePThread::Start(uint64_t periodicity, void* host_ptr, void* context) { - struct arg_list_struct* arg_list = new(struct arg_list_struct); - - arg_list->periodicity = periodicity; - arg_list->host_ptr = host_ptr; - arg_list->context = context; - - time_thread_function_ = &Tthread_function; - Stop(); - is_running_ = true; - if(pthread_create(&simple_timer_thread_, NULL, time_thread_function_, - arg_list)) { - LOGI("Error creating pthread\n"); - return; - } - LOGI("Created the Simple thread to support the timer callback\n"); -} - -void* SimplePThread::Tthread_function(void *arg_list) { - LOGI("Entered the SimplePThread::Tthread_function\n"); - - uint64_t periodicity = (static_cast(arg_list))->periodicity; - void* host_ptr = (static_cast(arg_list))->host_ptr; - void* context = (static_cast(arg_list))->context; - delete static_cast(arg_list); - - LOGI("starting the while loop in the secondary thread\n"); - - while (is_running_) { - usleep(static_cast(periodicity * 1000)); - if (is_running_) { - ((cdm::ContentDecryptionModule*)host_ptr)->TimerExpired(context); - tick_count_++; - } - } - - return NULL; -} - -uint32_t SimplePThread::GetTickCount() { - return tick_count_; -} - -class TestHost : public cdm::Host, public IHostTime { +class TestHost : public cdm::Host { public: // These structs are used to store the KeyMessages and KeyErrors passed to // this class' objects. struct KeyMessage { - KeyMessage() {} std::string session_id; std::string message; std::string default_url; @@ -281,11 +88,9 @@ class TestHost : public cdm::Host, public IHostTime { // cdm::Host implementation. virtual cdm::Buffer* Allocate(int32_t capacity) OVERRIDE; - MOCK_METHOD2(SetTimer, void(int64_t, void*)); - virtual void DoSetTimer(int64_t delay_ms, void* context); - // Returns the current "global" time from the GetCurrentTestTime() function - // unless the time is manually set using TestHost::SetCurrentTime()(). - MOCK_METHOD0(GetCurrentWallTimeInSeconds, double(void)); + virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE; + + virtual double GetCurrentWallTimeInSeconds() OVERRIDE; virtual void SendKeyMessage(const char* session_id, int32_t session_id_length, const char* message, int32_t message_length, @@ -296,30 +101,14 @@ class TestHost : public cdm::Host, public IHostTime { cdm::MediaKeyError error_code, uint32_t system_code) OVERRIDE; - virtual void GetPrivateData(int32_t* instance, - GetPrivateInterface* get_interface) OVERRIDE; + virtual void GetPlatformString(const std::string& name, + std::string* value) OVERRIDE; - virtual int GetPlatformString(const std::string& name, - std::string* value) OVERRIDE; - - virtual int SetPlatformString(const std::string& name, - const std::string& value) OVERRIDE;; - - virtual int PersistPlatformString(const std::string& name, - const std::string& value) OVERRIDE;; - - virtual int GetPlatformByteArray(const std::string& name, - std::vector* value) OVERRIDE; - - virtual int SetPlatformByteArray(const std::string& name, - const std::vector& value) OVERRIDE; - - virtual int PersistPlatformByteArray(const std::string& name, - const std::vector& value) OVERRIDE; + virtual void SetPlatformString(const std::string& name, + const std::string& value) OVERRIDE; // Methods only for this test. - void SetCurrentTime(double current_time); - void FastForwardTimeForNextTimerEvent(); + void FastForwardTime(double seconds); int KeyMessagesSize() const; int KeyErrorsSize() const; @@ -333,17 +122,23 @@ class TestHost : public cdm::Host, public IHostTime { KeyMessage GetKeyMessage(int index) const; KeyError GetKeyError(int index) const; - void* context() { return context_; } - void SetCdmPtr(cdm::ContentDecryptionModule* host_cdm_ptr); - - virtual double GetCurrentTestTime(); - uint32_t GetTickCount(); + void SetCdmPtr(cdm::ContentDecryptionModule* cdm); private: - // This is used in GetCurrentWallTimeInSeconds(). This field is used to - // control the "current time". + struct Timer { + Timer(double expiry_time, void* context) + : expiry_time(expiry_time), context(context) {} + + bool operator<(const Timer& other) const { + return expiry_time < other.expiry_time; + } + + double expiry_time; + void* context; + }; + double current_time_; - bool current_time_is_set_; + std::priority_queue timers_; std::vector key_messages_; std::vector key_errors_; @@ -351,29 +146,13 @@ class TestHost : public cdm::Host, public IHostTime { bool has_new_key_message_; bool has_new_key_error_; - void* context_; - int64_t delay_ms_; + std::map platform_strings_; - cdm::ContentDecryptionModule* host_cdm_ptr_; - - // These are containers for the platform sharing data. - std::set platform_strings_set_; - std::set platform_vectors_set_; - TestHostFile test_host_file_; - void LoadPersistentValues(); - SimplePThread simple_thread_; + cdm::ContentDecryptionModule* cdm_; CORE_DISALLOW_COPY_AND_ASSIGN(TestHost); }; -class TestHostFactory { - public: - TestHost* GetTestHost(); - - private: - scoped_ptr test_host_; -}; - TestBuffer* TestBuffer::Create(uint32_t capacity) { return new TestBuffer(capacity); } @@ -395,37 +174,34 @@ void TestBuffer::SetSize(int32_t size) { size_ = size; } int32_t TestBuffer::Size() const { return size_; } TestBuffer::TestBuffer(uint32_t capacity) - : buffer_(new uint8_t[capacity]), capacity_(capacity) {} + : buffer_(new uint8_t[capacity]), + capacity_(capacity) {} TestBuffer::~TestBuffer() {} TestHost::TestHost() - : current_time_(::GetCurrentTestTime()), - current_time_is_set_(false), + : current_time_(GetCurrentTime()), has_new_key_message_(false), has_new_key_error_(false), - context_(NULL), - delay_ms_(0), - host_cdm_ptr_(NULL) { - LoadPersistentValues(); + cdm_(NULL) { } -TestHost::~TestHost() {} +TestHost::~TestHost() { + if (cdm_) + cdm_->Destroy(); +} cdm::Buffer* TestHost::Allocate(int32_t capacity) { return TestBuffer::Create(capacity); } -// SetTimer is a mock method mapping to DoSetTimer(). The cdm::Host -// calls back to the TimerExpired callback -// every delay_ms. Zero delay_ms means WV is requesting -// termination of timer services. -void TestHost::DoSetTimer(int64_t delay_ms, void* context) { - if (delay_ms == 0) { - simple_thread_.Stop(); - return; - } - simple_thread_.Start(delay_ms, (void*)host_cdm_ptr_, context); +void TestHost::SetTimer(int64_t delay_ms, void* context) { + double expiry_time = current_time_ + (delay_ms / 1000.0); + timers_.push(Timer(expiry_time, context)); +} + +double TestHost::GetCurrentWallTimeInSeconds() { + return current_time_; } void TestHost::SendKeyMessage(const char* session_id, int32_t session_id_length, @@ -451,131 +227,34 @@ void TestHost::SendKeyError(const char* session_id, int32_t session_id_length, has_new_key_error_ = true; } -void TestHost::SetCurrentTime(double current_time) { - current_time_is_set_ = true; - current_time_ = current_time; -} - -void TestHost::FastForwardTimeForNextTimerEvent() { - current_time_is_set_ = true; - current_time_ += (delay_ms_ / 1000.0); - delay_ms_ = 0; -} - -void TestHost::GetPrivateData(int32_t* instance, - GetPrivateInterface* get_interface) { - LOGI("NOTIMPLEMENTED()"); -} - -// The Platform methods below are implemented to fill and retrieve -// platform specific information. The host application takes responsibility for -// this interface. - -int TestHost::GetPlatformString(const std::string& name, - std::string* value) { - std::set::iterator it; - - for (it = platform_strings_set_.begin(); - it != platform_strings_set_.end(); ++it) { - - StringPairs sp = *it; - std::string x = sp.first; - std::string y = sp.second; - if (x == name) { - *value = y; - return 1; +void TestHost::FastForwardTime(double seconds) { + double goal_time = current_time_ + seconds; + while (current_time_ < goal_time) { + if (timers_.empty()) { + current_time_ = goal_time; + } else { + Timer t = timers_.top(); + timers_.pop(); + current_time_ = t.expiry_time; + cdm_->TimerExpired(t.context); } } - *value = "Not Found!"; - return 0; } -int TestHost::SetPlatformString(const std::string& name, - const std::string& value) { - StringPairs sp(name, value); - platform_strings_set_.insert(sp); - return 1; +void TestHost::GetPlatformString(const std::string& name, + std::string* value) { + *value = platform_strings_[name]; } -int TestHost::PersistPlatformString(const std::string& name, - const std::string& value) { - StringPairs sp(name, value); - // Write the pairs to a file such that they can be retrieved by - // GetPlatformString() even after a power cycle. - return 1; -} - -int TestHost::SetPlatformByteArray(const std::string& name, - const std::vector& value) { - // A zero value pair is an erase only. - std::set::iterator it; - for (it = platform_vectors_set_.begin(); - it != platform_vectors_set_.end(); ++it) { - VectorPairs vp = *it; - if (vp.first == name) { - platform_vectors_set_.erase(vp); - } - } - - if (value.size() > 0) { - VectorPairs vp(name, value); - platform_vectors_set_.insert(vp); - } - - return 1; -} - -int TestHost::PersistPlatformByteArray(const std::string& name, - const std::vector& value) { - - // Assume input name = "DeviceCertificate" and - // test_host_file.fname_ == "cert.bin" - // No other cases are currently needed or supported. - if (value.size() == 0) { - remove(test_host_file_.GetFileName().c_str()); - - } else { - std::string string_buffer; - string_buffer.resize(value.size()); - memcpy(&string_buffer[0], &value[0], value.size()); - test_host_file_.Write(&string_buffer[0], value.size()); - } - VectorPairs vp(name, value); - - SetPlatformByteArray(name, value); - // open file and persist this so that it can be retrieved as a string/vector - // correlated pair. Vendor needs to implement this! - return 1; -} - -int TestHost::GetPlatformByteArray(const std::string& name, - std::vector* value) { - std::set::iterator it; - for (it = platform_vectors_set_.begin(); - it != platform_vectors_set_.end(); ++it) { - - VectorPairs vp = *it; - std::string x = vp.first; - if (x == name) { - *value = vp.second; - return 1; - } - } - - value = NULL; - return 0; +void TestHost::SetPlatformString(const std::string& name, + const std::string& value) { + platform_strings_[name] = value; } int TestHost::KeyMessagesSize() const { return key_messages_.size(); } int TestHost::KeyErrorsSize() const { return key_errors_.size(); } -double TestHost::GetCurrentTestTime() { - current_time_is_set_ = true; - FastForwardTimeForNextTimerEvent(); - return current_time_; -} - TestHost::KeyMessage TestHost::GetLastKeyMessage() { if (!has_new_key_message_) { LOGD("No NEW"); @@ -609,40 +288,8 @@ TestHost::KeyError TestHost::GetKeyError(int index) const { return key_errors_[index]; } -void TestHost::SetCdmPtr(cdm::ContentDecryptionModule* host_cdm_ptr) { - host_cdm_ptr_ = host_cdm_ptr; -} - -uint32_t TestHost::GetTickCount() { - return simple_thread_.GetTickCount(); -} - -TestHost* TestHostFactory::GetTestHost() { - - if (!test_host_.get()) test_host_.reset(new TestHost()); - - return test_host_.get(); - - return NULL; -} - -// The CDM::Host object is responsible for storing and retrieving items -// that are accessed via GetPlatformByteArray. Currently, support for -// the device certificate is all that is required. - -void TestHost::LoadPersistentValues() { - test_host_file_.Init("cert.bin"); - if (test_host_file_.Exists()) { - size_t size = test_host_file_.Size(); - std::string string_buffer; - string_buffer.resize(size); - test_host_file_.Read(&string_buffer[0], size); - - std::vector vector_buffer(size); - memcpy(&vector_buffer[0], &string_buffer[0], size); - SetPlatformByteArray("DeviceCertificate", vector_buffer); - - } +void TestHost::SetCdmPtr(cdm::ContentDecryptionModule* cdm) { + cdm_ = cdm; } class TestDecryptedBlock : public cdm::DecryptedBlock { @@ -686,6 +333,7 @@ void TestDecryptedBlock::SetTimestamp(int64_t timestamp) { int64_t TestDecryptedBlock::Timestamp() const { return timestamp_; } namespace { + // Default license server, can be configured using --server command line option // Default key id (pssh), can be configured using --keyid command line option const char kKeySystemWidevine[] = "com.widevine.alpha"; @@ -694,53 +342,49 @@ wvcdm::KeyId g_key_id; wvcdm::CdmKeySystem g_key_system; std::string g_license_server; wvcdm::KeyId g_wrong_key_id; -int g_use_full_path = 0; // cannot use boolean in getopt_long + +void* GetCdmHost(int host_interface_version, void* user_data) { + if (host_interface_version != cdm::kHostInterfaceVersion) + return NULL; + return user_data; +} } // namespace namespace wvcdm { -void* GetCdmHost(int host_interface_version, void* user_data); - class WvCdmApiTest : public testing::Test { public: WvCdmApiTest() : cdm_(NULL) {} - ~WvCdmApiTest() { - if (cdm_) cdm_->Destroy(); - } + ~WvCdmApiTest() {} protected: virtual void SetUp() { - cdm_ = reinterpret_cast(::CreateCdmInstance( - cdm::kCdmInterfaceVersion, kKeySystemWidevine, - strlen(kKeySystemWidevine), GetCdmHost, &host_factory_)); - host_ = host_factory_.GetTestHost(); - host_->SetCdmPtr(cdm_); + // Create the Host. + host_.reset(new TestHost()); + // Set various parameters that the CDM will query. host_->SetPlatformString("SecurityLevel", "L1"); host_->SetPlatformString("PrivacyOn", "False"); - host_->SetPlatformString("UsesServiceCertificates", "False"); - // By default we allow the timer to fire twice; once - // when the addkey comes, and once when the wvcdm destructs and - // disables the timer. - EXPECT_CALL(*host_, SetTimer(_,_)) - .Times(AtLeast(2)) - .WillRepeatedly(Invoke(host_, &TestHost::DoSetTimer)); - - EXPECT_CALL(*host_, GetCurrentWallTimeInSeconds()) - .Times(AtLeast(1)) - .WillRepeatedly(Invoke(host_, &TestHost::GetCurrentTestTime)); - - // emulate pulling a certificate out of persistent storage. + // Put a phony service certificate into persistent storage. static const size_t kPrivacyCertSize = 256; - VectorBytes cert(kPrivacyCertSize); + std::string cert(kPrivacyCertSize, '\0'); for (size_t i = 0; i < cert.size(); i++) { cert[i] = i; } + host_->SetPlatformString("ServiceCertificate", cert); - host_->SetPlatformByteArray("ServiceCertificate", cert); + // Initialize the CDM module before creating a CDM instance. INITIALIZE_CDM_MODULE(); + + // Create the CDM. + cdm_ = reinterpret_cast(::CreateCdmInstance( + cdm::kCdmInterfaceVersion, kKeySystemWidevine, + strlen(kKeySystemWidevine), GetCdmHost, host_.get())); + + // Tell the Host about the CDM. + host_->SetCdmPtr(cdm_); } void GenerateKeyRequest(const std::string& key_system, @@ -781,7 +425,6 @@ class WvCdmApiTest : public testing::Test { std::string GetKeyRequestResponse(const std::string& server_url, const std::string& client_auth, int expected_response) { - // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url + client_auth); if (!url_request.is_connected()) { return ""; @@ -790,8 +433,6 @@ class WvCdmApiTest : public testing::Test { url_request.PostRequest(key_msg_); std::string response; int resp_bytes = url_request.GetResponse(&response); - LOGD("response:\r\n%s", response.c_str()); - LOGD("end %d bytes response dump", resp_bytes); // Some license servers return 400 for invalid message, some // return 500; treat anything other than 200 as an invalid message. @@ -809,7 +450,7 @@ class WvCdmApiTest : public testing::Test { std::string drm_msg; LicenseRequest lic_request; lic_request.GetDrmMessage(response, drm_msg); - LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), + LOGV("drm msg: %u bytes\n%s", drm_msg.size(), HexEncode(reinterpret_cast(drm_msg.data()), drm_msg.size()).c_str()); return drm_msg; @@ -1310,15 +951,12 @@ class WvCdmApiTest : public testing::Test { EXPECT_EQ(cdm::kDecryptError, status); } - void TimerTest(void* session_id) { host_->SetTimer(1000, session_id); } - std::string key_msg_; std::string session_id_; std::string server_url_; - cdm::ContentDecryptionModule* cdm_; - TestHostFactory host_factory_; - TestHost* host_; + cdm::ContentDecryptionModule* cdm_; // owned by host_ + scoped_ptr host_; }; // Note that these tests, BaseMessageTest, NormalDecryption and TimeTest, @@ -1328,8 +966,7 @@ class WvCdmApiTest : public testing::Test { // and works in your test environment. TEST_F(WvCdmApiTest, DeviceCertificateTest) { - std::vector value(0); - host_->PersistPlatformByteArray("DeviceCertificate", value); + host_->SetPlatformString("DeviceCertificate", ""); GenerateKeyRequest(g_key_system, g_key_id); // It will have to provision - // in here. TestHost::KeyMessage key_msg = host_->GetLastKeyMessage(); @@ -1352,25 +989,6 @@ TEST_F(WvCdmApiTest, BaseMessageTest) { CancelKeyRequest(session_id_); } -TEST_F(WvCdmApiTest, BaseTimerTest) { - GenerateKeyRequest(g_key_system, g_key_id); - TestHost::KeyMessage key_msg = host_->GetLastKeyMessage(); - session_id_ = key_msg.session_id; - key_msg_ = key_msg.message; - std::string drm_msg = GetKeyRequestResponse(g_license_server, - g_client_auth, 200); - AddKey(key_msg.session_id, drm_msg); - - LOGI("Sleeping and allowing the time expirations to increment"); - uint32_t sleep_period = static_cast(12000 / 1000); - sleep(sleep_period); - LOGI("Tick count is %ld\n", host_->GetTickCount()); - // We should have close to, maybe slightly less than sleep_period ticks. - // If it ticked at all then we pass as the timer must be expriring. - EXPECT_GE(host_->GetTickCount(), 1U); - CancelKeyRequest(session_id_); -} - TEST_F(WvCdmApiTest, NormalDecryption) { GenerateKeyRequest(g_key_system, g_key_id); TestHost::KeyMessage key_msg = host_->GetLastKeyMessage(); @@ -1410,8 +1028,8 @@ TEST_F(WvCdmApiTest, TimeTest) { g_client_auth, 200); AddKey(key_msg.session_id, drm_msg); - host_->SetCurrentTime(host_->GetCurrentTestTime() + kTestPolicyRenewalDelaySeconds); - sleep(kDelayWaitToForRenewalMessageSeconds); + host_->FastForwardTime(kTestPolicyRenewalDelaySeconds + + kDelayWaitToForRenewalMessageSeconds); // When the timer expired, we should have sent a renewal, so we can // add this renewed key now, assuming things are working as expected. @@ -1419,7 +1037,10 @@ TEST_F(WvCdmApiTest, TimeTest) { session_id_ = key_msg2.session_id; key_msg_ = key_msg2.message; - drm_msg = GetKeyRequestResponse(key_msg2.default_url, g_client_auth, 200); + // Note that the client auth string is not appended when the CDM tells + // us what URL to use. + EXPECT_FALSE(key_msg2.default_url.empty()); + drm_msg = GetKeyRequestResponse(key_msg2.default_url, "", 200); AddKey(key_msg2.session_id, drm_msg); } @@ -1453,15 +1074,6 @@ TEST_F(WvCdmApiTest, SecureDecryptionLevel1WithMissingSubsampleInfo) { WithMissingSubsampleInfoTest(); } -void* GetCdmHost(int host_interface_version, void* user_data) { - if (!host_interface_version || !user_data) return NULL; - - if (host_interface_version != cdm::kHostInterfaceVersion) return NULL; - - TestHostFactory* host_factory = reinterpret_cast(user_data); - return host_factory->GetTestHost(); -} - } // namespace wvcdm int main(int argc, char** argv) { @@ -1480,16 +1092,13 @@ int main(int argc, char** argv) { int show_usage = 0; static const struct option long_options[] = { - {"use_full_path", no_argument, &g_use_full_path, 0}, {"keyid", required_argument, NULL, 'k'}, {"server", required_argument, NULL, 's'}, - {"vmodule", required_argument, NULL, 0}, - {"v", required_argument, NULL, 0}, {NULL, 0, NULL, '\0'}}; int option_index = 0; int opt = 0; - while ((opt = getopt_long(argc, argv, "k:p:s:u:v", long_options, + while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) { switch (opt) { case 'k': { @@ -1502,15 +1111,19 @@ int main(int argc, char** argv) { g_license_server.assign(optarg); break; } - case 'u': { - g_use_full_path = 1; + case 'v': { + // This option has already been consumed by wvcdm::InitLogging() above. + // We only tell getopt about it so that it is not an error. We ignore + // the option here when seen. + // TODO: Stop passing argv to InitLogging, and instead set the log + // level here through the logging API. We should keep all command-line + // parsing at the application level, rather than split between various + // apps and various platform-specific logging implementations. break; } case '?': { show_usage = 1; break; - case 'v': - break; } } } @@ -1518,10 +1131,6 @@ int main(int argc, char** argv) { 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 << " --server="; std::cout @@ -1534,10 +1143,6 @@ int main(int argc, char** argv) { 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; } diff --git a/core/include/cdm_client_property_set.h b/core/include/cdm_client_property_set.h index a081a285..853c7658 100644 --- a/core/include/cdm_client_property_set.h +++ b/core/include/cdm_client_property_set.h @@ -13,9 +13,9 @@ class CdmClientPropertySet { public: virtual ~CdmClientPropertySet() {} - virtual std::string security_level() const = 0; + virtual const std::string& security_level() const = 0; virtual bool use_privacy_mode() const = 0; - virtual std::vector service_certificate() const = 0; + virtual const std::string& service_certificate() const = 0; virtual bool is_session_sharing_enabled() const = 0; virtual uint32_t session_sharing_id() const = 0; virtual void set_session_sharing_id(uint32_t id) = 0; diff --git a/core/include/cdm_engine.h b/core/include/cdm_engine.h index e9c8ff84..a488b0da 100644 --- a/core/include/cdm_engine.h +++ b/core/include/cdm_engine.h @@ -24,95 +24,91 @@ class CdmEngine { virtual ~CdmEngine(); // Session related methods - CdmResponseType OpenSession(const CdmKeySystem& key_system, - const CdmClientPropertySet* property_set, - CdmSessionId* session_id); - CdmResponseType CloseSession(const CdmSessionId& session_id); + virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, + const CdmClientPropertySet* property_set, + CdmSessionId* session_id); + virtual CdmResponseType CloseSession(const CdmSessionId& session_id); - CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); - CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); + virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods // Construct a valid license request - CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id, - const InitializationData& init_data, - const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateKeyRequest( + const CdmSessionId& session_id, const CdmKeySetId& key_set_id, + const InitializationData& init_data, const CdmLicenseType license_type, + CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, + std::string* server_url); // Accept license response and extract key info. - CdmResponseType AddKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data, - CdmKeySetId* key_set_id); + virtual CdmResponseType AddKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data, + CdmKeySetId* key_set_id); - CdmResponseType RestoreKey(const CdmSessionId& session_id, - const CdmKeySetId& key_set_id); + virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id); - CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); + virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); // Construct valid renewal request for the current session keys. - CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, + CdmKeyMessage* key_request, + std::string* server_url); // Accept renewal response and update key info. - CdmResponseType RenewKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data); + virtual CdmResponseType RenewKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data); // Query system information - CdmResponseType QueryStatus(CdmQueryMap* info); + virtual CdmResponseType QueryStatus(CdmQueryMap* info); // Query session information virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id, CdmQueryMap* key_info); // Query license information - CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, - CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Query seesion control information - CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, - CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id, + CdmQueryMap* key_info); // Provisioning related methods - CdmResponseType GetProvisioningRequest( - CdmCertificateType cert_type, - const std::string& cert_authority, - CdmProvisioningRequest* request, - std::string* default_url); + virtual CdmResponseType GetProvisioningRequest( + CdmCertificateType cert_type, const std::string& cert_authority, + CdmProvisioningRequest* request, std::string* default_url); - CdmResponseType HandleProvisioningResponse( - CdmProvisioningResponse& response, - std::string* cert, + virtual CdmResponseType HandleProvisioningResponse( + CdmProvisioningResponse& response, std::string* cert, std::string* wrapped_key); - CdmResponseType Unprovision(CdmSecurityLevel security_level); + virtual CdmResponseType Unprovision(CdmSecurityLevel security_level); // Usage related methods for streaming licenses - CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); - CdmResponseType ReleaseUsageInfo(const CdmUsageInfoReleaseMessage& message); + virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); + virtual CdmResponseType ReleaseUsageInfo( + const CdmUsageInfoReleaseMessage& message); // Decryption and key related methods // Accept encrypted buffer and return decrypted data. - CdmResponseType Decrypt(const CdmSessionId& session_id, - const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmSessionId& session_id, + const CdmDecryptionParameters& parameters); - size_t SessionSize() const { return sessions_.size(); } + virtual size_t SessionSize() const { return sessions_.size(); } // Is the key known to any session? - bool IsKeyLoaded(const KeyId& key_id); - bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); + virtual bool IsKeyLoaded(const KeyId& key_id); + virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); // Event listener related methods - bool AttachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); - bool DetachEventListener(const CdmSessionId& session_id, - WvCdmEventListener* listener); + virtual bool AttachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); + virtual bool DetachEventListener(const CdmSessionId& session_id, + WvCdmEventListener* listener); // Timer expiration method - void OnTimerEvent(); + virtual void OnTimerEvent(); private: // private methods diff --git a/core/include/cdm_session.h b/core/include/cdm_session.h index 2c51efba..fc327cc6 100644 --- a/core/include/cdm_session.h +++ b/core/include/cdm_session.h @@ -22,81 +22,83 @@ class WvCdmEventListener; class CdmSession { public: explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set); - ~CdmSession(); + virtual ~CdmSession(); - CdmResponseType Init(); + virtual CdmResponseType Init(); - CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id, - const CdmLicenseType license_type); - CdmResponseType RestoreUsageSession(const CdmKeyMessage& key_request, - const CdmKeyResponse& key_response); + virtual CdmResponseType RestoreOfflineSession( + const CdmKeySetId& key_set_id, const CdmLicenseType license_type); + virtual CdmResponseType RestoreUsageSession( + const CdmKeyMessage& key_request, const CdmKeyResponse& key_response); - void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; } - const CdmKeySystem& key_system() { return key_system_; } + virtual void set_key_system(const CdmKeySystem& ksystem) { + key_system_ = ksystem; + } + virtual const CdmKeySystem& key_system() { return key_system_; } - const CdmSessionId& session_id() { return session_id_; } + virtual const CdmSessionId& session_id() { return session_id_; } - CdmResponseType GenerateKeyRequest(const InitializationData& init_data, - const CdmLicenseType license_type, - const CdmAppParameterMap& app_parameters, - CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateKeyRequest( + const InitializationData& init_data, const CdmLicenseType license_type, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, + std::string* server_url); // AddKey() - Accept license response and extract key info. - CdmResponseType AddKey(const CdmKeyResponse& key_response, - CdmKeySetId* key_set_id); + virtual CdmResponseType AddKey(const CdmKeyResponse& key_response, + CdmKeySetId* key_set_id); // CancelKeyRequest() - Cancel session. - CdmResponseType CancelKeyRequest(); + virtual CdmResponseType CancelKeyRequest(); // Query session status - CdmResponseType QueryStatus(CdmQueryMap* key_info); + virtual CdmResponseType QueryStatus(CdmQueryMap* key_info); // Query license information - CdmResponseType QueryKeyStatus(CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyStatus(CdmQueryMap* key_info); // Query session control info - CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info); + virtual CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info); // Decrypt() - Accept encrypted buffer and return decrypted data. - CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); // License renewal // GenerateRenewalRequest() - Construct valid renewal request for the current // session keys. - CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request, + std::string* server_url); // RenewKey() - Accept renewal response and update key info. - CdmResponseType RenewKey(const CdmKeyResponse& key_response); + virtual CdmResponseType RenewKey(const CdmKeyResponse& key_response); // License release // GenerateReleaseRequest() - Construct valid release request for the current // session keys. - CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request, - std::string* server_url); + virtual CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request, + std::string* server_url); // ReleaseKey() - Accept response and release key. - CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); + virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); - bool IsKeyLoaded(const KeyId& key_id); + virtual bool IsKeyLoaded(const KeyId& key_id); - bool AttachEventListener(WvCdmEventListener* listener); - bool DetachEventListener(WvCdmEventListener* listener); + virtual bool AttachEventListener(WvCdmEventListener* listener); + virtual bool DetachEventListener(WvCdmEventListener* listener); - void OnTimerEvent(); - void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); + virtual void OnTimerEvent(); + virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); - SecurityLevel GetRequestedSecurityLevel(); - CdmSecurityLevel GetSecurityLevel(); + virtual SecurityLevel GetRequestedSecurityLevel(); + virtual CdmSecurityLevel GetSecurityLevel(); - CdmResponseType UpdateUsageInformation(); + virtual CdmResponseType UpdateUsageInformation(); - bool is_usage_update_needed() { return is_usage_update_needed_; } - void reset_is_usage_update_needed() { is_usage_update_needed_ = false; } + virtual bool is_usage_update_needed() { return is_usage_update_needed_; } + virtual void reset_is_usage_update_needed() { + is_usage_update_needed_ = false; + } private: - // Generate unique ID for each new session. CdmSessionId GenerateSessionId(); bool GenerateKeySetId(CdmKeySetId* key_set_id); @@ -116,6 +118,7 @@ class CdmSession { bool is_offline_; bool is_release_; bool is_usage_update_needed_; + bool is_initial_decryption_; // information useful for offline and usage scenarios CdmKeyMessage key_request_; diff --git a/core/include/crypto_session.h b/core/include/crypto_session.h index 3c9ac281..ffc6d055 100644 --- a/core/include/crypto_session.h +++ b/core/include/crypto_session.h @@ -19,61 +19,60 @@ typedef std::map CryptoKeyMap; class CryptoSession { public: CryptoSession(); - ~CryptoSession(); + virtual ~CryptoSession(); - bool ValidateKeybox(); - bool GetToken(std::string* token); - CdmSecurityLevel GetSecurityLevel(); - bool GetDeviceUniqueId(std::string* device_id); - bool GetSystemId(uint32_t* system_id); - bool GetProvisioningId(std::string* provisioning_id); + virtual bool ValidateKeybox(); + virtual bool GetToken(std::string* token); + virtual CdmSecurityLevel GetSecurityLevel(); + virtual bool GetDeviceUniqueId(std::string* device_id); + virtual bool GetSystemId(uint32_t* system_id); + virtual bool GetProvisioningId(std::string* provisioning_id); - CdmResponseType Open() { return Open(kLevelDefault); } - CdmResponseType Open(SecurityLevel requested_security_level); - void Close(); + virtual CdmResponseType Open() { return Open(kLevelDefault); } + virtual CdmResponseType Open(SecurityLevel requested_security_level); + virtual void Close(); - bool IsOpen() { return open_; } - CryptoSessionId oec_session_id() { return oec_session_id_; } + virtual bool IsOpen() { return open_; } + virtual CryptoSessionId oec_session_id() { return oec_session_id_; } // Key request/response - void GenerateRequestId(std::string& req_id_str); - bool PrepareRequest(const std::string& key_deriv_message, - bool is_provisioning, std::string* signature); - bool PrepareRenewalRequest(const std::string& message, - std::string* signature); - CdmResponseType LoadKeys(const std::string& message, - const std::string& signature, - const std::string& mac_key_iv, - const std::string& mac_key, - const std::vector& key_array, - const std::string& provider_session_token); - bool LoadCertificatePrivateKey(std::string& wrapped_key); - bool RefreshKeys(const std::string& message, const std::string& signature, - int num_keys, const CryptoKey* key_array); - bool GenerateNonce(uint32_t* nonce); - bool GenerateDerivedKeys(const std::string& message); - bool GenerateDerivedKeys(const std::string& message, - const std::string& session_key); - bool RewrapDeviceRSAKey(const std::string& message, - const std::string& signature, - const std::string& nonce, - const std::string& enc_rsa_key, - const std::string& rsa_key_iv, - std::string* wrapped_rsa_key); + virtual void GenerateRequestId(std::string& req_id_str); + virtual bool PrepareRequest(const std::string& key_deriv_message, + bool is_provisioning, std::string* signature); + virtual bool PrepareRenewalRequest(const std::string& message, + std::string* signature); + virtual CdmResponseType LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + const std::vector& key_array, + const std::string& provider_session_token); + virtual bool LoadCertificatePrivateKey(std::string& wrapped_key); + virtual bool RefreshKeys(const std::string& message, + const std::string& signature, int num_keys, + const CryptoKey* key_array); + virtual bool GenerateNonce(uint32_t* nonce); + virtual bool GenerateDerivedKeys(const std::string& message); + virtual bool GenerateDerivedKeys(const std::string& message, + const std::string& session_key); + virtual bool RewrapDeviceRSAKey(const std::string& message, + const std::string& signature, + const std::string& nonce, + const std::string& enc_rsa_key, + const std::string& rsa_key_iv, + std::string* wrapped_rsa_key); // Media data path - CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); + virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); - CdmResponseType UpdateUsageInformation(); - CdmResponseType GenerateUsageReport( - const std::string& provider_session_token, - std::string* usage_report); - CdmResponseType ReleaseUsageInformation( - const std::string& message, - const std::string& signature, + virtual CdmResponseType UpdateUsageInformation(); + virtual CdmResponseType GenerateUsageReport( + const std::string& provider_session_token, std::string* usage_report); + virtual CdmResponseType ReleaseUsageInformation( + const std::string& message, const std::string& signature, const std::string& provider_session_token); - bool GetRandom(size_t data_length, uint8_t* random_data); + virtual bool GetRandom(size_t data_length, uint8_t* random_data); private: void Init(); diff --git a/core/include/license.h b/core/include/license.h index 677c73e5..54fc8286 100644 --- a/core/include/license.h +++ b/core/include/license.h @@ -23,32 +23,32 @@ class CdmLicense { public: CdmLicense() : session_(NULL), initialized_(false) {} - ~CdmLicense() {} + virtual ~CdmLicense() {} - bool Init(const std::string& token, CryptoSession* session, + virtual bool Init(const std::string& token, CryptoSession* session, PolicyEngine* policy_engine); - bool PrepareKeyRequest(const InitializationData& init_data, + virtual bool PrepareKeyRequest(const InitializationData& init_data, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, const CdmSessionId& session_id, CdmKeyMessage* signed_request, std::string* server_url); - bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, + virtual bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url); - CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); - CdmResponseType HandleKeyUpdateResponse( + virtual CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); + virtual CdmResponseType HandleKeyUpdateResponse( bool is_renewal, const CdmKeyResponse& license_response); - bool RestoreOfflineLicense(const CdmKeyMessage& license_request, + virtual bool RestoreOfflineLicense(const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response); - bool RestoreUsageLicense(const CdmKeyMessage& license_request, + virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request, const CdmKeyResponse& license_response); - bool HasInitData() { return !stored_init_data_.empty(); } - bool IsKeyLoaded(const KeyId& key_id); + virtual bool HasInitData() { return !stored_init_data_.empty(); } + virtual bool IsKeyLoaded(const KeyId& key_id); - std::string provider_session_token() { return provider_session_token_; } + virtual std::string provider_session_token() { return provider_session_token_; } private: bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, diff --git a/core/include/policy_engine.h b/core/include/policy_engine.h index e327391f..8ef323ac 100644 --- a/core/include/policy_engine.h +++ b/core/include/policy_engine.h @@ -10,6 +10,8 @@ namespace wvcdm { +using video_widevine_server::sdk::LicenseIdentification; + class Clock; class PolicyEngineTest; @@ -18,53 +20,46 @@ class PolicyEngineTest; class PolicyEngine { public: PolicyEngine(); - ~PolicyEngine(); + virtual ~PolicyEngine(); // The value returned should be taken as a hint rather than an absolute // status. It is computed during the last call to either SetLicense/ // UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync // depending on the amount of time elapsed. The current decryption // status is not calculated to avoid overhead in the decryption path. - inline bool can_decrypt() { return can_decrypt_; } + virtual bool can_decrypt() { return can_decrypt_; } // OnTimerEvent is called when a timer fires. It notifies the Policy Engine // that the timer has fired and that it should check whether any events have // occurred since the last timer event. If so, it sets event_occurred to true // and sets event to point to the event that occurred. If not, it sets // event_occurred to false. - void OnTimerEvent(bool* event_occurred, CdmEventType* event); + virtual void OnTimerEvent(bool* event_occurred, CdmEventType* event); // SetLicense is used in handling the initial license response. It stores // an exact copy of the policy information stored in the license. // The license state transitions to kLicenseStateCanPlay if the license // permits playback. - void SetLicense(const video_widevine_server::sdk::License& license); + virtual void SetLicense(const video_widevine_server::sdk::License& license); - // Call this on first decrypt to set the start of playback. This is - // for cases where usage begins not when the license is received, - // but at the start of playback - void BeginDecryption(void); + // Call this on first decrypt to set the start of playback. + virtual void BeginDecryption(void); // UpdateLicense is used in handling a license response for a renewal request. // The response may only contain any policy fields that have changed. In this // case an exact copy is not what we want to happen. We also will receive an // updated license_start_time from the server. The license will transition to // kLicenseStateCanPlay if the license permits playback. - void UpdateLicense(const video_widevine_server::sdk::License& license); + virtual void UpdateLicense( + const video_widevine_server::sdk::License& license); - CdmResponseType Query(CdmQueryMap* key_info); + virtual CdmResponseType Query(CdmQueryMap* key_info); - const video_widevine_server::sdk::LicenseIdentification& license_id() { - return license_id_; - } - - bool IsLicenseDurationExpired(int64_t current_time); - bool IsPlaybackDurationExpired(int64_t current_time); + virtual const LicenseIdentification& license_id() { return license_id_; } private: typedef enum { kLicenseStateInitial, - kLicenseStateInitialPendingUsage, kLicenseStateCanPlay, kLicenseStateNeedRenewal, kLicenseStateWaitingLicenseUpdate, @@ -73,6 +68,11 @@ class PolicyEngine { void Init(Clock* clock); + bool IsLicenseDurationExpired(int64_t current_time); + int64_t GetLicenseDurationRemaining(int64_t current_time); + bool IsPlaybackDurationExpired(int64_t current_time); + int64_t GetPlaybackDurationRemaining(int64_t current_time); + bool IsRenewalDelayExpired(int64_t current_time); bool IsRenewalRecoveryDurationExpired(int64_t current_time); bool IsRenewalRetryIntervalExpired(int64_t current_time); diff --git a/core/include/properties.h b/core/include/properties.h index a8c98f4e..f5406e0f 100644 --- a/core/include/properties.h +++ b/core/include/properties.h @@ -53,9 +53,10 @@ class Properties { static bool GetFactoryKeyboxPath(std::string* keybox); static bool GetOEMCryptoPath(std::string* library_name); static bool GetSecurityLevelDirectories(std::vector* dirs); - static const std::string GetSecurityLevel(const CdmSessionId& session_id); - static const std::vector GetServiceCertificate( - const CdmSessionId& session_id); + static bool GetSecurityLevel(const CdmSessionId& session_id, + std::string* security_level); + static bool GetServiceCertificate(const CdmSessionId& session_id, + std::string* service_certificate); static bool UsePrivacyMode(const CdmSessionId& session_id); static uint32_t GetSessionSharingId(const CdmSessionId& session_id); diff --git a/core/include/wv_cdm_types.h b/core/include/wv_cdm_types.h index f4931d2a..fea94185 100644 --- a/core/include/wv_cdm_types.h +++ b/core/include/wv_cdm_types.h @@ -28,11 +28,6 @@ typedef std::string CdmUsageInfoReleaseMessage; typedef std::string CdmProvisioningRequest; typedef std::string CdmProvisioningResponse; -// Types for shared host/cdm interface pairs used to shared vendor data. -typedef std::pair StringPairs; -typedef std::vector VectorBytes; -typedef std::pair VectorPairs; - enum CdmResponseType { NO_ERROR, UNKNOWN_ERROR, diff --git a/core/src/cdm_session.cpp b/core/src/cdm_session.cpp index 333c32f5..6e0d2b01 100644 --- a/core/src/cdm_session.cpp +++ b/core/src/cdm_session.cpp @@ -33,6 +33,7 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) is_offline_(false), is_release_(false), is_usage_update_needed_(false), + is_initial_decryption_(true), is_certificate_loaded_(false) { if (cdm_client_property_set) { Properties::AddSessionPropertySet(session_id_, cdm_client_property_set); @@ -64,6 +65,7 @@ CdmResponseType CdmSession::Init() { crypto_session_.reset(session.release()); license_received_ = false; reinitialize_session_ = false; + is_initial_decryption_ = true; return NO_ERROR; } @@ -313,6 +315,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { CdmResponseType status = crypto_session_->Decrypt(params); if (NO_ERROR == status) { + if (is_initial_decryption_) { + policy_engine_.BeginDecryption(); + is_initial_decryption_ = false; + } if (!is_usage_update_needed_) { is_usage_update_needed_ = !license_parser_.provider_session_token().empty(); @@ -515,8 +521,9 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { } SecurityLevel CdmSession::GetRequestedSecurityLevel() { - if (Properties::GetSecurityLevel(session_id_) - .compare(QUERY_VALUE_SECURITY_LEVEL_L3) == 0) { + std::string security_level; + if (Properties::GetSecurityLevel(session_id_, &security_level) && + security_level == QUERY_VALUE_SECURITY_LEVEL_L3) { return kLevel3; } diff --git a/core/src/license.cpp b/core/src/license.cpp index 232c1189..9d582fa8 100644 --- a/core/src/license.cpp +++ b/core/src/license.cpp @@ -193,10 +193,10 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data, } bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id); - std::vector cert = Properties::GetServiceCertificate(session_id); - std::string serialized_service_certificate(cert.begin(), cert.end()); - - if (serialized_service_certificate.empty()) + std::string serialized_service_certificate; + if (!Properties::GetServiceCertificate(session_id, + &serialized_service_certificate) || + serialized_service_certificate.empty()) serialized_service_certificate = service_certificate_; if (privacy_mode_enabled && serialized_service_certificate.empty()) { diff --git a/core/src/policy_engine.cpp b/core/src/policy_engine.cpp index 59949603..2b4f0244 100644 --- a/core/src/policy_engine.cpp +++ b/core/src/policy_engine.cpp @@ -2,16 +2,16 @@ #include "policy_engine.h" -#include -#include +#include + #include #include #include +#include "clock.h" #include "log.h" #include "properties.h" #include "string_conversions.h" -#include "clock.h" #include "wv_cdm_constants.h" namespace wvcdm { @@ -59,7 +59,6 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) { // Test to determine if renewal should be attempted. switch (license_state_) { - case kLicenseStateInitialPendingUsage: case kLicenseStateCanPlay: { if (IsRenewalDelayExpired(current_time)) renewal_needed = true; @@ -115,11 +114,6 @@ void PolicyEngine::UpdateLicense( policy_.MergeFrom(license.policy()); - if (!policy_.can_play()) { - license_state_ = kLicenseStateExpired; - return; - } - // some basic license validation if (license_state_ == kLicenseStateInitial) { // license start time needs to be present in the initial response @@ -158,20 +152,23 @@ void PolicyEngine::UpdateLicense( policy_max_duration_seconds_ = policy_.license_duration_seconds(); } + if (!policy_.can_play()) { + license_state_ = kLicenseStateExpired; + return; + } + + if (IsLicenseDurationExpired(current_time)) return; + if (IsPlaybackDurationExpired(current_time)) return; + // Update state - if (license_state_ == kLicenseStateInitial) { - license_state_ = kLicenseStateInitialPendingUsage; - } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; } void PolicyEngine::BeginDecryption() { if (playback_start_time_ == 0) { switch (license_state_) { - case kLicenseStateInitialPendingUsage: + case kLicenseStateCanPlay: case kLicenseStateNeedRenewal: case kLicenseStateWaitingLicenseUpdate: playback_start_time_ = clock_->GetCurrentTime(); @@ -179,12 +176,7 @@ void PolicyEngine::BeginDecryption() { if (policy_.renew_with_usage()) { license_state_ = kLicenseStateNeedRenewal; } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } break; - case kLicenseStateCanPlay: case kLicenseStateInitial: case kLicenseStateExpired: default: @@ -209,17 +201,10 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) { QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; (*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; - int64_t remaining_time = policy_max_duration_seconds_ + - license_received_time_ - current_time; - if (remaining_time < 0) - remaining_time = 0; - ss << remaining_time; + ss << GetLicenseDurationRemaining(current_time); (*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str(); - remaining_time = policy_.playback_duration_seconds() + playback_start_time_ - - current_time; - if (remaining_time < 0) - remaining_time = 0; - ss << remaining_time; + ss.str(""); + ss << GetPlaybackDurationRemaining(current_time); (*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str(); (*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url(); @@ -240,6 +225,16 @@ bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) { current_time; } +int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) { + if (0 == policy_max_duration_seconds_) return LLONG_MAX; + + int64_t remaining_time = policy_max_duration_seconds_ + + license_received_time_ - current_time; + + if (remaining_time < 0) remaining_time = 0; + return remaining_time; +} + bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) { return (policy_.playback_duration_seconds() > 0) && playback_start_time_ && @@ -247,6 +242,17 @@ bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) { current_time; } +int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) { + if (0 == policy_.playback_duration_seconds()) return LLONG_MAX; + if (0 == playback_start_time_) return policy_.playback_duration_seconds(); + + int64_t remaining_time = policy_.playback_duration_seconds() + + playback_start_time_ - current_time; + + if (remaining_time < 0) remaining_time = 0; + return remaining_time; +} + bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) { return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) && diff --git a/core/src/properties.cpp b/core/src/properties.cpp index 83796aa1..962c627b 100644 --- a/core/src/properties.cpp +++ b/core/src/properties.cpp @@ -60,27 +60,30 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet( return NULL; } -const std::string Properties::GetSecurityLevel(const CdmSessionId& session_id) { +bool Properties::GetSecurityLevel(const CdmSessionId& session_id, + std::string* security_level) { const CdmClientPropertySet* property_set = GetCdmClientPropertySet(session_id); if (NULL == property_set) { LOGE("Properties::GetSecurityLevel: cannot find property set for %s", session_id.c_str()); - return ""; + return false; } - return property_set->security_level(); + *security_level = property_set->security_level(); + return true; } -const std::vector Properties::GetServiceCertificate( - const CdmSessionId& session_id) { +bool Properties::GetServiceCertificate(const CdmSessionId& session_id, + std::string* service_certificate) { const CdmClientPropertySet* property_set = GetCdmClientPropertySet(session_id); if (NULL == property_set) { LOGE("Properties::GetServiceCertificate: cannot find property set for %s", session_id.c_str()); - return std::vector(); + return false; } - return property_set->service_certificate(); + *service_certificate = property_set->service_certificate(); + return true; } bool Properties::UsePrivacyMode(const CdmSessionId& session_id) { diff --git a/core/test/cdm_engine_test.cpp b/core/test/cdm_engine_test.cpp index 8793720e..3ee128b6 100644 --- a/core/test/cdm_engine_test.cpp +++ b/core/test/cdm_engine_test.cpp @@ -34,7 +34,6 @@ wvcdm::KeyId g_key_id_unwrapped; wvcdm::CdmKeySystem g_key_system; std::string g_license_server; wvcdm::KeyId g_wrong_key_id; -int g_use_full_path = 0; // cannot use boolean in getopt_long // This is an RSA certificate message from the provisioning server. // The client sends this certificate to a license server for device @@ -257,17 +256,14 @@ int main(int argc, char **argv) { int show_usage = 0; static const struct option long_options[] = { - { "use_full_path", no_argument, &g_use_full_path, 0 }, { "keyid", required_argument, NULL, 'k' }, { "server", required_argument, NULL, 's' }, - { "vmodule", required_argument, NULL, 0 }, - { "v", required_argument, NULL, 0 }, { NULL, 0, NULL, '\0' } }; int option_index = 0; int opt = 0; - while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) { switch (opt) { case 'k': { g_key_id_pssh.clear(); @@ -279,8 +275,15 @@ int main(int argc, char **argv) { g_license_server.assign(optarg); break; } - case 'u': { - g_use_full_path = 1; + case 'v': { + // This option _may_ have already been consumed by wvcdm::InitLogging() + // above, depending on the platform-specific logging implementation. + // We only tell getopt about it so that it is not an error. We ignore + // the option here when seen. + // TODO: Stop passing argv to InitLogging, and instead set the log + // level here through the logging API. We should keep all command-line + // parsing at the application level, rather than split between various + // apps and various platform-specific logging implementations. break; } case '?': { @@ -305,10 +308,6 @@ int main(int argc, char **argv) { 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_pssh << 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; } diff --git a/core/test/config_test_env.cpp b/core/test/config_test_env.cpp index 927eb7f7..d289fa2f 100644 --- a/core/test/config_test_env.cpp +++ b/core/test/config_test_env.cpp @@ -5,36 +5,33 @@ namespace { const std::string kWidevineKeySystem = "com.widevine.alpha"; -// Youtube Content Protection license server data -const std::string kYtCpLicenseServer = +// Content Protection license server data +const std::string kCpLicenseServer = "http://wv-ref-eme-player.appspot.com/proxy"; -const std::string kYtCpClientAuth = ""; -const std::string kYtCpKeyId = - "000000427073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id - "08011a0d7769646576696e655f7465737422" // pssh data (streaming) - "0f73747265616d696e675f636c697031"; - -const std::string kYtCpOfflineKeyId = - "000000407073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id - "08011a0d7769646576696e655f7465737422" //pssh data (offline) - "0d6f66666c696e655f636c697031"; - -// Youtube license server data -const std::string kYtLicenseServer = - "https://www.youtube.com/api/drm/" - "widevine?video_id=03681262dc412c06&source=YOUTUBE"; -const std::string kYtClientAuth = ""; -const std::string kYtKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id - "0801121093789920E8D6520098577DF8F2DD5546"; // pssh data +const std::string kCpClientAuth = ""; +const std::string kCpKeyId = + "00000042" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000022" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" + "74220f73747265616d696e675f636c69" + "7031"; +const std::string kCpOfflineKeyId = + "00000040" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000020" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" + "74220d6f66666c696e655f636c697031"; // Google Play license server data const std::string kGpLicenseServer = "https://jmt17.google.com/video/license/GetCencLicense"; - // Test client authorization string. // NOTE: Append a userdata attribute to place a unique marker that the // server team can use to track down specific requests during debugging @@ -42,6 +39,16 @@ const std::string kGpLicenseServer = // "&userdata=jbmr2.dev" const std::string kGpClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; +const std::string kGpKeyId = + "00000034" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000014" // pssh data size + // pssh data: + "08011210e02562e04cd55351b14b3d74" + "8d36ed8e"; +const std::string kGpOfflineKeyId = kGpKeyId; const std::string kGpClientOfflineQueryParameters = "&offline=true"; @@ -50,16 +57,16 @@ const std::string kGpClientOfflineRenewalQueryParameters = const std::string kGpClientOfflineReleaseQueryParameters = "&offline=true&release=true"; -const std::string kGpKeyId = - "000000347073736800000000" // blob size and pssh - "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id - "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data - // An invalid key id, expected to fail const std::string kWrongKeyId = - "000000347073736800000000" // blob size and pssh - "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id - "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data + "00000034" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000014" // pssh data size + // pssh data: + "0901121094889920e8d6520098577df8" + "f2dd5546"; // URL of provisioning server (returned by GetProvisioningRequest()) const std::string kProductionProvisioningServerUrl = @@ -67,22 +74,13 @@ const std::string kProductionProvisioningServerUrl = "certificateprovisioning/v1/devicecertificates/create" "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; -// Return production-rooted certificates that have test bit set, -// request_license_test uses this url. -const std::string kProductionTestProvisioningServerUrl = - "https://www.googleapis.com/" - "certificateprovisioning/v1exttest/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - -const std::string kServerSdkLicenseServer = - "http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi"; - const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = { - { wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId, - kGpKeyId }, - { wvcdm::kYouTubeContentProtectionServer, kYtCpLicenseServer, - kYtCpClientAuth, kYtCpKeyId, kYtCpOfflineKeyId } + { wvcdm::kGooglePlayServer, kGpLicenseServer, + kGpClientAuth, kGpKeyId, kGpOfflineKeyId }, + { wvcdm::kContentProtectionServer, kCpLicenseServer, + kCpClientAuth, kCpKeyId, kCpOfflineKeyId }, }; + } // namespace namespace wvcdm { diff --git a/core/test/config_test_env.h b/core/test/config_test_env.h index 34db6a2f..fd7c7000 100644 --- a/core/test/config_test_env.h +++ b/core/test/config_test_env.h @@ -9,7 +9,7 @@ namespace wvcdm { typedef enum { kGooglePlayServer, - kYouTubeContentProtectionServer + kContentProtectionServer, } LicenseServerId; // Configures default test environment. diff --git a/core/test/policy_engine_unittest.cpp b/core/test/policy_engine_unittest.cpp index c4e10447..24e67314 100644 --- a/core/test/policy_engine_unittest.cpp +++ b/core/test/policy_engine_unittest.cpp @@ -1,5 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. +#include + +#include #include #include "clock.h" @@ -9,9 +12,37 @@ #include "policy_engine.h" #include "wv_cdm_constants.h" +namespace { +const int64_t kDurationUnlimited = 0; +const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013 +const int64_t kRentalDuration = 604800; // 7 days +const int64_t kPlaybackDuration = 172800; // 48 hours +const int64_t kStreamingLicenseDuration = 300; // 5 minutes +const int64_t kOfflineLicenseDuration = kRentalDuration; +const int64_t kLicenseRenewalPeriod = 120; // 2 minutes +const int64_t kLicenseRenewalRetryInterval = 30; // 30 seconds +const int64_t kLicenseRenewalRecoveryDuration = 30; // 30 seconds +const int64_t kLowDuration = + std::min(std::min(std::min(kRentalDuration, kPlaybackDuration), + kStreamingLicenseDuration), + kOfflineLicenseDuration); +const int64_t kHighDuration = + std::max(std::max(std::max(kRentalDuration, kPlaybackDuration), + kStreamingLicenseDuration), + kOfflineLicenseDuration); +const char* kRenewalServerUrl = + "https://test.google.com/license/GetCencLicense"; + +int64_t GetLicenseRenewalDelay(int64_t license_duration) { + return license_duration > kLicenseRenewalPeriod + ? license_duration - kLicenseRenewalPeriod + : 0; +} +} // unnamed namespace + namespace wvcdm { -//protobuf generated classes. +// protobuf generated classes. using video_widevine_server::sdk::License; using video_widevine_server::sdk::License_Policy; using video_widevine_server::sdk::LicenseIdentification; @@ -22,7 +53,6 @@ using video_widevine_server::sdk::OFFLINE; using ::testing::Return; using ::testing::AtLeast; - class MockClock : public Clock { public: MOCK_METHOD0(GetCurrentTime, int64_t()); @@ -34,13 +64,7 @@ class PolicyEngineTest : public ::testing::Test { mock_clock_ = new MockClock(); policy_engine_ = new PolicyEngine(mock_clock_); - license_start_time_ = 1413517500; // ~ 01/01/2013 - license_renewal_delay_ = 604200; // 7 days - 10 minutes - license_renewal_retry_interval_ = 30; - license_duration_ = 604800; // 7 days - playback_duration_ = 86400; // 24 hours - - license_.set_license_start_time(license_start_time_); + license_.set_license_start_time(kLicenseStartTime); LicenseIdentification* id = license_.mutable_id(); id->set_version(1); @@ -49,20 +73,18 @@ class PolicyEngineTest : public ::testing::Test { License_Policy* policy = license_.mutable_policy(); policy = license_.mutable_policy(); policy->set_can_play(true); - policy->set_can_persist(true); + policy->set_can_persist(false); policy->set_can_renew(true); - policy->set_rental_duration_seconds(license_duration_); - policy->set_playback_duration_seconds(playback_duration_); - policy->set_license_duration_seconds(license_duration_); - policy->set_renewal_recovery_duration_seconds(license_duration_ - - license_renewal_delay_); // 10 minutes + policy->set_rental_duration_seconds(kRentalDuration); + policy->set_playback_duration_seconds(kPlaybackDuration); + policy->set_license_duration_seconds(kStreamingLicenseDuration); + policy->set_renewal_recovery_duration_seconds( + kLicenseRenewalRecoveryDuration); - // Note: not a real URL - used for testing Policy/PolicyEngine interfaces - policy->set_renewal_server_url( - "https://test.google.com/license/GetCencLicense"); - policy->set_renewal_delay_seconds(license_renewal_delay_); - policy->set_renewal_retry_interval_seconds( - license_renewal_retry_interval_); + policy->set_renewal_server_url(kRenewalServerUrl); + policy->set_renewal_delay_seconds( + GetLicenseRenewalDelay(kStreamingLicenseDuration)); + policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval); policy->set_renew_with_usage(false); } @@ -73,16 +95,21 @@ class PolicyEngineTest : public ::testing::Test { mock_clock_ = NULL; } + int64_t GetMinOfRentalPlaybackLicenseDurations() { + const License_Policy& policy = license_.policy(); + int64_t rental_duration = policy.rental_duration_seconds(); + int64_t playback_duration = policy.playback_duration_seconds(); + int64_t license_duration = policy.license_duration_seconds(); + if (rental_duration == kDurationUnlimited) rental_duration = LLONG_MAX; + if (playback_duration == kDurationUnlimited) playback_duration = LLONG_MAX; + if (license_duration == kDurationUnlimited) license_duration = LLONG_MAX; + return std::min(std::min(rental_duration, playback_duration), + license_duration); + } + MockClock* mock_clock_; PolicyEngine* policy_engine_; License license_; - License_Policy* policy_; - - int64_t license_start_time_; - int64_t license_renewal_delay_; - int64_t license_renewal_retry_interval_; - int64_t license_duration_; - int64_t playback_duration_; }; TEST_F(PolicyEngineTest, NoLicense) { @@ -91,9 +118,9 @@ TEST_F(PolicyEngineTest, NoLicense) { TEST_F(PolicyEngineTest, PlaybackSuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + 10)); policy_engine_->SetLicense(license_); @@ -107,13 +134,15 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) { } TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 5)); - License_Policy* policy = license_.mutable_policy(); policy->set_can_play(false); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)); + policy_engine_->SetLicense(license_); + EXPECT_FALSE(policy_engine_->can_decrypt()); bool event_occurred; CdmEventType event; @@ -125,14 +154,17 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { } TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_rental_duration_seconds(3600); + policy->set_rental_duration_seconds(kLowDuration); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + min_duration + 1)); policy_engine_->SetLicense(license_); @@ -152,14 +184,16 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) { } TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 10000)) - .WillOnce(Return(license_start_time_ + 13598)) - .WillOnce(Return(license_start_time_ + 13602)); - License_Policy* policy = license_.mutable_policy(); - policy->set_playback_duration_seconds(3600); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t playback_start_time = kLicenseStartTime + 10000; + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(playback_start_time)) + .WillOnce(Return(playback_start_time + kPlaybackDuration - 2)) + .WillOnce(Return(playback_start_time + kPlaybackDuration + 2)); policy_engine_->SetLicense(license_); @@ -179,14 +213,43 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) { } TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_license_duration_seconds(3600); + policy->set_can_renew(false); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); +} + +TEST_F(PolicyEngineTest, PlaybackFails_ExpiryBeforeRenewalDelay) { + License_Policy* policy = license_.mutable_policy(); + policy->set_renewal_delay_seconds(kStreamingLicenseDuration + 10); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)); policy_engine_->SetLicense(license_); @@ -206,15 +269,18 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) { } TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_rental_duration_seconds(0); - policy->set_license_duration_seconds(3600); + policy->set_rental_duration_seconds(kDurationUnlimited); + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + 1 + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration)) + .WillOnce(Return(kLicenseStartTime + 1 + kStreamingLicenseDuration)); policy_engine_->SetLicense(license_); @@ -226,6 +292,14 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -234,15 +308,19 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) { } TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 10000)) - .WillOnce(Return(license_start_time_ + 10005)) - .WillOnce(Return(license_start_time_ + 13598)) - .WillOnce(Return(license_start_time_ + 13602)); - License_Policy* policy = license_.mutable_policy(); - policy->set_playback_duration_seconds(0); - policy->set_license_duration_seconds(3600); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kHighDuration); + int64_t license_renewal_delay = GetLicenseRenewalDelay(kHighDuration); + policy->set_renewal_delay_seconds(license_renewal_delay); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 2)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration - 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 2)); policy_engine_->SetLicense(license_); @@ -254,6 +332,14 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); @@ -262,15 +348,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) { } TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 3600)) - .WillOnce(Return(license_start_time_ + 3601)); - License_Policy* policy = license_.mutable_policy(); - policy->set_license_duration_seconds(0); - policy->set_rental_duration_seconds(3600); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_rental_duration_seconds(kLowDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)); policy_engine_->SetLicense(license_); @@ -290,17 +378,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) { } TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 604800)) - .WillOnce(Return(license_start_time_ + 604810)); - License_Policy* policy = license_.mutable_policy(); - policy->set_rental_duration_seconds(0); - policy->set_playback_duration_seconds(0); - policy->set_license_duration_seconds(0); - policy->set_renewal_delay_seconds(604900); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_renewal_delay_seconds(kHighDuration + 10); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + kHighDuration)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 10)); policy_engine_->SetLicense(license_); @@ -319,16 +407,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { } TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_duration_ + 10)); - License_Policy* policy = license_.mutable_policy(); policy->set_can_renew(false); + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10)); policy_engine_->SetLicense(license_); @@ -351,15 +440,17 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 15)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + - license_renewal_retry_interval_ + 10)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 15)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + + kLicenseRenewalRetryInterval + 10)); policy_engine_->SetLicense(license_); @@ -377,8 +468,8 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { EXPECT_TRUE(policy_engine_->can_decrypt()); - license_.set_license_start_time(license_start_time_ + - license_renewal_delay_ + 15); + license_.set_license_start_time(kLicenseStartTime + license_renewal_delay + + 15); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); policy_engine_->UpdateLicense(license_); @@ -390,14 +481,16 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) { } TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_duration_ + 10)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10)); policy_engine_->SetLicense(license_); @@ -415,8 +508,8 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { EXPECT_TRUE(policy_engine_->can_decrypt()); - license_.set_license_start_time(license_start_time_ + - license_renewal_delay_ + 15); + license_.set_license_start_time(kLicenseStartTime + license_renewal_delay + + 15); policy_engine_->UpdateLicense(license_); policy_engine_->OnTimerEvent(&event_occurred, &event); @@ -433,18 +526,20 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { } TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80)) - .WillOnce(Return(license_start_time_ + license_duration_ + 15)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 50)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 70)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 80)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 15)); policy_engine_->SetLicense(license_); @@ -491,20 +586,22 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80)) - .WillOnce(Return(license_start_time_ + license_duration_ + 10)) - .WillOnce(Return(license_start_time_ + license_duration_ + 30)) - .WillOnce(Return(license_start_time_ + license_duration_ + 40)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 50)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 70)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 80)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 30)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 40)); policy_engine_->SetLicense(license_); @@ -549,14 +646,14 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { EXPECT_FALSE(policy_engine_->can_decrypt()); - license_.set_license_start_time(license_start_time_ + - license_duration_ + 20); + license_.set_license_start_time(kLicenseStartTime + + kStreamingLicenseDuration + 20); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); License_Policy* policy = license_.mutable_policy(); policy = license_.mutable_policy(); - policy->set_playback_duration_seconds(playback_duration_ + 100); - policy->set_license_duration_seconds(license_duration_ + 100); + policy->set_playback_duration_seconds(kPlaybackDuration + 100); + policy->set_license_duration_seconds(kStreamingLicenseDuration + 100); policy_engine_->UpdateLicense(license_); @@ -567,18 +664,20 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + license_duration_ - - playback_duration_ + 1)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 55)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 67)) - .WillOnce(Return(license_start_time_ + license_renewal_delay_ + 200)); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 50)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 55)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 67)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 200)); policy_engine_->SetLicense(license_); @@ -608,8 +707,8 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - license_.set_license_start_time(license_start_time_ + - license_renewal_delay_ + 55); + license_.set_license_start_time(kLicenseStartTime + license_renewal_delay + + 55); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); policy_engine_->UpdateLicense(license_); @@ -626,32 +725,33 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) { } TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)) - .WillOnce(Return(license_start_time_ + 20)) - .WillOnce(Return(license_start_time_ + 40)) - .WillOnce(Return(license_start_time_ + 50)); - License_Policy* policy = license_.mutable_policy(); policy->set_renew_with_usage(true); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + 20)) + .WillOnce(Return(kLicenseStartTime + 40)) + .WillOnce(Return(kLicenseStartTime + 50)); + policy_engine_->SetLicense(license_); bool event_occurred; CdmEventType event; policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); + EXPECT_TRUE(policy_engine_->can_decrypt()); policy_engine_->BeginDecryption(); - EXPECT_FALSE(policy_engine_->can_decrypt()); + EXPECT_TRUE(policy_engine_->can_decrypt()); policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_TRUE(event_occurred); EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); - license_.set_license_start_time(license_start_time_ + 30); + license_.set_license_start_time(kLicenseStartTime + 30); policy->set_renew_with_usage(false); LicenseIdentification* id = license_.mutable_id(); id->set_version(2); @@ -665,7 +765,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) { TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_)); + .WillOnce(Return(kLicenseStartTime)); CdmQueryMap query_info; EXPECT_EQ(UNKNOWN_ERROR, policy_engine_->Query(&query_info)); @@ -673,10 +773,8 @@ TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) { TEST_F(PolicyEngineTest, QuerySuccess) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 100)); - - License_Policy* policy = license_.mutable_policy(); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)); policy_engine_->SetLicense(license_); @@ -684,76 +782,133 @@ TEST_F(PolicyEngineTest, QuerySuccess) { EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); - EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); int64_t remaining_time; std::istringstream ss; ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_LT(0, remaining_time); + ss.clear(); + EXPECT_EQ(kStreamingLicenseDuration - 99, remaining_time); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_LT(0, remaining_time); - - EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], - policy->renewal_server_url()); + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } -TEST_F(PolicyEngineTest, QuerySuccess_Offline) { +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackNotBegun) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 100)); - - LicenseIdentification* id = license_.mutable_id(); - id->set_type(OFFLINE); - - License_Policy* policy = license_.mutable_policy(); - policy->set_can_play(false); - policy->set_can_persist(false); - policy->set_can_renew(false); + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 200)); policy_engine_->SetLicense(license_); + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kStreamingLicenseDuration - 99, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + ss.clear(); + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kStreamingLicenseDuration - 199, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackBegun) { + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 50)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 150)) + .WillOnce(Return(kLicenseStartTime + 200)); + + policy_engine_->SetLicense(license_); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kStreamingLicenseDuration - 49, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + bool event_occurred; CdmEventType event; policy_engine_->OnTimerEvent(&event_occurred, &event); EXPECT_FALSE(event_occurred); - policy_engine_->BeginDecryption(); - EXPECT_FALSE(policy_engine_->can_decrypt()); - - CdmQueryMap query_info; EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); - EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); - EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); - EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); - int64_t remaining_time; - std::istringstream ss; + ss.clear(); ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_EQ(0, remaining_time); + EXPECT_EQ(kStreamingLicenseDuration - 199, remaining_time); + ss.clear(); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; - EXPECT_EQ(0, remaining_time); - - EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], - policy->renewal_server_url()); + EXPECT_EQ(kPlaybackDuration - 100, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } -TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { - EXPECT_CALL(*mock_clock_, GetCurrentTime()) - .WillOnce(Return(license_start_time_ + 1)) - .WillOnce(Return(license_start_time_ + 5)) - .WillOnce(Return(license_start_time_ + 10)) - .WillOnce(Return(license_start_time_ + license_duration_ + 20)); - +TEST_F(PolicyEngineTest, QuerySuccess_Offline) { LicenseIdentification* id = license_.mutable_id(); id->set_type(OFFLINE); License_Policy* policy = license_.mutable_policy(); + policy->set_can_persist(true); + policy->set_can_renew(false); + policy->set_license_duration_seconds(kOfflineLicenseDuration); + policy->set_renewal_delay_seconds( + GetLicenseRenewalDelay(kOfflineLicenseDuration)); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 200)) + .WillOnce(Return(kLicenseStartTime + 300)); policy_engine_->SetLicense(license_); @@ -770,6 +925,101 @@ TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kOfflineLicenseDuration - 299, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - 100, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_CanPlayFalse) { + LicenseIdentification* id = license_.mutable_id(); + id->set_type(OFFLINE); + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_play(false); + policy->set_can_persist(true); + policy->set_license_duration_seconds(kOfflineLicenseDuration); + policy->set_renewal_delay_seconds( + GetLicenseRenewalDelay(kOfflineLicenseDuration)); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 100)) + .WillOnce(Return(kLicenseStartTime + 200)); + + policy_engine_->SetLicense(license_); + EXPECT_FALSE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->BeginDecryption(); + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kOfflineLicenseDuration - 199, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_RentalDurationExpired) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kLowDuration); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + min_duration + 1)) + .WillOnce(Return(kLicenseStartTime + min_duration + 5)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); int64_t remaining_time; @@ -777,12 +1027,338 @@ TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) { ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); ss >> remaining_time; EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - min_duration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackDurationExpired) { + License_Policy* policy = license_.mutable_policy(); + policy->set_playback_duration_seconds(kLowDuration); + policy->set_license_duration_seconds(kHighDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + int64_t playback_start_time = kLicenseStartTime + 10000; + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(playback_start_time)) + .WillOnce(Return(playback_start_time - 2 + min_duration)) + .WillOnce(Return(playback_start_time + 2 + min_duration)) + .WillOnce(Return(playback_start_time + 5 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kHighDuration - 10004 - min_duration, remaining_time); + ss.clear(); ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); ss >> remaining_time; EXPECT_EQ(0, remaining_time); - - EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL], - policy->renewal_server_url()); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } +TEST_F(PolicyEngineTest, QuerySuccess_LicenseDurationExpired) { + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)) + .WillOnce(Return(kLicenseStartTime + 5 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - min_duration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_RentalDuration0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + int64_t license_renewal_delay = + GetLicenseRenewalDelay(kStreamingLicenseDuration); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + 1 + license_renewal_delay)) + .WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration)) + .WillOnce(Return(kLicenseStartTime + 1 + kStreamingLicenseDuration)) + .WillOnce(Return(kLicenseStartTime + 5 + kStreamingLicenseDuration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - kStreamingLicenseDuration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_PlaybackDuration0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kHighDuration); + int64_t license_renewal_delay = GetLicenseRenewalDelay(kHighDuration); + policy->set_renewal_delay_seconds(license_renewal_delay); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay - 2)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 2)) + .WillOnce(Return(kLicenseStartTime + license_renewal_delay + 5)) + .WillOnce(Return(kLicenseStartTime + kHighDuration - 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 2)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 5)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kHighDuration - license_renewal_delay - 5, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + ss.clear(); + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_LicenseDuration0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_rental_duration_seconds(kStreamingLicenseDuration); + policy->set_renewal_delay_seconds(GetLicenseRenewalDelay(kHighDuration)); + int64_t min_duration = GetMinOfRentalPlaybackLicenseDurations(); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + min_duration)) + .WillOnce(Return(kLicenseStartTime + 1 + min_duration)) + .WillOnce(Return(kLicenseStartTime + 5 + min_duration)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_TRUE(event_occurred); + EXPECT_EQ(LICENSE_EXPIRED_EVENT, event); + + EXPECT_FALSE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(0, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(kPlaybackDuration - min_duration, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineTest, QuerySuccess_Durations0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kDurationUnlimited); + policy->set_renewal_delay_seconds(kHighDuration + 10); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + kHighDuration)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kHighDuration + 15)); + + policy_engine_->SetLicense(license_); + + policy_engine_->BeginDecryption(); + EXPECT_TRUE(policy_engine_->can_decrypt()); + + bool event_occurred; + CdmEventType event; + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + policy_engine_->OnTimerEvent(&event_occurred, &event); + EXPECT_FALSE(event_occurred); + + EXPECT_TRUE(policy_engine_->can_decrypt()); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + int64_t remaining_time; + std::istringstream ss; + ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + ss.clear(); + ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]); + ss >> remaining_time; + EXPECT_EQ(LLONG_MAX, remaining_time); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} } // wvcdm diff --git a/linux/src/log.cpp b/linux/src/log.cpp index a4db602e..719e7e51 100644 --- a/linux/src/log.cpp +++ b/linux/src/log.cpp @@ -1,13 +1,12 @@ // Copyright 2013 Google Inc. All Rights Reserved. // // Log - implemented using stdout. -// -#define LOG_BUF_SIZE 4096 #include #include #include #include + #include "log.h" namespace wvcdm { @@ -35,13 +34,13 @@ void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { } if (level > g_cutoff) return; - va_list ap; - char buf[LOG_BUF_SIZE]; - va_start(ap, fmt); - vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); - va_end(ap); printf("[%s:%s(%d)] ", severities[level], file, line); - fputs(buf, stdout); + + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + putc('\n', stdout); } diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 3d4ed3e5..ba095d4d 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -9,9 +9,9 @@ #ifndef OEMCRYPTO_CENC_H_ #define OEMCRYPTO_CENC_H_ -#include -#include -#include +#include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/platforms/global_config.gypi b/platforms/global_config.gypi index d83f7332..5dac2ef3 100644 --- a/platforms/global_config.gypi +++ b/platforms/global_config.gypi @@ -11,6 +11,7 @@ 'product_name%': '"UndefinedProductName"', 'buildinfo_data%': '"UndefinedBuildInfo"', 'oemcrypto_target%': '../oemcrypto/mock/oec_mock.gyp:oec_mock', + 'oemcrypto_v8%': 'false', 'protobuf_source_dir%': '', 'use_system_protobuf%': 'true', 'protoc_dir%': '/usr/bin', diff --git a/third_party/protobuf.gyp b/third_party/protobuf.gyp index 2f7f0db0..0729e234 100644 --- a/third_party/protobuf.gyp +++ b/third_party/protobuf.gyp @@ -52,6 +52,7 @@ 'target_name': 'protobuf', 'type': 'static_library', 'toolsets': ['host'], + 'includes': ['xcode_host.gypi'], # xcode workaround 'sources': [ '<@(protobuf_lite_sources)', @@ -89,6 +90,7 @@ 'target_name': 'protoc', 'type': 'executable', 'toolsets': ['host'], + 'includes': ['xcode_host.gypi'], # xcode workaround 'sources': [ '<(protobuf_source_dir)/src/google/protobuf/compiler/code_generator.cc', '<(protobuf_source_dir)/src/google/protobuf/compiler/command_line_interface.cc', diff --git a/third_party/xcode_host.gypi b/third_party/xcode_host.gypi new file mode 100644 index 00000000..c5af32bd --- /dev/null +++ b/third_party/xcode_host.gypi @@ -0,0 +1,15 @@ +# Copyright 2014 Google Inc. All Rights Reserved. + +# Since the Xcode generator for gyp doesn't support host targets, +# the workaround is to add a special xcode_settings section to any +# target that must be built for the host. This file encapsulates +# those settings such that they can be included into any target. + +{ + 'xcode_settings': { + 'VALID_ARCHS': 'i386 x86_64', + 'IPHONEOS_DEPLOYMENT_TARGET': '', + 'TARGETED_DEVICE_FAMILY': '', + 'SDKROOT': '', + }, +}