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/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