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 <IOKit/pwr_mgt/IOPMLib.h> 8 9 #include "base/bind.h" 10 #include "base/lazy_instance.h" 11 #include "base/mac/scoped_cftyperef.h" 12 #include "base/strings/sys_string_conversions.h" 13 #include "base/threading/platform_thread.h" 14 #include "base/threading/thread.h" 15 16 namespace content { 17 namespace { 18 19 // Power management cannot be done on the UI thread. IOPMAssertionCreate does a 20 // synchronous MIG call to configd, so if it is called on the main thread the UI 21 // is at the mercy of another process. See http://crbug.com/79559 and 22 // http://www.opensource.apple.com/source/IOKitUser/IOKitUser-514.16.31/pwr_mgt.subproj/IOPMLibPrivate.c . 23 struct PowerSaveBlockerLazyInstanceTraits { 24 static const bool kRegisterOnExit = false; 25 static const bool kAllowedToAccessOnNonjoinableThread = true; 26 27 static base::Thread* New(void* instance) { 28 base::Thread* thread = new (instance) base::Thread("PowerSaveBlocker"); 29 thread->Start(); 30 return thread; 31 } 32 static void Delete(base::Thread* instance) { } 33 }; 34 base::LazyInstance<base::Thread, PowerSaveBlockerLazyInstanceTraits> 35 g_power_thread = LAZY_INSTANCE_INITIALIZER; 36 37 } // namespace 38 39 class PowerSaveBlockerImpl::Delegate 40 : public base::RefCountedThreadSafe<PowerSaveBlockerImpl::Delegate> { 41 public: 42 Delegate(PowerSaveBlockerType type, const std::string& reason) 43 : type_(type), reason_(reason), assertion_(kIOPMNullAssertionID) {} 44 45 // Does the actual work to apply or remove the desired power save block. 46 void ApplyBlock(); 47 void RemoveBlock(); 48 49 private: 50 friend class base::RefCountedThreadSafe<Delegate>; 51 ~Delegate() {} 52 PowerSaveBlockerType type_; 53 std::string reason_; 54 IOPMAssertionID assertion_; 55 }; 56 57 void PowerSaveBlockerImpl::Delegate::ApplyBlock() { 58 DCHECK_EQ(base::PlatformThread::CurrentId(), 59 g_power_thread.Pointer()->thread_id()); 60 61 CFStringRef level = NULL; 62 // See QA1340 <http://developer.apple.com/library/mac/#qa/qa1340/> for more 63 // details. 64 switch (type_) { 65 case PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension: 66 level = kIOPMAssertionTypeNoIdleSleep; 67 break; 68 case PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep: 69 level = kIOPMAssertionTypeNoDisplaySleep; 70 break; 71 default: 72 NOTREACHED(); 73 break; 74 } 75 if (level) { 76 base::ScopedCFTypeRef<CFStringRef> cf_reason( 77 base::SysUTF8ToCFStringRef(reason_)); 78 IOReturn result = IOPMAssertionCreateWithName(level, 79 kIOPMAssertionLevelOn, 80 cf_reason, 81 &assertion_); 82 LOG_IF(ERROR, result != kIOReturnSuccess) 83 << "IOPMAssertionCreate: " << result; 84 } 85 } 86 87 void PowerSaveBlockerImpl::Delegate::RemoveBlock() { 88 DCHECK_EQ(base::PlatformThread::CurrentId(), 89 g_power_thread.Pointer()->thread_id()); 90 91 if (assertion_ != kIOPMNullAssertionID) { 92 IOReturn result = IOPMAssertionRelease(assertion_); 93 LOG_IF(ERROR, result != kIOReturnSuccess) 94 << "IOPMAssertionRelease: " << result; 95 } 96 } 97 98 PowerSaveBlockerImpl::PowerSaveBlockerImpl(PowerSaveBlockerType type, 99 const std::string& reason) 100 : delegate_(new Delegate(type, reason)) { 101 g_power_thread.Pointer()->message_loop()->PostTask( 102 FROM_HERE, 103 base::Bind(&Delegate::ApplyBlock, delegate_)); 104 } 105 106 PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { 107 g_power_thread.Pointer()->message_loop()->PostTask( 108 FROM_HERE, 109 base::Bind(&Delegate::RemoveBlock, delegate_)); 110 } 111 112 } // namespace content 113