| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/dbus/power_policy_controller.h" |
| |
| #include <stdint.h> |
| |
| #include <utility> |
| |
| #include "base/format_macros.h" |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| |
| // Avoid some ugly line-wrapping later. |
| using base::StringAppendF; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| PowerPolicyController* g_power_policy_controller = nullptr; |
| |
| // Appends a description of |field|, a field within |delays|, a |
| // power_manager::PowerManagementPolicy::Delays object, to |str|, an |
| // std::string, if the field is set. |name| is a char* describing the |
| // field. |
| #define APPEND_DELAY(str, delays, field, name) \ |
| { \ |
| if (delays.has_##field()) \ |
| StringAppendF(&str, name "=%" PRId64 " ", delays.field()); \ |
| } |
| |
| // Appends descriptions of all of the set delays in |delays|, a |
| // power_manager::PowerManagementPolicy::Delays object, to |str|, an |
| // std::string. |prefix| should be a char* containing either "ac" or |
| // "battery". |
| #define APPEND_DELAYS(str, delays, prefix) \ |
| { \ |
| APPEND_DELAY(str, delays, screen_dim_ms, prefix "_screen_dim_ms"); \ |
| APPEND_DELAY(str, delays, screen_off_ms, prefix "_screen_off_ms"); \ |
| APPEND_DELAY(str, delays, screen_lock_ms, prefix "_screen_lock_ms"); \ |
| APPEND_DELAY(str, delays, idle_warning_ms, prefix "_idle_warning_ms"); \ |
| APPEND_DELAY(str, delays, idle_ms, prefix "_idle_ms"); \ |
| } |
| |
| // Returns the power_manager::PowerManagementPolicy_Action value |
| // corresponding to |action|. |
| power_manager::PowerManagementPolicy_Action GetProtoAction( |
| PowerPolicyController::Action action) { |
| switch (action) { |
| case PowerPolicyController::ACTION_SUSPEND: |
| return power_manager::PowerManagementPolicy_Action_SUSPEND; |
| case PowerPolicyController::ACTION_STOP_SESSION: |
| return power_manager::PowerManagementPolicy_Action_STOP_SESSION; |
| case PowerPolicyController::ACTION_SHUT_DOWN: |
| return power_manager::PowerManagementPolicy_Action_SHUT_DOWN; |
| case PowerPolicyController::ACTION_DO_NOTHING: |
| return power_manager::PowerManagementPolicy_Action_DO_NOTHING; |
| default: |
| NOTREACHED() << "Unhandled action " << action; |
| return power_manager::PowerManagementPolicy_Action_DO_NOTHING; |
| } |
| } |
| |
| // Returns false if |use_audio_activity| and |use_audio_activity| prevent wake |
| // locks created for |reason| from being honored or true otherwise. |
| bool IsWakeLockReasonHonored(PowerPolicyController::WakeLockReason reason, |
| bool use_audio_activity, |
| bool use_video_activity) { |
| if (reason == PowerPolicyController::REASON_AUDIO_PLAYBACK && |
| !use_audio_activity) |
| return false; |
| if (reason == PowerPolicyController::REASON_VIDEO_PLAYBACK && |
| !use_video_activity) |
| return false; |
| return true; |
| } |
| |
| } // namespace |
| |
| const int PowerPolicyController::kScreenLockAfterOffDelayMs = 10000; // 10 sec. |
| const char PowerPolicyController::kPrefsReason[] = "Prefs"; |
| |
| // -1 is interpreted as "unset" by powerd, resulting in powerd's default |
| // delays being used instead. There are no similarly-interpreted values |
| // for the other fields, unfortunately (but the constructor-assigned values |
| // will only reach powerd if Chrome messes up and forgets to override them |
| // with the pref-assigned values). |
| PowerPolicyController::PrefValues::PrefValues() |
| : ac_screen_dim_delay_ms(-1), |
| ac_screen_off_delay_ms(-1), |
| ac_screen_lock_delay_ms(-1), |
| ac_idle_warning_delay_ms(-1), |
| ac_idle_delay_ms(-1), |
| battery_screen_dim_delay_ms(-1), |
| battery_screen_off_delay_ms(-1), |
| battery_screen_lock_delay_ms(-1), |
| battery_idle_warning_delay_ms(-1), |
| battery_idle_delay_ms(-1), |
| ac_idle_action(ACTION_SUSPEND), |
| battery_idle_action(ACTION_SUSPEND), |
| lid_closed_action(ACTION_SUSPEND), |
| use_audio_activity(true), |
| use_video_activity(true), |
| ac_brightness_percent(-1.0), |
| battery_brightness_percent(-1.0), |
| allow_screen_wake_locks(true), |
| enable_auto_screen_lock(false), |
| presentation_screen_dim_delay_factor(1.0), |
| user_activity_screen_dim_delay_factor(1.0), |
| wait_for_initial_user_activity(false), |
| force_nonzero_brightness_for_user_activity(true) {} |
| |
| // static |
| std::string PowerPolicyController::GetPolicyDebugString( |
| const power_manager::PowerManagementPolicy& policy) { |
| std::string str; |
| if (policy.has_ac_delays()) |
| APPEND_DELAYS(str, policy.ac_delays(), "ac"); |
| if (policy.has_battery_delays()) |
| APPEND_DELAYS(str, policy.battery_delays(), "battery"); |
| if (policy.has_ac_idle_action()) |
| StringAppendF(&str, "ac_idle=%d ", policy.ac_idle_action()); |
| if (policy.has_battery_idle_action()) |
| StringAppendF(&str, "battery_idle=%d ", policy.battery_idle_action()); |
| if (policy.has_lid_closed_action()) |
| StringAppendF(&str, "lid_closed=%d ", policy.lid_closed_action()); |
| if (policy.has_screen_wake_lock()) |
| StringAppendF(&str, "screen_wake_lock=%d ", policy.screen_wake_lock()); |
| if (policy.has_dim_wake_lock()) |
| StringAppendF(&str, "dim_wake_lock=%d ", policy.dim_wake_lock()); |
| if (policy.has_system_wake_lock()) |
| StringAppendF(&str, "system_wake_lock=%d ", policy.system_wake_lock()); |
| if (policy.has_use_audio_activity()) |
| StringAppendF(&str, "use_audio=%d ", policy.use_audio_activity()); |
| if (policy.has_use_video_activity()) |
| StringAppendF(&str, "use_video=%d ", policy.use_audio_activity()); |
| if (policy.has_ac_brightness_percent()) |
| StringAppendF(&str, "ac_brightness_percent=%f ", |
| policy.ac_brightness_percent()); |
| if (policy.has_battery_brightness_percent()) { |
| StringAppendF(&str, "battery_brightness_percent=%f ", |
| policy.battery_brightness_percent()); |
| } |
| if (policy.has_presentation_screen_dim_delay_factor()) { |
| StringAppendF(&str, "presentation_screen_dim_delay_factor=%f ", |
| policy.presentation_screen_dim_delay_factor()); |
| } |
| if (policy.has_user_activity_screen_dim_delay_factor()) { |
| StringAppendF(&str, "user_activity_screen_dim_delay_factor=%f ", |
| policy.user_activity_screen_dim_delay_factor()); |
| } |
| if (policy.has_wait_for_initial_user_activity()) { |
| StringAppendF(&str, "wait_for_initial_user_activity=%d ", |
| policy.wait_for_initial_user_activity()); |
| } |
| if (policy.has_force_nonzero_brightness_for_user_activity()) { |
| StringAppendF(&str, "force_nonzero_brightness_for_user_activity=%d ", |
| policy.force_nonzero_brightness_for_user_activity()); |
| } |
| if (policy.has_reason()) |
| StringAppendF(&str, "reason=\"%s\" ", policy.reason().c_str()); |
| base::TrimWhitespaceASCII(str, base::TRIM_TRAILING, &str); |
| return str; |
| } |
| |
| // static |
| void PowerPolicyController::Initialize(PowerManagerClient* client) { |
| DCHECK(!IsInitialized()); |
| g_power_policy_controller = new PowerPolicyController(client); |
| } |
| |
| // static |
| bool PowerPolicyController::IsInitialized() { |
| return g_power_policy_controller; |
| } |
| |
| // static |
| void PowerPolicyController::Shutdown() { |
| DCHECK(IsInitialized()); |
| delete g_power_policy_controller; |
| g_power_policy_controller = nullptr; |
| } |
| |
| // static |
| PowerPolicyController* PowerPolicyController::Get() { |
| DCHECK(IsInitialized()); |
| return g_power_policy_controller; |
| } |
| |
| void PowerPolicyController::ApplyPrefs(const PrefValues& values) { |
| prefs_policy_.Clear(); |
| |
| power_manager::PowerManagementPolicy::Delays* delays = |
| prefs_policy_.mutable_ac_delays(); |
| delays->set_screen_dim_ms(values.ac_screen_dim_delay_ms); |
| delays->set_screen_off_ms(values.ac_screen_off_delay_ms); |
| delays->set_screen_lock_ms(values.ac_screen_lock_delay_ms); |
| delays->set_idle_warning_ms(values.ac_idle_warning_delay_ms); |
| delays->set_idle_ms(values.ac_idle_delay_ms); |
| |
| // If auto screen-locking is enabled, ensure that the screen is locked soon |
| // after it's turned off due to user inactivity. |
| int64_t lock_ms = delays->screen_off_ms() + kScreenLockAfterOffDelayMs; |
| if (values.enable_auto_screen_lock && delays->screen_off_ms() > 0 && |
| (delays->screen_lock_ms() <= 0 || lock_ms < delays->screen_lock_ms()) && |
| lock_ms < delays->idle_ms()) { |
| delays->set_screen_lock_ms(lock_ms); |
| } |
| |
| delays = prefs_policy_.mutable_battery_delays(); |
| delays->set_screen_dim_ms(values.battery_screen_dim_delay_ms); |
| delays->set_screen_off_ms(values.battery_screen_off_delay_ms); |
| delays->set_screen_lock_ms(values.battery_screen_lock_delay_ms); |
| delays->set_idle_warning_ms(values.battery_idle_warning_delay_ms); |
| delays->set_idle_ms(values.battery_idle_delay_ms); |
| |
| lock_ms = delays->screen_off_ms() + kScreenLockAfterOffDelayMs; |
| if (values.enable_auto_screen_lock && delays->screen_off_ms() > 0 && |
| (delays->screen_lock_ms() <= 0 || lock_ms < delays->screen_lock_ms()) && |
| lock_ms < delays->idle_ms()) { |
| delays->set_screen_lock_ms(lock_ms); |
| } |
| |
| prefs_policy_.set_ac_idle_action(GetProtoAction(values.ac_idle_action)); |
| prefs_policy_.set_battery_idle_action( |
| GetProtoAction(values.battery_idle_action)); |
| prefs_policy_.set_lid_closed_action(GetProtoAction(values.lid_closed_action)); |
| prefs_policy_.set_use_audio_activity(values.use_audio_activity); |
| prefs_policy_.set_use_video_activity(values.use_video_activity); |
| if (values.ac_brightness_percent >= 0.0) |
| prefs_policy_.set_ac_brightness_percent(values.ac_brightness_percent); |
| if (values.battery_brightness_percent >= 0.0) { |
| prefs_policy_.set_battery_brightness_percent( |
| values.battery_brightness_percent); |
| } |
| prefs_policy_.set_presentation_screen_dim_delay_factor( |
| values.presentation_screen_dim_delay_factor); |
| prefs_policy_.set_user_activity_screen_dim_delay_factor( |
| values.user_activity_screen_dim_delay_factor); |
| prefs_policy_.set_wait_for_initial_user_activity( |
| values.wait_for_initial_user_activity); |
| prefs_policy_.set_force_nonzero_brightness_for_user_activity( |
| values.force_nonzero_brightness_for_user_activity); |
| |
| honor_screen_wake_locks_ = values.allow_screen_wake_locks; |
| |
| prefs_were_set_ = true; |
| SendCurrentPolicy(); |
| } |
| |
| int PowerPolicyController::AddScreenWakeLock(WakeLockReason reason, |
| const std::string& description) { |
| return AddWakeLockInternal(WakeLock::TYPE_SCREEN, reason, description); |
| } |
| |
| int PowerPolicyController::AddDimWakeLock(WakeLockReason reason, |
| const std::string& description) { |
| return AddWakeLockInternal(WakeLock::TYPE_DIM, reason, description); |
| } |
| |
| int PowerPolicyController::AddSystemWakeLock(WakeLockReason reason, |
| const std::string& description) { |
| return AddWakeLockInternal(WakeLock::TYPE_SYSTEM, reason, description); |
| } |
| |
| void PowerPolicyController::RemoveWakeLock(int id) { |
| if (!wake_locks_.erase(id)) |
| LOG(WARNING) << "Ignoring request to remove nonexistent wake lock " << id; |
| else |
| SendCurrentPolicy(); |
| } |
| |
| void PowerPolicyController::PowerManagerRestarted() { |
| SendCurrentPolicy(); |
| } |
| |
| void PowerPolicyController::NotifyChromeIsExiting() { |
| if (chrome_is_exiting_) |
| return; |
| chrome_is_exiting_ = true; |
| SendCurrentPolicy(); |
| } |
| |
| void PowerPolicyController::SetEncryptionMigrationActive(bool active) { |
| if (encryption_migration_active_ == active) |
| return; |
| |
| encryption_migration_active_ = active; |
| SendCurrentPolicy(); |
| } |
| |
| PowerPolicyController::PowerPolicyController(PowerManagerClient* client) |
| : client_(client), |
| prefs_were_set_(false), |
| honor_screen_wake_locks_(true), |
| next_wake_lock_id_(1), |
| chrome_is_exiting_(false), |
| encryption_migration_active_(false) { |
| DCHECK(client_); |
| client_->AddObserver(this); |
| } |
| |
| PowerPolicyController::~PowerPolicyController() { |
| client_->RemoveObserver(this); |
| } |
| |
| PowerPolicyController::WakeLock::WakeLock(Type type, |
| WakeLockReason reason, |
| const std::string& description) |
| : type(type), reason(reason), description(description) { |
| } |
| |
| PowerPolicyController::WakeLock::~WakeLock() = default; |
| |
| int PowerPolicyController::AddWakeLockInternal(WakeLock::Type type, |
| WakeLockReason reason, |
| const std::string& description) { |
| const int id = next_wake_lock_id_++; |
| wake_locks_.insert(std::make_pair(id, WakeLock(type, reason, description))); |
| SendCurrentPolicy(); |
| return id; |
| } |
| |
| void PowerPolicyController::SendCurrentPolicy() { |
| std::string causes; |
| |
| power_manager::PowerManagementPolicy policy = prefs_policy_; |
| if (prefs_were_set_) |
| causes = kPrefsReason; |
| |
| bool have_screen_wake_locks = false; |
| bool have_dim_wake_locks = false; |
| bool have_system_wake_locks = false; |
| for (const auto& it : wake_locks_) { |
| // Skip audio and video locks that should be ignored due to policy. |
| if (!IsWakeLockReasonHonored(it.second.reason, policy.use_audio_activity(), |
| policy.use_video_activity())) |
| continue; |
| |
| switch (it.second.type) { |
| case WakeLock::TYPE_SCREEN: |
| have_screen_wake_locks = true; |
| break; |
| case WakeLock::TYPE_DIM: |
| have_dim_wake_locks = true; |
| break; |
| case WakeLock::TYPE_SYSTEM: |
| have_system_wake_locks = true; |
| break; |
| } |
| causes += (causes.empty() ? "" : ", ") + it.second.description; |
| } |
| |
| // Downgrade full-brightness and dimmed-brightness locks to system locks if |
| // wake locks aren't allowed to keep the screen on. |
| if (!honor_screen_wake_locks_ && |
| (have_screen_wake_locks || have_dim_wake_locks)) { |
| have_system_wake_locks = true; |
| have_screen_wake_locks = false; |
| have_dim_wake_locks = false; |
| } |
| |
| if (have_screen_wake_locks) |
| policy.set_screen_wake_lock(true); |
| if (have_dim_wake_locks) |
| policy.set_dim_wake_lock(true); |
| if (have_system_wake_locks) |
| policy.set_system_wake_lock(true); |
| |
| if (encryption_migration_active_ && |
| policy.lid_closed_action() != |
| power_manager::PowerManagementPolicy_Action_DO_NOTHING) { |
| policy.set_lid_closed_action( |
| power_manager::PowerManagementPolicy_Action_SUSPEND); |
| causes += |
| std::string((causes.empty() ? "" : ", ")) + "encryption migration"; |
| } |
| |
| // To avoid a race in the case where the user asks Chrome to sign out |
| // and then immediately closes the lid, override the lid-closed action |
| // so the system will stay awake while Chrome is exiting. When Chrome |
| // restarts to display the login screen, it will send an updated |
| // policy that powerd can act on. |
| if (chrome_is_exiting_ && |
| (!policy.has_lid_closed_action() || |
| policy.lid_closed_action() == |
| power_manager::PowerManagementPolicy_Action_SUSPEND || |
| policy.lid_closed_action() == |
| power_manager::PowerManagementPolicy_Action_SHUT_DOWN)) { |
| policy.set_lid_closed_action( |
| power_manager::PowerManagementPolicy_Action_DO_NOTHING); |
| } |
| |
| if (!causes.empty()) |
| policy.set_reason(causes); |
| client_->SetPolicy(policy); |
| } |
| |
| } // namespace chromeos |