Home | History | Annotate | Download | only in chromeos
      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/chromeos/session_length_limiter.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ash/shell.h"
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/location.h"
     13 #include "base/logging.h"
     14 #include "base/prefs/pref_registry_simple.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/lifetime/application_lifetime.h"
     18 #include "chrome/common/pref_names.h"
     19 #include "ui/events/event.h"
     20 #include "ui/wm/core/user_activity_detector.h"
     21 
     22 namespace chromeos {
     23 
     24 namespace {
     25 
     26 // The minimum session time limit that can be set.
     27 const int kSessionLengthLimitMinMs = 30 * 1000; // 30 seconds.
     28 
     29 // The maximum session time limit that can be set.
     30 const int kSessionLengthLimitMaxMs = 24 * 60 * 60 * 1000; // 24 hours.
     31 
     32 // A default delegate implementation that returns the current time and does end
     33 // the current user's session when requested. This can be replaced with a mock
     34 // in tests.
     35 class SessionLengthLimiterDelegateImpl : public SessionLengthLimiter::Delegate {
     36  public:
     37   SessionLengthLimiterDelegateImpl();
     38   virtual ~SessionLengthLimiterDelegateImpl();
     39 
     40   virtual const base::TimeTicks GetCurrentTime() const OVERRIDE;
     41   virtual void StopSession() OVERRIDE;
     42 
     43  private:
     44   DISALLOW_COPY_AND_ASSIGN(SessionLengthLimiterDelegateImpl);
     45 };
     46 
     47 SessionLengthLimiterDelegateImpl::SessionLengthLimiterDelegateImpl() {
     48 }
     49 
     50 SessionLengthLimiterDelegateImpl::~SessionLengthLimiterDelegateImpl() {
     51 }
     52 
     53 const base::TimeTicks SessionLengthLimiterDelegateImpl::GetCurrentTime() const {
     54   return base::TimeTicks::Now();
     55 }
     56 
     57 void SessionLengthLimiterDelegateImpl::StopSession() {
     58   chrome::AttemptUserExit();
     59 }
     60 
     61 }  // namespace
     62 
     63 SessionLengthLimiter::Delegate::~Delegate() {
     64 }
     65 
     66 // static
     67 void SessionLengthLimiter::RegisterPrefs(PrefRegistrySimple* registry) {
     68   registry->RegisterBooleanPref(prefs::kSessionUserActivitySeen, false);
     69   registry->RegisterInt64Pref(prefs::kSessionStartTime, 0);
     70   registry->RegisterIntegerPref(prefs::kSessionLengthLimit, 0);
     71   registry->RegisterBooleanPref(prefs::kSessionWaitForInitialUserActivity,
     72                                 false);
     73 }
     74 
     75 SessionLengthLimiter::SessionLengthLimiter(Delegate* delegate,
     76                                            bool browser_restarted)
     77     : delegate_(delegate ? delegate : new SessionLengthLimiterDelegateImpl),
     78       user_activity_seen_(false) {
     79   DCHECK(thread_checker_.CalledOnValidThread());
     80 
     81   PrefService* local_state = g_browser_process->local_state();
     82   pref_change_registrar_.Init(local_state);
     83   pref_change_registrar_.Add(prefs::kSessionLengthLimit,
     84                              base::Bind(&SessionLengthLimiter::UpdateLimit,
     85                                         base::Unretained(this)));
     86   pref_change_registrar_.Add(
     87       prefs::kSessionWaitForInitialUserActivity,
     88       base::Bind(&SessionLengthLimiter::UpdateSessionStartTime,
     89                  base::Unretained(this)));
     90 
     91   // If this is a browser restart after a crash, try to restore the session
     92   // start time and the boolean indicating user activity from local state. If
     93   // this is not a browser restart after a crash or the attempt to restore
     94   // fails, set  the session start time to the current time and clear the
     95   // boolean indicating user activity.
     96   if (!browser_restarted || !RestoreStateAfterCrash()) {
     97     local_state->ClearPref(prefs::kSessionUserActivitySeen);
     98     UpdateSessionStartTime();
     99   }
    100 
    101   if (!user_activity_seen_ && ash::Shell::HasInstance())
    102     ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this);
    103 }
    104 
    105 SessionLengthLimiter::~SessionLengthLimiter() {
    106   if (!user_activity_seen_ && ash::Shell::HasInstance())
    107     ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
    108 }
    109 
    110 void SessionLengthLimiter::OnUserActivity(const ui::Event* event) {
    111   if (user_activity_seen_)
    112     return;
    113   if (ash::Shell::HasInstance())
    114     ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
    115   user_activity_seen_ = true;
    116 
    117   PrefService* local_state = g_browser_process->local_state();
    118   local_state->SetBoolean(prefs::kSessionUserActivitySeen, true);
    119   if (session_start_time_.is_null()) {
    120     // If instructed to wait for initial user activity and this is the first
    121     // activity in the session, set the session start time to the current time
    122     // and persist it in local state.
    123     session_start_time_ = delegate_->GetCurrentTime();
    124     local_state->SetInt64(prefs::kSessionStartTime,
    125                           session_start_time_.ToInternalValue());
    126   }
    127   local_state->CommitPendingWrite();
    128 
    129   UpdateLimit();
    130 }
    131 
    132 bool SessionLengthLimiter::RestoreStateAfterCrash() {
    133   PrefService* local_state = g_browser_process->local_state();
    134   const base::TimeTicks session_start_time =
    135       base::TimeTicks::FromInternalValue(
    136           local_state->GetInt64(prefs::kSessionStartTime));
    137   if (session_start_time.is_null() ||
    138       session_start_time >= delegate_->GetCurrentTime()) {
    139     return false;
    140   }
    141 
    142   session_start_time_ = session_start_time;
    143   user_activity_seen_ =
    144       local_state->GetBoolean(prefs::kSessionUserActivitySeen);
    145 
    146   UpdateLimit();
    147   return true;
    148 }
    149 
    150 void SessionLengthLimiter::UpdateSessionStartTime() {
    151   DCHECK(thread_checker_.CalledOnValidThread());
    152 
    153   if (user_activity_seen_)
    154     return;
    155 
    156   PrefService* local_state = g_browser_process->local_state();
    157   if (local_state->GetBoolean(prefs::kSessionWaitForInitialUserActivity)) {
    158     session_start_time_ = base::TimeTicks();
    159     local_state->ClearPref(prefs::kSessionStartTime);
    160   } else {
    161     session_start_time_ = delegate_->GetCurrentTime();
    162     local_state->SetInt64(prefs::kSessionStartTime,
    163                           session_start_time_.ToInternalValue());
    164   }
    165   local_state->CommitPendingWrite();
    166 
    167   UpdateLimit();
    168 }
    169 
    170 void SessionLengthLimiter::UpdateLimit() {
    171   DCHECK(thread_checker_.CalledOnValidThread());
    172 
    173   // Stop any currently running timer.
    174   timer_.reset();
    175 
    176   // If instructed to wait for initial user activity and no user activity has
    177   // occurred yet, do not start a timer.
    178   if (session_start_time_.is_null())
    179     return;
    180 
    181   // If no session length limit is set, do not start a timer.
    182   int limit;
    183   const PrefService::Preference* session_length_limit_pref =
    184       pref_change_registrar_.prefs()->
    185           FindPreference(prefs::kSessionLengthLimit);
    186   if (session_length_limit_pref->IsDefaultValue() ||
    187       !session_length_limit_pref->GetValue()->GetAsInteger(&limit)) {
    188     return;
    189   }
    190 
    191   // Clamp the session length limit to the valid range.
    192   const base::TimeDelta session_length_limit =
    193       base::TimeDelta::FromMilliseconds(std::min(std::max(
    194           limit, kSessionLengthLimitMinMs), kSessionLengthLimitMaxMs));
    195 
    196   // Calculate the remaining session time.
    197   const base::TimeDelta remaining = session_length_limit -
    198       (delegate_->GetCurrentTime() - session_start_time_);
    199 
    200   // Log out the user immediately if the session length limit has been reached
    201   // or exceeded.
    202   if (remaining <= base::TimeDelta()) {
    203     delegate_->StopSession();
    204     return;
    205   }
    206 
    207   // Set a timer to log out the user when the session length limit is reached.
    208   timer_.reset(new base::OneShotTimer<SessionLengthLimiter::Delegate>);
    209   timer_->Start(FROM_HERE, remaining, delegate_.get(),
    210                 &SessionLengthLimiter::Delegate::StopSession);
    211 }
    212 
    213 }  // namespace chromeos
    214