1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/power_save_blocker_impl.h"
6
7#include <windows.h>
8
9#include "base/logging.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/win/scoped_handle.h"
12#include "base/win/windows_version.h"
13#include "content/public/browser/browser_thread.h"
14
15namespace content {
16namespace {
17
18int g_blocker_count[2];
19
20HANDLE CreatePowerRequest(POWER_REQUEST_TYPE type, const std::string& reason) {
21  typedef HANDLE (WINAPI* PowerCreateRequestPtr)(PREASON_CONTEXT);
22  typedef BOOL (WINAPI* PowerSetRequestPtr)(HANDLE, POWER_REQUEST_TYPE);
23
24  if (type == PowerRequestExecutionRequired &&
25      base::win::GetVersion() < base::win::VERSION_WIN8) {
26    return INVALID_HANDLE_VALUE;
27  }
28
29  static PowerCreateRequestPtr PowerCreateRequestFn = NULL;
30  static PowerSetRequestPtr PowerSetRequestFn = NULL;
31
32  if (!PowerCreateRequestFn || !PowerSetRequestFn) {
33    HMODULE module = GetModuleHandle(L"kernel32.dll");
34    PowerCreateRequestFn = reinterpret_cast<PowerCreateRequestPtr>(
35        GetProcAddress(module, "PowerCreateRequest"));
36    PowerSetRequestFn = reinterpret_cast<PowerSetRequestPtr>(
37        GetProcAddress(module, "PowerSetRequest"));
38
39    if (!PowerCreateRequestFn || !PowerSetRequestFn)
40      return INVALID_HANDLE_VALUE;
41  }
42  base::string16 wide_reason = base::ASCIIToUTF16(reason);
43  REASON_CONTEXT context = {0};
44  context.Version = POWER_REQUEST_CONTEXT_VERSION;
45  context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
46  context.Reason.SimpleReasonString = const_cast<wchar_t*>(wide_reason.c_str());
47
48  base::win::ScopedHandle handle(PowerCreateRequestFn(&context));
49  if (!handle.IsValid())
50    return INVALID_HANDLE_VALUE;
51
52  if (PowerSetRequestFn(handle.Get(), type))
53    return handle.Take();
54
55  // Something went wrong.
56  return INVALID_HANDLE_VALUE;
57}
58
59// Takes ownership of the |handle|.
60void DeletePowerRequest(POWER_REQUEST_TYPE type, HANDLE handle) {
61  base::win::ScopedHandle request_handle(handle);
62  if (!request_handle.IsValid())
63    return;
64
65  if (type == PowerRequestExecutionRequired &&
66      base::win::GetVersion() < base::win::VERSION_WIN8) {
67    return;
68  }
69
70  typedef BOOL (WINAPI* PowerClearRequestPtr)(HANDLE, POWER_REQUEST_TYPE);
71  HMODULE module = GetModuleHandle(L"kernel32.dll");
72  PowerClearRequestPtr PowerClearRequestFn =
73      reinterpret_cast<PowerClearRequestPtr>(
74          GetProcAddress(module, "PowerClearRequest"));
75
76  if (!PowerClearRequestFn)
77    return;
78
79  BOOL success = PowerClearRequestFn(request_handle.Get(), type);
80  DCHECK(success);
81}
82
83void ApplySimpleBlock(PowerSaveBlocker::PowerSaveBlockerType type,
84                      int delta) {
85  g_blocker_count[type] += delta;
86  DCHECK_GE(g_blocker_count[type], 0);
87
88  if (g_blocker_count[type] > 1)
89    return;
90
91  DWORD this_flag = 0;
92  if (type == PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension)
93    this_flag |= ES_SYSTEM_REQUIRED;
94  else
95    this_flag |= ES_DISPLAY_REQUIRED;
96
97  DCHECK(this_flag);
98
99  static DWORD flags = ES_CONTINUOUS;
100  if (!g_blocker_count[type])
101    flags &= ~this_flag;
102  else
103    flags |= this_flag;
104
105  SetThreadExecutionState(flags);
106}
107
108}  // namespace
109
110class PowerSaveBlockerImpl::Delegate
111    : public base::RefCountedThreadSafe<PowerSaveBlockerImpl::Delegate> {
112 public:
113  Delegate(PowerSaveBlockerType type, const std::string& reason)
114      : type_(type), reason_(reason) {}
115
116  // Does the actual work to apply or remove the desired power save block.
117  void ApplyBlock();
118  void RemoveBlock();
119
120  // Returns the equivalent POWER_REQUEST_TYPE for this request.
121  POWER_REQUEST_TYPE RequestType();
122
123 private:
124  friend class base::RefCountedThreadSafe<Delegate>;
125  ~Delegate() {}
126
127  PowerSaveBlockerType type_;
128  const std::string reason_;
129  base::win::ScopedHandle handle_;
130
131  DISALLOW_COPY_AND_ASSIGN(Delegate);
132};
133
134void PowerSaveBlockerImpl::Delegate::ApplyBlock() {
135  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
136  if (base::win::GetVersion() < base::win::VERSION_WIN7)
137    return ApplySimpleBlock(type_, 1);
138
139  handle_.Set(CreatePowerRequest(RequestType(), reason_));
140}
141
142void PowerSaveBlockerImpl::Delegate::RemoveBlock() {
143  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
144  if (base::win::GetVersion() < base::win::VERSION_WIN7)
145    return ApplySimpleBlock(type_, -1);
146
147  DeletePowerRequest(RequestType(), handle_.Take());
148}
149
150POWER_REQUEST_TYPE PowerSaveBlockerImpl::Delegate::RequestType() {
151  if (type_ == kPowerSaveBlockPreventDisplaySleep)
152    return PowerRequestDisplayRequired;
153
154  if (base::win::GetVersion() < base::win::VERSION_WIN8)
155    return PowerRequestSystemRequired;
156
157  return PowerRequestExecutionRequired;
158}
159
160PowerSaveBlockerImpl::PowerSaveBlockerImpl(PowerSaveBlockerType type,
161                                           const std::string& reason)
162    : delegate_(new Delegate(type, reason)) {
163  BrowserThread::PostTask(
164      BrowserThread::UI, FROM_HERE,
165      base::Bind(&Delegate::ApplyBlock, delegate_));
166}
167
168PowerSaveBlockerImpl::~PowerSaveBlockerImpl() {
169  BrowserThread::PostTask(
170      BrowserThread::UI, FROM_HERE,
171      base::Bind(&Delegate::RemoveBlock, delegate_));
172}
173
174}  // namespace content
175