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
16namespace content {
17namespace {
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 .
23struct 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};
36base::LazyInstance<base::Thread, PowerSaveBlockerLazyInstanceTraits>
37    g_power_thread = LAZY_INSTANCE_INITIALIZER;
38
39}  // namespace
40
41class 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
59void 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
89void 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
100PowerSaveBlockerImpl::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
108PowerSaveBlockerImpl::~PowerSaveBlockerImpl() {
109  g_power_thread.Pointer()->message_loop()->PostTask(
110      FROM_HERE,
111      base::Bind(&Delegate::RemoveBlock, delegate_));
112}
113
114}  // namespace content
115