1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media/media_foundation_service_monitor.h"
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/json/values_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_file_util.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/service_process_info.h"
#include "media/base/media_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace {
inline constexpr char kSite1[] = "https://site1.com";
inline constexpr char kSite2[] = "https://site2.com";
inline constexpr char kSite3[] = "https://subdomain1.site1.com";
inline constexpr char kMediaFoundationServiceProcessName[] =
"media.mojom.MediaFoundationServiceBroker";
} // namespace
class MediaFoundationServiceMonitorTest
: public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
// Set up a testing profile manager.
ChromeRenderViewHostTestHarness::SetUp();
profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
// Create a new temporary directory, and store the path
const base::FilePath temp_user_data_dir =
base::CreateUniqueTempDirectoryScopedToTest();
ASSERT_TRUE(profile_manager_->SetUp(temp_user_data_dir));
monitor_ = MediaFoundationServiceMonitor::GetInstance();
CHECK(monitor_);
// Tests could write to local state and profile. Must clear them.
ClearLocalState();
ClearProfile();
// MediaFoundationServiceMonitor is a singleton and holds state between
// test. Must clear it.
monitor_->ResetForTesting();
}
void TearDown() override {
TestingBrowserProcess::GetGlobal()->SetProfileManager(nullptr);
ChromeRenderViewHostTestHarness::TearDown();
}
protected:
// Test the `days_since_last_disabling_date` against `disabled_dates`, both
// of which are integer(s) number of days since an arbitrary base time.
void TestEarliestEnableDate(std::vector<int> disabled_dates,
int days_since_last_disabling_date) {
// An arbitrary base time for the tests.
base::Time base_time;
EXPECT_TRUE(base::Time::FromString("22 Sep 2022 12:23 GMT", &base_time));
std::vector<base::Time> disabled_times;
for (const auto& days : disabled_dates) {
disabled_times.push_back(base_time + base::Days(days));
}
auto enable_time =
MediaFoundationServiceMonitor::GetEarliestEnableTime(disabled_times);
auto expected_time =
disabled_times.back() + base::Days(days_since_last_disabling_date);
// Expect the `enable_time` to be in a range to avoid testing rounding
// logic.
EXPECT_LE(enable_time, expected_time + base::Days(1));
EXPECT_GE(enable_time, expected_time - base::Days(1));
}
// Creates an entry for `site` in profile.
void InitProfileForSite(const GURL& site) {
auto* user_prefs =
profile_manager_->profile_manager()->GetLastUsedProfile()->GetPrefs();
ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
update.Get()
.EnsureDict(url::Origin::Create(site).Serialize())
->EnsureList(prefs::kHardwareSecureDecryptionDisabledTimes);
}
// Clears user profile.
void ClearProfile() {
auto* user_prefs =
profile_manager_->profile_manager()->GetLastUsedProfile()->GetPrefs();
ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
for (auto [key, value] : update.Get()) {
ASSERT_TRUE(update.Get().Remove(key));
}
}
// Clears local state.
void ClearLocalState() {
auto* user_prefs =
profile_manager_->profile_manager()->GetLastUsedProfile()->GetPrefs();
ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
profile_manager_->local_state()->Get()->ClearPref(
prefs::kGlobalHardwareSecureDecryptionDisabledTimes);
}
bool IsAllowedForSite(GURL site) {
return MediaFoundationServiceMonitor::
IsHardwareSecureDecryptionAllowedForSite(site);
}
std::unique_ptr<TestingProfileManager> profile_manager_;
raw_ptr<MediaFoundationServiceMonitor> monitor_;
};
TEST_F(MediaFoundationServiceMonitorTest, GetEarliestEnableTime_Default) {
// One disabling event will cause the feature to be disabled for 30 days,
// which is the minimum disabling days.
TestEarliestEnableDate({0}, 30);
TestEarliestEnableDate({1}, 30);
TestEarliestEnableDate({10}, 30);
// Two close disabling events will cause the feature to be disabled for 180
// days, which is the maximum disabling days.
TestEarliestEnableDate({10, 10}, 180);
TestEarliestEnableDate({10, 20}, 180);
TestEarliestEnableDate({10, 40}, 180);
// The closer the two disabling events are, the longer the feature will be
// disabled.
TestEarliestEnableDate({10, 50}, 142);
TestEarliestEnableDate({10, 100}, 80);
TestEarliestEnableDate({10, 1000}, 34);
// Two far apart disabling events will cause the feature to be disabled for 30
// days, which is the minimum disabling days.
TestEarliestEnableDate({10, 10000}, 30);
// The third disabling event time doesn't matter.
TestEarliestEnableDate({10, 50}, 142);
TestEarliestEnableDate({1, 10, 50}, 142);
}
TEST_F(MediaFoundationServiceMonitorTest, GetEarliestEnableTime_Overridden) {
// Ensure we take any base::Feature overrides into account.
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
media::kHardwareSecureDecryptionFallback,
{{"min_disabling_days", "10"}, {"max_disabling_days", "60"}});
// One disabling event will cause the feature to be disabled for 30 days,
// which is the minimum disabling days.
TestEarliestEnableDate({0}, 10);
TestEarliestEnableDate({1}, 10);
TestEarliestEnableDate({10}, 10);
// Two close disabling events will cause the feature to be disabled for 60
// days, which is the maximum disabling days.
TestEarliestEnableDate({10, 10}, 60);
TestEarliestEnableDate({10, 20}, 60);
// The closer the two disabling events are, the longer the feature will be
// disabled.
TestEarliestEnableDate({10, 40}, 26);
TestEarliestEnableDate({10, 50}, 22);
TestEarliestEnableDate({10, 100}, 15);
// Two far apart disabling events will cause the feature to be disabled for 10
// days, which is the minimum disabling days.
TestEarliestEnableDate({10, 1000}, 10);
TestEarliestEnableDate({10, 10000}, 10);
// The third disabling event time doesn't matter.
TestEarliestEnableDate({10, 50}, 22);
TestEarliestEnableDate({1, 10, 50}, 22);
}
TEST_F(MediaFoundationServiceMonitorTest, CrashInSiteShouldDisable) {
content::ServiceProcessInfo crashed_info(
kMediaFoundationServiceProcessName, GURL(kSite1),
content::ServiceProcessId(), base::Process::Current());
// Creation of user profile happens after media foundation cdm is created. We
// need to force creating an entry to user profile first for testing.
InitProfileForSite(crashed_info.site().value());
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
// First crash still allows the site to play as first crash could be
// transient.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
// Second crash should disallow hardware secure decryption.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_FALSE(IsAllowedForSite(crashed_info.site().value()));
}
TEST_F(MediaFoundationServiceMonitorTest, CrashInSiteShouldNotAffectOtherSite) {
content::ServiceProcessInfo crashed_info(
kMediaFoundationServiceProcessName, GURL(kSite1),
content::ServiceProcessId(), base::Process::Current());
content::ServiceProcessInfo ok_info(kMediaFoundationServiceProcessName,
GURL(kSite2), content::ServiceProcessId(),
base::Process::Current());
// Creation of user profile happens after media foundation cdm is created. We
// need to force creating an entry to user profile first for testing.
InitProfileForSite(crashed_info.site().value());
InitProfileForSite(ok_info.site().value());
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
EXPECT_TRUE(IsAllowedForSite(ok_info.site().value()));
// First crash still allows the site to play as first crash could be
// transient.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
EXPECT_TRUE(IsAllowedForSite(ok_info.site().value()));
// Second crash should disallow hardware secure decryption.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_FALSE(IsAllowedForSite(crashed_info.site().value()));
EXPECT_TRUE(IsAllowedForSite(ok_info.site().value()));
}
TEST_F(MediaFoundationServiceMonitorTest, NewSiteShouldUseGlobalDisable) {
content::ServiceProcessInfo crashed_info(
kMediaFoundationServiceProcessName, GURL(kSite1),
content::ServiceProcessId(), base::Process::Current());
// Creation of user profile happens after media foundation cdm is created. We
// need to force creating an entry to user profile first for testing.
InitProfileForSite(crashed_info.site().value());
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
// First crash still allows the site to play as first crash could be
// transient.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
monitor_->OnServiceProcessCrashed(crashed_info);
// Second crash should disallow hardware secure decryption.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_FALSE(IsAllowedForSite(crashed_info.site().value()));
// New site should not be allowed to use hardware secure decryption.
content::ServiceProcessInfo new_info(
kMediaFoundationServiceProcessName, GURL(kSite2),
content::ServiceProcessId(), base::Process::Current());
EXPECT_FALSE(IsAllowedForSite(new_info.site().value()));
EXPECT_TRUE(MediaFoundationServiceMonitor::
IsHardwareSecureDecryptionDisabledByPref());
}
TEST_F(MediaFoundationServiceMonitorTest, CrashInSiteCausesGlobalDisable) {
content::ServiceProcessInfo crashed_info(
kMediaFoundationServiceProcessName, GURL(kSite1),
content::ServiceProcessId(), base::Process::Current());
// Creation of user profile happens after media foundation cdm is created. We
// need to force creating an entry to user profile first for testing.
InitProfileForSite(crashed_info.site().value());
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
// First crash still allows the site to play as first crash could be
// transient.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
// Second crash should disallow hardware secure decryption.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_FALSE(IsAllowedForSite(crashed_info.site().value()));
// Crash should disallow hardware secure decryption globally.
EXPECT_TRUE(MediaFoundationServiceMonitor::
IsHardwareSecureDecryptionDisabledByPref());
}
TEST_F(MediaFoundationServiceMonitorTest, CrashedSiteAffectAllSameOrigins) {
content::ServiceProcessInfo crashed_info(
kMediaFoundationServiceProcessName, GURL(kSite1),
content::ServiceProcessId(), base::Process::Current());
content::ServiceProcessInfo ok_info(kMediaFoundationServiceProcessName,
GURL(kSite2), content::ServiceProcessId(),
base::Process::Current());
content::ServiceProcessInfo same_origin_info(
kMediaFoundationServiceProcessName, GURL(kSite3),
content::ServiceProcessId(), base::Process::Current());
// Creation of user profile happens after media foundation cdm is created. We
// need to force creating an entry to user profile first for testing.
InitProfileForSite(crashed_info.site().value());
InitProfileForSite(ok_info.site().value());
InitProfileForSite(same_origin_info.site().value());
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
EXPECT_TRUE(IsAllowedForSite(ok_info.site().value()));
EXPECT_TRUE(IsAllowedForSite(same_origin_info.site().value()));
// First crash still allows the site to play as first crash could be
// transient.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_TRUE(IsAllowedForSite(crashed_info.site().value()));
// Second crash should disallow hardware secure decryption.
monitor_->OnServiceProcessCrashed(crashed_info);
EXPECT_FALSE(IsAllowedForSite(crashed_info.site().value()));
// Site with same origin should also be disabled.
EXPECT_FALSE(IsAllowedForSite(same_origin_info.site().value()));
// Site with different origin should be allowed.
EXPECT_TRUE(IsAllowedForSite(ok_info.site().value()));
}
|