1// Copyright 2014 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 "extensions/browser/api/power/power_api_manager.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "extensions/browser/extension_registry.h"
10#include "extensions/common/extension.h"
11
12namespace extensions {
13
14namespace {
15
16const char kPowerSaveBlockerReason[] = "extension";
17
18content::PowerSaveBlocker::PowerSaveBlockerType
19LevelToPowerSaveBlockerType(core_api::power::Level level) {
20  switch (level) {
21    case core_api::power::LEVEL_SYSTEM:
22      return content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension;
23    case core_api::power::LEVEL_DISPLAY:  // fallthrough
24    case core_api::power::LEVEL_NONE:
25      return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep;
26  }
27  NOTREACHED() << "Unhandled level " << level;
28  return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep;
29}
30
31base::LazyInstance<BrowserContextKeyedAPIFactory<PowerApiManager> > g_factory =
32    LAZY_INSTANCE_INITIALIZER;
33
34}  // namespace
35
36// static
37PowerApiManager* PowerApiManager::Get(content::BrowserContext* context) {
38  return BrowserContextKeyedAPIFactory<PowerApiManager>::Get(context);
39}
40
41// static
42BrowserContextKeyedAPIFactory<PowerApiManager>*
43PowerApiManager::GetFactoryInstance() {
44  return g_factory.Pointer();
45}
46
47void PowerApiManager::AddRequest(const std::string& extension_id,
48                                 core_api::power::Level level) {
49  extension_levels_[extension_id] = level;
50  UpdatePowerSaveBlocker();
51}
52
53void PowerApiManager::RemoveRequest(const std::string& extension_id) {
54  extension_levels_.erase(extension_id);
55  UpdatePowerSaveBlocker();
56}
57
58void PowerApiManager::SetCreateBlockerFunctionForTesting(
59    CreateBlockerFunction function) {
60  create_blocker_function_ = !function.is_null() ? function :
61      base::Bind(&content::PowerSaveBlocker::Create);
62}
63
64void PowerApiManager::OnExtensionUnloaded(
65    content::BrowserContext* browser_context,
66    const Extension* extension,
67    UnloadedExtensionInfo::Reason reason) {
68  RemoveRequest(extension->id());
69  UpdatePowerSaveBlocker();
70}
71
72PowerApiManager::PowerApiManager(content::BrowserContext* context)
73    : browser_context_(context),
74      create_blocker_function_(base::Bind(&content::PowerSaveBlocker::Create)),
75      current_level_(core_api::power::LEVEL_SYSTEM) {
76  ExtensionRegistry::Get(browser_context_)->AddObserver(this);
77}
78
79PowerApiManager::~PowerApiManager() {}
80
81void PowerApiManager::UpdatePowerSaveBlocker() {
82  if (extension_levels_.empty()) {
83    power_save_blocker_.reset();
84    return;
85  }
86
87  core_api::power::Level new_level = core_api::power::LEVEL_SYSTEM;
88  for (ExtensionLevelMap::const_iterator it = extension_levels_.begin();
89       it != extension_levels_.end(); ++it) {
90    if (it->second == core_api::power::LEVEL_DISPLAY)
91      new_level = it->second;
92  }
93
94  // If the level changed and we need to create a new blocker, do a swap
95  // to ensure that there isn't a brief period where power management is
96  // unblocked.
97  if (!power_save_blocker_ || new_level != current_level_) {
98    content::PowerSaveBlocker::PowerSaveBlockerType type =
99        LevelToPowerSaveBlockerType(new_level);
100    scoped_ptr<content::PowerSaveBlocker> new_blocker(
101        create_blocker_function_.Run(type, kPowerSaveBlockerReason));
102    power_save_blocker_.swap(new_blocker);
103    current_level_ = new_level;
104  }
105}
106
107void PowerApiManager::Shutdown() {
108  // Unregister here rather than in the d'tor; otherwise this call will recreate
109  // the already-deleted ExtensionRegistry.
110  ExtensionRegistry::Get(browser_context_)->RemoveObserver(this);
111  power_save_blocker_.reset();
112}
113
114}  // namespace extensions
115