diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index afa73db2..883370a6 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -360,6 +360,16 @@ class CdmEngine { virtual void SetUserId(uint32_t user_id) { user_id_ = user_id; } virtual uint32_t GetUserId() const { return user_id_; } + // Changes the rules used for calculating the fallback duration + // when OTA keybox provisioning fails. + // Default rules use fallback duration measured in days, with exponential + // backoff. + // Fast rules use fallback durations of a few seconds, without exponential + // backoff. + // This method has no effect if OTA keybox is not required. + virtual void SetDefaultOtaKeyboxFallbackDurationRules(); + virtual void SetFastOtaKeyboxFallbackDurationRules(); + protected: friend class CdmEngineFactory; diff --git a/libwvdrmengine/cdm/core/include/okp_fallback_policy.h b/libwvdrmengine/cdm/core/include/okp_fallback_policy.h index 36129a63..419ab02c 100644 --- a/libwvdrmengine/cdm/core/include/okp_fallback_policy.h +++ b/libwvdrmengine/cdm/core/include/okp_fallback_policy.h @@ -22,13 +22,14 @@ static constexpr int64_t kSecondsPerDay = kSecondsPerHour * 24; // Initial backoff duration. Subsequent backoff durations for the // same engine will double its previous duration. static constexpr int64_t kAverageInitialBackoffDuration = kSecondsPerDay; -static constexpr int64_t kInitalBackoffDurationDelta = kSecondsPerHour * 12; +static constexpr int64_t kFastBackoffDuration = 30; // 30 seconds. +static constexpr int64_t kInitialBackoffDurationDelta = kSecondsPerHour * 12; // Minimum backoff duration which an device will be required to // backoff the first time. static constexpr int64_t kMinInitialBackoffDuration = - kAverageInitialBackoffDuration - kInitalBackoffDurationDelta; + kAverageInitialBackoffDuration - kInitialBackoffDurationDelta; static constexpr int64_t kMaxInitialBackoffDuration = - kAverageInitialBackoffDuration + kInitalBackoffDurationDelta; + kAverageInitialBackoffDuration + kInitialBackoffDurationDelta; // SystemFallbackPolicy is a centralized OKP state manager which allows // multiple CDM engines to communicate between each other. In a production @@ -70,6 +71,9 @@ class SystemFallbackPolicy { bool IsProvisioned(); bool IsInFallbackMode(); + void SetDefaultBackoffDurationRules(); + void SetFastBackoffDurationRules(); + ~SystemFallbackPolicy(); private: @@ -81,6 +85,8 @@ class SystemFallbackPolicy { void StoreInfo(); + int64_t GenerateInitialBackoffDuration(); + int64_t GetSecondsSinceBackoffStart() const; void EndBackoffPeriod(); @@ -93,6 +99,10 @@ class SystemFallbackPolicy { SystemFallbackInfo info_; + // When |fast_fallback_| is true, falling back only lasts a few + // seconds, and exponential backoff is disabled. + bool fast_fallback_ = false; + // Handle for the DeviceFiles instance used to store the OKP // information. // Not set for test instances. diff --git a/libwvdrmengine/cdm/core/include/okp_info.h b/libwvdrmengine/cdm/core/include/okp_info.h index 3bd91424..add43ea9 100644 --- a/libwvdrmengine/cdm/core/include/okp_info.h +++ b/libwvdrmengine/cdm/core/include/okp_info.h @@ -45,6 +45,7 @@ class SystemFallbackInfo { backoff_duration_ = (duration > 0 ? duration : 0); } void DoubleBackoffDuration() { backoff_duration_ *= 2; } + void ClearBackoffDuration() { backoff_duration_ = 0; } bool HasProvisioningTime() const { return provisioning_time_ != 0; } int64_t provisioning_time() const { return provisioning_time_; } diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index dee205f4..fd5234f3 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -21,6 +21,7 @@ #include "device_files.h" #include "file_store.h" #include "log.h" +#include "okp_fallback_policy.h" #include "ota_keybox_provisioner.h" #include "properties.h" #include "string_conversions.h" @@ -2154,4 +2155,26 @@ void CdmEngine::OkpCleanUp() { } okp_provisioner_.reset(); } + +void CdmEngine::SetDefaultOtaKeyboxFallbackDurationRules() { + OkpCheck(); + std::unique_lock lock(okp_mutex_); + auto* system_fallback_policy = CryptoSession::GetOkpFallbackPolicy(); + if (!system_fallback_policy) { + LOGW("No system fallback policy available"); + return; + } + system_fallback_policy->SetDefaultBackoffDurationRules(); +} + +void CdmEngine::SetFastOtaKeyboxFallbackDurationRules() { + OkpCheck(); + std::unique_lock lock(okp_mutex_); + auto* system_fallback_policy = CryptoSession::GetOkpFallbackPolicy(); + if (!system_fallback_policy) { + LOGW("No system fallback policy available"); + return; + } + system_fallback_policy->SetFastBackoffDurationRules(); +} } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/okp_fallback_policy.cpp b/libwvdrmengine/cdm/core/src/okp_fallback_policy.cpp index 4e6505e6..b40c9d46 100644 --- a/libwvdrmengine/cdm/core/src/okp_fallback_policy.cpp +++ b/libwvdrmengine/cdm/core/src/okp_fallback_policy.cpp @@ -16,11 +16,6 @@ namespace okp { using UniqueLock = std::unique_lock; namespace { constexpr int64_t kErrorTime = -1; - -int64_t GenerateInitialBackoffDuration() { - return static_cast(CdmRandom::RandomInRange( - kMinInitialBackoffDuration, kMaxInitialBackoffDuration)); -} } // namespace // static @@ -131,11 +126,11 @@ void SystemFallbackPolicy::TriggerFallback() { info_.SetFirstCheckedTime(current_time); } info_.SetBackoffStartTime(GetCurrentTime()); - if (info_.HasBackoffDuration()) { - // Doubling backoff duration for exponential backoff. + if (!fast_fallback_ && info_.HasBackoffDuration()) { + // Doubling backoff duration for exponential backoff. Except when + // performing fast fallback off. info_.DoubleBackoffDuration(); } else { - // Use a random backoff period to avoid server spam across all devices. info_.SetBackoffDuration(GenerateInitialBackoffDuration()); } StoreInfo(); @@ -185,6 +180,37 @@ bool SystemFallbackPolicy::IsInFallbackMode() { return false; // Only stored if previously in fallback and has ended. } +void SystemFallbackPolicy::SetDefaultBackoffDurationRules() { + UniqueLock lock(mutex_); + fast_fallback_ = false; + if (state() == SystemState::kFallbackMode) { + LOGI("Ending fallback"); + EndBackoffPeriod(); + } + info_.ClearBackoffDuration(); + StoreInfo(); +} + +void SystemFallbackPolicy::SetFastBackoffDurationRules() { + UniqueLock lock(mutex_); + fast_fallback_ = true; + if (state() == SystemState::kFallbackMode) { + LOGI("Ending fallback"); + EndBackoffPeriod(); + } + info_.ClearBackoffDuration(); + StoreInfo(); +} + +int64_t SystemFallbackPolicy::GenerateInitialBackoffDuration() { + if (fast_fallback_) { + return kFastBackoffDuration; + } + // Use a random backoff period to avoid server spam across all devices. + return static_cast(CdmRandom::RandomInRange( + kMinInitialBackoffDuration, kMaxInitialBackoffDuration)); +} + int64_t SystemFallbackPolicy::GetSecondsSinceBackoffStart() const { if (!info_.HasBackoffStartTime()) return 0; const int64_t backoff_start_time = info_.backoff_start_time(); diff --git a/libwvdrmengine/cdm/core/test/okp_fallback_policy_test.cpp b/libwvdrmengine/cdm/core/test/okp_fallback_policy_test.cpp index ea57a42d..ba6dddbf 100644 --- a/libwvdrmengine/cdm/core/test/okp_fallback_policy_test.cpp +++ b/libwvdrmengine/cdm/core/test/okp_fallback_policy_test.cpp @@ -368,5 +368,110 @@ TEST_F(OkpFallbackPolicyTest, Restore_NeedsProvisioningAgain) { EXPECT_FALSE(system_policy_->IsInFallbackMode()); EXPECT_EQ(system_policy_->info().first_checked_time(), kRestoreTime); } + +// Setup: +// 1) Device needs OKP +// 2) App requests using fast backoff settings. +// 3) Fallback occurs +// 4) After the fast fallback duration, check for if in fallback +// Expectation: +// Policy should indicate fallback, duration should be "fast" and +// the info is updated. +// After the fast fallback duration has passed, the system should +// leave fallback state. +TEST_F(OkpFallbackPolicyTest, FastRules) { + system_policy_->SetFastBackoffDurationRules(); + constexpr int64_t kFallbackTime = kInitialTime + 10; + clock_.SetTime(kFallbackTime); + system_policy_->TriggerFallback(); + // Checks. + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_TRUE(system_policy_->IsInFallbackMode()); + EXPECT_EQ(system_policy_->info().backoff_start_time(), kFallbackTime); + EXPECT_EQ(system_policy_->info().backoff_duration(), kFastBackoffDuration); + + constexpr int64_t kPostFallbackTime = + kFallbackTime + kFastBackoffDuration + 5; + clock_.SetTime(kPostFallbackTime); + // Checks. + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_FALSE(system_policy_->IsInFallbackMode()); +} + +// Setup: +// 1) Device needs OKP +// 2) Fallback occurs +// 3) App requests using fast backoff settings. +// 4) Another fallback occurs +// Expectation: +// 1) Setting rules to fast should end fallback +// 2) Second fallback should have a short duration. +TEST_F(OkpFallbackPolicyTest, FastRules_AfterFallback) { + // First fallback. + constexpr int64_t kFirstFallbackTime = kInitialTime + 10; + clock_.SetTime(kFirstFallbackTime); + system_policy_->TriggerFallback(); + EXPECT_TRUE(system_policy_->IsInFallbackMode()); + + // Set fast fallback. + system_policy_->SetFastBackoffDurationRules(); + EXPECT_FALSE(system_policy_->IsInFallbackMode()); + + // Second fallaback. + constexpr int64_t kSecondFallbackTime = kFirstFallbackTime + 10; + clock_.SetTime(kSecondFallbackTime); + system_policy_->TriggerFallback(); + EXPECT_TRUE(system_policy_->IsInFallbackMode()); + + // Checks. + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_TRUE(system_policy_->IsInFallbackMode()); + EXPECT_EQ(system_policy_->info().backoff_start_time(), kSecondFallbackTime); + EXPECT_EQ(system_policy_->info().backoff_duration(), kFastBackoffDuration); +} + +// Setup: +// 1) Device needs OKP +// 2) App requests using fast backoff settings. +// 3) Fallback occurs +// 4) After the fast fallback duration, check for if in fallback +// 5) Another fallback occurs +// 6) After the fast fallback duration, check for if in fallback +// Expectation: +// There should not be any exponential backoff, similar to FastRules +// in all other ways. +TEST_F(OkpFallbackPolicyTest, FastRules_FallbackTwice) { + system_policy_->SetFastBackoffDurationRules(); + constexpr int64_t kFirstFallbackTime = kInitialTime + 10; + clock_.SetTime(kFirstFallbackTime); + system_policy_->TriggerFallback(); + // Checks. + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_TRUE(system_policy_->IsInFallbackMode()); + EXPECT_EQ(system_policy_->info().backoff_start_time(), kFirstFallbackTime); + EXPECT_EQ(system_policy_->info().backoff_duration(), kFastBackoffDuration); + + constexpr int64_t kPostFirstFallbackTime = + kFirstFallbackTime + kFastBackoffDuration + 5; + clock_.SetTime(kPostFirstFallbackTime); + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_FALSE(system_policy_->IsInFallbackMode()); + + constexpr int64_t kSecondFallbackTime = kPostFirstFallbackTime + 10; + clock_.SetTime(kSecondFallbackTime); + system_policy_->TriggerFallback(); + + // Checks. + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_TRUE(system_policy_->IsInFallbackMode()); + EXPECT_EQ(system_policy_->info().backoff_start_time(), kSecondFallbackTime); + EXPECT_EQ(system_policy_->info().backoff_duration(), kFastBackoffDuration); + + constexpr int64_t kPostSecondFallbackTime = + kSecondFallbackTime + kFastBackoffDuration + 5; + clock_.SetTime(kPostSecondFallbackTime); + EXPECT_FALSE(system_policy_->IsProvisioned()); + EXPECT_FALSE(system_policy_->IsInFallbackMode()); +} } // namespace okp } // namespace wvcdm diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 06a75613..1630ae93 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -188,6 +188,9 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { virtual CdmResponseType GetSessionUserId(const CdmSessionId& session_id, uint32_t* user_id); + virtual bool SetDefaultOtaKeyboxFallbackDurationRules(); + virtual bool SetFastOtaKeyboxFallbackDurationRules(); + private: struct CdmInfo { CdmInfo(); diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index ded47c25..3256d102 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -645,4 +645,19 @@ CdmResponseType WvContentDecryptionModule::GetSessionUserId( *user_id = cdm_engine->GetUserId(); return NO_ERROR; } + +bool WvContentDecryptionModule::SetDefaultOtaKeyboxFallbackDurationRules() { + CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier); + if (!cdm_engine) return false; + cdm_engine->SetDefaultOtaKeyboxFallbackDurationRules(); + return true; +} + +bool WvContentDecryptionModule::SetFastOtaKeyboxFallbackDurationRules() { + CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier); + if (!cdm_engine) return false; + cdm_engine->SetFastOtaKeyboxFallbackDurationRules(); + return true; +} + } // namespace wvcdm diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index b7e67d53..76f09a49 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -689,6 +689,19 @@ status_t WVDrmPlugin::setPropertyString(const String8& name, return mapCdmResponseType(res); } else if (name == "decryptHashSessionId") { mDecryptHashSessionId = value.string(); + } else if (name == "debugOtaKeyboxFallbackDuration") { + bool success = false; + if (value == "default") { + success = mCDM->SetDefaultOtaKeyboxFallbackDurationRules(); + } else if (value == "fast") { + success = mCDM->SetFastOtaKeyboxFallbackDurationRules(); + } else { + ALOGE("Unknown OTA fallback duration value %s", value.string()); + return android::BAD_VALUE; + } + if (!success) { + return android::UNKNOWN_ERROR; + } } else if (name == "atscMode") { if (value == kEnable) { mPropertySet.set_use_atsc_mode(true); diff --git a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp index cb3709f9..aa1efe37 100644 --- a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp @@ -1470,6 +1470,19 @@ Return WVDrmPlugin::setPropertyString(const hidl_string& propertyName, ALOGE("App requested unknown ATSC mode %s", _value.c_str()); return Status::BAD_VALUE; } + } else if (name == "debugOtaKeyboxFallbackDuration") { + bool success = false; + if (value == "default") { + success = mCDM->SetDefaultOtaKeyboxFallbackDurationRules(); + } else if (value == "fast") { + success = mCDM->SetFastOtaKeyboxFallbackDurationRules(); + } else { + ALOGE("Unknown OTA fallback duration value %s", _value.c_str()); + return Status::BAD_VALUE; + } + if (!success) { + return Status::ERROR_DRM_UNKNOWN; + } } else { ALOGE("App set unknown string property %s", name.c_str()); return Status::ERROR_DRM_CANNOT_HANDLE;