1// Copyright (c) 2013 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 "chrome/browser/extensions/api/power/power_api_manager.h"
6
7#include "base/bind.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/common/extensions/extension.h"
10#include "content/public/browser/notification_service.h"
11
12namespace extensions {
13
14namespace {
15
16const char kPowerSaveBlockerReason[] = "extension";
17
18content::PowerSaveBlocker::PowerSaveBlockerType
19LevelToPowerSaveBlockerType(api::power::Level level) {
20  switch (level) {
21    case api::power::LEVEL_SYSTEM:
22      return content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension;
23    case api::power::LEVEL_DISPLAY:  // fallthrough
24    case api::power::LEVEL_NONE:
25      return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep;
26  }
27  NOTREACHED() << "Unhandled level " << level;
28  return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep;
29}
30
31}  // namespace
32
33// static
34PowerApiManager* PowerApiManager::GetInstance() {
35  return Singleton<PowerApiManager>::get();
36}
37
38void PowerApiManager::AddRequest(const std::string& extension_id,
39                                 api::power::Level level) {
40  extension_levels_[extension_id] = level;
41  UpdatePowerSaveBlocker();
42}
43
44void PowerApiManager::RemoveRequest(const std::string& extension_id) {
45  extension_levels_.erase(extension_id);
46  UpdatePowerSaveBlocker();
47}
48
49void PowerApiManager::SetCreateBlockerFunctionForTesting(
50    CreateBlockerFunction function) {
51  create_blocker_function_ = !function.is_null() ? function :
52      base::Bind(&content::PowerSaveBlocker::Create);
53}
54
55void PowerApiManager::Observe(int type,
56                              const content::NotificationSource& source,
57                              const content::NotificationDetails& details) {
58  switch (type) {
59    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
60      RemoveRequest(content::Details<extensions::UnloadedExtensionInfo>(
61          details)->extension->id());
62      UpdatePowerSaveBlocker();
63      break;
64    case chrome::NOTIFICATION_APP_TERMINATING:
65      power_save_blocker_.reset();
66      break;
67    default:
68      NOTREACHED() << "Unexpected notification " << type;
69  }
70}
71
72PowerApiManager::PowerApiManager()
73    : create_blocker_function_(base::Bind(&content::PowerSaveBlocker::Create)),
74      current_level_(api::power::LEVEL_SYSTEM) {
75  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
76                  content::NotificationService::AllSources());
77  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
78                 content::NotificationService::AllSources());
79}
80
81PowerApiManager::~PowerApiManager() {}
82
83void PowerApiManager::UpdatePowerSaveBlocker() {
84  if (extension_levels_.empty()) {
85    power_save_blocker_.reset();
86    return;
87  }
88
89  api::power::Level new_level = api::power::LEVEL_SYSTEM;
90  for (ExtensionLevelMap::const_iterator it = extension_levels_.begin();
91       it != extension_levels_.end(); ++it) {
92    if (it->second == api::power::LEVEL_DISPLAY)
93      new_level = it->second;
94  }
95
96  // If the level changed and we need to create a new blocker, do a swap
97  // to ensure that there isn't a brief period where power management is
98  // unblocked.
99  if (!power_save_blocker_ || new_level != current_level_) {
100    content::PowerSaveBlocker::PowerSaveBlockerType type =
101        LevelToPowerSaveBlockerType(new_level);
102    scoped_ptr<content::PowerSaveBlocker> new_blocker(
103        create_blocker_function_.Run(type, kPowerSaveBlockerReason));
104    power_save_blocker_.swap(new_blocker);
105    current_level_ = new_level;
106  }
107}
108
109}  // namespace extensions
110