blob: 97ac113d4b09e055bf76375ca0181f7c1a32aeda [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS 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 "power_manager/powerd/daemon.h"
#include <inttypes.h>
#include <algorithm>
#include <cmath>
#include <map>
#include <utility>
#include <base/bind.h>
#include <base/files/file_util.h>
#include <base/format_macros.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/system/sys_info.h>
#include <base/threading/platform_thread.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/message.h>
#include "cryptohome/proto_bindings/rpc.pb.h"
#include "power_manager/common/activity_logger.h"
#include "power_manager/common/battery_percentage_converter.h"
#include "power_manager/common/metrics_sender.h"
#include "power_manager/common/power_constants.h"
#include "power_manager/common/prefs.h"
#include "power_manager/common/util.h"
#if USE_BUFFET
#include "power_manager/powerd/buffet/command_handlers.h"
#endif
#include "power_manager/powerd/daemon_delegate.h"
#include "power_manager/powerd/metrics_collector.h"
#include "power_manager/powerd/policy/backlight_controller.h"
#include "power_manager/powerd/policy/input_device_controller.h"
#include "power_manager/powerd/policy/keyboard_backlight_controller.h"
#include "power_manager/powerd/policy/shutdown_from_suspend.h"
#include "power_manager/powerd/policy/state_controller.h"
#include "power_manager/powerd/policy/thermal_event_handler.h"
#include "power_manager/powerd/system/acpi_wakeup_helper_interface.h"
#include "power_manager/powerd/system/ambient_light_sensor_manager_interface.h"
#include "power_manager/powerd/system/arc_timer_manager.h"
#include "power_manager/powerd/system/audio_client_interface.h"
#include "power_manager/powerd/system/backlight_interface.h"
#include "power_manager/powerd/system/charge_controller_helper_interface.h"
#include "power_manager/powerd/system/cros_ec_helper_interface.h"
#include "power_manager/powerd/system/dark_resume_interface.h"
#include "power_manager/powerd/system/dbus_wrapper.h"
#include "power_manager/powerd/system/display/display_power_setter.h"
#include "power_manager/powerd/system/display/display_watcher.h"
#include "power_manager/powerd/system/event_device_interface.h"
#include "power_manager/powerd/system/input_watcher_interface.h"
#include "power_manager/powerd/system/lockfile_checker.h"
#include "power_manager/powerd/system/peripheral_battery_watcher.h"
#include "power_manager/powerd/system/power_supply.h"
#include "power_manager/powerd/system/smart_discharge_configurator.h"
#include "power_manager/powerd/system/suspend_configurator.h"
#include "power_manager/powerd/system/thermal/thermal_device.h"
#include "power_manager/powerd/system/udev.h"
#include "power_manager/powerd/system/user_proximity_watcher_interface.h"
#include "power_manager/powerd/system/wake_on_dp_configurator.h"
#include "power_manager/powerd/system/wakeup_source_identifier.h"
#include "power_manager/powerd/system/wilco_charge_controller_helper.h"
#include "power_manager/proto_bindings/idle.pb.h"
#include "power_manager/proto_bindings/policy.pb.h"
#if USE_BUFFET
namespace dbus {
class Bus;
} // namespace dbus
#endif // USE_BUFFET
namespace power_manager {
namespace {
// Default values for |*_path_| members (which can be overridden for tests).
const char kDefaultSuspendedStatePath[] =
"/var/lib/power_manager/powerd_suspended";
const char kDefaultWakeupCountPath[] = "/sys/power/wakeup_count";
const char kDefaultOobeCompletedPath[] = "/home/chronos/.oobe_completed";
// Directory checked for lockfiles indicating that powerd shouldn't suspend or
// shut down the system (typically due to a firmware update).
const char kPowerOverrideLockfileDir[] = "/run/lock/power_override";
// Basename appended to |run_dir| (see Daemon's c'tor) to produce
// |suspend_announced_path_|.
const char kSuspendAnnouncedFile[] = "suspend_announced";
// Strings for states that powerd cares about from the session manager's
// SessionStateChanged signal. This value is defined in the session_manager
// codebase.
const char kSessionStarted[] = "started";
// After noticing that power management is overridden while suspending, wait up
// to this long for the lockfile(s) to be removed before reporting a suspend
// failure. The event loop is blocked during this period.
constexpr base::TimeDelta kSuspendLockfileTimeout =
base::TimeDelta::FromMilliseconds(500);
// Interval between successive polls during kSuspendLockfileTimeout.
constexpr base::TimeDelta kSuspendLockfilePollInterval =
base::TimeDelta::FromMilliseconds(100);
// Interval between attempts to retry shutting the system down while power
// management is overridden, in seconds.
constexpr base::TimeDelta kShutdownLockfileRetryInterval =
base::TimeDelta::FromSeconds(5);
// Maximum amount of time to wait for responses to D-Bus method calls to other
// processes.
const int kSessionManagerDBusTimeoutMs = 3000;
const int kCryptohomedDBusTimeoutMs = 2 * 60 * 1000; // Two minutes.
// Interval between log messages while user, audio, or video activity is
// ongoing, in seconds.
const int kLogOngoingActivitySec = 180;
// Time after the last report from Chrome of video or user activity at which
// point a message is logged about the activity having stopped. Chrome reports
// these every five seconds, but longer delays here reduce the amount of log
// spam due to sporadic activity.
const int kLogVideoActivityStoppedDelaySec = 20;
const int kLogUserActivityStoppedDelaySec = 20;
// Delay to wait before logging that hovering has stopped. This is ideally
// smaller than kKeyboardBacklightKeepOnMsPref; otherwise the backlight can be
// turned off before the hover-off event that triggered it is logged.
const int64_t kLogHoveringStoppedDelaySec = 20;
// Passes |method_call| to |handler| and passes the response to
// |response_sender|. If |handler| returns NULL, an empty response is
// created and sent.
void HandleSynchronousDBusMethodCall(
base::Callback<std::unique_ptr<dbus::Response>(dbus::MethodCall*)> handler,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
std::unique_ptr<dbus::Response> response = handler.Run(method_call);
if (!response)
response = dbus::Response::FromMethodCall(method_call);
std::move(response_sender).Run(std::move(response));
}
// Creates a new "invalid args" reply to |method_call|.
std::unique_ptr<dbus::Response> CreateInvalidArgsError(
dbus::MethodCall* method_call, std::string message) {
return std::unique_ptr<dbus::Response>(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, message));
}
} // namespace
// static
constexpr char Daemon::kAlreadyRanFileName[];
// Performs actions requested by |state_controller_|. The reason that
// this is a nested class of Daemon rather than just being implemented as
// part of Daemon is to avoid method naming conflicts.
class Daemon::StateControllerDelegate
: public policy::StateController::Delegate {
public:
explicit StateControllerDelegate(Daemon* daemon) : daemon_(daemon) {}
~StateControllerDelegate() override { daemon_ = nullptr; }
// Overridden from policy::StateController::Delegate:
bool IsUsbInputDeviceConnected() override {
return daemon_->input_watcher_->IsUSBInputDeviceConnected();
}
bool IsOobeCompleted() override {
return base::PathExists(base::FilePath(daemon_->oobe_completed_path_));
}
bool IsHdmiAudioActive() override {
return daemon_->audio_client_ ? daemon_->audio_client_->GetHdmiActive()
: false;
}
bool IsHeadphoneJackPlugged() override {
return daemon_->audio_client_
? daemon_->audio_client_->GetHeadphoneJackPlugged()
: false;
}
void DimScreen() override { daemon_->SetBacklightsDimmedForInactivity(true); }
void UndimScreen() override {
daemon_->SetBacklightsDimmedForInactivity(false);
}
void TurnScreenOff() override {
daemon_->SetBacklightsOffForInactivity(true);
}
void TurnScreenOn() override {
daemon_->SetBacklightsOffForInactivity(false);
}
void LockScreen() override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerLockScreen);
daemon_->dbus_wrapper_->CallMethodSync(
daemon_->session_manager_dbus_proxy_, &method_call,
base::TimeDelta::FromMilliseconds(kSessionManagerDBusTimeoutMs));
}
void Suspend(policy::StateController::ActionReason reason) override {
SuspendImminent::Reason suspend_reason = SuspendImminent_Reason_OTHER;
switch (reason) {
case policy::StateController::ActionReason::IDLE:
suspend_reason = SuspendImminent_Reason_IDLE;
break;
case policy::StateController::ActionReason::LID_CLOSED:
suspend_reason = SuspendImminent_Reason_LID_CLOSED;
break;
}
daemon_->Suspend(suspend_reason, false, 0, base::TimeDelta());
}
void StopSession() override {
// This session manager method takes a string argument, although it
// doesn't currently do anything with it.
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStopSession);
dbus::MessageWriter writer(&method_call);
writer.AppendString("");
daemon_->dbus_wrapper_->CallMethodSync(
daemon_->session_manager_dbus_proxy_, &method_call,
base::TimeDelta::FromMilliseconds(kSessionManagerDBusTimeoutMs));
}
void ShutDown() override {
daemon_->ShutDown(ShutdownMode::POWER_OFF,
ShutdownReason::STATE_TRANSITION);
}
void ReportUserActivityMetrics() override {
daemon_->metrics_collector_->GenerateUserActivityMetrics();
}
private:
Daemon* daemon_; // weak
DISALLOW_COPY_AND_ASSIGN(StateControllerDelegate);
};
Daemon::Daemon(DaemonDelegate* delegate, const base::FilePath& run_dir)
: delegate_(delegate),
state_controller_delegate_(new StateControllerDelegate(this)),
state_controller_(new policy::StateController),
input_event_handler_(new policy::InputEventHandler),
input_device_controller_(new policy::InputDeviceController),
shutdown_from_suspend_(std::make_unique<policy::ShutdownFromSuspend>()),
suspender_(new policy::Suspender),
wifi_controller_(std::make_unique<policy::WifiController>()),
#if USE_TROGDOR_SAR_HACK
cellular_controller_(
std::make_unique<policy::CellularControllerTrogdor>()),
#else // USE_TROGDOR_SAR_HACK
cellular_controller_(std::make_unique<policy::CellularController>()),
#endif // USE_TROGDOR_SAR_HACK
metrics_collector_(new metrics::MetricsCollector),
arc_timer_manager_(std::make_unique<system::ArcTimerManager>()),
retry_shutdown_for_lockfile_timer_(),
wakeup_count_path_(kDefaultWakeupCountPath),
oobe_completed_path_(kDefaultOobeCompletedPath),
run_dir_(run_dir),
suspended_state_path_(kDefaultSuspendedStatePath),
suspend_announced_path_(run_dir.Append(kSuspendAnnouncedFile)),
already_ran_path_(run_dir.Append(Daemon::kAlreadyRanFileName)),
video_activity_logger_(new PeriodicActivityLogger(
"Video activity",
base::TimeDelta::FromSeconds(kLogVideoActivityStoppedDelaySec),
base::TimeDelta::FromSeconds(kLogOngoingActivitySec))),
user_activity_logger_(new PeriodicActivityLogger(
"User activity",
base::TimeDelta::FromSeconds(kLogUserActivityStoppedDelaySec),
base::TimeDelta::FromSeconds(kLogOngoingActivitySec))),
audio_activity_logger_(new StartStopActivityLogger(
"Audio activity",
base::TimeDelta(),
base::TimeDelta::FromSeconds(kLogOngoingActivitySec))),
hovering_logger_(new StartStopActivityLogger(
"Hovering",
base::TimeDelta::FromSeconds(kLogHoveringStoppedDelaySec),
base::TimeDelta())),
weak_ptr_factory_(this) {
}
Daemon::~Daemon() {
if (dbus_wrapper_)
dbus_wrapper_->RemoveObserver(this);
if (audio_client_)
audio_client_->RemoveObserver(this);
if (power_supply_)
power_supply_->RemoveObserver(this);
}
void Daemon::Init() {
// Check if this is the first run of powerd after boot.
first_run_after_boot_ = !base::PathExists(already_ran_path_);
if (first_run_after_boot_) {
if (base::WriteFile(already_ran_path_, "", 0) != 0)
PLOG(ERROR) << "Couldn't create " << already_ran_path_.value();
}
prefs_ = delegate_->CreatePrefs();
InitDBus();
factory_mode_ = BoolPrefIsTrue(kFactoryModePref);
if (factory_mode_)
LOG(INFO) << "Factory mode enabled; most functionality will be disabled";
metrics_sender_ = delegate_->CreateMetricsSender();
udev_ = delegate_->CreateUdev();
input_watcher_ = delegate_->CreateInputWatcher(prefs_.get(), udev_.get());
suspend_configurator_ = delegate_->CreateSuspendConfigurator(prefs_.get());
wakeup_source_identifier_ =
std::make_unique<system::WakeupSourceIdentifier>(udev_.get());
const TabletMode tablet_mode = input_watcher_->GetTabletMode();
if (tablet_mode == TabletMode::ON)
LOG(INFO) << "Tablet mode enabled at startup";
const LidState lid_state = input_watcher_->QueryLidState();
if (lid_state == LidState::CLOSED)
LOG(INFO) << "Lid closed at startup";
display_watcher_ = delegate_->CreateDisplayWatcher(udev_.get());
display_power_setter_ =
delegate_->CreateDisplayPowerSetter(dbus_wrapper_.get());
// Ignore the ALS and backlights in factory mode.
if (!factory_mode_) {
light_sensor_manager_ =
delegate_->CreateAmbientLightSensorManager(prefs_.get());
if (BoolPrefIsTrue(kExternalDisplayOnlyPref)) {
display_backlight_controller_ =
delegate_->CreateExternalBacklightController(
display_watcher_.get(), display_power_setter_.get(),
dbus_wrapper_.get());
} else {
display_backlight_ = delegate_->CreateInternalBacklight(
base::FilePath(kInternalBacklightPath), kInternalBacklightPattern);
if (!display_backlight_) {
LOG(ERROR) << "Failed to initialize display backlight under "
<< kInternalBacklightPath << " using pattern "
<< kInternalBacklightPattern;
} else {
display_backlight_controller_ =
delegate_->CreateInternalBacklightController(
display_backlight_.get(), prefs_.get(),
light_sensor_manager_->GetSensorForInternalBacklight(),
display_power_setter_.get(), dbus_wrapper_.get(), lid_state);
}
}
if (display_backlight_controller_)
all_backlight_controllers_.push_back(display_backlight_controller_.get());
if (BoolPrefIsTrue(kHasKeyboardBacklightPref)) {
keyboard_backlight_ = delegate_->CreatePluggableInternalBacklight(
udev_.get(), kKeyboardBacklightUdevSubsystem,
base::FilePath(kKeyboardBacklightPath), kKeyboardBacklightPattern);
keyboard_backlight_controller_ =
delegate_->CreateKeyboardBacklightController(
keyboard_backlight_.get(), prefs_.get(),
light_sensor_manager_->GetSensorForKeyboardBacklight(),
dbus_wrapper_.get(), display_backlight_controller_.get(),
lid_state, tablet_mode);
all_backlight_controllers_.push_back(
keyboard_backlight_controller_.get());
}
}
prefs_->GetBool(kMosysEventlogPref, &log_suspend_with_mosys_eventlog_);
prefs_->GetBool(kSuspendToIdlePref, &suspend_to_idle_);
battery_percentage_converter_ =
BatteryPercentageConverter::CreateFromPrefs(prefs_.get());
power_supply_ = delegate_->CreatePowerSupply(
base::FilePath(kPowerStatusPath), prefs_.get(), udev_.get(),
dbus_wrapper_.get(), battery_percentage_converter_.get());
power_supply_->AddObserver(this);
if (!power_supply_->RefreshImmediately())
LOG(ERROR) << "Initial power supply refresh failed; brace for weirdness";
const system::PowerStatus power_status = power_supply_->GetPowerStatus();
metrics_collector_->Init(prefs_.get(), display_backlight_controller_.get(),
keyboard_backlight_controller_.get(), power_status,
first_run_after_boot_);
dark_resume_ = delegate_->CreateDarkResume(prefs_.get(),
wakeup_source_identifier_.get());
shutdown_from_suspend_->Init(prefs_.get(), power_supply_.get());
suspender_->Init(this, dbus_wrapper_.get(), dark_resume_.get(),
display_watcher_.get(), wakeup_source_identifier_.get(),
shutdown_from_suspend_.get(), prefs_.get());
input_event_handler_->Init(input_watcher_.get(), this, display_watcher_.get(),
dbus_wrapper_.get(), prefs_.get());
acpi_wakeup_helper_ = delegate_->CreateAcpiWakeupHelper();
ec_helper_ = delegate_->CreateCrosEcHelper();
input_device_controller_->Init(display_backlight_controller_.get(),
udev_.get(), acpi_wakeup_helper_.get(),
ec_helper_.get(), lid_state, tablet_mode,
DisplayMode::NORMAL, prefs_.get());
const PowerSource power_source =
power_status.line_power_on ? PowerSource::AC : PowerSource::BATTERY;
state_controller_->Init(state_controller_delegate_.get(), prefs_.get(),
dbus_wrapper_.get(), power_source, lid_state);
if (BoolPrefIsTrue(kUseCrasPref)) {
audio_client_ = delegate_->CreateAudioClient(dbus_wrapper_.get(), run_dir_);
audio_client_->AddObserver(this);
}
wifi_controller_->Init(this, prefs_.get(), udev_.get(), tablet_mode);
cellular_controller_->Init(this, prefs_.get());
peripheral_battery_watcher_ = delegate_->CreatePeripheralBatteryWatcher(
dbus_wrapper_.get(), udev_.get());
power_override_lockfile_checker_ = delegate_->CreateLockfileChecker(
base::FilePath(kPowerOverrideLockfileDir), {});
user_proximity_watcher_ =
delegate_->CreateUserProximityWatcher(prefs_.get(), udev_.get());
user_proximity_handler_ = std::make_unique<policy::UserProximityHandler>();
user_proximity_handler_->Init(user_proximity_watcher_.get(),
wifi_controller_.get(),
cellular_controller_.get());
arc_timer_manager_->Init(dbus_wrapper_.get());
if (BoolPrefIsTrue(kHasChargeControllerPref)) {
charge_controller_helper_ = delegate_->CreateChargeControllerHelper();
charge_controller_ = std::make_unique<policy::ChargeController>(),
charge_controller_->Init(charge_controller_helper_.get(),
battery_percentage_converter_.get());
}
// Asynchronously undo the previous force-lid-open request to the EC (if there
// was one).
if (!factory_mode_ && BoolPrefIsTrue(kUseLidPref))
RunSetuidHelper("set_force_lid_open", "--noforce_lid_open", false);
// This needs to happen *after* all D-Bus methods are exported:
// https://crbug.com/331431
CHECK(dbus_wrapper_->PublishService()) << "Failed to publish D-Bus service";
// configure wake on dp only if the preference is set.
bool wake_on_dp = false;
if (prefs_->GetBool(kWakeOnDpPref, &wake_on_dp))
system::ConfigureWakeOnDp(wake_on_dp);
// Configure wake for the EC.
if (acpi_wakeup_helper_->IsSupported()) {
acpi_wakeup_helper_->SetWakeupEnabled("CREC", true);
}
// Configure Smart Discharge in EC.
int64_t to_zero_hr = -1, cutoff_ua = -1, hibernate_ua = -1;
prefs_->GetInt64(kSmartDischargeToZeroHrPref, &to_zero_hr);
prefs_->GetInt64(kCutoffPowerUaPref, &cutoff_ua);
prefs_->GetInt64(kHibernatePowerUaPref, &hibernate_ua);
system::ConfigureSmartDischarge(to_zero_hr, cutoff_ua, hibernate_ua);
thermal_devices_ = delegate_->CreateThermalDevices();
std::vector<system::ThermalDeviceInterface*> weak_thermal_device;
for (const auto& thermal_device : thermal_devices_) {
weak_thermal_device.push_back(thermal_device.get());
}
thermal_event_handler_ = std::make_unique<policy::ThermalEventHandler>(
weak_thermal_device, dbus_wrapper_.get());
thermal_event_handler_->Init();
// Call this last to ensure that all of our members are already initialized.
OnPowerStatusUpdate();
}
bool Daemon::TriggerRetryShutdownTimerForTesting() {
if (!retry_shutdown_for_lockfile_timer_.IsRunning())
return false;
retry_shutdown_for_lockfile_timer_.user_task().Run();
return true;
}
bool Daemon::BoolPrefIsTrue(const std::string& name) const {
bool value = false;
return prefs_->GetBool(name, &value) && value;
}
bool Daemon::SuspendAndShutdownAreBlocked(std::string* details_out) {
const std::vector<base::FilePath> paths =
power_override_lockfile_checker_->GetValidLockfiles();
*details_out = util::JoinPaths(paths, ", ");
return !paths.empty();
}
int Daemon::RunSetuidHelper(const std::string& action,
const std::string& additional_args,
bool wait_for_completion) {
std::string command = kSetuidHelperPath + std::string(" --action=" + action);
if (!additional_args.empty())
command += " " + additional_args;
if (wait_for_completion) {
return delegate_->Run(command.c_str());
} else {
delegate_->Launch(command.c_str());
return 0;
}
}
void Daemon::HandleLidClosed() {
LOG(INFO) << "Lid closed";
// It is important that we notify InputDeviceController first so that it can
// inhibit input devices quickly. StateController will issue a blocking call
// to Chrome which can take longer than a second.
input_device_controller_->SetLidState(LidState::CLOSED);
state_controller_->HandleLidStateChange(LidState::CLOSED);
for (auto controller : all_backlight_controllers_)
controller->HandleLidStateChange(LidState::CLOSED);
dbus_wrapper_->EmitBareSignal(kLidClosedSignal);
}
void Daemon::HandleLidOpened() {
LOG(INFO) << "Lid opened";
suspender_->HandleLidOpened();
state_controller_->HandleLidStateChange(LidState::OPEN);
input_device_controller_->SetLidState(LidState::OPEN);
for (auto controller : all_backlight_controllers_)
controller->HandleLidStateChange(LidState::OPEN);
dbus_wrapper_->EmitBareSignal(kLidOpenedSignal);
}
void Daemon::HandlePowerButtonEvent(ButtonState state) {
// Don't log spammy repeat events if we see them.
if (state != ButtonState::REPEAT)
LOG(INFO) << "Power button " << ButtonStateToString(state);
metrics_collector_->HandlePowerButtonEvent(state);
if (state == ButtonState::DOWN) {
delegate_->Launch("sync");
for (auto controller : all_backlight_controllers_)
controller->HandlePowerButtonPress();
}
}
void Daemon::HandleHoverStateChange(bool hovering) {
if (hovering)
hovering_logger_->OnActivityStarted();
else
hovering_logger_->OnActivityStopped();
for (auto controller : all_backlight_controllers_)
controller->HandleHoverStateChange(hovering);
}
void Daemon::HandleTabletModeChange(TabletMode mode) {
DCHECK_NE(mode, TabletMode::UNSUPPORTED);
LOG(INFO) << "Tablet mode " << TabletModeToString(mode);
state_controller_->HandleTabletModeChange(mode);
input_device_controller_->SetTabletMode(mode);
for (auto controller : all_backlight_controllers_)
controller->HandleTabletModeChange(mode);
wifi_controller_->HandleTabletModeChange(mode);
cellular_controller_->HandleTabletModeChange(mode);
}
void Daemon::ShutDownForPowerButtonWithNoDisplay() {
LOG(INFO) << "Shutting down due to power button press while no display is "
<< "connected";
metrics_collector_->HandlePowerButtonEvent(ButtonState::DOWN);
ShutDown(ShutdownMode::POWER_OFF, ShutdownReason::USER_REQUEST);
}
void Daemon::HandleMissingPowerButtonAcknowledgment() {
LOG(INFO) << "Didn't receive power button acknowledgment from Chrome";
}
void Daemon::ReportPowerButtonAcknowledgmentDelay(base::TimeDelta delay) {
metrics_collector_->SendPowerButtonAcknowledgmentDelayMetric(delay);
}
int Daemon::GetInitialSuspendId() {
// Take powerd's PID modulo 2**15 (/proc/sys/kernel/pid_max is currently
// 2**15, but just in case...) and multiply it by 2**16, leaving it able to
// fit in a signed 32-bit int. This allows for 2**16 suspend attempts and
// suspend delays per powerd run before wrapping or intruding on another
// run's ID range (neither of which should be particularly problematic, but
// doing this reduces the chances of a confused client that's using stale
// IDs from a previous powerd run being able to conflict with the new run's
// IDs).
return (delegate_->GetPid() % 32768) * 65536 + 1;
}
int Daemon::GetInitialDarkSuspendId() {
// We use the upper half of the suspend ID space for dark suspend attempts.
// Assuming that we will go through dark suspend IDs faster than the regular
// suspend IDs, we should never have a collision between the suspend ID and
// the dark suspend ID until the dark suspend IDs wrap around.
return GetInitialSuspendId() + 32768;
}
bool Daemon::IsLidClosedForSuspend() {
return input_watcher_->QueryLidState() == LidState::CLOSED;
}
bool Daemon::ReadSuspendWakeupCount(uint64_t* wakeup_count) {
DCHECK(wakeup_count);
LOG(INFO) << "Reading wakeup count from " << wakeup_count_path_.value();
if (!util::ReadUint64File(wakeup_count_path_, wakeup_count)) {
return false;
}
LOG(INFO) << "Read wakeup count " << *wakeup_count;
return true;
}
void Daemon::SetSuspendAnnounced(bool announced) {
if (announced) {
if (base::WriteFile(suspend_announced_path_, nullptr, 0) < 0)
PLOG(ERROR) << "Couldn't create " << suspend_announced_path_.value();
} else {
if (!base::DeleteFile(suspend_announced_path_, false))
PLOG(ERROR) << "Couldn't delete " << suspend_announced_path_.value();
}
}
bool Daemon::GetSuspendAnnounced() {
return base::PathExists(suspend_announced_path_);
}
void Daemon::PrepareToSuspend() {
// Before announcing the suspend request, notify the backlight controller so
// it can turn the backlight off and tell the kernel to resume the current
// level after resuming. This must occur before Chrome is told that the
// system is going to suspend (Chrome turns the display back on while leaving
// the backlight off).
SetBacklightsSuspended(true);
power_supply_->SetSuspended(true);
if (audio_client_)
audio_client_->SetSuspended(true);
metrics_collector_->PrepareForSuspend();
}
policy::Suspender::Delegate::SuspendResult Daemon::DoSuspend(
uint64_t wakeup_count, bool wakeup_count_valid, base::TimeDelta duration) {
// If power management is overridden by a lockfile, spin for a bit to wait for
// the process to finish: http://crosbug.com/p/38947
base::TimeDelta elapsed;
std::string details;
while (SuspendAndShutdownAreBlocked(&details)) {
if (elapsed >= kSuspendLockfileTimeout) {
LOG(INFO) << "Aborting suspend attempt for lockfile(s): " << details;
return policy::Suspender::Delegate::SuspendResult::FAILURE;
}
elapsed += kSuspendLockfilePollInterval;
base::PlatformThread::Sleep(kSuspendLockfilePollInterval);
}
// Touch a file that crash-reporter can inspect later to determine
// whether the system was suspended while an unclean shutdown occurred.
// If the file already exists, assume that crash-reporter hasn't seen it
// yet and avoid unlinking it after resume.
created_suspended_state_file_ = false;
if (!base::PathExists(suspended_state_path_)) {
if (base::WriteFile(suspended_state_path_, nullptr, 0) == 0)
created_suspended_state_file_ = true;
else
PLOG(ERROR) << "Unable to create " << suspended_state_path_.value();
}
// This command is run synchronously to ensure that it finishes before the
// system is suspended.
if (log_suspend_with_mosys_eventlog_) {
RunSetuidHelper("mosys_eventlog", "--mosys_eventlog_code=0xa7", true);
}
std::vector<std::string> args;
if (wakeup_count_valid) {
args.push_back("--suspend_wakeup_count_valid");
args.push_back(
base::StringPrintf("--suspend_wakeup_count=%" PRIu64, wakeup_count));
}
if (suspend_to_idle_)
args.push_back("--suspend_to_idle");
suspend_configurator_->PrepareForSuspend(duration);
const int exit_code =
RunSetuidHelper("suspend", base::JoinString(args, " "), true);
LOG(INFO) << "powerd_suspend returned " << exit_code;
if (log_suspend_with_mosys_eventlog_)
RunSetuidHelper("mosys_eventlog", "--mosys_eventlog_code=0xa8", false);
if (created_suspended_state_file_) {
if (!base::DeleteFile(base::FilePath(suspended_state_path_), false))
PLOG(ERROR) << "Failed to delete " << suspended_state_path_.value();
}
if (!suspend_configurator_->UndoPrepareForSuspend())
return policy::Suspender::Delegate::SuspendResult::FAILURE;
// These exit codes are defined in powerd/powerd_suspend.
switch (exit_code) {
case 0:
return policy::Suspender::Delegate::SuspendResult::SUCCESS;
case 1:
return policy::Suspender::Delegate::SuspendResult::FAILURE;
case 2: // Wakeup event received before write to wakeup_count.
case 3: // Wakeup event received after write to wakeup_count.
return policy::Suspender::Delegate::SuspendResult::CANCELED;
default:
LOG(ERROR) << "Treating unexpected exit code " << exit_code
<< " as suspend failure";
return policy::Suspender::Delegate::SuspendResult::FAILURE;
}
}
void Daemon::UndoPrepareToSuspend(bool success, int num_suspend_attempts) {
LidState lid_state = input_watcher_->QueryLidState();
// Update the lid state first so that resume does not turn the internal
// backlight on if the lid is still closed on resume.
for (auto controller : all_backlight_controllers_)
controller->HandleLidStateChange(lid_state);
// Let State controller know about resume with the latest lid state.
state_controller_->HandleResume(lid_state);
// Resume the backlight right after announcing resume. This might be where we
// turn on the display, so we want to do this as early as possible. This
// happens when we idle suspend (and the requested power state in Chrome is
// off for the displays).
SetBacklightsSuspended(false);
if (audio_client_)
audio_client_->SetSuspended(false);
power_supply_->SetSuspended(false);
if (success)
metrics_collector_->HandleResume(num_suspend_attempts);
else if (num_suspend_attempts > 0)
metrics_collector_->HandleCanceledSuspendRequest(num_suspend_attempts);
}
void Daemon::GenerateDarkResumeMetrics(
const std::vector<policy::Suspender::DarkResumeInfo>&
dark_resume_wake_durations,
base::TimeDelta suspend_duration) {
metrics_collector_->GenerateDarkResumeMetrics(dark_resume_wake_durations,
suspend_duration);
}
void Daemon::ShutDownForFailedSuspend() {
ShutDown(ShutdownMode::POWER_OFF, ShutdownReason::SUSPEND_FAILED);
}
void Daemon::ShutDownFromSuspend() {
ShutDown(ShutdownMode::POWER_OFF, ShutdownReason::SHUTDOWN_FROM_SUSPEND);
}
void Daemon::SetWifiTransmitPower(RadioTransmitPower power,
WifiRegDomain domain) {
const std::string power_arg = (power == RadioTransmitPower::LOW)
? "--wifi_transmit_power_tablet"
: "--nowifi_transmit_power_tablet";
std::string domain_arg = "--wifi_transmit_power_domain=none";
switch (domain) {
case WifiRegDomain::FCC:
domain_arg = "--wifi_transmit_power_domain=fcc";
break;
case WifiRegDomain::EU:
domain_arg = "--wifi_transmit_power_domain=eu";
break;
case WifiRegDomain::REST_OF_WORLD:
domain_arg = "--wifi_transmit_power_domain=rest-of-world";
break;
default:
break;
}
const std::string args =
base::StringPrintf("%s %s", power_arg.c_str(), domain_arg.c_str());
LOG(INFO) << ((power == RadioTransmitPower::LOW) ? "Enabling" : "Disabling")
<< " tablet mode wifi transmit power";
RunSetuidHelper("set_wifi_transmit_power", args, false);
}
#if USE_TROGDOR_SAR_HACK
void Daemon::SetCellularTransmitPower(RadioTransmitPower power) {
const std::string args =
base::StringPrintf("--cellular_transmit_power_trogdor_level=%d", power);
RunSetuidHelper("set_cellular_transmit_power_trogdor", args, false);
}
#else // USE_TROGDOR_SAR_HACK
void Daemon::SetCellularTransmitPower(RadioTransmitPower power,
int64_t dpr_gpio_number) {
const bool is_power_low = (power == RadioTransmitPower::LOW);
const std::string args = base::StringPrintf(
"--cellular_transmit_power_low=%s "
"--cellular_transmit_power_gpio=%" PRId64,
is_power_low ? "true" : "false", dpr_gpio_number);
LOG(INFO) << "Setting cellular transmit power "
<< (is_power_low ? "low" : "high");
RunSetuidHelper("set_cellular_transmit_power", args, false);
}
#endif // USE_TROGDOR_SAR_HACK
void Daemon::OnAudioStateChange(bool active) {
if (active)
audio_activity_logger_->OnActivityStarted();
else
audio_activity_logger_->OnActivityStopped();
state_controller_->HandleAudioStateChange(active);
}
void Daemon::OnDBusNameOwnerChanged(const std::string& name,
const std::string& old_owner,
const std::string& new_owner) {
if (name == login_manager::kSessionManagerServiceName && !new_owner.empty()) {
LOG(INFO) << "D-Bus " << name << " ownership changed to " << new_owner;
HandleSessionManagerAvailableOrRestarted(true);
} else if (name == chromeos::kDisplayServiceName && !new_owner.empty()) {
LOG(INFO) << "D-Bus " << name << " ownership changed to " << new_owner;
HandleDisplayServiceAvailableOrRestarted(true);
}
suspender_->HandleDBusNameOwnerChanged(name, old_owner, new_owner);
}
void Daemon::OnPowerStatusUpdate() {
const system::PowerStatus status = power_supply_->GetPowerStatus();
if (status.battery_is_present)
LOG(INFO) << system::GetPowerStatusBatteryDebugString(status);
metrics_collector_->HandlePowerStatusUpdate(status);
const PowerSource power_source =
status.line_power_on ? PowerSource::AC : PowerSource::BATTERY;
for (auto controller : all_backlight_controllers_)
controller->HandlePowerSourceChange(power_source);
state_controller_->HandlePowerSourceChange(power_source);
thermal_event_handler_->HandlePowerSourceChange(power_source);
if (status.battery_is_present && status.battery_below_shutdown_threshold) {
if (factory_mode_) {
LOG(INFO) << "Battery is low, but not shutting down in factory mode";
} else {
LOG(INFO) << "Shutting down due to low battery ("
<< base::StringPrintf("%0.2f", status.battery_percentage)
<< "%, "
<< util::TimeDeltaToString(status.battery_time_to_empty)
<< " until empty, "
<< base::StringPrintf("%0.3f",
status.observed_battery_charge_rate)
<< "A observed charge rate)";
ShutDown(ShutdownMode::POWER_OFF, ShutdownReason::LOW_BATTERY);
}
}
}
void Daemon::InitDBus() {
dbus_wrapper_ = delegate_->CreateDBusWrapper();
dbus_wrapper_->AddObserver(this);
dbus::ObjectProxy* display_service_proxy = dbus_wrapper_->GetObjectProxy(
chromeos::kDisplayServiceName, chromeos::kDisplayServicePath);
dbus_wrapper_->RegisterForServiceAvailability(
display_service_proxy,
base::Bind(&Daemon::HandleDisplayServiceAvailableOrRestarted,
weak_ptr_factory_.GetWeakPtr()));
session_manager_dbus_proxy_ =
dbus_wrapper_->GetObjectProxy(login_manager::kSessionManagerServiceName,
login_manager::kSessionManagerServicePath);
dbus_wrapper_->RegisterForServiceAvailability(
session_manager_dbus_proxy_,
base::Bind(&Daemon::HandleSessionManagerAvailableOrRestarted,
weak_ptr_factory_.GetWeakPtr()));
dbus_wrapper_->RegisterForSignal(
session_manager_dbus_proxy_, login_manager::kSessionManagerInterface,
login_manager::kSessionStateChangedSignal,
base::Bind(&Daemon::HandleSessionStateChangedSignal,
weak_ptr_factory_.GetWeakPtr()));
int64_t tpm_threshold = 0;
prefs_->GetInt64(kTpmCounterSuspendThresholdPref, &tpm_threshold);
if (tpm_threshold > 0) {
cryptohomed_dbus_proxy_ = dbus_wrapper_->GetObjectProxy(
cryptohome::kCryptohomeServiceName, cryptohome::kCryptohomeServicePath);
dbus_wrapper_->RegisterForServiceAvailability(
cryptohomed_dbus_proxy_, base::Bind(&Daemon::HandleCryptohomedAvailable,
weak_ptr_factory_.GetWeakPtr()));
int64_t tpm_status_sec = 0;
prefs_->GetInt64(kTpmStatusIntervalSecPref, &tpm_status_sec);
tpm_status_interval_ = base::TimeDelta::FromSeconds(tpm_status_sec);
}
// Export Daemon's D-Bus method calls.
typedef std::unique_ptr<dbus::Response> (Daemon::*DaemonMethod)(
dbus::MethodCall*);
const std::map<const char*, DaemonMethod> kDaemonMethods = {
{kRequestShutdownMethod, &Daemon::HandleRequestShutdownMethod},
{kRequestRestartMethod, &Daemon::HandleRequestRestartMethod},
{kRequestSuspendMethod, &Daemon::HandleRequestSuspendMethod},
{kHandleVideoActivityMethod, &Daemon::HandleVideoActivityMethod},
{kHandleUserActivityMethod, &Daemon::HandleUserActivityMethod},
{kHandleWakeNotificationMethod, &Daemon::HandleWakeNotificationMethod},
{kSetIsProjectingMethod, &Daemon::HandleSetIsProjectingMethod},
{kSetPolicyMethod, &Daemon::HandleSetPolicyMethod},
{kSetBacklightsForcedOffMethod,
&Daemon::HandleSetBacklightsForcedOffMethod},
{kGetBacklightsForcedOffMethod,
&Daemon::HandleGetBacklightsForcedOffMethod},
{kHasAmbientColorDeviceMethod,
&Daemon::HandleHasAmbientColorDeviceMethod},
{kChangeWifiRegDomainMethod, &Daemon::HandleChangeWifiRegDomainMethod},
};
for (const auto& it : kDaemonMethods) {
dbus_wrapper_->ExportMethod(
it.first, base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(it.second, base::Unretained(this))));
}
#if USE_BUFFET
// There's no underlying dbus::Bus object when we're being tested.
dbus::Bus* bus = dbus_wrapper_->GetBus();
if (bus) {
buffet::InitCommandHandlers(
bus, base::Bind(&Daemon::ShutDown, weak_ptr_factory_.GetWeakPtr(),
ShutdownMode::REBOOT, ShutdownReason::USER_REQUEST));
}
#endif // USE_BUFFET
}
void Daemon::HandleDisplayServiceAvailableOrRestarted(bool available) {
if (!available) {
LOG(ERROR) << "Failed waiting for DisplayService to become available";
return;
}
for (auto controller : all_backlight_controllers_)
controller->HandleDisplayServiceStart();
// When running in the factory, we avoid initializing any backlight
// controllers, but we need to still tell Chrome to initially turn displays on
// so it will restore the correct display power state when returning from VT2:
// http://b/78436034
if (factory_mode_) {
DCHECK(all_backlight_controllers_.empty());
display_power_setter_->SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
base::TimeDelta());
}
}
void Daemon::HandleSessionManagerAvailableOrRestarted(bool available) {
if (!available) {
LOG(ERROR) << "Failed waiting for session manager to become available";
return;
}
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerRetrieveSessionState);
std::unique_ptr<dbus::Response> response = dbus_wrapper_->CallMethodSync(
session_manager_dbus_proxy_, &method_call,
base::TimeDelta::FromMilliseconds(kSessionManagerDBusTimeoutMs));
if (!response)
return;
std::string state;
dbus::MessageReader reader(response.get());
if (!reader.PopString(&state)) {
LOG(ERROR) << "Unable to read "
<< login_manager::kSessionManagerRetrieveSessionState << " args";
return;
}
OnSessionStateChange(state);
}
void Daemon::HandleCryptohomedAvailable(bool available) {
if (!available) {
LOG(ERROR) << "Failed waiting for cryptohomed to become available";
return;
}
if (!cryptohomed_dbus_proxy_)
return;
RequestTpmStatus();
if (tpm_status_interval_ > base::TimeDelta::FromSeconds(0)) {
tpm_status_timer_.Start(FROM_HERE, tpm_status_interval_, this,
&Daemon::RequestTpmStatus);
}
}
void Daemon::HandleSessionStateChangedSignal(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
std::string state;
if (reader.PopString(&state)) {
OnSessionStateChange(state);
} else {
LOG(ERROR) << "Unable to read " << login_manager::kSessionStateChangedSignal
<< " args";
}
}
void Daemon::HandleGetTpmStatusResponse(dbus::Response* response) {
if (!response) {
LOG(ERROR) << cryptohome::kCryptohomeGetTpmStatus << " call failed";
return;
}
cryptohome::BaseReply base_reply;
dbus::MessageReader reader(response);
if (!reader.PopArrayOfBytesAsProto(&base_reply)) {
LOG(ERROR) << "Unable to parse " << cryptohome::kCryptohomeGetTpmStatus
<< "response";
return;
}
if (base_reply.has_error()) {
LOG(ERROR) << cryptohome::kCryptohomeGetTpmStatus << " response contains "
<< "error code " << base_reply.error();
return;
}
if (!base_reply.HasExtension(cryptohome::GetTpmStatusReply::reply)) {
LOG(ERROR) << cryptohome::kCryptohomeGetTpmStatus << " response doesn't "
<< "contain nested reply";
return;
}
cryptohome::GetTpmStatusReply tpm_reply =
base_reply.GetExtension(cryptohome::GetTpmStatusReply::reply);
LOG(INFO) << "Received " << cryptohome::kCryptohomeGetTpmStatus
<< " response with dictionary attack count "
<< tpm_reply.dictionary_attack_counter();
state_controller_->HandleTpmStatus(tpm_reply.dictionary_attack_counter());
}
std::unique_ptr<dbus::Response> Daemon::HandleRequestShutdownMethod(
dbus::MethodCall* method_call) {
// Both arguments are optional.
dbus::MessageReader reader(method_call);
int32_t arg = 0;
ShutdownReason reason = ShutdownReason::OTHER_REQUEST_TO_POWERD;
if (reader.PopInt32(&arg)) {
switch (static_cast<RequestShutdownReason>(arg)) {
case REQUEST_SHUTDOWN_FOR_USER:
reason = ShutdownReason::USER_REQUEST;
break;
case REQUEST_SHUTDOWN_OTHER:
reason = ShutdownReason::OTHER_REQUEST_TO_POWERD;
break;
default:
LOG(WARNING) << "Got unknown shutdown reason " << arg;
}
}
std::string description;
reader.PopString(&description);
LOG(INFO) << "Got " << kRequestShutdownMethod << " message from "
<< method_call->GetSender() << " with reason "
<< ShutdownReasonToString(reason) << " (" << description << ")";
ShutDown(ShutdownMode::POWER_OFF, reason);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleRequestRestartMethod(
dbus::MethodCall* method_call) {
// Both arguments are optional.
dbus::MessageReader reader(method_call);
int32_t arg = 0;
ShutdownReason reason = ShutdownReason::OTHER_REQUEST_TO_POWERD;
if (reader.PopInt32(&arg)) {
switch (static_cast<RequestRestartReason>(arg)) {
case REQUEST_RESTART_FOR_USER:
reason = ShutdownReason::USER_REQUEST;
break;
case REQUEST_RESTART_FOR_UPDATE:
reason = ShutdownReason::SYSTEM_UPDATE;
break;
case REQUEST_RESTART_OTHER:
reason = ShutdownReason::OTHER_REQUEST_TO_POWERD;
break;
default:
LOG(WARNING) << "Got unknown restart reason " << arg;
}
}
std::string description;
reader.PopString(&description);
LOG(INFO) << "Got " << kRequestRestartMethod << " message from "
<< method_call->GetSender() << " with reason "
<< ShutdownReasonToString(reason) << " (" << description << ")";
ShutDown(ShutdownMode::REBOOT, reason);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleRequestSuspendMethod(
dbus::MethodCall* method_call) {
// Read an optional uint64_t argument specifying the wakeup count that is
// expected.
dbus::MessageReader reader(method_call);
uint64_t external_wakeup_count = 0;
const bool got_external_wakeup_count =
reader.PopUint64(&external_wakeup_count);
LOG(INFO) << "Got " << kRequestSuspendMethod << " message"
<< (got_external_wakeup_count
? base::StringPrintf(" with external wakeup count %" PRIu64,
external_wakeup_count)
.c_str()
: "")
<< " from " << method_call->GetSender();
// Read an optional int32_t argument specifying the wakeup timeout for a
// suspend request.
int32_t wakeup_timeout = 0;
reader.PopInt32(&wakeup_timeout);
base::TimeDelta duration = base::TimeDelta::FromSeconds(wakeup_timeout);
Suspend(SuspendImminent_Reason_OTHER, got_external_wakeup_count,
external_wakeup_count, duration);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleVideoActivityMethod(
dbus::MethodCall* method_call) {
bool fullscreen = false;
dbus::MessageReader reader(method_call);
if (!reader.PopBool(&fullscreen))
LOG(ERROR) << "Unable to read " << kHandleVideoActivityMethod << " args";
video_activity_logger_->OnActivityReported();
for (auto controller : all_backlight_controllers_)
controller->HandleVideoActivity(fullscreen);
state_controller_->HandleVideoActivity();
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleUserActivityMethod(
dbus::MethodCall* method_call) {
user_activity_logger_->OnActivityReported();
int type_int = USER_ACTIVITY_OTHER;
dbus::MessageReader reader(method_call);
if (!reader.PopInt32(&type_int))
LOG(ERROR) << "Unable to read " << kHandleUserActivityMethod << " args";
UserActivityType type = static_cast<UserActivityType>(type_int);
suspender_->HandleUserActivity();
state_controller_->HandleUserActivity();
for (auto controller : all_backlight_controllers_)
controller->HandleUserActivity(type);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleWakeNotificationMethod(
dbus::MethodCall* method_call) {
suspender_->HandleWakeNotification();
state_controller_->HandleWakeNotification();
for (auto controller : all_backlight_controllers_)
controller->HandleWakeNotification();
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleSetIsProjectingMethod(
dbus::MethodCall* method_call) {
bool is_projecting = false;
dbus::MessageReader reader(method_call);
if (!reader.PopBool(&is_projecting)) {
LOG(ERROR) << "Unable to read " << kSetIsProjectingMethod << " args";
return CreateInvalidArgsError(method_call, "Expected boolean state");
}
DisplayMode mode =
is_projecting ? DisplayMode::PRESENTATION : DisplayMode::NORMAL;
LOG(INFO) << "Chrome is using " << DisplayModeToString(mode)
<< " display mode";
state_controller_->HandleDisplayModeChange(mode);
suspender_->HandleDisplayModeChange(mode);
input_device_controller_->SetDisplayMode(mode);
for (auto controller : all_backlight_controllers_)
controller->HandleDisplayModeChange(mode);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleSetPolicyMethod(
dbus::MethodCall* method_call) {
PowerManagementPolicy policy;
dbus::MessageReader reader(method_call);
if (!reader.PopArrayOfBytesAsProto(&policy)) {
LOG(ERROR) << "Unable to parse " << kSetPolicyMethod << " request";
return CreateInvalidArgsError(method_call, "Expected protobuf");
}
LOG(INFO) << "Received updated external policy: "
<< policy::StateController::GetPolicyDebugString(policy);
state_controller_->HandlePolicyChange(policy);
if (charge_controller_) {
charge_controller_->HandlePolicyChange(policy);
}
for (auto controller : all_backlight_controllers_)
controller->HandlePolicyChange(policy);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleSetBacklightsForcedOffMethod(
dbus::MethodCall* method_call) {
bool force_off = false;
if (!dbus::MessageReader(method_call).PopBool(&force_off)) {
LOG(ERROR) << "Unable to read " << kSetBacklightsForcedOffMethod << " args";
return CreateInvalidArgsError(method_call, "Expected bool");
}
LOG(INFO) << "Received request to " << (force_off ? "start" : "stop")
<< " forcing backlights off";
for (auto controller : all_backlight_controllers_)
controller->SetForcedOff(force_off);
return nullptr;
}
std::unique_ptr<dbus::Response> Daemon::HandleGetBacklightsForcedOffMethod(
dbus::MethodCall* method_call) {
std::unique_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
// We can get the current state from any backlight controller.
bool forced_off = all_backlight_controllers_.empty()
? false
: all_backlight_controllers_.front()->GetForcedOff();
dbus::MessageWriter(response.get()).AppendBool(forced_off);
return response;
}
std::unique_ptr<dbus::Response> Daemon::HandleHasAmbientColorDeviceMethod(
dbus::MethodCall* method_call) {
std::unique_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
bool has_color_device = false;
if (light_sensor_manager_)
has_color_device = light_sensor_manager_->HasColorSensor();
dbus::MessageWriter(response.get()).AppendBool(has_color_device);
return response;
}
std::unique_ptr<dbus::Response> Daemon::HandleChangeWifiRegDomainMethod(
dbus::MethodCall* method_call) {
int32_t arg = 0;
dbus::MessageReader reader(method_call);
WifiRegDomain domain = WifiRegDomain::NONE;
if (!reader.PopInt32(&arg)) {
LOG(ERROR) << "Unable to read " << kChangeWifiRegDomainMethod << " args";
return CreateInvalidArgsError(method_call, "Expected Int32");
}
switch (static_cast<WifiRegDomainDbus>(arg)) {
case WIFI_REG_DOMAIN_FCC:
domain = WifiRegDomain::FCC;
break;
case WIFI_REG_DOMAIN_EU:
domain = WifiRegDomain::EU;
break;
case WIFI_REG_DOMAIN_REST_OF_WORLD:
domain = WifiRegDomain::REST_OF_WORLD;
break;
case WIFI_REG_DOMAIN_NONE:
break;
default:
LOG(WARNING) << "Got unknown WiFi regulatory domain " << arg;
}
LOG(INFO) << "Received request to change reg domain to \""
<< WifiRegDomainToString(domain) << "\"";
wifi_controller_->HandleRegDomainChange(domain);
return nullptr;
}
void Daemon::OnSessionStateChange(const std::string& state_str) {
SessionState state = (state_str == kSessionStarted) ? SessionState::STARTED
: SessionState::STOPPED;
if (state == session_state_)
return;
LOG(INFO) << "Session state changed to " << SessionStateToString(state);
session_state_ = state;
metrics_collector_->HandleSessionStateChange(state);
state_controller_->HandleSessionStateChange(state);
for (auto controller : all_backlight_controllers_)
controller->HandleSessionStateChange(state);
}
void Daemon::RequestTpmStatus() {
DCHECK(cryptohomed_dbus_proxy_);
dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
cryptohome::kCryptohomeGetTpmStatus);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(cryptohome::GetTpmStatusRequest());
dbus_wrapper_->CallMethodAsync(
cryptohomed_dbus_proxy_, &method_call,
base::TimeDelta::FromMilliseconds(kCryptohomedDBusTimeoutMs),
base::Bind(&Daemon::HandleGetTpmStatusResponse,
weak_ptr_factory_.GetWeakPtr()));
}
void Daemon::ShutDown(ShutdownMode mode, ShutdownReason reason) {
if (shutting_down_) {
LOG(INFO) << "Shutdown already initiated; ignoring additional request";
return;
}
std::string details;
if (SuspendAndShutdownAreBlocked(&details)) {
LOG(INFO) << "Postponing shutdown for lockfile(s): " << details;
if (!retry_shutdown_for_lockfile_timer_.IsRunning()) {
retry_shutdown_for_lockfile_timer_.Start(
FROM_HERE, kShutdownLockfileRetryInterval,
base::Bind(&Daemon::ShutDown, weak_ptr_factory_.GetWeakPtr(), mode,
reason));
}
return;
}
shutting_down_ = true;
retry_shutdown_for_lockfile_timer_.Stop();
suspender_->HandleShutdown();
metrics_collector_->HandleShutdown(reason);
for (auto controller : all_backlight_controllers_) {
// If we're going to display a low-battery alert while shutting down, don't
// turn the screen off immediately.
if (!(reason == ShutdownReason::LOW_BATTERY &&
controller == display_backlight_controller_.get()))
controller->SetShuttingDown(true);
}
const std::string reason_str = ShutdownReasonToString(reason);
switch (mode) {
case ShutdownMode::POWER_OFF:
LOG(INFO) << "Shutting down, reason: " << reason_str;
RunSetuidHelper("shut_down", "--shutdown_reason=" + reason_str, false);
break;
case ShutdownMode::REBOOT:
if (state_controller_->in_docked_mode()) {
LOG(INFO) << "In docked mode, so telling EC to force lid open to avoid "
<< "shutting down after reboot";
RunSetuidHelper("set_force_lid_open", "--force_lid_open", true);
}
LOG(INFO) << "Restarting, reason: " << reason_str;
RunSetuidHelper("reboot", "--shutdown_reason=" + reason_str, false);
break;
}
}
void Daemon::Suspend(SuspendImminent::Reason reason,
bool use_external_wakeup_count,
uint64_t external_wakeup_count,
base::TimeDelta duration) {
if (shutting_down_) {
LOG(INFO) << "Ignoring request for suspend with outstanding shutdown";
return;
}
if (use_external_wakeup_count) {
suspender_->RequestSuspendWithExternalWakeupCount(
reason, external_wakeup_count, duration);
} else {
suspender_->RequestSuspend(reason, duration);
}
}
void Daemon::SetBacklightsDimmedForInactivity(bool dimmed) {
for (auto controller : all_backlight_controllers_)
controller->SetDimmedForInactivity(dimmed);
metrics_collector_->HandleScreenDimmedChange(
dimmed, state_controller_->last_user_activity_time());
}
void Daemon::SetBacklightsOffForInactivity(bool off) {
for (auto controller : all_backlight_controllers_)
controller->SetOffForInactivity(off);
metrics_collector_->HandleScreenOffChange(
off, state_controller_->last_user_activity_time());
}
void Daemon::SetBacklightsSuspended(bool suspended) {
for (auto controller : all_backlight_controllers_)
controller->SetSuspended(suspended);
}
} // namespace power_manager