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 "chrome/browser/extensions/api/idle/idle_manager.h"
6
7#include <utility>
8
9#include "base/stl_util.h"
10#include "chrome/browser/extensions/api/idle/idle_api_constants.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/common/extensions/api/idle.h"
13#include "chrome/common/extensions/extension_constants.h"
14#include "extensions/browser/event_router.h"
15#include "extensions/browser/extension_registry.h"
16#include "extensions/common/extension.h"
17
18namespace keys = extensions::idle_api_constants;
19namespace idle = extensions::api::idle;
20
21namespace extensions {
22
23namespace {
24
25const int kDefaultIdleThreshold = 60;
26const int kPollInterval = 1;
27
28class DefaultEventDelegate : public IdleManager::EventDelegate {
29 public:
30  explicit DefaultEventDelegate(Profile* profile);
31  virtual ~DefaultEventDelegate();
32
33  virtual void OnStateChanged(const std::string& extension_id,
34                              IdleState new_state) OVERRIDE;
35  virtual void RegisterObserver(EventRouter::Observer* observer) OVERRIDE;
36  virtual void UnregisterObserver(EventRouter::Observer* observer) OVERRIDE;
37
38 private:
39  Profile* profile_;
40};
41
42DefaultEventDelegate::DefaultEventDelegate(Profile* profile)
43    : profile_(profile) {
44}
45
46DefaultEventDelegate::~DefaultEventDelegate() {
47}
48
49void DefaultEventDelegate::OnStateChanged(const std::string& extension_id,
50                                          IdleState new_state) {
51  scoped_ptr<base::ListValue> args(new base::ListValue());
52  args->Append(IdleManager::CreateIdleValue(new_state));
53  scoped_ptr<Event> event(new Event(idle::OnStateChanged::kEventName,
54                                    args.Pass()));
55  event->restrict_to_browser_context = profile_;
56  EventRouter::Get(profile_)
57      ->DispatchEventToExtension(extension_id, event.Pass());
58}
59
60void DefaultEventDelegate::RegisterObserver(
61    EventRouter::Observer* observer) {
62  EventRouter::Get(profile_)
63      ->RegisterObserver(observer, idle::OnStateChanged::kEventName);
64}
65
66void DefaultEventDelegate::UnregisterObserver(EventRouter::Observer* observer) {
67  EventRouter::Get(profile_)->UnregisterObserver(observer);
68}
69
70class DefaultIdleProvider : public IdleManager::IdleTimeProvider {
71 public:
72  DefaultIdleProvider();
73  virtual ~DefaultIdleProvider();
74
75  virtual void CalculateIdleState(int idle_threshold,
76                                  IdleCallback notify) OVERRIDE;
77  virtual void CalculateIdleTime(IdleTimeCallback notify) OVERRIDE;
78  virtual bool CheckIdleStateIsLocked() OVERRIDE;
79};
80
81DefaultIdleProvider::DefaultIdleProvider() {
82}
83
84DefaultIdleProvider::~DefaultIdleProvider() {
85}
86
87void DefaultIdleProvider::CalculateIdleState(int idle_threshold,
88                                             IdleCallback notify) {
89  ::CalculateIdleState(idle_threshold, notify);
90}
91
92void DefaultIdleProvider::CalculateIdleTime(IdleTimeCallback notify) {
93  ::CalculateIdleTime(notify);
94}
95
96bool DefaultIdleProvider::CheckIdleStateIsLocked() {
97  return ::CheckIdleStateIsLocked();
98}
99
100IdleState IdleTimeToIdleState(bool locked, int idle_time, int idle_threshold) {
101  IdleState state;
102
103  if (locked) {
104    state = IDLE_STATE_LOCKED;
105  } else if (idle_time >= idle_threshold) {
106    state = IDLE_STATE_IDLE;
107  } else {
108    state = IDLE_STATE_ACTIVE;
109  }
110  return state;
111}
112
113}  // namespace
114
115IdleMonitor::IdleMonitor(IdleState initial_state)
116    : last_state(initial_state),
117      listeners(0),
118      threshold(kDefaultIdleThreshold) {
119}
120
121IdleManager::IdleManager(Profile* profile)
122    : profile_(profile),
123      last_state_(IDLE_STATE_ACTIVE),
124      idle_time_provider_(new DefaultIdleProvider()),
125      event_delegate_(new DefaultEventDelegate(profile)),
126      extension_registry_observer_(this),
127      weak_factory_(this) {
128}
129
130IdleManager::~IdleManager() {
131}
132
133void IdleManager::Init() {
134  extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
135  event_delegate_->RegisterObserver(this);
136}
137
138void IdleManager::Shutdown() {
139  DCHECK(thread_checker_.CalledOnValidThread());
140  event_delegate_->UnregisterObserver(this);
141}
142
143void IdleManager::OnExtensionUnloaded(content::BrowserContext* browser_context,
144                                      const Extension* extension,
145                                      UnloadedExtensionInfo::Reason reason) {
146  DCHECK(thread_checker_.CalledOnValidThread());
147  monitors_.erase(extension->id());
148}
149
150void IdleManager::OnListenerAdded(const EventListenerInfo& details) {
151  DCHECK(thread_checker_.CalledOnValidThread());
152
153  ++GetMonitor(details.extension_id)->listeners;
154  StartPolling();
155}
156
157void IdleManager::OnListenerRemoved(const EventListenerInfo& details) {
158  DCHECK(thread_checker_.CalledOnValidThread());
159
160  // During unload the monitor could already have been deleted. No need to do
161  // anything in that case.
162  MonitorMap::iterator it = monitors_.find(details.extension_id);
163  if (it != monitors_.end()) {
164    DCHECK_GT(it->second.listeners, 0);
165    // Note: Deliberately leave the listener count as 0 rather than erase()ing
166    // this record so that the threshold doesn't get reset when all listeners
167    // are removed.
168    --it->second.listeners;
169  }
170}
171
172void IdleManager::QueryState(int threshold, QueryStateCallback notify) {
173  DCHECK(thread_checker_.CalledOnValidThread());
174  idle_time_provider_->CalculateIdleState(threshold, notify);
175}
176
177void IdleManager::SetThreshold(const std::string& extension_id,
178                               int threshold) {
179  DCHECK(thread_checker_.CalledOnValidThread());
180  GetMonitor(extension_id)->threshold = threshold;
181}
182
183// static
184base::StringValue* IdleManager::CreateIdleValue(IdleState idle_state) {
185  const char* description;
186
187  if (idle_state == IDLE_STATE_ACTIVE) {
188    description = keys::kStateActive;
189  } else if (idle_state == IDLE_STATE_IDLE) {
190    description = keys::kStateIdle;
191  } else {
192    description = keys::kStateLocked;
193  }
194
195  return new base::StringValue(description);
196}
197
198void IdleManager::SetEventDelegateForTest(
199    scoped_ptr<EventDelegate> event_delegate) {
200  DCHECK(thread_checker_.CalledOnValidThread());
201  event_delegate_ = event_delegate.Pass();
202}
203
204void IdleManager::SetIdleTimeProviderForTest(
205    scoped_ptr<IdleTimeProvider> idle_time_provider) {
206  DCHECK(thread_checker_.CalledOnValidThread());
207  idle_time_provider_ = idle_time_provider.Pass();
208}
209
210IdleMonitor* IdleManager::GetMonitor(const std::string& extension_id) {
211  DCHECK(thread_checker_.CalledOnValidThread());
212  MonitorMap::iterator it = monitors_.find(extension_id);
213
214  if (it == monitors_.end()) {
215    it = monitors_.insert(std::make_pair(extension_id,
216                                         IdleMonitor(last_state_))).first;
217  }
218  return &it->second;
219}
220
221void IdleManager::StartPolling() {
222  DCHECK(thread_checker_.CalledOnValidThread());
223  if (!poll_timer_.IsRunning()) {
224    poll_timer_.Start(FROM_HERE,
225                      base::TimeDelta::FromSeconds(kPollInterval),
226                      this,
227                      &IdleManager::UpdateIdleState);
228  }
229}
230
231void IdleManager::StopPolling() {
232  DCHECK(thread_checker_.CalledOnValidThread());
233  poll_timer_.Stop();
234}
235
236void IdleManager::UpdateIdleState() {
237  DCHECK(thread_checker_.CalledOnValidThread());
238  idle_time_provider_->CalculateIdleTime(
239      base::Bind(
240          &IdleManager::UpdateIdleStateCallback,
241          weak_factory_.GetWeakPtr()));
242}
243
244void IdleManager::UpdateIdleStateCallback(int idle_time) {
245  DCHECK(thread_checker_.CalledOnValidThread());
246  bool locked = idle_time_provider_->CheckIdleStateIsLocked();
247  int listener_count = 0;
248
249  // Remember this state for initializing new event listeners.
250  last_state_ = IdleTimeToIdleState(locked,
251                                    idle_time,
252                                    kDefaultIdleThreshold);
253
254  for (MonitorMap::iterator it = monitors_.begin();
255       it != monitors_.end(); ++it) {
256    IdleMonitor& monitor = it->second;
257    IdleState new_state =
258        IdleTimeToIdleState(locked, idle_time, monitor.threshold);
259    // TODO(kalman): Use EventRouter::HasListeners for these sorts of checks.
260    if (monitor.listeners > 0 && monitor.last_state != new_state)
261      event_delegate_->OnStateChanged(it->first, new_state);
262    monitor.last_state = new_state;
263    listener_count += monitor.listeners;
264  }
265
266  if (listener_count == 0)
267    StopPolling();
268}
269
270}  // namespace extensions
271