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 #ifndef NDEBUG 26 static const bool kAllowedToAccessOnNonjoinableThread = true; 27 #endif 28 29 static base::Thread* New(void* instance) { 30 base::Thread* thread = new (instance) base::Thread("PowerSaveBlocker"); 31 thread->Start(); 32 return thread; 33 } 34 static void Delete(base::Thread* instance) { } 35 }; 36 base::LazyInstance<base::Thread, PowerSaveBlockerLazyInstanceTraits> 37 g_power_thread = LAZY_INSTANCE_INITIALIZER; 38 39 } // namespace 40 41 class PowerSaveBlockerImpl::Delegate 42 : public base::RefCountedThreadSafe<PowerSaveBlockerImpl::Delegate> { 43 public: 44 Delegate(PowerSaveBlockerType type, const std::string& reason) 45 : type_(type), reason_(reason), assertion_(kIOPMNullAssertionID) {} 46 47 // Does the actual work to apply or remove the desired power save block. 48 void ApplyBlock(); 49 void RemoveBlock(); 50 51 private: 52 friend class base::RefCountedThreadSafe<Delegate>; 53 ~Delegate() {} 54 PowerSaveBlockerType type_; 55 std::string reason_; 56 IOPMAssertionID assertion_; 57 }; 58 59 void PowerSaveBlockerImpl::Delegate::ApplyBlock() { 60 DCHECK_EQ(base::PlatformThread::CurrentId(), 61 g_power_thread.Pointer()->thread_id()); 62 63 CFStringRef level = NULL; 64 // See QA1340 <http://developer.apple.com/library/mac/#qa/qa1340/> for more 65 // details. 66 switch (type_) { 67 case PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension: 68 level = kIOPMAssertionTypeNoIdleSleep; 69 break; 70 case PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep: 71 level = kIOPMAssertionTypeNoDisplaySleep; 72 break; 73 default: 74 NOTREACHED(); 75 break; 76 } 77 if (level) { 78 base::ScopedCFTypeRef<CFStringRef> cf_reason( 79 base::SysUTF8ToCFStringRef(reason_)); 80 IOReturn result = IOPMAssertionCreateWithName(level, 81 kIOPMAssertionLevelOn, 82 cf_reason, 83 &assertion_); 84 LOG_IF(ERROR, result != kIOReturnSuccess) 85 << "IOPMAssertionCreate: " << result; 86 } 87 } 88 89 void PowerSaveBlockerImpl::Delegate::RemoveBlock() { 90 DCHECK_EQ(base::PlatformThread::CurrentId(), 91 g_power_thread.Pointer()->thread_id()); 92 93 if (assertion_ != kIOPMNullAssertionID) { 94 IOReturn result = IOPMAssertionRelease(assertion_); 95 LOG_IF(ERROR, result != kIOReturnSuccess) 96 << "IOPMAssertionRelease: " << result; 97 } 98 } 99 100 PowerSaveBlockerImpl::PowerSaveBlockerImpl(PowerSaveBlockerType type, 101 const std::string& reason) 102 : delegate_(new Delegate(type, reason)) { 103 g_power_thread.Pointer()->message_loop()->PostTask( 104 FROM_HERE, 105 base::Bind(&Delegate::ApplyBlock, delegate_)); 106 } 107 108 PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { 109 g_power_thread.Pointer()->message_loop()->PostTask( 110 FROM_HERE, 111 base::Bind(&Delegate::RemoveBlock, delegate_)); 112 } 113 114 } // namespace content 115