diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index e9925da3..3871af80 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -96,7 +96,8 @@ const char kUsageInfoFileNameExt[] = ".bin"; const char kUsageInfoFileNamePrefix[] = "usage"; const char kUsageTableFileName[] = "usgtable.bin"; const char kWildcard[] = "*"; -constexpr int64_t kFourMonthsInSeconds = (2 * 30 + 2 * 31) * 24 * 60 * 60; +// TODO(b/192430982): Renable expiration of legacy DRM certificates +// constexpr int64_t kFourMonthsInSeconds = (2 * 30 + 2 * 31) * 24 * 60 * 60; // Helper methods bool SetDeviceCertificate(const std::string& certificate, @@ -427,7 +428,9 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate, if (default_certificate) { Clock clock; device_certificate->set_acquisition_time_seconds(clock.GetCurrentTime()); - } else { + } + /* TODO(b/192430982): Renable expiration of legacy DRM certificates + else { // Since certificates of type kCertificateAtsc are not allowed to be // stored, this is a certificate of type kCertificateLegacy. // The only time when a legacy certificate is stored is when it does not @@ -440,6 +443,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate, current_time + kFourMonthsInSeconds + rng.RandomInRange(kFourMonthsInSeconds)); } + */ std::string serialized_file; file.SerializeToString(&serialized_file); @@ -595,6 +599,7 @@ DeviceFiles::CertificateState DeviceFiles::RetrieveCertificate( } case kCertificateLegacy: { + /* TODO(b/192430982): Renable expiration of legacy DRM certificates // Validation check for DRM certificate without an expiration // time set by the provisioning service. Add an expiry time // within the next 6 months +/- 2 months, if one has not been set. @@ -611,6 +616,7 @@ DeviceFiles::CertificateState DeviceFiles::RetrieveCertificate( } if (current_time > expiration_time_seconds) return kCertificateExpired; + */ return kCertificateValid; } diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 39e2865b..2aab53f0 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -1568,8 +1568,10 @@ const CertificateErrorData kRetrieveLegacyCertificateErrorData[] = { kTestLegacyCertificateFileDataInvalidClientExpiration}, }; +/* TODO(b/192430982): Renable expiration of legacy DRM certificates constexpr size_t kNumberOfLegacyCertificates = ArraySize(kRetrieveLegacyCertificateErrorData); +*/ const CertificateErrorData kRetrieveDefaultCertificateErrorData[] = { // Certificate expired @@ -4020,6 +4022,7 @@ TEST_F(DeviceFilesTest, RetrieveAtscCertificateNotFound) { &serial_number, &system_id)); } +/* TODO(b/192430982): Renable expiration of legacy DRM certificates TEST_F(DeviceFilesTest, RetrieveLegacyCertificateWithoutExpirationTime) { MockFileSystem file_system; std::string certificate_file_name; @@ -4221,6 +4224,7 @@ TEST_F(DeviceFilesTest, RetrieveDefaultCertificate) { EXPECT_EQ(kTestWrappedKey, private_key); EXPECT_EQ("7CB49F987A635E1E0A52184694582D6E", b2a_hex(serial_number)); } +*/ TEST_F(DeviceFilesTest, RetrieveDefaultCertificateNeverExpires) { MockFileSystem file_system; @@ -4331,7 +4335,9 @@ TEST_F(DeviceFilesTest, RetrieveCertificateWithoutKeyType) { // Call to Open will return a unique_ptr, freeing this object. // The file will be re-written with a new client expiration time MockFile* read_file = new MockFile(); + /* TODO(b/192430982): Renable expiration of legacy DRM certificates MockFile* write_file = new MockFile(); + */ EXPECT_CALL(file_system, Exists(StrEq(device_legacy_certificate_path))) .Times(AtLeast(1)) .WillRepeatedly(Return(true)); @@ -4341,17 +4347,21 @@ TEST_F(DeviceFilesTest, RetrieveCertificateWithoutKeyType) { EXPECT_CALL(file_system, FileSize(StrEq(device_legacy_certificate_path))) .WillOnce(Return(data.size())); EXPECT_CALL(file_system, DoOpen(StrEq(device_legacy_certificate_path), _)) - .WillOnce(Return(read_file)) - .WillOnce(Return(write_file)); + .WillOnce(Return(read_file)); + /* TODO(b/192430982): Renable expiration of legacy DRM certificates + .WillOnce(Return(write_file)); + */ EXPECT_CALL(*read_file, Read(NotNull(), Eq(data.size()))) .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), Return(data.size()))); EXPECT_CALL(*read_file, Write(_, _)).Times(0); + /* TODO(b/192430982): Renable expiration of legacy DRM certificates EXPECT_CALL(*write_file, Read(_, _)).Times(0); EXPECT_CALL(*write_file, Write(_, _)) .With(AllArgs(StrAndLenContains(std::vector{ kTestCertificateWithoutExpiration, kTestWrappedKey.key()}))) .WillOnce(ReturnArg<1>()); + */ DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index b1a9c8c4..4095a6ca 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -215,6 +215,14 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { 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 GetMetricsInternal(const CdmIdentifier& identifier, + drm_metrics::WvCdmMetrics* metrics); + static std::mutex session_sharing_id_generation_lock_; std::mutex timer_lock_; Timer timer_; @@ -222,14 +230,27 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // instance variables // This manages the lifetime of the CDM instances. std::map cdms_; - std::mutex cdms_lock_; - // This contains weak pointers to the CDM instances contained in |cdms_|. std::map 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_; CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule); }; - } // namespace wvcdm - #endif // CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index b6645127..53763827 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -71,6 +71,7 @@ CdmResponseType WvContentDecryptionModule::OpenSession( CdmResponseType sts = cdm_engine->OpenSession(key_system, property_set, event_listener, session_id); if (sts == NO_ERROR) { + std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_[*session_id] = cdm_engine; } return sts; @@ -82,12 +83,11 @@ CdmResponseType WvContentDecryptionModule::CloseSession( CdmEngine* cdm_engine = GetCdmForSessionId(session_id); // TODO(rfrias): Avoid reusing the error codes from CdmEngine. if (!cdm_engine) return SESSION_NOT_FOUND_1; - std::unique_lock auto_lock(cdms_lock_); - CdmResponseType sts = cdm_engine->CloseSession(session_id); + const CdmResponseType sts = cdm_engine->CloseSession(session_id); if (sts == NO_ERROR) { + std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_.erase(session_id); } - return sts; } @@ -107,6 +107,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( if (license_type == kLicenseTypeRelease) { sts = cdm_engine->OpenKeySetSession(key_set_id, property_set, nullptr); if (sts != NO_ERROR) return sts; + std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_[key_set_id] = cdm_engine; } @@ -130,6 +131,7 @@ CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( app_parameters, key_request); if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) { cdm_engine->CloseKeySetSession(key_set_id); + std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_.erase(key_set_id); } return sts; @@ -152,6 +154,7 @@ CdmResponseType WvContentDecryptionModule::AddKey( // Empty session id indicates license type release. if (sts == KEY_ADDED && session_id.empty()) { cdm_engine->CloseKeySetSession(release_key_set_id); + std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_.erase(release_key_set_id); } return sts; @@ -297,11 +300,11 @@ CdmResponseType WvContentDecryptionModule::GetSecureStopIds( } CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); - CdmResponseType sts = + const CdmResponseType sts = cdm_engine->ListUsageIds(app_id, kSecurityLevelL1, nullptr, ssids); std::vector secure_stop_ids; - CdmResponseType sts_l3 = cdm_engine->ListUsageIds(app_id, kSecurityLevelL3, - nullptr, &secure_stop_ids); + const CdmResponseType sts_l3 = cdm_engine->ListUsageIds( + app_id, kSecurityLevelL3, nullptr, &secure_stop_ids); ssids->insert(ssids->end(), secure_stop_ids.begin(), secure_stop_ids.end()); return sts_l3 != NO_ERROR ? sts_l3 : sts; } @@ -383,17 +386,19 @@ CdmResponseType WvContentDecryptionModule::GetMetrics( if (!metrics || !full_list_returned) { return PARAMETER_NULL; } + std::unique_lock auto_lock(cdms_lock_); for (auto& key_value_pair : cdms_) { + const CdmIdentifier& identifier = key_value_pair.first; drm_metrics::WvCdmMetrics metric; - CdmResponseType status = GetMetrics(key_value_pair.first, &metric); + const CdmResponseType status = GetMetricsInternal(identifier, &metric); if (status == NO_ERROR) { metrics->push_back(metric); } else { LOGD("GetMetrics call failed: cdm identifier=%u, error=%d", - key_value_pair.first.unique_id, status); + identifier.unique_id, status); } } - // With no streaming activities, cdms_ size would be zero, + // With no streaming activities, |cdms_| size would be zero, // treat it as a non full list in that case. *full_list_returned = !cdms_.empty() && metrics->size() == cdms_.size(); @@ -410,6 +415,12 @@ CdmResponseType WvContentDecryptionModule::GetMetrics( return PARAMETER_NULL; } std::unique_lock auto_lock(cdms_lock_); + return GetMetricsInternal(identifier, metrics); +} + +CdmResponseType WvContentDecryptionModule::GetMetricsInternal( + const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) { + // Note: Caller should lock before calling. auto it = cdms_.find(identifier); if (it == cdms_.end()) { LOGE("Cdm Identifier not found"); @@ -454,6 +465,7 @@ CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier( CdmEngine* WvContentDecryptionModule::GetCdmForSessionId( const std::string& session_id) { + std::unique_lock auto_lock(cdms_lock_); // Use find to avoid creating empty entries when not found. auto it = cdm_by_session_id_.find(session_id); if (it == cdm_by_session_id_.end()) return nullptr; @@ -462,10 +474,8 @@ CdmEngine* WvContentDecryptionModule::GetCdmForSessionId( void WvContentDecryptionModule::CloseAllCdms() { std::unique_lock auto_lock(cdms_lock_); - - for (auto it = cdms_.begin(); it != cdms_.end();) { - it = cdms_.erase(it); - } + cdm_by_session_id_.clear(); + cdms_.clear(); } CdmResponseType WvContentDecryptionModule::CloseCdm(