// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/power_monitor/power_monitor_device_source.h"
#include <string>
#include "base/logging.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_source.h"
#include "base/strings/string_util.h"
#include "base/task/current_thread.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/win/wrapped_window_proc.h"
namespace base {
void ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event) {
PowerMonitorSource::ProcessPowerEvent(event);
}
namespace {
constexpr wchar_t kWindowClassName[] = L"Base_PowerMessageWindow";
void ProcessWmPowerBroadcastMessage(WPARAM event_id) {
PowerMonitorSource::PowerEvent power_event;
switch (event_id) {
case PBT_APMPOWERSTATUSCHANGE: // The power status changed.
power_event = PowerMonitorSource::POWER_STATE_EVENT;
break;
case PBT_APMRESUMEAUTOMATIC: // Resume from suspend.
// We don't notify for PBT_APMRESUMESUSPEND
// because, if it occurs, it is always sent as a
// second event after PBT_APMRESUMEAUTOMATIC.
power_event = PowerMonitorSource::RESUME_EVENT;
break;
case PBT_APMSUSPEND: // System has been suspended.
power_event = PowerMonitorSource::SUSPEND_EVENT;
break;
default:
return;
// Other Power Events:
// PBT_APMBATTERYLOW - removed in Vista.
// PBT_APMOEMEVENT - removed in Vista.
// PBT_APMQUERYSUSPEND - removed in Vista.
// PBT_APMQUERYSUSPENDFAILED - removed in Vista.
// PBT_APMRESUMECRITICAL - removed in Vista.
// PBT_POWERSETTINGCHANGE - user changed the power settings.
}
ProcessPowerEventHelper(power_event);
}
} // namespace
void PowerMonitorDeviceSource::PlatformInit() {
// Only for testing.
if (!CurrentUIThread::IsSet()) {
return;
}
speed_limit_observer_ =
std::make_unique<base::SequenceBound<SpeedLimitObserverWin>>(
base::ThreadPool::CreateSequencedTaskRunner({}),
BindRepeating(&PowerMonitorSource::ProcessSpeedLimitEvent));
}
void PowerMonitorDeviceSource::PlatformDestroy() {
// Because |speed_limit_observer_| is sequence bound, the actual destruction
// happens asynchronously on its task runner. Until this has completed it is
// still possible for PowerMonitorSource::ProcessSpeedLimitEvent to be called.
speed_limit_observer_.reset();
}
PowerStateObserver::BatteryPowerStatus
PowerMonitorDeviceSource::GetBatteryPowerStatus() {
SYSTEM_POWER_STATUS status;
if (!::GetSystemPowerStatus(&status)) {
DPLOG(ERROR) << "GetSystemPowerStatus failed";
return PowerStateObserver::BatteryPowerStatus::kUnknown;
}
return (status.ACLineStatus == 0)
? PowerStateObserver::BatteryPowerStatus::kBatteryPower
: PowerStateObserver::BatteryPowerStatus::kExternalPower;
}
int PowerMonitorDeviceSource::GetInitialSpeedLimit() {
// Returns the maximum value once at start. Subsequent actual values will be
// provided asynchronously via callbacks instead.
return PowerThermalObserver::kSpeedLimitMax;
}
PowerMonitorDeviceSource::PowerMessageWindow::PowerMessageWindow() {
if (!CurrentUIThread::IsSet()) {
// Creating this window in (e.g.) a renderer inhibits shutdown on Windows.
// See http://crbug.com/230122. TODO(vandebo): http://crbug.com/236031
DLOG(ERROR)
<< "Cannot create windows on non-UI thread, power monitor disabled!";
return;
}
WNDCLASSEX window_class;
base::win::InitializeWindowClass(
kWindowClassName,
&base::win::WrappedWindowProc<
PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk>,
0, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, &window_class);
instance_ = window_class.hInstance;
ATOM clazz = ::RegisterClassEx(&window_class);
DCHECK(clazz);
message_hwnd_ =
::CreateWindowEx(WS_EX_NOACTIVATE, kWindowClassName, nullptr, WS_POPUP, 0,
0, 0, 0, nullptr, nullptr, instance_, nullptr);
if (message_hwnd_) {
// On machines with modern standby calling RegisterSuspendResumeNotification
// is required in order to get the PBT_APMSUSPEND message.
power_notify_handle_ = ::RegisterSuspendResumeNotification(
message_hwnd_, DEVICE_NOTIFY_WINDOW_HANDLE);
}
}
PowerMonitorDeviceSource::PowerMessageWindow::~PowerMessageWindow() {
if (message_hwnd_) {
if (power_notify_handle_)
::UnregisterSuspendResumeNotification(power_notify_handle_);
::DestroyWindow(message_hwnd_);
::UnregisterClass(kWindowClassName, instance_);
}
}
// static
LRESULT CALLBACK PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk(
HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
switch (message) {
case WM_POWERBROADCAST:
ProcessWmPowerBroadcastMessage(wparam);
return TRUE;
default:
return ::DefWindowProc(hwnd, message, wparam, lparam);
}
}
} // namespace base