[ Cherry-pick of http://ag/34331848 ] [ Merge of http://go/wvgerrit/224951 ] There was a potential dangling pointer issue that was enabled by how CdmInfo is initilized. The file system that was passed into the CdmEngine instance was pointing to a location in memory that was not stable between move operations in the CdmInfo. See b/429054262 for memory diagram of issue. The CdmInfo is a private class within the Android CDM class, which restricts the potential operations on it. The easiest solution is wrap the file system in a unique pointer; ensuring the pointer remains stable even if a particular data segment of CdmInfo is moved. The default constructor for CdmInfo is deleted; this will force the compiler to fail if |cdms_| is used in ways that would result in uninitialized pointers. Bug: 429054262 Test: WvTs on Komodo Change-Id: I76a49fc5181ebd1613e238aa49986083a9f397ec (cherry picked from commit 4c105faa4923bd9bd6352f757dedf3eaf9ed88fd)
394 lines
17 KiB
C++
394 lines
17 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
|
|
#define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
|
|
|
|
#include <time.h>
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
|
|
#include "utils/RefBase.h"
|
|
|
|
#include "cdm_identifier.h"
|
|
#include "disallow_copy_and_assign.h"
|
|
#include "file_store.h"
|
|
#include "timer.h"
|
|
#include "wv_cdm_types.h"
|
|
#include "wv_metrics.pb.h"
|
|
|
|
namespace wvcdm {
|
|
|
|
class CdmClientPropertySet;
|
|
class CdmEngine;
|
|
class WvCdmEventListener;
|
|
|
|
class WvMetricsSnapshot {
|
|
public:
|
|
WvMetricsSnapshot(const CdmIdentifier& identifier,
|
|
drm_metrics::WvCdmMetrics&& metrics, ::time_t ts);
|
|
|
|
// Acts as a constructor, but uses the current time.
|
|
static WvMetricsSnapshot MakeSnapshot(const CdmIdentifier& identifier,
|
|
drm_metrics::WvCdmMetrics&& metrics);
|
|
|
|
WvMetricsSnapshot() = default;
|
|
WvMetricsSnapshot(const WvMetricsSnapshot&) = default;
|
|
WvMetricsSnapshot(WvMetricsSnapshot&&) = default;
|
|
WvMetricsSnapshot& operator=(const WvMetricsSnapshot&) = default;
|
|
WvMetricsSnapshot& operator=(WvMetricsSnapshot&&) = default;
|
|
|
|
const CdmIdentifier& cdm_id() const { return cdm_id_; }
|
|
const drm_metrics::WvCdmMetrics& metrics() const { return metrics_; }
|
|
::time_t timestamp() const { return timestamp_; }
|
|
std::string GetFormattedTimestamp() const;
|
|
|
|
private:
|
|
CdmIdentifier cdm_id_;
|
|
drm_metrics::WvCdmMetrics metrics_;
|
|
::time_t timestamp_ = 0;
|
|
};
|
|
|
|
class WvMetricsSnapshotQueue
|
|
: public android::LightRefBase<WvMetricsSnapshotQueue> {
|
|
public:
|
|
WvMetricsSnapshotQueue();
|
|
explicit WvMetricsSnapshotQueue(size_t capacity) : capacity_(capacity) {}
|
|
|
|
size_t capacity() const { return capacity_; }
|
|
size_t size() const { return snapshots_.size(); }
|
|
|
|
WvMetricsSnapshotQueue(const WvMetricsSnapshotQueue&) = delete;
|
|
WvMetricsSnapshotQueue(WvMetricsSnapshotQueue&&) = delete;
|
|
WvMetricsSnapshotQueue& operator=(const WvMetricsSnapshotQueue&) = delete;
|
|
WvMetricsSnapshotQueue& operator=(WvMetricsSnapshotQueue&&) = delete;
|
|
|
|
void PushMetrics(WvMetricsSnapshot&& snapshot);
|
|
void PushMetrics(const WvMetricsSnapshot& snapshot);
|
|
|
|
bool GetAll(std::vector<WvMetricsSnapshot>* snapshots) const;
|
|
|
|
void Clear();
|
|
|
|
private:
|
|
void ReFit();
|
|
|
|
const size_t capacity_;
|
|
mutable std::mutex mutex_;
|
|
std::deque<WvMetricsSnapshot> snapshots_;
|
|
};
|
|
|
|
class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|
public:
|
|
WvContentDecryptionModule();
|
|
explicit WvContentDecryptionModule(WvMetricsSnapshotQueue* metrics_queue);
|
|
virtual ~WvContentDecryptionModule();
|
|
|
|
// Static methods
|
|
static bool IsSupported(const std::string& init_data_type);
|
|
static bool IsCenc(const std::string& init_data_type);
|
|
static bool IsWebm(const std::string& init_data_type);
|
|
static bool IsHls(const std::string& init_data_type);
|
|
static bool IsAudio(const std::string& init_data_type);
|
|
|
|
// Session related methods
|
|
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
|
CdmClientPropertySet* property_set,
|
|
const CdmIdentifier& identifier,
|
|
WvCdmEventListener* event_listener,
|
|
CdmSessionId* session_id);
|
|
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
|
virtual bool IsOpenSession(const CdmSessionId& session_id);
|
|
|
|
// Construct a valid license request.
|
|
virtual CdmResponseType GenerateKeyRequest(
|
|
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
|
const std::string& init_data_type, const CdmInitData& init_data,
|
|
const CdmLicenseType license_type, CdmAppParameterMap& app_parameters,
|
|
CdmClientPropertySet* property_set, const CdmIdentifier& identifier,
|
|
CdmKeyRequest* key_request);
|
|
|
|
// Accept license response and extract key info.
|
|
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
|
const CdmKeyResponse& key_data,
|
|
CdmKeySetId* key_set_id);
|
|
|
|
// Setup keys for offline usage which were retrived in an earlier key request
|
|
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,
|
|
const CdmKeySetId& key_set_id);
|
|
|
|
// Cancel session
|
|
virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id);
|
|
|
|
// Query system information
|
|
virtual CdmResponseType QueryStatus(RequestedSecurityLevel security_level,
|
|
const std::string& key,
|
|
std::string* value);
|
|
// Query session information
|
|
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
|
|
CdmQueryMap* key_info);
|
|
|
|
// Query license information
|
|
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
|
CdmQueryMap* key_info);
|
|
|
|
// Query device CSR information for Provisioning 4.0
|
|
virtual CdmResponseType QueryDeviceSignedCsrPayload(
|
|
const std::string& challenge, const std::string& device_info,
|
|
std::string* value);
|
|
|
|
// Query OEMCrypto session ID
|
|
virtual CdmResponseType QueryOemCryptoSessionId(
|
|
const CdmSessionId& session_id, CdmQueryMap* response);
|
|
|
|
// Query security level support
|
|
static bool IsSecurityLevelSupported(CdmSecurityLevel level);
|
|
|
|
// Provisioning related methods
|
|
virtual CdmResponseType GetProvisioningRequest(
|
|
CdmCertificateType cert_type, const std::string& cert_authority,
|
|
const CdmIdentifier& identifier, const std::string& service_certificate,
|
|
RequestedSecurityLevel requested_security_level,
|
|
CdmProvisioningRequest* request, std::string* default_url);
|
|
|
|
virtual CdmResponseType HandleProvisioningResponse(
|
|
const CdmIdentifier& identifier, const CdmProvisioningResponse& response,
|
|
RequestedSecurityLevel requested_security_level, std::string* cert,
|
|
std::string* wrapped_key);
|
|
|
|
virtual CdmResponseType Unprovision(CdmSecurityLevel level,
|
|
const CdmIdentifier& identifier);
|
|
|
|
virtual bool IsProvisioned(CdmSecurityLevel level, const std::string& origin,
|
|
const std::string& spoid, bool atsc_mode_enabled);
|
|
|
|
// Secure stop related methods
|
|
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
|
const CdmIdentifier& identifier,
|
|
CdmUsageReportList* usage_reports);
|
|
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
|
const CdmSecureStopId& ssid,
|
|
const CdmIdentifier& identifier,
|
|
CdmUsageReport* usage_report);
|
|
virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id,
|
|
const CdmIdentifier& identifier);
|
|
virtual CdmResponseType RemoveUsageInfo(
|
|
const std::string& app_id, const CdmIdentifier& identifier,
|
|
const CdmSecureStopId& secure_stop_id);
|
|
virtual CdmResponseType ReleaseUsageInfo(const CdmKeyResponse& message,
|
|
const CdmIdentifier& identifier);
|
|
|
|
virtual CdmResponseType GetSecureStopIds(const std::string& app_id,
|
|
const CdmIdentifier& identifier,
|
|
std::vector<CdmSecureStopId>* ssids);
|
|
|
|
// Accept encrypted buffer and decrypt data.
|
|
// Decryption parameters that need to be specified are
|
|
// is_encrypted, is_secure, key_id, encrypt_buffer, encrypt_length,
|
|
// iv, block_offset, decrypt_buffer, decrypt_buffer_length,
|
|
// decrypt_buffer_offset and subsample_flags
|
|
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
|
|
bool validate_key_id,
|
|
const CdmDecryptionParameters& parameters);
|
|
|
|
// Accept encrypted sample and decrypt data.
|
|
virtual CdmResponseType DecryptV16(
|
|
const CdmSessionId& session_id, bool validate_key_id,
|
|
const CdmDecryptionParametersV16& parameters);
|
|
|
|
virtual void NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
|
uint32_t height);
|
|
|
|
// Validate a passed-in service certificate
|
|
virtual bool IsValidServiceCertificate(const std::string& certificate);
|
|
|
|
// Fill the |metrics| parameter with the engine metrics data for the
|
|
// CdmEngine associated with the given CdmIdentifier.
|
|
// If the CdmEngine instance does not exist, this will return an error.
|
|
virtual CdmResponseType GetCurrentMetrics(const CdmIdentifier& identifier,
|
|
drm_metrics::WvCdmMetrics* metrics);
|
|
|
|
// Fill the |snapshots| parameter wrapped engine metrics for all CdmEngine
|
|
// instances that currently enabled (actively decrypting or performing
|
|
// other engine-level operations).
|
|
// Current metrics snapshots contain the engine identifier, metrics data
|
|
// and time (roughly current time).
|
|
// |full_list_returned| will indicate whether all existing enginers were
|
|
// able to report their metrics successfully.
|
|
virtual CdmResponseType GetAllCurrentMetricsSnapshots(
|
|
std::vector<WvMetricsSnapshot>* snapshots, bool* full_list_returned);
|
|
|
|
// Fill the |snapshots| parameter wrapped engine metrics for several of
|
|
// the most recently closed CdmEngine instances.
|
|
// Saved metrics snapshots contain the engine identifier, metrics data
|
|
// and time of closing.
|
|
// The same engine identifier may appear multiple times in the list (
|
|
// depending on how the app utilizes the MediaDrm plugin).
|
|
virtual CdmResponseType GetAllSavedMetricsSnapshots(
|
|
std::vector<WvMetricsSnapshot>* snapshots);
|
|
|
|
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
|
|
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
|
|
|
|
// When positive, the keybox will be ignored at initialization and force the
|
|
// device to request a keybox OTA reprovisioning.
|
|
virtual CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count);
|
|
|
|
virtual CdmResponseType SetDecryptHash(const std::string& hash_data,
|
|
CdmSessionId* session_id);
|
|
virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id,
|
|
std::string* hash_error_string);
|
|
|
|
// Return the list of key_set_ids stored on the current (origin-specific)
|
|
// file system.
|
|
virtual CdmResponseType ListStoredLicenses(
|
|
CdmSecurityLevel security_level, const CdmIdentifier& identifier,
|
|
std::vector<CdmKeySetId>* key_set_ids);
|
|
|
|
// Retrieve offline license state using key_set_id.
|
|
virtual CdmResponseType GetOfflineLicenseState(
|
|
const CdmKeySetId& key_set_id, CdmSecurityLevel security_level,
|
|
const CdmIdentifier& identifier, CdmOfflineLicenseState* licenseState);
|
|
|
|
// Remove offline license using key_set_id.
|
|
virtual CdmResponseType RemoveOfflineLicense(const CdmKeySetId& key_set_id,
|
|
CdmSecurityLevel security_level,
|
|
const CdmIdentifier& identifier);
|
|
|
|
virtual CdmResponseType SetPlaybackId(const CdmSessionId& session_id,
|
|
const std::string& playback_id);
|
|
|
|
virtual CdmResponseType GetSessionUserId(const CdmSessionId& session_id,
|
|
uint32_t* user_id);
|
|
|
|
virtual CdmResponseType StoreAtscLicense(
|
|
const CdmIdentifier& identifier, RequestedSecurityLevel security_level,
|
|
const CdmKeySetId& key_set_id,
|
|
const std::string& serialized_license_data);
|
|
virtual bool SetDefaultOtaKeyboxFallbackDurationRules();
|
|
virtual bool SetFastOtaKeyboxFallbackDurationRules();
|
|
|
|
// Generic crypto API.
|
|
virtual CdmResponseType GenericEncrypt(const CdmSessionId& session_id,
|
|
const KeyId& key_id,
|
|
const std::string& input,
|
|
const std::string& iv,
|
|
CdmEncryptionAlgorithm algorithm,
|
|
std::string* output);
|
|
|
|
virtual CdmResponseType GenericDecrypt(const CdmSessionId& session_id,
|
|
const KeyId& key_id,
|
|
const std::string& input,
|
|
const std::string& iv,
|
|
CdmEncryptionAlgorithm algorithm,
|
|
std::string* output);
|
|
|
|
virtual CdmResponseType GenericSign(const CdmSessionId& session_id,
|
|
const KeyId& key_id,
|
|
const std::string& input,
|
|
CdmSigningAlgorithm algorithm,
|
|
std::string* signature);
|
|
|
|
virtual CdmResponseType GenericVerify(const CdmSessionId& session_id,
|
|
const KeyId& key_id,
|
|
const std::string& input,
|
|
CdmSigningAlgorithm algorithm,
|
|
const std::string& signature);
|
|
|
|
private:
|
|
class CdmInfo {
|
|
public:
|
|
// This should never be used.
|
|
CdmInfo() = delete;
|
|
// It is expected that the filesystem loaded into |cdm_engine|
|
|
// is the same instance as |file_system|.
|
|
CdmInfo(std::unique_ptr<wvutil::FileSystem>&& file_system,
|
|
std::unique_ptr<CdmEngine>&& cdm_engine);
|
|
// No copy operators.
|
|
CdmInfo(const CdmInfo&) = delete;
|
|
CdmInfo& operator=(const CdmInfo&) = delete;
|
|
// Move operators OK.
|
|
CdmInfo(CdmInfo&&) = default;
|
|
CdmInfo& operator==(CdmInfo&& other);
|
|
|
|
wvutil::FileSystem* file_system() { return file_system_.get(); }
|
|
CdmEngine* cdm_engine() { return cdm_engine_.get(); }
|
|
|
|
private:
|
|
// Order matters, |cdm_engine_| is expected to contain a pointer
|
|
// to |file_system_|.
|
|
std::unique_ptr<wvutil::FileSystem> file_system_;
|
|
std::unique_ptr<CdmEngine> cdm_engine_;
|
|
};
|
|
|
|
// Finds the CdmEngine instance for the given identifier, creating one if
|
|
// needed.
|
|
CdmEngine* EnsureCdmForIdentifier(const CdmIdentifier& identifier);
|
|
// Finds the CdmEngine instance for the given session id, returning nullptr
|
|
// if not found.
|
|
CdmEngine* GetCdmForSessionId(const std::string& session_id);
|
|
|
|
// Close all of the open CdmEngine instances. This is used when ready to close
|
|
// the WvContentDecryptionModule instance.
|
|
void CloseAllCdms();
|
|
|
|
uint32_t GenerateSessionSharingId();
|
|
|
|
// Timer related methods to drive policy decisions
|
|
void EnableTimer();
|
|
void DisableTimer();
|
|
// Call this method only if invoked from a thread other than the timer thread.
|
|
// Otherwise this might result in deadlock.
|
|
void DisableTimerAndWaitForExit();
|
|
void OnTimerEvent();
|
|
|
|
// Fill the |metrics| parameter with the metrics data for the CdmEngine
|
|
// associated with the given |identifier|. If the CdmEngine instance does
|
|
// not exist, this will return an error.
|
|
// This methods assumes that |metrics| is not null and that the |cdms_lock_|
|
|
// has already been acquired.
|
|
CdmResponseType GetCurrentMetricsInternal(const CdmIdentifier& identifier,
|
|
drm_metrics::WvCdmMetrics* metrics);
|
|
void SaveMetrics(const CdmIdentifier& identifier,
|
|
drm_metrics::WvCdmMetrics&& metrics);
|
|
|
|
static std::mutex session_sharing_id_generation_lock_;
|
|
std::mutex timer_lock_;
|
|
Timer timer_;
|
|
|
|
// instance variables
|
|
// This manages the lifetime of the CDM instances.
|
|
std::map<CdmIdentifier, CdmInfo> cdms_;
|
|
// This contains weak pointers to the CDM instances contained in |cdms_|.
|
|
std::map<std::string, CdmEngine*> cdm_by_session_id_;
|
|
// Lock for accessing either |cdms_| or |cdm_by_session_id_|.
|
|
// This lock should be acquired for any of the following:
|
|
// 1) Getting a CDM instance from |cdms_| or |cdm_by_session_id_|
|
|
// - Hold lock while searching, release lock once pointer is acquired
|
|
// 2) Iterating over |cdms_| or |cdm_by_session_id_|
|
|
// - Hold lock for the entire duration of iterating over CDMs
|
|
// - DO NOT release the lock until all operations are complete
|
|
// - This MUST be done regardless of whether |cdms_| or
|
|
// |cdm_by_session_id_| is being modified
|
|
// 3) Creating a new CDM instance with |cdms_|
|
|
// - Hold lock when creating AND initializing the CDM
|
|
// - Release once CDM is fully initialized
|
|
// 4) Linking a session to a CDM with |cdm_by_session_id_|
|
|
// - Hold lock when mapping a session ID to a CDM, release once set
|
|
// 5) Unlinking a session from a CDM with |cdm_by_session_id_|
|
|
// - Hold lock when erasing, release once erased.
|
|
std::mutex cdms_lock_;
|
|
|
|
// Contains a finite list of histories of different CDM engine instances.
|
|
// When a CDM engine is closed, its metrics will be saved.
|
|
// Only used if not null.
|
|
WvMetricsSnapshotQueue* saved_metrics_snapshots_ = nullptr;
|
|
|
|
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
|
|
};
|
|
} // namespace wvcdm
|
|
#endif // CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
|