chromium/chrome/browser/ui/webui/ash/settings/pages/power/device_power_handler.cc

// Copyright 2017 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/ui/webui/ash/settings/pages/power/device_power_handler.h"

#include <memory>
#include <utility>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/power_utils.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/base/webui/web_ui_util.h"

namespace ash::settings {

namespace {

using ::chromeos::PowerManagerClient;
using ::chromeos::PowerPolicyController;

std::u16string GetBatteryTimeText(base::TimeDelta time_left) {
  int hour = 0;
  int min = 0;
  power_utils::SplitTimeIntoHoursAndMinutes(time_left, &hour, &min);

  std::u16string time_text;
  if (hour == 0 || min == 0) {
    // Display only one unit ("2 hours" or "10 minutes").
    return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
                                  ui::TimeFormat::LENGTH_LONG, time_left);
  }

  return ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
                                  ui::TimeFormat::LENGTH_LONG,
                                  -1,  // force hour and minute output
                                  time_left);
}

int PowerSourceToDisplayId(
    const power_manager::PowerSupplyProperties_PowerSource& source) {
  switch (source.port()) {
    case power_manager::PowerSupplyProperties_PowerSource_Port_UNKNOWN:
      return IDS_POWER_SOURCE_PORT_UNKNOWN;
    case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT:
      return IDS_POWER_SOURCE_PORT_LEFT;
    case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT:
      return IDS_POWER_SOURCE_PORT_RIGHT;
    case power_manager::PowerSupplyProperties_PowerSource_Port_BACK:
      return IDS_POWER_SOURCE_PORT_BACK;
    case power_manager::PowerSupplyProperties_PowerSource_Port_FRONT:
      return IDS_POWER_SOURCE_PORT_FRONT;
    case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT_FRONT:
      return IDS_POWER_SOURCE_PORT_LEFT_FRONT;
    case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT_BACK:
      return IDS_POWER_SOURCE_PORT_LEFT_BACK;
    case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT_FRONT:
      return IDS_POWER_SOURCE_PORT_RIGHT_FRONT;
    case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT_BACK:
      return IDS_POWER_SOURCE_PORT_RIGHT_BACK;
    case power_manager::PowerSupplyProperties_PowerSource_Port_BACK_LEFT:
      return IDS_POWER_SOURCE_PORT_BACK_LEFT;
    case power_manager::PowerSupplyProperties_PowerSource_Port_BACK_RIGHT:
      return IDS_POWER_SOURCE_PORT_BACK_RIGHT;
  }
  NOTREACHED_IN_MIGRATION();
  return 0;
}

}  // namespace

PowerHandler::IdleBehaviorInfo::IdleBehaviorInfo() = default;
PowerHandler::IdleBehaviorInfo::~IdleBehaviorInfo() = default;

PowerHandler::IdleBehaviorInfo::IdleBehaviorInfo(
    const std::set<PowerHandler::IdleBehavior>& possible_behaviors,
    const PowerHandler::IdleBehavior& current_behavior,
    const bool is_managed)
    : possible_behaviors(possible_behaviors),
      current_behavior(current_behavior),
      is_managed(is_managed) {}

PowerHandler::IdleBehaviorInfo::IdleBehaviorInfo(const IdleBehaviorInfo& o) =
    default;

const char PowerHandler::kPowerManagementSettingsChangedName[] =
    "power-management-settings-changed";

const char PowerHandler::kPossibleAcIdleBehaviorsKey[] =
    "possibleAcIdleBehaviors";
const char PowerHandler::kPossibleBatteryIdleBehaviorsKey[] =
    "possibleBatteryIdleBehaviors";
const char PowerHandler::kCurrentAcIdleBehaviorKey[] = "currentAcIdleBehavior";
const char PowerHandler::kCurrentBatteryIdleBehaviorKey[] =
    "currentBatteryIdleBehavior";
const char PowerHandler::kLidClosedBehaviorKey[] = "lidClosedBehavior";
const char PowerHandler::kAcIdleManagedKey[] = "acIdleManaged";
const char PowerHandler::kBatteryIdleManagedKey[] = "batteryIdleManaged";

const char PowerHandler::kLidClosedControlledKey[] = "lidClosedControlled";
const char PowerHandler::kHasLidKey[] = "hasLid";
const char PowerHandler::kAdaptiveChargingKey[] = "adaptiveCharging";
const char PowerHandler::kAdaptiveChargingManagedKey[] =
    "adaptiveChargingManaged";
const char PowerHandler::kBatterySaverFeatureEnabledKey[] =
    "batterySaverFeatureEnabled";

PowerHandler::TestAPI::TestAPI(PowerHandler* handler) : handler_(handler) {}

PowerHandler::TestAPI::~TestAPI() = default;

void PowerHandler::TestAPI::RequestPowerManagementSettings() {
  handler_->HandleRequestPowerManagementSettings(base::Value::List());
}

void PowerHandler::TestAPI::SetIdleBehavior(IdleBehavior behavior,
                                            bool when_on_ac) {
  base::Value::List args;
  args.Append(static_cast<int>(behavior));
  args.Append(when_on_ac);
  handler_->HandleSetIdleBehavior(args);
}

void PowerHandler::TestAPI::SetLidClosedBehavior(
    PowerPolicyController::Action behavior) {
  base::Value::List args;
  args.Append(behavior);
  handler_->HandleSetLidClosedBehavior(args);
}

void PowerHandler::TestAPI::SetAdaptiveCharging(bool enabled) {
  base::Value::List args;
  args.Append(enabled);
  handler_->HandleSetAdaptiveCharging(args);
}

PowerHandler::PowerHandler(PrefService* prefs) : prefs_(prefs) {}

PowerHandler::~PowerHandler() {}

void PowerHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "updatePowerStatus",
      base::BindRepeating(&PowerHandler::HandleUpdatePowerStatus,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setPowerSource", base::BindRepeating(&PowerHandler::HandleSetPowerSource,
                                            base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "requestPowerManagementSettings",
      base::BindRepeating(&PowerHandler::HandleRequestPowerManagementSettings,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setLidClosedBehavior",
      base::BindRepeating(&PowerHandler::HandleSetLidClosedBehavior,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setIdleBehavior",
      base::BindRepeating(&PowerHandler::HandleSetIdleBehavior,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setAdaptiveCharging",
      base::BindRepeating(&PowerHandler::HandleSetAdaptiveCharging,
                          base::Unretained(this)));
}

void PowerHandler::OnJavascriptAllowed() {
  PowerManagerClient* power_manager_client = PowerManagerClient::Get();
  power_manager_client_observation_.Observe(power_manager_client);
  power_manager_client->GetSwitchStates(base::BindOnce(
      &PowerHandler::OnGotSwitchStates, weak_ptr_factory_.GetWeakPtr()));

  // Observe power management prefs used in the UI.
  base::RepeatingClosure callback(
      base::BindRepeating(&PowerHandler::SendPowerManagementSettings,
                          base::Unretained(this), false /* force */));
  pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
  pref_change_registrar_->Init(prefs_);
  pref_change_registrar_->Add(ash::prefs::kPowerAcIdleAction, callback);
  pref_change_registrar_->Add(ash::prefs::kPowerAcScreenDimDelayMs, callback);
  pref_change_registrar_->Add(ash::prefs::kPowerAcScreenOffDelayMs, callback);
  pref_change_registrar_->Add(ash::prefs::kPowerAcScreenLockDelayMs, callback);
  pref_change_registrar_->Add(ash::prefs::kPowerBatteryIdleAction, callback);
  pref_change_registrar_->Add(ash::prefs::kPowerBatteryScreenDimDelayMs,
                              callback);
  pref_change_registrar_->Add(ash::prefs::kPowerBatteryScreenOffDelayMs,
                              callback);
  pref_change_registrar_->Add(ash::prefs::kPowerBatteryScreenLockDelayMs,
                              callback);
  pref_change_registrar_->Add(ash::prefs::kPowerLidClosedAction, callback);
  pref_change_registrar_->Add(ash::prefs::kPowerAdaptiveChargingEnabled,
                              callback);
}

void PowerHandler::OnJavascriptDisallowed() {
  power_manager_client_observation_.Reset();
  pref_change_registrar_.reset();
}

void PowerHandler::PowerChanged(
    const power_manager::PowerSupplyProperties& proto) {
  SendBatteryStatus();
  SendPowerSources();
}

void PowerHandler::PowerManagerRestarted() {
  PowerManagerClient::Get()->GetSwitchStates(base::BindOnce(
      &PowerHandler::OnGotSwitchStates, weak_ptr_factory_.GetWeakPtr()));
}

void PowerHandler::LidEventReceived(PowerManagerClient::LidState state,
                                    base::TimeTicks timestamp) {
  lid_state_ = state;
  SendPowerManagementSettings(false /* force */);
}

void PowerHandler::HandleUpdatePowerStatus(const base::Value::List& args) {
  AllowJavascript();
  chromeos::PowerManagerClient::Get()->RequestStatusUpdate();
}

void PowerHandler::HandleSetPowerSource(const base::Value::List& args) {
  AllowJavascript();

  const std::string& id = args[0].GetString();
  chromeos::PowerManagerClient::Get()->SetPowerSource(id);
}

void PowerHandler::HandleRequestPowerManagementSettings(
    const base::Value::List& args) {
  AllowJavascript();
  SendPowerManagementSettings(true /* force */);
}

void PowerHandler::HandleSetIdleBehavior(const base::Value::List& args) {
  AllowJavascript();

  const auto& list = args;
  CHECK_GE(list.size(), 2u);
  int value = list[0].GetInt();
  bool when_on_ac = list[1].GetBool();

  const char* idle_pref = when_on_ac ? ash::prefs::kPowerAcIdleAction
                                     : ash::prefs::kPowerBatteryIdleAction;
  const char* screen_dim_delay_pref =
      when_on_ac ? ash::prefs::kPowerAcScreenDimDelayMs
                 : ash::prefs::kPowerBatteryScreenDimDelayMs;
  const char* screen_off_delay_pref =
      when_on_ac ? ash::prefs::kPowerAcScreenOffDelayMs
                 : ash::prefs::kPowerBatteryScreenOffDelayMs;
  const char* screen_lock_delay_pref =
      when_on_ac ? ash::prefs::kPowerAcScreenLockDelayMs
                 : ash::prefs::kPowerBatteryScreenLockDelayMs;

  switch (static_cast<IdleBehavior>(value)) {
    case IdleBehavior::DISPLAY_OFF_SLEEP:
      // The default behavior is to turn the display off and sleep.
      // Clear the prefs so we use the default delays.
      prefs_->ClearPref(idle_pref);
      prefs_->ClearPref(screen_dim_delay_pref);
      prefs_->ClearPref(screen_off_delay_pref);
      prefs_->ClearPref(screen_lock_delay_pref);
      break;
    case IdleBehavior::DISPLAY_OFF:
      // Override idle actions to keep the system awake, but use the
      // default screen delays.
      prefs_->SetInteger(idle_pref, PowerPolicyController::ACTION_DO_NOTHING);
      prefs_->ClearPref(screen_dim_delay_pref);
      prefs_->ClearPref(screen_off_delay_pref);
      prefs_->ClearPref(screen_lock_delay_pref);
      break;
    case IdleBehavior::DISPLAY_ON:
      // Override idle actions and set screen delays to 0 in order to
      // disable them (i.e. keep the screen on).
      prefs_->SetInteger(idle_pref, PowerPolicyController::ACTION_DO_NOTHING);
      prefs_->SetInteger(screen_dim_delay_pref, 0);
      prefs_->SetInteger(screen_off_delay_pref, 0);
      prefs_->SetInteger(screen_lock_delay_pref, 0);
      break;
    default:
      NOTREACHED_IN_MIGRATION() << "Invalid idle behavior " << value;
  }
}

void PowerHandler::HandleSetLidClosedBehavior(const base::Value::List& args) {
  AllowJavascript();

  const auto& list = args;
  CHECK_GE(list.size(), 1u);
  int value = list[0].GetInt();
  switch (static_cast<PowerPolicyController::Action>(value)) {
    case PowerPolicyController::ACTION_SUSPEND:
      prefs_->ClearPref(ash::prefs::kPowerLidClosedAction);
      break;
    case PowerPolicyController::ACTION_DO_NOTHING:
      prefs_->SetInteger(ash::prefs::kPowerLidClosedAction,
                         PowerPolicyController::ACTION_DO_NOTHING);
      break;
    default:
      NOTREACHED_IN_MIGRATION() << "Unsupported lid-closed behavior " << value;
  }
}

void PowerHandler::HandleSetAdaptiveCharging(const base::Value::List& args) {
  AllowJavascript();

  CHECK_GE(args.size(), 1u);
  bool enabled = args[0].GetBool();

  prefs_->SetBoolean(ash::prefs::kPowerAdaptiveChargingEnabled, enabled);
}

void PowerHandler::SendBatteryStatus() {
  const std::optional<power_manager::PowerSupplyProperties>& proto =
      PowerManagerClient::Get()->GetLastStatus();
  DCHECK(proto);
  bool charging = proto->battery_state() ==
                  power_manager::PowerSupplyProperties_BatteryState_CHARGING;
  bool calculating = proto->is_calculating_battery_time();
  int percent = power_utils::GetRoundedBatteryPercent(proto->battery_percent());
  base::TimeDelta time_left;
  bool show_time = false;

  if (!calculating) {
    time_left = base::Seconds(charging ? proto->battery_time_to_full_sec()
                                       : proto->battery_time_to_empty_sec());
    show_time = power_utils::ShouldDisplayBatteryTime(time_left);
  }

  std::u16string status_text;
  if (show_time) {
    status_text = l10n_util::GetStringFUTF16(
        charging ? IDS_SETTINGS_BATTERY_STATUS_CHARGING
                 : IDS_SETTINGS_BATTERY_STATUS,
        base::NumberToString16(percent), GetBatteryTimeText(time_left));
  } else {
    status_text = l10n_util::GetStringFUTF16(IDS_SETTINGS_BATTERY_STATUS_SHORT,
                                             base::NumberToString16(percent));
  }

  auto battery_dict =
      base::Value::Dict()
          .Set(
              "present",
              proto->battery_state() !=
                  power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT)
          .Set("charging", charging)
          .Set("calculating", calculating)
          .Set("percent", percent)
          .Set("statusText", status_text);

  FireWebUIListener("battery-status-changed", battery_dict);
}

void PowerHandler::SendPowerSources() {
  const std::optional<power_manager::PowerSupplyProperties>& proto =
      PowerManagerClient::Get()->GetLastStatus();
  DCHECK(proto);
  base::Value::List sources_list;
  for (int i = 0; i < proto->available_external_power_source_size(); i++) {
    const auto& source = proto->available_external_power_source(i);
    sources_list.Append(
        base::Value::Dict()
            .Set("id", source.id())
            .Set("is_dedicated_charger", source.active_by_default())
            .Set("description",
                 l10n_util::GetStringUTF16(PowerSourceToDisplayId(source))));
  }

  FireWebUIListener(
      "power-sources-changed", sources_list,
      base::Value(proto->external_power_source_id()),
      base::Value(proto->external_power() ==
                  power_manager::PowerSupplyProperties_ExternalPower_USB),
      base::Value(proto->external_power() ==
                  power_manager::PowerSupplyProperties_ExternalPower_AC));
}

void PowerHandler::SendPowerManagementSettings(bool force) {
  const PowerHandler::IdleBehaviorInfo ac_idle_info =
      GetAllowedIdleBehaviors(PowerSource::kAc);
  const PowerHandler::IdleBehaviorInfo battery_idle_info =
      GetAllowedIdleBehaviors(PowerSource::kBattery);

  const PowerPolicyController::Action lid_closed_behavior =
      static_cast<PowerPolicyController::Action>(
          prefs_->GetInteger(ash::prefs::kPowerLidClosedAction));
  const bool lid_closed_controlled =
      prefs_->IsManagedPreference(ash::prefs::kPowerLidClosedAction);
  const bool has_lid = lid_state_ != PowerManagerClient::LidState::NOT_PRESENT;

  const bool adaptive_charging =
      prefs_->GetBoolean(ash::prefs::kPowerAdaptiveChargingEnabled);
  const bool adaptive_charging_managed =
      prefs_->IsManagedPreference(ash::prefs::kPowerAdaptiveChargingEnabled);
  const bool battery_saver_feature_enabled =
      ash::features::IsBatterySaverAvailable();
  // Don't notify the UI if nothing changed.
  if (!force && ac_idle_info == last_ac_idle_info_ &&
      battery_idle_info == last_battery_idle_info_ &&
      lid_closed_behavior == last_lid_closed_behavior_ &&
      lid_closed_controlled == last_lid_closed_controlled_ &&
      has_lid == last_has_lid_ &&
      adaptive_charging == last_adaptive_charging_ &&
      adaptive_charging_managed == last_adaptive_charging_managed_ &&
      battery_saver_feature_enabled == last_battery_saver_feature_enabled_) {
    return;
  }

  auto dict =
      base::Value::Dict()
          .Set(kCurrentAcIdleBehaviorKey,
               static_cast<int>(ac_idle_info.current_behavior))
          .Set(kCurrentBatteryIdleBehaviorKey,
               static_cast<int>(battery_idle_info.current_behavior))
          .Set(kLidClosedBehaviorKey, lid_closed_behavior)
          .Set(kAcIdleManagedKey, ac_idle_info.is_managed)
          .Set(kBatteryIdleManagedKey, battery_idle_info.is_managed)
          .Set(kLidClosedControlledKey, lid_closed_controlled)
          .Set(kHasLidKey, has_lid)
          .Set(kAdaptiveChargingKey, adaptive_charging)
          .Set(kAdaptiveChargingManagedKey, adaptive_charging_managed)
          .Set(kBatterySaverFeatureEnabledKey, battery_saver_feature_enabled);

  base::Value::List* list = dict.EnsureList(kPossibleAcIdleBehaviorsKey);
  for (auto idle_behavior : ac_idle_info.possible_behaviors) {
    list->Append(static_cast<int>(idle_behavior));
  }

  list = dict.EnsureList(kPossibleBatteryIdleBehaviorsKey);
  for (auto idle_behavior : battery_idle_info.possible_behaviors) {
    list->Append(static_cast<int>(idle_behavior));
  }

  FireWebUIListener(kPowerManagementSettingsChangedName, dict);

  last_ac_idle_info_ = ac_idle_info;
  last_battery_idle_info_ = battery_idle_info;
  last_lid_closed_behavior_ = lid_closed_behavior;
  last_lid_closed_controlled_ = lid_closed_controlled;
  last_has_lid_ = has_lid;
  last_adaptive_charging_ = adaptive_charging;
  last_adaptive_charging_managed_ = adaptive_charging_managed;
  last_battery_saver_feature_enabled_ = battery_saver_feature_enabled;
}

void PowerHandler::OnGotSwitchStates(
    std::optional<PowerManagerClient::SwitchStates> result) {
  if (!result.has_value()) {
    return;
  }
  lid_state_ = result->lid_state;
  SendPowerManagementSettings(false /* force */);
}

PowerHandler::IdleBehaviorInfo PowerHandler::GetAllowedIdleBehaviors(
    PowerSource power_source) {
  const char* idle_pref = power_source == PowerSource::kAc
                              ? ash::prefs::kPowerAcIdleAction
                              : ash::prefs::kPowerBatteryIdleAction;
  const char* screen_off_delay_pref =
      power_source == PowerSource::kAc
          ? ash::prefs::kPowerAcScreenOffDelayMs
          : ash::prefs::kPowerBatteryScreenOffDelayMs;

  std::set<IdleBehavior> possible_behaviors;
  IdleBehavior current_idle_behavior;

  // If idle action is managed and set to suspend, only possible idle
  // behaviour is sleep with display off.
  if (prefs_->IsManagedPreference(idle_pref) &&
      prefs_->GetInteger(idle_pref) == PowerPolicyController::ACTION_SUSPEND) {
    current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP;
    possible_behaviors.insert(IdleBehavior::DISPLAY_OFF_SLEEP);
    return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                            IsIdleManaged(power_source));
  }

  // If idle action is managed and set to STOP_SESSION, STOP_SESSION is the only
  // possibility.
  if (prefs_->IsManagedPreference(idle_pref) &&
      (prefs_->GetInteger(idle_pref) ==
       PowerPolicyController::ACTION_STOP_SESSION)) {
    current_idle_behavior = IdleBehavior::STOP_SESSION;
    possible_behaviors.insert(IdleBehavior::STOP_SESSION);
    return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                            IsIdleManaged(power_source));
  }

  // If idle action is managed and set to SHUT_DOWN, SHUT_DOWN is the only
  // possibility.
  if (prefs_->IsManagedPreference(idle_pref) &&
      (prefs_->GetInteger(idle_pref) ==
       PowerPolicyController::ACTION_SHUT_DOWN)) {
    current_idle_behavior = IdleBehavior::SHUT_DOWN;
    possible_behaviors.insert(IdleBehavior::SHUT_DOWN);
    return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                            IsIdleManaged(power_source));
  }

  // Note that after this point |idle_pref| should either be:
  //    1. Not managed.
  //    2. Or managed and set to
  //    PowerPolicyController::ACTION_DO_NOTHING.
  DCHECK(!prefs_->IsManagedPreference(idle_pref) ||
         (prefs_->GetInteger(idle_pref) ==
          PowerPolicyController::ACTION_DO_NOTHING));

  // If screen off delay is managed and set to a value greater than 0
  // and
  //   1. If idle action is managed and set to DO_NOTHING, only
  //      possible idle behavior is DISPLAY_OFF.
  //   2. If idle action is not managed then possible idle options
  //      are DiSPLAY_OFF and DISPLAY_OFF_SLEEP
  if (prefs_->IsManagedPreference(screen_off_delay_pref) &&
      prefs_->GetInteger(screen_off_delay_pref) > 0) {
    if (prefs_->IsManagedPreference(idle_pref) &&
        prefs_->GetInteger(idle_pref) ==
            PowerPolicyController::ACTION_DO_NOTHING) {
      current_idle_behavior = IdleBehavior::DISPLAY_OFF;
      possible_behaviors.insert(IdleBehavior::DISPLAY_OFF);
      return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                              IsIdleManaged(power_source));
    }

    possible_behaviors.insert(IdleBehavior::DISPLAY_OFF);
    possible_behaviors.insert(IdleBehavior::DISPLAY_OFF_SLEEP);
    // Set the current default option based on the current idle action.
    const PowerPolicyController::Action idle_action =
        static_cast<PowerPolicyController::Action>(
            prefs_->GetInteger(idle_pref));

    if (idle_action == PowerPolicyController::ACTION_SUSPEND) {
      current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP;
    } else {
      current_idle_behavior = IdleBehavior::DISPLAY_OFF;
    }
    return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                            IsIdleManaged(power_source));
  }

  // If idle action is managed and set to DO_NOTHING, and
  //   1. If screen off delay is also managed (and set to 0), only
  //   possible idle
  //      action is DISPLAY_ON.
  //   2. If AC screen off delay is not managed, possible idle actions
  //      are DISPLAY_ON && DISPLAY_OFF.
  if (prefs_->IsManagedPreference(idle_pref) &&
      prefs_->GetInteger(idle_pref) ==
          PowerPolicyController::ACTION_DO_NOTHING) {
    if (prefs_->IsManagedPreference(screen_off_delay_pref)) {
      // Note that we reach here only when screen off delays are
      // set by enterprise policy to 0 to prevent display from turning
      // off.
      DCHECK(prefs_->GetInteger(screen_off_delay_pref) == 0);
      current_idle_behavior = IdleBehavior::DISPLAY_ON;
      possible_behaviors.insert(IdleBehavior::DISPLAY_ON);
      return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                              IsIdleManaged(power_source));
    }
    possible_behaviors.insert(IdleBehavior::DISPLAY_ON);
    possible_behaviors.insert(IdleBehavior::DISPLAY_OFF);
    // Set the current default option based on the current screen off
    // delay.
    current_idle_behavior = prefs_->GetInteger(screen_off_delay_pref) > 0
                                ? IdleBehavior::DISPLAY_OFF
                                : IdleBehavior::DISPLAY_ON;
    return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                            IsIdleManaged(power_source));
  }

  // Looks like we did not find enterprise policy restricitng the idle
  // options. So add all three idle options to what user can select
  // from.
  possible_behaviors.insert(IdleBehavior::DISPLAY_ON);
  possible_behaviors.insert(IdleBehavior::DISPLAY_OFF);
  possible_behaviors.insert(IdleBehavior::DISPLAY_OFF_SLEEP);

  // Infer the idle behavior based on the current idle action
  // (determining whether we'll sleep eventually or not) and the AC
  // screen-off delay.
  const PowerPolicyController::Action idle_action =
      static_cast<PowerPolicyController::Action>(prefs_->GetInteger(idle_pref));

  if (idle_action == PowerPolicyController::ACTION_SUSPEND) {
    current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP;
  } else if (idle_action == PowerPolicyController::ACTION_DO_NOTHING) {
    current_idle_behavior = (prefs_->GetInteger(screen_off_delay_pref) > 0
                                 ? IdleBehavior::DISPLAY_OFF
                                 : IdleBehavior::DISPLAY_ON);
  } else {
    NOTREACHED_IN_MIGRATION()
        << "Idle behavior is set to a enterprise-only value, but "
        << "the setting is not enterprise managed. Defaulting to "
        << "DISPLAY_OFF_SLEEP behavior.";
    current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP;
  }

  return IdleBehaviorInfo(possible_behaviors, current_idle_behavior,
                          IsIdleManaged(power_source));
}

bool PowerHandler::IsIdleManaged(PowerSource power_source) {
  switch (power_source) {
    case PowerSource::kAc:
      return prefs_->IsManagedPreference(ash::prefs::kPowerAcIdleAction) ||
             prefs_->IsManagedPreference(
                 ash::prefs::kPowerAcScreenDimDelayMs) ||
             prefs_->IsManagedPreference(
                 ash::prefs::kPowerAcScreenOffDelayMs) ||
             prefs_->IsManagedPreference(ash::prefs::kPowerAcScreenLockDelayMs);
    case PowerSource::kBattery:
      return prefs_->IsManagedPreference(ash::prefs::kPowerBatteryIdleAction) ||
             prefs_->IsManagedPreference(
                 ash::prefs::kPowerBatteryScreenDimDelayMs) ||
             prefs_->IsManagedPreference(
                 ash::prefs::kPowerBatteryScreenOffDelayMs) ||
             prefs_->IsManagedPreference(
                 ash::prefs::kPowerBatteryScreenLockDelayMs);
  }
}

}  // namespace ash::settings