// Copyright (c) 2012 The Chromium 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 "content/browser/power_save_blocker_impl.h" #include <windows.h> #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_handle.h" #include "base/win/windows_version.h" #include "content/public/browser/browser_thread.h" namespace content { namespace { int g_blocker_count[2]; HANDLE CreatePowerRequest(POWER_REQUEST_TYPE type, const std::string& reason) { typedef HANDLE (WINAPI* PowerCreateRequestPtr)(PREASON_CONTEXT); typedef BOOL (WINAPI* PowerSetRequestPtr)(HANDLE, POWER_REQUEST_TYPE); if (type == PowerRequestExecutionRequired && base::win::GetVersion() < base::win::VERSION_WIN8) { return INVALID_HANDLE_VALUE; } static PowerCreateRequestPtr PowerCreateRequestFn = NULL; static PowerSetRequestPtr PowerSetRequestFn = NULL; if (!PowerCreateRequestFn || !PowerSetRequestFn) { HMODULE module = GetModuleHandle(L"kernel32.dll"); PowerCreateRequestFn = reinterpret_cast<PowerCreateRequestPtr>( GetProcAddress(module, "PowerCreateRequest")); PowerSetRequestFn = reinterpret_cast<PowerSetRequestPtr>( GetProcAddress(module, "PowerSetRequest")); if (!PowerCreateRequestFn || !PowerSetRequestFn) return INVALID_HANDLE_VALUE; } base::string16 wide_reason = ASCIIToUTF16(reason); REASON_CONTEXT context = {0}; context.Version = POWER_REQUEST_CONTEXT_VERSION; context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; context.Reason.SimpleReasonString = const_cast<wchar_t*>(wide_reason.c_str()); base::win::ScopedHandle handle(PowerCreateRequestFn(&context)); if (!handle.IsValid()) return INVALID_HANDLE_VALUE; if (PowerSetRequestFn(handle, type)) return handle.Take(); // Something went wrong. return INVALID_HANDLE_VALUE; } // Takes ownership of the |handle|. void DeletePowerRequest(POWER_REQUEST_TYPE type, HANDLE handle) { base::win::ScopedHandle request_handle(handle); if (!request_handle.IsValid()) return; if (type == PowerRequestExecutionRequired && base::win::GetVersion() < base::win::VERSION_WIN8) { return; } typedef BOOL (WINAPI* PowerClearRequestPtr)(HANDLE, POWER_REQUEST_TYPE); HMODULE module = GetModuleHandle(L"kernel32.dll"); PowerClearRequestPtr PowerClearRequestFn = reinterpret_cast<PowerClearRequestPtr>( GetProcAddress(module, "PowerClearRequest")); if (!PowerClearRequestFn) return; BOOL success = PowerClearRequestFn(request_handle, type); DCHECK(success); } void ApplySimpleBlock(PowerSaveBlocker::PowerSaveBlockerType type, int delta) { g_blocker_count[type] += delta; DCHECK_GE(g_blocker_count[type], 0); if (g_blocker_count[type] > 1) return; DWORD this_flag = 0; if (type == PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension) this_flag |= ES_SYSTEM_REQUIRED; else this_flag |= ES_DISPLAY_REQUIRED; DCHECK(this_flag); static DWORD flags = ES_CONTINUOUS; if (!g_blocker_count[type]) flags &= ~this_flag; else flags |= this_flag; SetThreadExecutionState(flags); } } // namespace class PowerSaveBlockerImpl::Delegate : public base::RefCountedThreadSafe<PowerSaveBlockerImpl::Delegate> { public: Delegate(PowerSaveBlockerType type, const std::string& reason) : type_(type), reason_(reason) {} // Does the actual work to apply or remove the desired power save block. void ApplyBlock(); void RemoveBlock(); // Returns the equivalent POWER_REQUEST_TYPE for this request. POWER_REQUEST_TYPE RequestType(); private: friend class base::RefCountedThreadSafe<Delegate>; ~Delegate() {} PowerSaveBlockerType type_; const std::string reason_; base::win::ScopedHandle handle_; DISALLOW_COPY_AND_ASSIGN(Delegate); }; void PowerSaveBlockerImpl::Delegate::ApplyBlock() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (base::win::GetVersion() < base::win::VERSION_WIN7) return ApplySimpleBlock(type_, 1); handle_.Set(CreatePowerRequest(RequestType(), reason_)); } void PowerSaveBlockerImpl::Delegate::RemoveBlock() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (base::win::GetVersion() < base::win::VERSION_WIN7) return ApplySimpleBlock(type_, -1); DeletePowerRequest(RequestType(), handle_.Take()); } POWER_REQUEST_TYPE PowerSaveBlockerImpl::Delegate::RequestType() { if (type_ == kPowerSaveBlockPreventDisplaySleep) return PowerRequestDisplayRequired; if (base::win::GetVersion() < base::win::VERSION_WIN8) return PowerRequestSystemRequired; return PowerRequestExecutionRequired; } PowerSaveBlockerImpl::PowerSaveBlockerImpl(PowerSaveBlockerType type, const std::string& reason) : delegate_(new Delegate(type, reason)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&Delegate::ApplyBlock, delegate_)); } PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&Delegate::RemoveBlock, delegate_)); } } // namespace content