From 48e23cca39c5a7e5ceadf2a4e5289d9b16edc5fc Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Fri, 15 Oct 2021 19:32:29 -0700 Subject: [PATCH 1/2] Update fallback policy for fast fallback. [ Merge of http://go/wvgerrit/136329 ] CDM core has been updated to support very short fallback durations in the case of failures during OTA keybox provisioning. This is intended to be used during testing via specialized developer apps or GTS tests. Bug: 187646550 Test: Android unit tests Change-Id: I8a75d2e1c404d6caed535b087e8dd29da5c21b83 --- .../cdm/core/include/okp_fallback_policy.h | 16 ++- libwvdrmengine/cdm/core/include/okp_info.h | 1 + .../cdm/core/src/okp_fallback_policy.cpp | 42 +++++-- .../core/test/okp_fallback_policy_test.cpp | 105 ++++++++++++++++++ 4 files changed, 153 insertions(+), 11 deletions(-) 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 From e73faabc52c2ac57aa141d6dd1119867d2d91d72 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Fri, 15 Oct 2021 19:54:02 -0700 Subject: [PATCH 2/2] Added debugOtaKeyboxFallbackDuration property. [ Merge of http://go/wvgerrit/136330 ] This changes adds a custom debug property for changing the fallback policy used for the system. Depending on the value set, the device will either use a "fast" fallback (30 seconds) or "default" fallback (~1 day with exponential backoff). Setting this property to either "fast" or "default" will end the current fallback if it has been triggered. Bug: 187646550 Test: Android unit tests Change-Id: I5271f96139c1e468242f7fa742668cc791ffcf91 --- libwvdrmengine/cdm/core/include/cdm_engine.h | 10 ++++++++ libwvdrmengine/cdm/core/src/cdm_engine.cpp | 23 +++++++++++++++++++ .../include/wv_content_decryption_module.h | 3 +++ .../cdm/src/wv_content_decryption_module.cpp | 15 ++++++++++++ libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 13 +++++++++++ .../mediadrm/src_hidl/WVDrmPlugin.cpp | 13 +++++++++++ 6 files changed, 77 insertions(+) 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/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/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 f7841394..c85f6259 100644 --- a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp @@ -1463,6 +1463,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;