| // 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/performance_manager/public/user_tuning/user_performance_tuning_manager.h" |
| |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/power_monitor/battery_state_sampler.h" |
| #include "base/power_monitor/power_monitor.h" |
| #include "base/power_monitor/power_observer.h" |
| #include "base/run_loop.h" |
| #include "base/values.h" |
| #include "chrome/browser/performance_manager/metrics/page_timeline_monitor.h" |
| #include "chrome/browser/performance_manager/policies/heuristic_memory_saver_policy.h" |
| #include "chrome/browser/performance_manager/policies/high_efficiency_mode_policy.h" |
| #include "chrome/browser/performance_manager/policies/page_discarding_helper.h" |
| #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h" |
| #include "components/performance_manager/public/features.h" |
| #include "components/performance_manager/public/performance_manager.h" |
| #include "components/performance_manager/public/user_tuning/prefs.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/frame_rate_throttling.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "chromeos/dbus/power/power_manager_client.h" |
| #endif |
| |
| using performance_manager::user_tuning::prefs::HighEfficiencyModeState; |
| using performance_manager::user_tuning::prefs::kHighEfficiencyModeState; |
| |
| namespace performance_manager::user_tuning { |
| namespace { |
| |
| UserPerformanceTuningManager* g_user_performance_tuning_manager = nullptr; |
| |
| constexpr base::TimeDelta kBatteryUsageWriteFrequency = base::Days(1); |
| |
| // On certain platforms (ChromeOS), the battery level displayed to the user is |
| // artificially lower than the actual battery level. Unfortunately, the battery |
| // level that Battery Saver Mode looks at is the "actual" level, so users on |
| // that platform may see Battery Saver Mode trigger at say 17% rather than the |
| // "advertised" 20%. This parameter allows us to heuristically tweak the |
| // threshold on those platforms, by being added to the 20% threshold value (so |
| // setting this parameter to 3 would result in battery saver being activated at |
| // 23% actual battery level). |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // On ChromeOS, the adjustment generally seems to be around 3%, sometimes 2%. We |
| // choose 3% because it gets us close enough, or overestimates (which is better |
| // than underestimating in this instance). |
| constexpr int kBatterySaverModeThresholdAdjustmentForDisplayLevel = 3; |
| #else |
| constexpr int kBatterySaverModeThresholdAdjustmentForDisplayLevel = 0; |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| class FrameThrottlingDelegateImpl |
| : public performance_manager::user_tuning::UserPerformanceTuningManager:: |
| FrameThrottlingDelegate { |
| public: |
| void StartThrottlingAllFrameSinks() override { |
| content::StartThrottlingAllFrameSinks(base::Hertz(30)); |
| NotifyPageTimelineMonitor(/*battery_saver_mode_enabled=*/true); |
| } |
| |
| void StopThrottlingAllFrameSinks() override { |
| content::StopThrottlingAllFrameSinks(); |
| NotifyPageTimelineMonitor(/*battery_saver_mode_enabled=*/false); |
| } |
| |
| ~FrameThrottlingDelegateImpl() override = default; |
| |
| private: |
| void NotifyPageTimelineMonitor(bool battery_saver_mode_enabled) { |
| performance_manager::PerformanceManager::CallOnGraph( |
| FROM_HERE, |
| base::BindOnce( |
| [](bool enabled, performance_manager::Graph* graph) { |
| auto* monitor = graph->GetRegisteredObjectAs< |
| performance_manager::metrics::PageTimelineMonitor>(); |
| // It's possible for this to be null if the PageTimeline finch |
| // feature is disabled. |
| if (monitor) { |
| monitor->SetBatterySaverEnabled(enabled); |
| } |
| }, |
| battery_saver_mode_enabled)); |
| } |
| }; |
| |
| class HighEfficiencyModeDelegateImpl |
| : public performance_manager::user_tuning::UserPerformanceTuningManager:: |
| HighEfficiencyModeDelegate { |
| public: |
| void ToggleHighEfficiencyMode(bool enabled) override { |
| performance_manager::PerformanceManager::CallOnGraph( |
| FROM_HERE, |
| base::BindOnce( |
| [](bool enabled, performance_manager::Graph* graph) { |
| if (base::FeatureList::IsEnabled( |
| performance_manager::features::kHeuristicMemorySaver)) { |
| CHECK(policies::HeuristicMemorySaverPolicy::GetInstance()); |
| policies::HeuristicMemorySaverPolicy::GetInstance()->SetActive( |
| enabled); |
| } else { |
| CHECK(policies::HighEfficiencyModePolicy::GetInstance()); |
| policies::HighEfficiencyModePolicy::GetInstance() |
| ->OnHighEfficiencyModeChanged(enabled); |
| } |
| }, |
| enabled)); |
| } |
| |
| void SetTimeBeforeDiscard(base::TimeDelta time_before_discard) override { |
| performance_manager::PerformanceManager::CallOnGraph( |
| FROM_HERE, |
| base::BindOnce( |
| [](base::TimeDelta time_before_discard, |
| performance_manager::Graph* graph) { |
| if (!base::FeatureList::IsEnabled( |
| performance_manager::features::kHeuristicMemorySaver)) { |
| CHECK(policies::HighEfficiencyModePolicy::GetInstance()); |
| policies::HighEfficiencyModePolicy::GetInstance() |
| ->SetTimeBeforeDiscard(time_before_discard); |
| } |
| }, |
| time_before_discard)); |
| } |
| |
| ~HighEfficiencyModeDelegateImpl() override = default; |
| }; |
| |
| } // namespace |
| |
| class DesktopBatterySaverProvider |
| : public UserPerformanceTuningManager::BatterySaverProvider, |
| public base::PowerStateObserver, |
| public base::BatteryStateSampler::Observer { |
| public: |
| DesktopBatterySaverProvider(UserPerformanceTuningManager* manager, |
| PrefService* local_state) |
| : manager_(manager) { |
| CHECK(manager_); |
| |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch( |
| UserPerformanceTuningManager::kForceDeviceHasBatterySwitch)) { |
| force_has_battery_ = true; |
| has_battery_ = true; |
| } |
| |
| pref_change_registrar_.Init(local_state); |
| |
| pref_change_registrar_.Add( |
| performance_manager::user_tuning::prefs::kBatterySaverModeState, |
| base::BindRepeating( |
| &DesktopBatterySaverProvider::OnBatterySaverModePrefChanged, |
| base::Unretained(this))); |
| |
| on_battery_power_ = |
| base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(this); |
| |
| base::BatteryStateSampler* battery_state_sampler = |
| base::BatteryStateSampler::Get(); |
| // Some platforms don't have a battery sampler, treat them as if they had no |
| // battery at all. |
| if (battery_state_sampler) { |
| battery_state_sampler_obs_.Observe(battery_state_sampler); |
| } |
| |
| OnBatterySaverModePrefChanged(); |
| } |
| |
| ~DesktopBatterySaverProvider() override { |
| base::PowerMonitor::RemovePowerStateObserver(this); |
| } |
| |
| // BatterySaverProvider: |
| bool DeviceHasBattery() const override { return has_battery_; } |
| bool IsBatterySaverActive() const override { |
| return battery_saver_mode_enabled_; |
| } |
| bool IsUsingBatteryPower() const override { return on_battery_power_; } |
| base::Time GetLastBatteryUsageTimestamp() const override { |
| return pref_change_registrar_.prefs()->GetTime( |
| performance_manager::user_tuning::prefs::kLastBatteryUseTimestamp); |
| } |
| int SampledBatteryPercentage() const override { return battery_percentage_; } |
| void SetTemporaryBatterySaverDisabledForSession(bool disabled) override { |
| // Setting the temporary mode to its current state is a no-op. |
| if (battery_saver_mode_disabled_for_session_ == disabled) { |
| return; |
| } |
| |
| battery_saver_mode_disabled_for_session_ = disabled; |
| UpdateBatterySaverModeState(); |
| } |
| |
| bool IsBatterySaverModeDisabledForSession() const override { |
| return battery_saver_mode_disabled_for_session_; |
| } |
| |
| private: |
| void OnBatterySaverModePrefChanged() { |
| battery_saver_mode_disabled_for_session_ = false; |
| UpdateBatterySaverModeState(); |
| } |
| |
| void UpdateBatterySaverModeState() { |
| using BatterySaverModeState = |
| performance_manager::user_tuning::prefs::BatterySaverModeState; |
| BatterySaverModeState state = performance_manager::user_tuning::prefs:: |
| GetCurrentBatterySaverModeState(pref_change_registrar_.prefs()); |
| |
| bool previously_enabled = battery_saver_mode_enabled_; |
| |
| battery_saver_mode_enabled_ = false; |
| |
| if (!battery_saver_mode_disabled_for_session_) { |
| switch (state) { |
| case BatterySaverModeState::kEnabled: |
| battery_saver_mode_enabled_ = true; |
| break; |
| case BatterySaverModeState::kEnabledOnBattery: |
| battery_saver_mode_enabled_ = on_battery_power_; |
| break; |
| case BatterySaverModeState::kEnabledBelowThreshold: |
| battery_saver_mode_enabled_ = |
| on_battery_power_ && is_below_low_battery_threshold_; |
| break; |
| default: |
| battery_saver_mode_enabled_ = false; |
| break; |
| } |
| } |
| |
| // Don't change throttling or notify observers if the mode didn't change. |
| if (previously_enabled == battery_saver_mode_enabled_) { |
| return; |
| } |
| |
| manager_->NotifyOnBatterySaverModeChanged(battery_saver_mode_enabled_); |
| } |
| |
| // base::PowerStateObserver: |
| void OnPowerStateChange(bool on_battery_power) override { |
| on_battery_power_ = on_battery_power; |
| |
| // Plugging in the device unsets the temporary disable BSM flag |
| if (!on_battery_power) { |
| battery_saver_mode_disabled_for_session_ = false; |
| } |
| |
| manager_->NotifyOnExternalPowerConnectedChanged(on_battery_power); |
| |
| UpdateBatterySaverModeState(); |
| } |
| |
| // base::BatteryStateSampler::Observer: |
| void OnBatteryStateSampled( |
| const absl::optional<base::BatteryLevelProvider::BatteryState>& |
| battery_state) override { |
| if (!battery_state) { |
| return; |
| } |
| |
| bool had_battery = has_battery_; |
| has_battery_ = force_has_battery_ || battery_state->battery_count > 0; |
| |
| // If the "has battery" state changed, notify observers. |
| if (had_battery != has_battery_) { |
| manager_->NotifyOnDeviceHasBatteryChanged(has_battery_); |
| } |
| |
| // Log the unplugged battery usage to local pref if the previous value is |
| // more than a day old. |
| if (has_battery_ && !battery_state->is_external_power_connected && |
| (base::Time::Now() - GetLastBatteryUsageTimestamp() > |
| kBatteryUsageWriteFrequency)) { |
| pref_change_registrar_.prefs()->SetTime( |
| performance_manager::user_tuning::prefs::kLastBatteryUseTimestamp, |
| base::Time::Now()); |
| } |
| |
| if (!battery_state->current_capacity || |
| !battery_state->full_charged_capacity) { |
| // This should only happen if there are no batteries connected, or |
| // multiple batteries connected (in which case their units may not match |
| // so they don't report a charge). We're not under the threshold for any |
| // battery. |
| DCHECK_NE(1, battery_state->battery_count); |
| |
| is_below_low_battery_threshold_ = false; |
| return; |
| } |
| |
| battery_percentage_ = *(battery_state->full_charged_capacity) > 0 |
| ? *(battery_state->current_capacity) * 100 / |
| *(battery_state->full_charged_capacity) |
| : 100; |
| |
| bool was_below_threshold = is_below_low_battery_threshold_; |
| |
| // A battery is below the threshold if it's under 20% charge. On some |
| // platforms, we adjust the threshold by a value specified in Finch to |
| // account for the displayed battery level being artificially lower than the |
| // actual level. See |
| // `power_manager::BatteryPercentageConverter::ConvertActualToDisplay`. |
| uint64_t adjusted_low_battery_threshold = |
| UserPerformanceTuningManager::kLowBatteryThresholdPercent + |
| kBatterySaverModeThresholdAdjustmentForDisplayLevel; |
| is_below_low_battery_threshold_ = |
| battery_percentage_ < static_cast<int>(adjusted_low_battery_threshold); |
| |
| if (is_below_low_battery_threshold_ && !was_below_threshold) { |
| manager_->NotifyOnBatteryThresholdReached(); |
| } |
| |
| UpdateBatterySaverModeState(); |
| } |
| |
| bool battery_saver_mode_enabled_ = false; |
| bool battery_saver_mode_disabled_for_session_ = false; |
| |
| bool has_battery_ = false; |
| bool force_has_battery_ = false; |
| bool on_battery_power_ = false; |
| bool is_below_low_battery_threshold_ = false; |
| int battery_percentage_ = -1; |
| |
| base::ScopedObservation<base::BatteryStateSampler, |
| base::BatteryStateSampler::Observer> |
| battery_state_sampler_obs_{this}; |
| PrefChangeRegistrar pref_change_registrar_; |
| |
| raw_ptr<UserPerformanceTuningManager> manager_; |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| class ChromeOSBatterySaverProvider |
| : public UserPerformanceTuningManager::BatterySaverProvider, |
| public chromeos::PowerManagerClient::Observer { |
| public: |
| explicit ChromeOSBatterySaverProvider(UserPerformanceTuningManager* manager) |
| : manager_(manager) { |
| CHECK(manager_); |
| |
| chromeos::PowerManagerClient* client = chromeos::PowerManagerClient::Get(); |
| CHECK(client); |
| |
| power_manager_client_observer_.Observe(client); |
| client->GetBatterySaverModeState(base::BindOnce( |
| &ChromeOSBatterySaverProvider::OnInitialBatterySaverModeObtained, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| ~ChromeOSBatterySaverProvider() override = default; |
| |
| void OnInitialBatterySaverModeObtained( |
| absl::optional<power_manager::BatterySaverModeState> state) { |
| if (state) { |
| BatterySaverModeStateChanged(*state); |
| } |
| } |
| |
| // chromeos::PowerManagerClient::Observer: |
| void BatterySaverModeStateChanged( |
| const power_manager::BatterySaverModeState& state) override { |
| if (!state.has_enabled() || enabled_ == state.enabled()) { |
| return; |
| } |
| |
| enabled_ = state.enabled(); |
| |
| manager_->NotifyOnBatterySaverModeChanged(enabled_); |
| } |
| |
| // BatterySaverProvider: |
| bool DeviceHasBattery() const override { return false; } |
| bool IsBatterySaverActive() const override { return enabled_; } |
| bool IsUsingBatteryPower() const override { return false; } |
| base::Time GetLastBatteryUsageTimestamp() const override { |
| return base::Time(); |
| } |
| int SampledBatteryPercentage() const override { return -1; } |
| void SetTemporaryBatterySaverDisabledForSession(bool disabled) override { |
| NOTREACHED(); |
| // No-op when BSM is controlled by the OS |
| } |
| bool IsBatterySaverModeDisabledForSession() const override { return false; } |
| |
| private: |
| bool enabled_ = false; |
| |
| base::ScopedObservation<chromeos::PowerManagerClient, |
| chromeos::PowerManagerClient::Observer> |
| power_manager_client_observer_{this}; |
| |
| raw_ptr<UserPerformanceTuningManager> manager_; |
| |
| base::WeakPtrFactory<ChromeOSBatterySaverProvider> weak_ptr_factory_{this}; |
| }; |
| #endif |
| |
| const uint64_t UserPerformanceTuningManager::kLowBatteryThresholdPercent = 20; |
| |
| const char UserPerformanceTuningManager::kTimeBeforeDiscardInMinutesSwitch[] = |
| "time-before-discard-in-minutes"; |
| |
| const char UserPerformanceTuningManager::kForceDeviceHasBatterySwitch[] = |
| "force-device-has-battery"; |
| |
| WEB_CONTENTS_USER_DATA_KEY_IMPL( |
| UserPerformanceTuningManager::PreDiscardResourceUsage); |
| |
| UserPerformanceTuningManager::PreDiscardResourceUsage::PreDiscardResourceUsage( |
| content::WebContents* contents, |
| uint64_t memory_footprint_estimate, |
| ::mojom::LifecycleUnitDiscardReason discard_reason) |
| : content::WebContentsUserData<PreDiscardResourceUsage>(*contents), |
| memory_footprint_estimate_(memory_footprint_estimate), |
| discard_reason_(discard_reason), |
| discard_liveticks_(base::LiveTicks::Now()) {} |
| |
| UserPerformanceTuningManager::PreDiscardResourceUsage:: |
| ~PreDiscardResourceUsage() = default; |
| |
| // static |
| bool UserPerformanceTuningManager::HasInstance() { |
| return g_user_performance_tuning_manager; |
| } |
| |
| // static |
| UserPerformanceTuningManager* UserPerformanceTuningManager::GetInstance() { |
| DCHECK(g_user_performance_tuning_manager); |
| return g_user_performance_tuning_manager; |
| } |
| |
| UserPerformanceTuningManager::~UserPerformanceTuningManager() { |
| DCHECK_EQ(this, g_user_performance_tuning_manager); |
| g_user_performance_tuning_manager = nullptr; |
| } |
| |
| void UserPerformanceTuningManager::AddObserver(Observer* o) { |
| observers_.AddObserver(o); |
| } |
| |
| void UserPerformanceTuningManager::RemoveObserver(Observer* o) { |
| observers_.RemoveObserver(o); |
| } |
| |
| bool UserPerformanceTuningManager::DeviceHasBattery() const { |
| return battery_saver_provider_ && battery_saver_provider_->DeviceHasBattery(); |
| } |
| |
| void UserPerformanceTuningManager::SetTemporaryBatterySaverDisabledForSession( |
| bool disabled) { |
| CHECK(battery_saver_provider_); |
| battery_saver_provider_->SetTemporaryBatterySaverDisabledForSession(disabled); |
| } |
| |
| bool UserPerformanceTuningManager::IsBatterySaverModeDisabledForSession() |
| const { |
| return battery_saver_provider_ && |
| battery_saver_provider_->IsBatterySaverModeDisabledForSession(); |
| } |
| |
| bool UserPerformanceTuningManager::IsHighEfficiencyModeActive() { |
| HighEfficiencyModeState state = performance_manager::user_tuning::prefs:: |
| GetCurrentHighEfficiencyModeState(pref_change_registrar_.prefs()); |
| return state != HighEfficiencyModeState::kDisabled; |
| } |
| |
| bool UserPerformanceTuningManager::IsHighEfficiencyModeManaged() const { |
| auto* pref = |
| pref_change_registrar_.prefs()->FindPreference(kHighEfficiencyModeState); |
| return pref->IsManaged(); |
| } |
| |
| bool UserPerformanceTuningManager::IsHighEfficiencyModeDefault() const { |
| auto* pref = |
| pref_change_registrar_.prefs()->FindPreference(kHighEfficiencyModeState); |
| return pref->IsDefaultValue(); |
| } |
| |
| void UserPerformanceTuningManager::SetHighEfficiencyModeEnabled(bool enabled) { |
| HighEfficiencyModeState state = enabled |
| ? HighEfficiencyModeState::kEnabledOnTimer |
| : HighEfficiencyModeState::kDisabled; |
| pref_change_registrar_.prefs()->SetInteger(kHighEfficiencyModeState, |
| static_cast<int>(state)); |
| } |
| |
| bool UserPerformanceTuningManager::IsBatterySaverActive() const { |
| return battery_saver_provider_ && |
| battery_saver_provider_->IsBatterySaverActive(); |
| } |
| |
| bool UserPerformanceTuningManager::IsUsingBatteryPower() const { |
| return battery_saver_provider_ && |
| battery_saver_provider_->IsUsingBatteryPower(); |
| } |
| |
| base::Time UserPerformanceTuningManager::GetLastBatteryUsageTimestamp() const { |
| return battery_saver_provider_ |
| ? battery_saver_provider_->GetLastBatteryUsageTimestamp() |
| : base::Time(); |
| } |
| |
| int UserPerformanceTuningManager::SampledBatteryPercentage() const { |
| return battery_saver_provider_ |
| ? battery_saver_provider_->SampledBatteryPercentage() |
| : -1; |
| } |
| |
| // static |
| void UserPerformanceTuningManager::SetDefaultTimeBeforeDiscardFromSwitch( |
| PrefService* local_state) { |
| // TODO(https://crbug.com/1424220): remove this function after multistate |
| // memory saver UI is available as the discard time pref will be configurable |
| // by the user |
| const PrefService::Preference* time_before_discard_pref = |
| local_state->FindPreference( |
| performance_manager::user_tuning::prefs:: |
| kHighEfficiencyModeTimeBeforeDiscardInMinutes); |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (time_before_discard_pref->IsDefaultValue() && |
| command_line->HasSwitch(kTimeBeforeDiscardInMinutesSwitch)) { |
| int time_before_discard_in_minutes; |
| std::string time_before_discard_in_minutes_string = |
| command_line->GetSwitchValueASCII(kTimeBeforeDiscardInMinutesSwitch); |
| if (base::StringToInt(time_before_discard_in_minutes_string, |
| &time_before_discard_in_minutes) && |
| time_before_discard_in_minutes > 0) { |
| local_state->SetDefaultPrefValue( |
| performance_manager::user_tuning::prefs:: |
| kHighEfficiencyModeTimeBeforeDiscardInMinutes, |
| base::Value(time_before_discard_in_minutes)); |
| } |
| } |
| } |
| |
| UserPerformanceTuningManager::UserPerformanceTuningReceiverImpl:: |
| ~UserPerformanceTuningReceiverImpl() = default; |
| |
| void UserPerformanceTuningManager::UserPerformanceTuningReceiverImpl:: |
| NotifyTabCountThresholdReached() { |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce([]() { |
| // Hitting this CHECK would mean this task is running after |
| // PostMainMessageLoopRun, which shouldn't happen. |
| CHECK(g_user_performance_tuning_manager); |
| GetInstance()->NotifyTabCountThresholdReached(); |
| })); |
| } |
| |
| void UserPerformanceTuningManager::UserPerformanceTuningReceiverImpl:: |
| NotifyMemoryThresholdReached() { |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce([]() { |
| // Hitting this CHECK would mean this task is running after |
| // PostMainMessageLoopRun, which shouldn't happen. |
| CHECK(g_user_performance_tuning_manager); |
| GetInstance()->NotifyMemoryThresholdReached(); |
| })); |
| } |
| |
| void UserPerformanceTuningManager::UserPerformanceTuningReceiverImpl:: |
| NotifyMemoryMetricsRefreshed() { |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce([]() { |
| // Hitting this CHECK would mean this task is running after |
| // PostMainMessageLoopRun, which shouldn't happen. |
| CHECK(g_user_performance_tuning_manager); |
| GetInstance()->NotifyMemoryMetricsRefreshed(); |
| })); |
| } |
| |
| void UserPerformanceTuningManager::NotifyOnBatterySaverModeChanged( |
| bool battery_saver_mode_enabled) { |
| if (battery_saver_mode_enabled) { |
| frame_throttling_delegate_->StartThrottlingAllFrameSinks(); |
| } else { |
| frame_throttling_delegate_->StopThrottlingAllFrameSinks(); |
| } |
| |
| for (auto& obs : observers_) { |
| obs.OnBatterySaverModeChanged(battery_saver_mode_enabled); |
| } |
| } |
| void UserPerformanceTuningManager::NotifyOnExternalPowerConnectedChanged( |
| bool on_battery_power) { |
| for (auto& obs : observers_) { |
| obs.OnExternalPowerConnectedChanged(on_battery_power); |
| } |
| } |
| |
| void UserPerformanceTuningManager::NotifyOnDeviceHasBatteryChanged( |
| bool has_battery) { |
| for (auto& obs : observers_) { |
| obs.OnDeviceHasBatteryChanged(has_battery); |
| } |
| } |
| void UserPerformanceTuningManager::NotifyOnBatteryThresholdReached() { |
| for (auto& obs : observers_) { |
| obs.OnBatteryThresholdReached(); |
| } |
| } |
| |
| UserPerformanceTuningManager::UserPerformanceTuningManager( |
| PrefService* local_state, |
| std::unique_ptr<UserPerformanceTuningNotifier> notifier, |
| std::unique_ptr<FrameThrottlingDelegate> frame_throttling_delegate, |
| std::unique_ptr<HighEfficiencyModeDelegate> high_efficiency_mode_delegate) |
| : frame_throttling_delegate_( |
| frame_throttling_delegate |
| ? std::move(frame_throttling_delegate) |
| : std::make_unique<FrameThrottlingDelegateImpl>()), |
| high_efficiency_mode_delegate_( |
| high_efficiency_mode_delegate |
| ? std::move(high_efficiency_mode_delegate) |
| : std::make_unique<HighEfficiencyModeDelegateImpl>()) { |
| DCHECK(!g_user_performance_tuning_manager); |
| g_user_performance_tuning_manager = this; |
| |
| if (notifier) { |
| performance_manager::PerformanceManager::PassToGraph(FROM_HERE, |
| std::move(notifier)); |
| } |
| |
| performance_manager::user_tuning::prefs::MigrateHighEfficiencyModePref( |
| local_state); |
| |
| SetDefaultTimeBeforeDiscardFromSwitch(local_state); |
| |
| pref_change_registrar_.Init(local_state); |
| } |
| |
| void UserPerformanceTuningManager::Start() { |
| pref_change_registrar_.Add( |
| performance_manager::user_tuning::prefs:: |
| kHighEfficiencyModeTimeBeforeDiscardInMinutes, |
| base::BindRepeating(&UserPerformanceTuningManager:: |
| OnHighEfficiencyModeTimeBeforeDiscardChanged, |
| base::Unretained(this))); |
| // Make sure the initial state of the discard timer pref is passed on to the |
| // policy before it can be enabled, because the policy initially has a dummy |
| // value for time_before_discard_. This prevents tabs' discard timers from |
| // starting with a value different from the pref. |
| OnHighEfficiencyModeTimeBeforeDiscardChanged(); |
| |
| pref_change_registrar_.Add( |
| kHighEfficiencyModeState, |
| base::BindRepeating( |
| &UserPerformanceTuningManager::OnHighEfficiencyModePrefChanged, |
| base::Unretained(this))); |
| // Make sure the initial state of the pref is passed on to the policy. |
| OnHighEfficiencyModePrefChanged(); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| if (base::FeatureList::IsEnabled(features::kUseDeviceBatterySaverChromeOS)) { |
| battery_saver_provider_ = |
| std::make_unique<ChromeOSBatterySaverProvider>(this); |
| } else { |
| battery_saver_provider_ = std::make_unique<DesktopBatterySaverProvider>( |
| this, pref_change_registrar_.prefs()); |
| } |
| #else |
| battery_saver_provider_ = std::make_unique<DesktopBatterySaverProvider>( |
| this, pref_change_registrar_.prefs()); |
| #endif |
| } |
| |
| void UserPerformanceTuningManager::OnHighEfficiencyModePrefChanged() { |
| high_efficiency_mode_delegate_->ToggleHighEfficiencyMode( |
| IsHighEfficiencyModeActive()); |
| |
| for (auto& obs : observers_) { |
| obs.OnHighEfficiencyModeChanged(); |
| } |
| } |
| |
| void UserPerformanceTuningManager:: |
| OnHighEfficiencyModeTimeBeforeDiscardChanged() { |
| base::TimeDelta time_before_discard = performance_manager::user_tuning:: |
| prefs::GetCurrentHighEfficiencyModeTimeBeforeDiscard( |
| pref_change_registrar_.prefs()); |
| high_efficiency_mode_delegate_->SetTimeBeforeDiscard(time_before_discard); |
| } |
| |
| void UserPerformanceTuningManager::NotifyTabCountThresholdReached() { |
| for (auto& obs : observers_) { |
| obs.OnTabCountThresholdReached(); |
| } |
| } |
| |
| void UserPerformanceTuningManager::NotifyMemoryThresholdReached() { |
| for (auto& obs : observers_) { |
| obs.OnMemoryThresholdReached(); |
| } |
| } |
| |
| void UserPerformanceTuningManager::NotifyMemoryMetricsRefreshed() { |
| for (auto& obs : observers_) { |
| obs.OnMemoryMetricsRefreshed(); |
| } |
| } |
| |
| void UserPerformanceTuningManager::DiscardPageForTesting( |
| content::WebContents* web_contents) { |
| base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); |
| performance_manager::PerformanceManager::CallOnGraph( |
| FROM_HERE, |
| base::BindOnce( |
| [](base::RepeatingClosure quit_closure, |
| base::WeakPtr<performance_manager::PageNode> page_node, |
| performance_manager::Graph* graph) { |
| if (page_node) { |
| performance_manager::policies::PageDiscardingHelper::GetFromGraph( |
| graph) |
| ->ImmediatelyDiscardSpecificPage( |
| page_node.get(), |
| ::mojom::LifecycleUnitDiscardReason::PROACTIVE); |
| quit_closure.Run(); |
| } |
| }, |
| run_loop.QuitClosure(), |
| performance_manager::PerformanceManager:: |
| GetPrimaryPageNodeForWebContents(web_contents))); |
| run_loop.Run(); |
| } |
| |
| } // namespace performance_manager::user_tuning |