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