Source release v3.1.0

This commit is contained in:
Gene Morgan
2016-07-19 18:43:15 -07:00
parent 7a7f78d654
commit 643b91b616
108 changed files with 16537 additions and 7174 deletions

View File

@@ -7,7 +7,7 @@
{
'variables': {
# Override if you intend to link against a different OEMCrypto API version.
'oemcrypto_version%': 10,
'oemcrypto_version%': 11,
# Override if you can't depend on OpenSSL for privacy features.
# If set to 'dummy', privacy mode in the CDM will fail.
@@ -84,6 +84,7 @@
'include_dirs': [
'../core/include',
'../oemcrypto/include',
'../third_party/jsmn',
'../third_party/stringencoders/src',
],
'direct_dependent_settings': {
@@ -104,10 +105,10 @@
'../core/include/device_files.h',
'../core/include/file_store.h',
'../core/include/initialization_data.h',
'../core/include/license_key_status.h',
'../core/include/license.h',
'../core/include/lock.h',
'../core/include/log.h',
'../core/include/max_res_engine.h',
'../core/include/oemcrypto_adapter.h',
'../core/include/policy_engine.h',
'../core/include/privacy_crypto.h',
@@ -124,13 +125,18 @@
'../core/src/crypto_session.cpp',
'../core/src/device_files.cpp',
'../core/src/initialization_data.cpp',
'../core/src/license_key_status.cpp',
'../core/src/license.cpp',
'../core/src/max_res_engine.cpp',
'../core/src/oemcrypto_adapter_static.cpp',
'../core/src/policy_engine.cpp',
'../core/src/privacy_crypto_<(privacy_crypto_impl).cpp',
'../core/src/properties.cpp',
'../core/src/string_conversions.cpp',
'../third_party/jsmn/jsmn.h',
'../third_party/jsmn/jsmn.c',
'../third_party/stringencoders/src/modp_b64_data.h',
'../third_party/stringencoders/src/modp_b64.cpp',
'../third_party/stringencoders/src/modp_b64.h',
'../third_party/stringencoders/src/modp_b64w_data.h',
'../third_party/stringencoders/src/modp_b64w.cpp',
'../third_party/stringencoders/src/modp_b64w.h',
@@ -148,6 +154,12 @@
'../core/src/oemcrypto_adapter_static_v10.cpp',
],
}],
['oemcrypto_version < 11', {
'sources': [
# Include APIs introduced in v10.
'../core/src/oemcrypto_adapter_static_v11.cpp',
],
}],
['privacy_crypto_impl=="openssl"', {
'conditions': [
['openssl_config == "target"', {

View File

@@ -9,12 +9,14 @@
'../core/test/cdm_session_unittest.cpp',
'../core/test/config_test_env.cpp',
'../core/test/device_files_unittest.cpp',
'../core/test/generic_crypto_unittest.cpp',
'../core/test/http_socket.cpp',
'../core/test/initialization_data_unittest.cpp',
'../core/test/license_request.cpp',
'../core/test/license_unittest.cpp',
'../core/test/max_res_engine_unittest.cpp',
'../core/test/license_keys_unittest.cpp',
'../core/test/policy_engine_unittest.cpp',
'../core/test/policy_engine_constraints_unittest.cpp',
'../core/test/test_printers.cpp',
'../core/test/url_request.cpp',
],

View File

@@ -1,6 +1,6 @@
// Copyright 2015 Google Inc. All Rights Reserved.
// Based on the EME draft spec from 2015 November 20.
// https://rawgit.com/w3c/encrypted-media/1dab9e5/index.html
// Based on the EME draft spec from 2016 June 10.
// http://www.w3.org/TR/2016/WD-encrypted-media-20160610/"
#ifndef WVCDM_CDM_CDM_H_
#define WVCDM_CDM_CDM_H_
@@ -58,7 +58,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
kLicenseRequest = 0,
kLicenseRenewal = 1,
kLicenseRelease = 2,
kIndividualizationRequest = 3,
kIndividualizationRequest = 3, // Not used. Direct Individualization
// is used instead of App-Assisted
} MessageType;
typedef enum {
@@ -77,6 +78,10 @@ class CDM_EXPORT Cdm : public ITimerClient {
kQuotaExceeded = 8,
kRangeError = 9,
// The action could not be completed yet but has been scheduled to be done
// later. A call to |event_listener.onDeferredComplete| will be made once
// the action is complete.
kDeferred = 99998,
// This covers errors that we do not expect (see logs for details):
kUnexpectedError = 99999,
} Status;
@@ -86,8 +91,18 @@ class CDM_EXPORT Cdm : public ITimerClient {
kCenc = 0,
kKeyIds = 1, // NOTE: not supported by Widevine at this time
kWebM = 2,
// This type is not defined by EME but is supported by Widevine
kHls = 10000,
} InitDataType;
// These are the crypto schemes supported by CENC 3.0.
typedef enum {
kClear = 0,
kAesCtr = 1, // AES-CTR, for use with cenc and cens modes
kAesCbc = 2, // AES-CBC, for use with cbc1 and cbcs modes
} EncryptionScheme;
// These are key statuses defined by EME.
typedef enum {
kUsable = 0,
@@ -99,6 +114,19 @@ class CDM_EXPORT Cdm : public ITimerClient {
kReleased = 5,
} KeyStatus;
// Permissible usages for a key. Returned as a set of flags; multiple
// flags may be set. The specific settings are defined in the license
// and the OEMCrypto Key Control Block. The CDM uses settings in the
// license to derive these flags.
typedef uint32_t KeyAllowedUsageFlags;
static const KeyAllowedUsageFlags kAllowNone = 0;
static const KeyAllowedUsageFlags kAllowDecryptToClearBuffer = 1;
static const KeyAllowedUsageFlags kAllowDecryptToSecureBuffer = 2;
static const KeyAllowedUsageFlags kAllowGenericEncrypt = 4;
static const KeyAllowedUsageFlags kAllowGenericDecrypt = 8;
static const KeyAllowedUsageFlags kAllowGenericSign = 16;
static const KeyAllowedUsageFlags kAllowGenericSignatureVerify = 32;
// These are defined by Widevine. The CDM can be configured to decrypt in
// three modes (dependent on OEMCrypto support).
typedef enum {
@@ -137,20 +165,6 @@ class CDM_EXPORT Cdm : public ITimerClient {
// See Cdm::createSession().
class IEventListener {
public:
// A URL to be added to a renewal request message.
// This call will immediately precede the onMessage() call.
// Do not override this call if the URL is not needed.
//
// WARNING: this call exists temporarily to allow interoperation with
// older versions of Chromium and the prefixed EME API. This call will
// be removed in a future release. Therefore: (1) Do not use this call
// unless you are certain that it is needed on your platform for your
// application, and (2) If it is needed, figure how move to a new version
// of Chromium and the unprefixed EME API as soon as possible.
// TODO: Remove this call (see b/24776024).
virtual void onMessageUrl(const std::string& session_id,
const std::string& server_url) {}
// A message (license request, renewal, etc.) to be dispatched to the
// application's license server.
// The response, if successful, should be provided back to the CDM via a
@@ -165,14 +179,30 @@ class CDM_EXPORT Cdm : public ITimerClient {
// A remove() operation has been completed.
virtual void onRemoveComplete(const std::string& session_id) = 0;
// Called when a deferred action has completed.
virtual void onDeferredComplete(const std::string& session_id,
Status result) = 0;
// Called when the CDM requires a new device certificate
virtual void onDirectIndividualizationRequest(
const std::string& session_id, const std::string& request) = 0;
protected:
IEventListener() {}
virtual ~IEventListener() {}
};
// A storage interface provided by the application, independent of CDM
// instances.
// See Cdm::initialize().
// A storage interface provided by the application. This defines the "origin"
// that the CDM will operate in by the files it can access. Passing different
// IStorage instances to Cdm::create will cause those CDM instances to be in
// different "origins" as defined by the IStorage instance. For example,
// different IStorage instances could be tied to different folders for
// different origins.
//
// It is important for multi-origin hosts to verify the application's origin.
// This ensures that the application does not access files from another
// origin.
//
// NOTE: It is important for users of your application to be able to clear
// stored data. Also, browsers or other multi-application systems should
// store data separately per-app or per-origin.
@@ -258,35 +288,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
std::string build_info;
};
// Device certificate request information.
// The structure is passed by the application to the library in as an output
// parameter to Cdm::initialize().
// All fields are filled in by the library to instruct the application to
// handle device certificate requests, if needed.
struct DeviceCertificateRequest {
// If false, the library is ready to create and/or load sessions.
// If true, a device certificate is needed first.
// Sessions cannot be created or loaded until the device certificate has
// been provisioned.
bool needed;
// If |needed| is true, this string contains the URL that must be used to
// provision a device certificate. The request must be a POST.
std::string url;
// If |needed| is true, the response from the above-described HTTP POST
// must be provided as an argument to this method.
// Returns kSuccess if the provisioning was successful.
// Any other return value means the provisioning failed and the CDM cannot
// be used yet.
Status acceptReply(const std::string& reply);
};
// Initialize the CDM library and provide access to platform services.
// All platform interfaces are required.
// The |device_certificate_request| parameter will be filled in by
// initialize().
// See documentation for DeviceCertificateRequest for more information.
// Logging is controlled by |verbosity|.
// Must be called and must return kSuccess before create() is called.
static Status initialize(
@@ -295,7 +298,6 @@ class CDM_EXPORT Cdm : public ITimerClient {
IStorage* storage,
IClock* clock,
ITimer* timer,
DeviceCertificateRequest* device_certificate_request,
LogLevel verbosity);
// Query the CDM library version.
@@ -306,6 +308,9 @@ class CDM_EXPORT Cdm : public ITimerClient {
// instance may be constructed.
// The CDM may notify of events at any time via the provided |listener|,
// which may not be NULL.
// |storage| defines the storage to use for this instance. This can be used
// to provide per-origin storage. Passing NULL will use the storage passed
// to initialize().
// If |privacy_mode| is true, server certificates are required and will be
// used to encrypt messages to the license server.
// By using server certificates to encrypt communication with the license
@@ -315,6 +320,7 @@ class CDM_EXPORT Cdm : public ITimerClient {
// This is particularly useful for browser environments, but is recommended
// for use whenever possible.
static Cdm* create(IEventListener* listener,
IStorage* storage,
bool privacy_mode);
virtual ~Cdm() {}
@@ -360,6 +366,18 @@ class CDM_EXPORT Cdm : public ITimerClient {
virtual Status getKeyStatuses(const std::string& session_id,
KeyStatusMap* key_statuses) = 0;
// Gets the permitted usage for a specific key by ID.
virtual Status getKeyAllowedUsages(const std::string& session_id,
const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) = 0;
// Gets the permitted usage for a specific key by ID.
// Search for key across all known sessions. If there are keys in separate
// sessions that match the given key_id, return kTypeError unless all such
// keys have identical Allowed Usage settings.
virtual Status getKeyAllowedUsages(const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) = 0;
// Indicates that the application no longer needs the session and the CDM
// should release any resources associated with it and close it.
// Does not generate release messages for persistent sessions.
@@ -373,6 +391,28 @@ class CDM_EXPORT Cdm : public ITimerClient {
// session is fully removed.
virtual Status remove(const std::string& session_id) = 0;
// Describes a repeating pattern as defined by the CENC 3.0 standard. A
// CENC 3.0 pattern consists of a number of encrypted blocks followed by a
// number of clear blocks, after which it repeats.
struct Pattern {
public:
Pattern()
: encrypted_blocks(0),
clear_blocks(0) {}
Pattern(uint32_t encrypt, uint32_t clear)
: encrypted_blocks(encrypt),
clear_blocks(clear) {}
// The number of crypto blocks that are encrypted and therefore need to be
// decrypted.
uint32_t encrypted_blocks;
// The number of crypto blocks that are not encrypted and therefore should
// be skipped when doing decryption.
uint32_t clear_blocks;
};
struct InputBuffer {
public:
InputBuffer()
@@ -380,10 +420,11 @@ class CDM_EXPORT Cdm : public ITimerClient {
key_id_length(0),
iv(NULL),
iv_length(0),
pattern(),
data(NULL),
data_length(0),
block_offset(0),
is_encrypted(true),
encryption_scheme(kAesCtr),
is_video(true),
first_subsample(true),
last_subsample(true) {}
@@ -395,18 +436,34 @@ class CDM_EXPORT Cdm : public ITimerClient {
const uint8_t* iv;
uint32_t iv_length;
// Describes the repeating pattern with which the content was encrypted. If
// left at its default value of (0,0), patterns will be disabled. Should
// only be changed for content that uses patterns, such as for CENC 3.0
// "cens" and "cbcs" content or for HLS content.
Pattern pattern;
// This pointer and length describe the data to be decrypted. This data
// should be ready to be decrypted with no further processing. If the data
// is coming from a format that requires processing before decryption, that
// processing needs to happen before the data is passed in here. For
// example, content coming from HLS will need to have its extra start code
// emulation prevention removed before it is passed to Widevine.
const uint8_t* data;
uint32_t data_length;
// |data|'s offset within its 16-byte AES block, used for CENC subsamples.
// Should start at 0 for each sample, then go up by |data_length| (mod 16)
// after the |is_encrypted| part of each subsample.
// |data|'s offset within its 16-byte AES block. Only used for encrypted
// subsamples from content using CENC standards before 3.0 or the
// equivalent mode in CENC 3.0, "cenc" mode. Should always be 0 in CENC 3.0
// modes "cens," "cbc1," and "cbcs," as well as for HLS content. When used,
// it should start at 0 for each sample, then go up by |data_length| (mod
// 16) after the |is_encrypted| part of each subsample.
uint32_t block_offset;
// If false, copies the input data directly to the output buffer. Used for
// secure output types, where the output buffer cannot be directly accessed
// above the CDM.
bool is_encrypted;
// Specifies the encryption scheme, if any, to be used to decrypt the data.
// When set to kClear, decryption will copy the input data directly to the
// output buffer. This is necessary for secure output types, where the
// output buffer cannot be directly accessed above the CDM.
EncryptionScheme encryption_scheme;
// Used by secure output type kDirectRender, where the secure hardware must
// decode and render the decrypted content:
@@ -473,6 +530,44 @@ class CDM_EXPORT Cdm : public ITimerClient {
// Clears all the values in the custom app settings. See setAppParameter().
virtual Status clearAppParameters() = 0;
// Generic crypto - functions for applying crypto operations to
// app-level data (outside the content stream).
typedef enum {
kEncryptionAlgorithmUnknown,
kEncryptionAlgorithmAesCbc128,
} GenericEncryptionAlgorithmType;
typedef enum {
kSigningAlgorithmUnknown,
kSigningAlgorithmHmacSha256
} GenericSigningAlgorithmType;
// Encrypts a buffer of app-level data.
virtual Status genericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) = 0;
// Decrypts a buffer of app-level data.
virtual Status genericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) = 0;
// Signs a buffer of app-level data.
virtual Status genericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
std::string* signature) = 0;
// Verifies the signature on a buffer of app-level data.
// Returns kSuccess if signature is verified, otherwise returns kDecryptError.
virtual Status genericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
const std::string& signature) = 0;
protected:
Cdm() {}
};

View File

@@ -1,2 +1,2 @@
// Widevine CE CDM Version
#define CDM_VERSION "v3.0.5-0-g897db53-ce"
#define CDM_VERSION "v3.1.0-0-g63dfeca-ce"

View File

@@ -4,6 +4,8 @@
# Does not include the test runner main.
{
'sources': [
'../oemcrypto/test/oec_device_features.cpp',
'../oemcrypto/test/oec_session_util.cpp',
'../oemcrypto/test/oemcrypto_test.cpp',
],
'include_dirs': [

View File

@@ -38,14 +38,14 @@ struct HostType {
Cdm::IStorage* storage;
Cdm::IClock* clock;
Cdm::ITimer* timer;
CdmEngine* provisioning_engine;
FileSystem* file_system;
bool initialized;
HostType()
: storage(NULL),
clock(NULL),
timer(NULL),
provisioning_engine(NULL),
file_system(NULL),
initialized(false) {}
} host;
@@ -109,8 +109,7 @@ class PropertySet : public CdmClientPropertySet {
class CdmImpl : public Cdm,
public WvCdmEventListener {
public:
CdmImpl(IEventListener* listener,
bool privacy_mode);
CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode);
virtual ~CdmImpl();
@@ -135,6 +134,13 @@ class CdmImpl : public Cdm,
virtual Status getKeyStatuses(const std::string& session_id,
KeyStatusMap* key_statuses) OVERRIDE;
virtual Status getKeyAllowedUsages(
const std::string& session_id, const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) OVERRIDE;
virtual Status getKeyAllowedUsages(
const std::string& key_id, KeyAllowedUsageFlags* usage_flags) OVERRIDE;
virtual Status setAppParameter(const std::string& key,
const std::string& value) OVERRIDE;
@@ -152,6 +158,25 @@ class CdmImpl : public Cdm,
virtual Status decrypt(const InputBuffer& input,
const OutputBuffer& output) OVERRIDE;
virtual Status genericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm,
std::string* out_buffer) OVERRIDE;
virtual Status genericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm,
std::string* out_buffer) OVERRIDE;
virtual Status genericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
std::string* signature) OVERRIDE;
virtual Status genericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
const std::string& signature) OVERRIDE;
// ITimerClient:
virtual void onTimerExpired(void* context) OVERRIDE;
@@ -166,9 +191,17 @@ class CdmImpl : public Cdm,
int64_t new_expiry_time_seconds) OVERRIDE;
private:
KeyAllowedUsageFlags KeyAllowedFlags(const CdmKeyAllowedUsage& usages);
bool SendProvisioningRequest(const std::string& session_id);
CdmEncryptionAlgorithm ConvertEncryptionAlgorithm(
GenericEncryptionAlgorithmType algorithm);
CdmSigningAlgorithm ConvertSigningAlgorithm(
GenericSigningAlgorithmType algorithm);
IEventListener* listener_;
bool policy_timer_enabled_;
FileSystem file_system_;
CdmEngine cdm_engine_;
PropertySet property_set_;
CdmAppParameterMap app_parameters_;
@@ -184,13 +217,33 @@ class CdmImpl : public Cdm,
type((SessionType)-1),
expiration(0) {}
};
struct UnprovisionedSessionMetadata {
SessionType type;
std::string init_data;
InitDataType init_data_type;
bool has_init_data;
bool is_load;
UnprovisionedSessionMetadata()
: type((SessionType)-1),
init_data_type((InitDataType)-1),
has_init_data(false),
is_load(false) {}
};
typedef std::map<std::string, UnprovisionedSessionMetadata> UnprovisionedMap;
std::map<std::string, SessionMetadata> sessions_;
UnprovisionedMap unprovisioned_sessions_;
bool provision_request_sent_;
};
CdmImpl::CdmImpl(IEventListener* listener,
bool privacy_mode)
CdmImpl::CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode)
: listener_(listener),
policy_timer_enabled_(false) {
policy_timer_enabled_(false),
file_system_("", storage),
cdm_engine_(&file_system_),
provision_request_sent_(false) {
assert(NULL != listener_);
property_set_.set_use_privacy_mode(privacy_mode);
}
@@ -220,7 +273,7 @@ Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
Cdm::Status CdmImpl::createSession(SessionType session_type,
std::string* session_id) {
if (!session_id) {
if (NULL == session_id) {
LOGE("Missing session ID pointer.");
return kTypeError;
}
@@ -238,17 +291,17 @@ Cdm::Status CdmImpl::createSession(SessionType session_type,
return kNotSupported;
}
std::string empty_origin;
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, empty_origin, this,
NULL, session_id);
"com.widevine.alpha", &property_set_, this, session_id);
switch (result) {
case NO_ERROR:
sessions_[*session_id].type = session_type;
return kSuccess;
case NEED_PROVISIONING:
LOGE("A device certificate is needed.");
return kNeedsDeviceCertificate;
// The session does not exist in |cdm_engine_| yet, will be added in
// generateRequest.
unprovisioned_sessions_[*session_id].type = session_type;
return kSuccess;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -259,8 +312,43 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
InitDataType init_data_type,
const std::string& init_data) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
UnprovisionedMap::iterator session_iter =
unprovisioned_sessions_.find(session_id);
if (session_iter == unprovisioned_sessions_.end()) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (cdm_engine_.IsProvisioned(kSecurityLevelL1)) {
// We are provisioned now, create the session and generate the request
// like normal.
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, session_id, this);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
sessions_[session_id].type = unprovisioned_sessions_[session_id].type;
unprovisioned_sessions_.erase(session_iter);
} else {
// We are not provisioned yet, save the init data and send a provisioning
// request if needed.
if (unprovisioned_sessions_[session_id].has_init_data) {
LOGE("Request already generated: %s", session_id.c_str());
return kInvalidState;
}
unprovisioned_sessions_[session_id].has_init_data = true;
unprovisioned_sessions_[session_id].init_data_type = init_data_type;
unprovisioned_sessions_[session_id].init_data = init_data;
if (provision_request_sent_)
return kDeferred;
if (!SendProvisioningRequest(session_id))
return kUnexpectedError;
return kSuccess;
}
}
if (sessions_[session_id].callable) {
@@ -296,6 +384,9 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
case kWebM:
init_data_type_name = WEBM_INIT_DATA_FORMAT;
break;
case kHls:
init_data_type_name = HLS_INIT_DATA_FORMAT;
break;
default:
LOGE("Invalid init data type: %d", init_data_type);
return kTypeError;
@@ -315,14 +406,11 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
return kNotSupported;
}
std::string key_request;
CdmKeyRequestType key_request_type;
std::string ignored_server_url;
CdmKeyRequest key_request;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, init_data_obj,
license_type, app_parameters_, &key_request, &key_request_type,
&ignored_server_url, NULL);
license_type, app_parameters_, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
@@ -330,7 +418,9 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
}
sessions_[session_id].callable = true;
assert(key_request_type == kKeyRequestTypeInitial);
assert(key_request.type == kKeyRequestTypeInitial);
MessageType message_type;
if (property_set_.use_privacy_mode() &&
property_set_.service_certificate().empty()) {
// We can deduce that this is a server cert request, even though CdmEgine
@@ -339,10 +429,12 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
// The EME editor has clarified that this was a misinterpretation of the
// spec, and that this should also be kLicenseRequest.
LOGI("A server certificate request has been generated.");
message_type = kLicenseRequest;
} else {
LOGI("A license request has been generated.");
message_type = kLicenseRequest;
}
listener_->onMessage(session_id, kLicenseRequest, key_request);
listener_->onMessage(session_id, message_type, key_request.message);
return kSuccess;
}
@@ -357,23 +449,28 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
return kQuotaExceeded;
}
std::string empty_origin;
std::string ignored_session_id_output;
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, empty_origin, this,
&session_id, &ignored_session_id_output);
"com.widevine.alpha", &property_set_, session_id, this);
switch (result) {
case NO_ERROR:
break;
case NEED_PROVISIONING:
LOGE("A device certificate is needed.");
return kNeedsDeviceCertificate;
unprovisioned_sessions_[session_id].is_load = true;
if (provision_request_sent_)
return kDeferred;
// Send a provisioning request right away. Then we will load the session
// again in |update|.
if (!SendProvisioningRequest(session_id))
return kUnexpectedError;
return kSuccess;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
DeviceFiles f;
DeviceFiles f(&file_system_);
if (!f.Init(kSecurityLevelUnknown)) {
LOGE("Unexpected error, failed to init DeviceFiles");
return kUnexpectedError;
@@ -381,8 +478,8 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
if (!f.LicenseExists(session_id)) {
// This might be a usage record session which needs to be loaded.
CdmKeyMessage release_message;
result = cdm_engine_.LoadUsageSession(session_id, &release_message);
CdmKeyMessage ignored_release_message;
result = cdm_engine_.LoadUsageSession(session_id, &ignored_release_message);
if (result == LOAD_USAGE_INFO_MISSING) {
LOGE("Unable to load license: %s", session_id.c_str());
cdm_engine_.CloseSession(session_id);
@@ -393,10 +490,6 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
return kUnexpectedError;
}
LOGI("A usage record release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, release_message);
sessions_[session_id].type = kPersistentUsageRecord;
sessions_[session_id].callable = true;
return kSuccess;
@@ -405,24 +498,7 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
result = cdm_engine_.RestoreKey(session_id, session_id);
if (result == GET_RELEASED_LICENSE_ERROR) {
// This was partially removed already.
// The EME spec states that we should send a release message right away.
InitializationData empty_initialization_data;
CdmKeyMessage key_request;
std::string ignored_server_url;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_initialization_data,
kLicenseTypeRelease, app_parameters_, &key_request, NULL,
&ignored_server_url, NULL);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_.CloseSession(session_id);
return kUnexpectedError;
}
LOGI("A license release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, key_request);
// The EME spec states that we should be able to load it, but not use it.
} else if (result != KEY_ADDED) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -435,6 +511,64 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
Cdm::Status CdmImpl::update(const std::string& session_id,
const std::string& response) {
if (provision_request_sent_) {
std::string ignored_cert;
std::string ignored_wrapped_key;
provision_request_sent_ = false;
CdmResponseType result = cdm_engine_.HandleProvisioningResponse(
response, &ignored_cert, &ignored_wrapped_key);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
// We are now provisioned, we need to recreate the unprovisioned sessions.
Cdm::Status ret = kSuccess;
for (UnprovisionedMap::iterator it = unprovisioned_sessions_.begin();
it != unprovisioned_sessions_.end();) {
if (it->second.is_load) {
Cdm::Status load_status = load(it->first);
if (it->first != session_id)
listener_->onDeferredComplete(it->first, load_status);
else if (load_status != kSuccess)
ret = load_status;
unprovisioned_sessions_.erase(it++);
continue;
}
result = cdm_engine_.OpenSession("com.widevine.alpha", &property_set_,
it->first, this);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
if (it->first == session_id)
ret = kUnexpectedError;
else
listener_->onDeferredComplete(it->first, kUnexpectedError);
// We failed to create the session. Keep it in the unprovisioned
// sessions map so that another call to generateRequest will try to
// create the session again.
++it;
continue;
}
sessions_[it->first].type = it->second.type;
// Call generate request again for the session.
if (it->second.has_init_data) {
Cdm::Status generate_status = generateRequest(
it->first, it->second.init_data_type, it->second.init_data);
if (it->first != session_id)
listener_->onDeferredComplete(it->first, generate_status);
else if (generate_status != kSuccess)
// Still erase the item because the session exists in |cdm_engine_|.
ret = generate_status;
}
unprovisioned_sessions_.erase(it++);
}
return ret;
}
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
@@ -467,14 +601,11 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
// The underlying session in CdmEngine has stored a copy of the original
// init data, so we can use an empty one this time.
InitializationData empty_init_data;
std::string key_request;
CdmKeyRequestType key_request_type;
std::string ignored_server_url;
CdmKeyRequest key_request;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
app_parameters_, &key_request, &key_request_type,
&ignored_server_url, NULL);
app_parameters_, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
@@ -482,9 +613,9 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
}
LOGI("A deferred license request has been generated.");
assert(key_request_type == kKeyRequestTypeInitial);
assert(key_request.type == kKeyRequestTypeInitial);
MessageType message_type = kLicenseRequest;
listener_->onMessage(session_id, message_type, key_request);
listener_->onMessage(session_id, message_type, key_request.message);
return kSuccess;
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
LOGE("A temporary session cannot be used for a persistent license.");
@@ -518,6 +649,10 @@ Cdm::Status CdmImpl::getExpiration(const std::string& session_id,
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (NULL == expiration) {
LOGE("Missing pointer to expiration result.");
return kTypeError;
}
*expiration = sessions_[session_id].expiration;
return kSuccess;
@@ -529,11 +664,69 @@ Cdm::Status CdmImpl::getKeyStatuses(const std::string& session_id,
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (NULL == key_statuses) {
LOGE("Missing pointer to KeyStatusMap result.");
return kTypeError;
}
*key_statuses = sessions_[session_id].key_statuses;
return kSuccess;
}
Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& session_id,
const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (NULL == usage_flags) {
LOGE("Missing pointer to KeyAllowedUsageFlags result.");
return kTypeError;
}
CdmKeyAllowedUsage usage_for_key;
CdmResponseType result = cdm_engine_.QueryKeyAllowedUsage(session_id,
key_id,
&usage_for_key);
if (result != NO_ERROR) {
if (result == KEY_NOT_FOUND_1) {
return kNoKey;
} else {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
}
*usage_flags = KeyAllowedFlags(usage_for_key);
return kSuccess;
}
Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) {
if (NULL == usage_flags) {
LOGE("Missing pointer to KeyAllowedUsageFlags result.");
return kTypeError;
}
CdmKeyAllowedUsage usage_for_key;
CdmResponseType result = cdm_engine_.QueryKeyAllowedUsage(key_id,
&usage_for_key);
if (result != NO_ERROR) {
if (result == KEY_NOT_FOUND_1 || result == KEY_NOT_FOUND_2) {
return kNoKey;
} else if (result == KEY_CONFLICT_1) {
return kTypeError;
} else {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
}
*usage_flags = KeyAllowedFlags(usage_for_key);
return kSuccess;
}
Cdm::Status CdmImpl::setAppParameter(const std::string& key,
const std::string& value) {
if (key.empty()) {
@@ -602,8 +795,7 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
}
InitializationData empty_initialization_data;
CdmKeyMessage key_request;
std::string ignored_server_url;
CdmKeyRequest key_request;
// Mark all keys as released ahead of generating the release request.
// When released, cdm_engine_ will mark all keys as expired, which we will
@@ -615,8 +807,7 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_initialization_data,
kLicenseTypeRelease, app_parameters_, &key_request, NULL,
&ignored_server_url, NULL);
kLicenseTypeRelease, app_parameters_, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_.CloseSession(session_id);
@@ -625,13 +816,14 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
LOGI("A license release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, key_request);
listener_->onMessage(session_id, message_type, key_request.message);
return kSuccess;
}
Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
const OutputBuffer& output) {
if (input.is_encrypted && input.iv_length != 16) {
const bool is_encrypted = (input.encryption_scheme != kClear);
if (is_encrypted && input.iv_length != 16) {
LOGE("The IV must be 16 bytes long.");
return kTypeError;
}
@@ -646,8 +838,13 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
std::vector<uint8_t> iv(input.iv, input.iv + input.iv_length);
CdmDecryptionParameters parameters;
parameters.is_encrypted = input.is_encrypted;
parameters.is_encrypted = is_encrypted;
parameters.is_secure = output.is_secure;
if (input.encryption_scheme == kAesCtr) {
parameters.cipher_mode = kCipherModeCtr;
} else if (input.encryption_scheme == kAesCbc) {
parameters.cipher_mode = kCipherModeCbc;
}
parameters.key_id = &key_id;
parameters.encrypt_buffer = input.data;
parameters.encrypt_length = input.data_length;
@@ -660,6 +857,8 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
(input.first_subsample ? OEMCrypto_FirstSubsample : 0) |
(input.last_subsample ? OEMCrypto_LastSubsample : 0);
parameters.is_video = input.is_video;
parameters.pattern_descriptor.encrypt_blocks = input.pattern.encrypted_blocks;
parameters.pattern_descriptor.skip_blocks = input.pattern.clear_blocks;
CdmSessionId empty_session_id;
CdmResponseType result = cdm_engine_.Decrypt(empty_session_id, parameters);
@@ -675,6 +874,118 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
return kDecryptError;
}
Cdm::Status CdmImpl::genericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) {
CdmEncryptionAlgorithm cdm_algorithm = ConvertEncryptionAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kEncryptionAlgorithmUnknown) {
LOGE("Unrecognized encryption algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericEncrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_13) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_3 || result == KEY_ERROR_1) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
Cdm::Status CdmImpl::genericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) {
CdmEncryptionAlgorithm cdm_algorithm = ConvertEncryptionAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kEncryptionAlgorithmUnknown) {
LOGE("Unrecognized encryption algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericDecrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_14) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_4 || result == KEY_ERROR_2) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
Cdm::Status CdmImpl::genericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
std::string* signature) {
CdmSigningAlgorithm cdm_algorithm = ConvertSigningAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kSigningAlgorithmUnknown) {
LOGE("Unrecognized signing algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericSign(
session_id, message, key_id, cdm_algorithm, signature);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_15) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_5 || result == KEY_ERROR_3) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
Cdm::Status CdmImpl::genericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
const std::string& signature) {
CdmSigningAlgorithm cdm_algorithm = ConvertSigningAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kSigningAlgorithmUnknown) {
LOGE("Unrecognized signing algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericVerify(
session_id, message, key_id, cdm_algorithm, signature);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_16) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_6 || result == KEY_ERROR_4) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
void CdmImpl::onTimerExpired(void* context) {
if (context == kPolicyTimerContext) {
if (policy_timer_enabled_) {
@@ -687,10 +998,9 @@ void CdmImpl::onTimerExpired(void* context) {
}
void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
CdmKeyMessage message;
std::string server_url;
CdmKeyRequest key_request;
CdmResponseType result =
cdm_engine_.GenerateRenewalRequest(session_id, &message, &server_url);
cdm_engine_.GenerateRenewalRequest(session_id, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
return;
@@ -698,12 +1008,7 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
LOGI("A license renewal has been generated.");
MessageType message_type = kLicenseRenewal;
// Post the server_url before providing the message.
// For systems that still require the server URL,
// the listener will add the URL to its renewal request.
listener_->onMessageUrl(session_id, server_url);
listener_->onMessage(session_id, message_type, message);
listener_->onMessage(session_id, message_type, key_request.message);
}
void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
@@ -756,6 +1061,55 @@ void CdmImpl::OnExpirationUpdate(const CdmSessionId& session_id,
}
}
Cdm::KeyAllowedUsageFlags CdmImpl::KeyAllowedFlags(
const CdmKeyAllowedUsage& usages) {
KeyAllowedUsageFlags flags = kAllowNone;
flags |= (usages.decrypt_to_clear_buffer) ? kAllowDecryptToClearBuffer : 0;
flags |= (usages.decrypt_to_secure_buffer) ? kAllowDecryptToSecureBuffer : 0;
flags |= (usages.generic_encrypt) ? kAllowGenericEncrypt : 0;
flags |= (usages.generic_decrypt) ? kAllowGenericDecrypt : 0;
flags |= (usages.generic_sign) ? kAllowGenericSign : 0;
flags |= (usages.generic_verify) ? kAllowGenericSignatureVerify : 0;
return flags;
}
bool CdmImpl::SendProvisioningRequest(const std::string& session_id) {
// Generate a provisioning request.
std::string empty_authority;
std::string ignored_base_url;
std::string signed_request;
CdmResponseType result = cdm_engine_.GetProvisioningRequest(
kCertificateWidevine, empty_authority, &signed_request,
&ignored_base_url);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return false;
}
listener_->onDirectIndividualizationRequest(session_id, signed_request);
provision_request_sent_ = true;
return true;
}
CdmEncryptionAlgorithm CdmImpl::ConvertEncryptionAlgorithm(
GenericEncryptionAlgorithmType algorithm) {
if (algorithm == Cdm::kEncryptionAlgorithmAesCbc128) {
return wvcdm::kEncryptionAlgorithmAesCbc128;
} else {
LOGW("Unknown encryption algorithm: %d", algorithm);
return wvcdm::kEncryptionAlgorithmUnknown;
}
}
CdmSigningAlgorithm CdmImpl::ConvertSigningAlgorithm(
GenericSigningAlgorithmType algorithm) {
if (algorithm == Cdm::kSigningAlgorithmHmacSha256) {
return wvcdm::kSigningAlgorithmHmacSha256;
} else {
LOGW("Unknown signing algorithm: %d", algorithm);
return wvcdm::kSigningAlgorithmUnknown;
}
}
bool VerifyL1() {
CryptoSession cs;
@@ -772,7 +1126,6 @@ Cdm::Status Cdm::initialize(
IStorage* storage,
IClock* clock,
ITimer* timer,
DeviceCertificateRequest* device_certificate_request,
LogLevel verbosity) {
// If you want to direct-render on L3, CryptoSession will pass that request
// along to OEMCrypto. But if you want to use an opaque handle on L3,
@@ -813,11 +1166,6 @@ Cdm::Status Cdm::initialize(
return kTypeError;
}
if (!device_certificate_request) {
LOGE("Device certificate request pointer is required!");
return kTypeError;
}
// Our enum values match those in core/include/log.h
g_cutoff = static_cast<LogPriority>(verbosity);
@@ -827,33 +1175,6 @@ Cdm::Status Cdm::initialize(
host.storage = storage;
host.clock = clock;
host.timer = timer;
device_certificate_request->needed = false;
if (!host.provisioning_engine) {
host.provisioning_engine = new CdmEngine();
}
bool has_cert = host.provisioning_engine->IsProvisioned(
kSecurityLevelL1, "" /* origin */);
if (!has_cert) {
device_certificate_request->needed = true;
std::string empty_authority;
std::string empty_origin;
std::string base_url;
std::string signed_request;
CdmResponseType result = host.provisioning_engine->GetProvisioningRequest(
kCertificateWidevine, empty_authority, empty_origin,
&signed_request, &base_url);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
device_certificate_request->url = base_url;
device_certificate_request->url.append("&signedRequest=");
device_certificate_request->url.append(signed_request);
}
host.initialized = true;
return kSuccess;
}
@@ -865,6 +1186,7 @@ const char* Cdm::version() {
// static
Cdm* Cdm::create(IEventListener* listener,
IStorage* storage,
bool privacy_mode) {
if (!host.initialized) {
LOGE("Not initialized!");
@@ -874,28 +1196,10 @@ Cdm* Cdm::create(IEventListener* listener,
LOGE("No listener!");
return NULL;
}
return new CdmImpl(listener, privacy_mode);
}
if (!storage)
storage = host.storage;
Cdm::Status Cdm::DeviceCertificateRequest::acceptReply(
const std::string& reply) {
if (!host.provisioning_engine) {
LOGE("Provisioning reply received while not in a provisioning state!");
return kTypeError;
}
std::string empty_origin;
std::string ignored_cert;
std::string ignored_wrapped_key;
CdmResponseType result =
host.provisioning_engine->HandleProvisioningResponse(
empty_origin, reply, &ignored_cert, &ignored_wrapped_key);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
return new CdmImpl(listener, storage, privacy_mode);
}
} // namespace widevine
@@ -911,35 +1215,22 @@ int64_t Clock::GetCurrentTime() {
class File::Impl {
public:
Cdm::IStorage* storage;
std::string name;
bool read_only;
bool truncate;
};
File::File() : impl_(NULL) {}
File::File(Impl* impl) : impl_(impl) {}
File::~File() {
Close();
}
bool File::Open(const std::string& file_path, int flags) {
if (!(flags & kCreate) && !host.storage->exists(file_path)) {
return false;
}
impl_ = new Impl;
impl_->name = file_path;
impl_->read_only = (flags & kReadOnly);
impl_->truncate = (flags & kTruncate);
return true;
}
File::~File() {}
ssize_t File::Read(char* buffer, size_t bytes) {
if (!impl_) {
return -1;
}
std::string data;
if (!host.storage->read(impl_->name, &data)) {
if (!impl_->storage->read(impl_->name, &data)) {
return -1;
}
@@ -957,7 +1248,7 @@ ssize_t File::Write(const char* buffer, size_t bytes) {
return -1;
}
std::string data(buffer, bytes);
if (!host.storage->write(impl_->name, data)) {
if (!impl_->storage->write(impl_->name, data)) {
return -1;
}
return bytes;
@@ -968,45 +1259,61 @@ void File::Close() {
delete impl_;
}
impl_ = NULL;
delete this;
}
bool File::Exists(const std::string& file_path) {
// An empty path is the "base directory" for CE CDM's file storage.
// Therefore, it should always be seen as existing.
// If it ever does not exist, CdmEngine detects this as a "factory reset"
// and wipes out all usage table data.
return file_path.empty() || host.storage->exists(file_path);
class FileSystem::Impl {
public:
widevine::Cdm::IStorage* storage;
};
FileSystem::FileSystem() : FileSystem("", NULL) {}
FileSystem::FileSystem(const std::string& origin, void* extra_data)
: impl_(new Impl), origin_(origin) {
if (extra_data)
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
else
impl_->storage = host.storage;
}
bool File::Remove(const std::string& file_path) {
return host.storage->remove(file_path);
FileSystem::~FileSystem() {
delete impl_;
impl_ = NULL;
}
bool File::Copy(const std::string& old_path, const std::string& new_path) {
std::string data;
bool read_ok = host.storage->read(old_path, &data);
if (!read_ok) return false;
return host.storage->write(new_path, data);
File* FileSystem::Open(const std::string& file_path, int flags) {
if (!impl_ || (!(flags & kCreate) && !impl_->storage->exists(file_path))) {
return NULL;
}
File::Impl* file_impl = new File::Impl;
file_impl->storage = impl_->storage;
file_impl->name = file_path;
file_impl->read_only = (flags & kReadOnly);
file_impl->truncate = (flags & kTruncate);
return new File(file_impl);
}
bool File::List(const std::string& path, std::vector<std::string>* files) {
return false;
bool FileSystem::Exists(const std::string& file_path) {
if (!impl_)
return false;
return file_path.empty() || impl_->storage->exists(file_path);
}
bool File::CreateDirectory(const std::string dir_path) {
return true;
bool FileSystem::Remove(const std::string& file_path) {
if (!impl_)
return false;
return impl_->storage->remove(file_path);
}
bool File::IsDirectory(const std::string& dir_path) {
return false;
}
ssize_t FileSystem::FileSize(const std::string& file_path) {
if (!impl_)
return -1;
bool File::IsRegularFile(const std::string& file_path) {
return host.storage->exists(file_path);
}
ssize_t File::FileSize(const std::string& file_path) {
return host.storage->size(file_path);
return impl_->storage->size(file_path);
}
} // namespace wvcdm

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
#include "test_host.h"
#if defined(OEMCRYPTO_TESTS)
# include "oemcrypto_test.h"
# include "oec_device_features.h"
#endif
using namespace widevine;
@@ -68,13 +68,11 @@ int main(int argc, char** argv) {
#endif
client_info.build_info = __DATE__;
Cdm::DeviceCertificateRequest cert_request;
Cdm::Status status = Cdm::initialize(
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host, &cert_request,
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host,
static_cast<Cdm::LogLevel>(verbosity));
(void)status; // status is now used when assertions are turned off.
assert(status == Cdm::kSuccess);
assert(cert_request.needed == false);
#if defined(OEMCRYPTO_TESTS)
// Set up the OEMCrypto test harness.

View File

@@ -71,6 +71,7 @@ bool TestHost::exists(const std::string& name) {
}
bool TestHost::remove(const std::string& name) {
LOGD("remove: %s", name.c_str());
files_.erase(name);
return true;
}