1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150

base / power_monitor / power_monitor_device_source_win.cc [blame]

// 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() const {
  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() const {
  // 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