|  | // 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 <IOKit/pwr_mgt/IOPMLib.h> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/mac/scoped_cftyperef.h" | 
|  | #include "base/strings/sys_string_conversions.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/threading/thread.h" | 
|  |  | 
|  | namespace content { | 
|  | namespace { | 
|  |  | 
|  | // Power management cannot be done on the UI thread. IOPMAssertionCreate does a | 
|  | // synchronous MIG call to configd, so if it is called on the main thread the UI | 
|  | // is at the mercy of another process. See http://crbug.com/79559 and | 
|  | // http://www.opensource.apple.com/source/IOKitUser/IOKitUser-514.16.31/pwr_mgt.subproj/IOPMLibPrivate.c . | 
|  | struct PowerSaveBlockerLazyInstanceTraits { | 
|  | static const bool kRegisterOnExit = false; | 
|  | #ifndef NDEBUG | 
|  | static const bool kAllowedToAccessOnNonjoinableThread = true; | 
|  | #endif | 
|  |  | 
|  | static base::Thread* New(void* instance) { | 
|  | base::Thread* thread = new (instance) base::Thread("PowerSaveBlocker"); | 
|  | thread->Start(); | 
|  | return thread; | 
|  | } | 
|  | static void Delete(base::Thread* instance) { } | 
|  | }; | 
|  | base::LazyInstance<base::Thread, PowerSaveBlockerLazyInstanceTraits> | 
|  | g_power_thread = LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class PowerSaveBlockerImpl::Delegate | 
|  | : public base::RefCountedThreadSafe<PowerSaveBlockerImpl::Delegate> { | 
|  | public: | 
|  | Delegate(PowerSaveBlockerType type, const std::string& description) | 
|  | : type_(type), | 
|  | description_(description), | 
|  | assertion_(kIOPMNullAssertionID) {} | 
|  |  | 
|  | // Does the actual work to apply or remove the desired power save block. | 
|  | void ApplyBlock(); | 
|  | void RemoveBlock(); | 
|  |  | 
|  | private: | 
|  | friend class base::RefCountedThreadSafe<Delegate>; | 
|  | ~Delegate() {} | 
|  | PowerSaveBlockerType type_; | 
|  | std::string description_; | 
|  | IOPMAssertionID assertion_; | 
|  | }; | 
|  |  | 
|  | void PowerSaveBlockerImpl::Delegate::ApplyBlock() { | 
|  | DCHECK_EQ(base::PlatformThread::CurrentId(), | 
|  | g_power_thread.Pointer()->GetThreadId()); | 
|  |  | 
|  | CFStringRef level = NULL; | 
|  | // See QA1340 <http://developer.apple.com/library/mac/#qa/qa1340/> for more | 
|  | // details. | 
|  | switch (type_) { | 
|  | case PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension: | 
|  | level = kIOPMAssertionTypeNoIdleSleep; | 
|  | break; | 
|  | case PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep: | 
|  | level = kIOPMAssertionTypeNoDisplaySleep; | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | if (level) { | 
|  | base::ScopedCFTypeRef<CFStringRef> cf_description( | 
|  | base::SysUTF8ToCFStringRef(description_)); | 
|  | IOReturn result = IOPMAssertionCreateWithName(level, kIOPMAssertionLevelOn, | 
|  | cf_description, &assertion_); | 
|  | LOG_IF(ERROR, result != kIOReturnSuccess) | 
|  | << "IOPMAssertionCreate: " << result; | 
|  | } | 
|  | } | 
|  |  | 
|  | void PowerSaveBlockerImpl::Delegate::RemoveBlock() { | 
|  | DCHECK_EQ(base::PlatformThread::CurrentId(), | 
|  | g_power_thread.Pointer()->GetThreadId()); | 
|  |  | 
|  | if (assertion_ != kIOPMNullAssertionID) { | 
|  | IOReturn result = IOPMAssertionRelease(assertion_); | 
|  | LOG_IF(ERROR, result != kIOReturnSuccess) | 
|  | << "IOPMAssertionRelease: " << result; | 
|  | } | 
|  | } | 
|  |  | 
|  | PowerSaveBlockerImpl::PowerSaveBlockerImpl( | 
|  | PowerSaveBlockerType type, | 
|  | Reason reason, | 
|  | const std::string& description, | 
|  | scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | 
|  | scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner) | 
|  | : delegate_(new Delegate(type, description)), | 
|  | ui_task_runner_(ui_task_runner), | 
|  | blocking_task_runner_(blocking_task_runner) { | 
|  | g_power_thread.Pointer()->message_loop()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&Delegate::ApplyBlock, delegate_)); | 
|  | } | 
|  |  | 
|  | PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { | 
|  | g_power_thread.Pointer()->message_loop()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&Delegate::RemoveBlock, delegate_)); | 
|  | } | 
|  |  | 
|  | }  // namespace content |