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