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.
5 #include "chrome/browser/chromeos/session_length_limiter.h"
10 #include "ash/wm/user_activity_detector.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/prefs/pref_registry_simple.h"
16 #include "base/prefs/pref_service.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/lifetime/application_lifetime.h"
19 #include "chrome/common/pref_names.h"
20 #include "ui/events/event.h"
26 // The minimum session time limit that can be set.
27 const int kSessionLengthLimitMinMs
= 30 * 1000; // 30 seconds.
29 // The maximum session time limit that can be set.
30 const int kSessionLengthLimitMaxMs
= 24 * 60 * 60 * 1000; // 24 hours.
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
35 class SessionLengthLimiterDelegateImpl
: public SessionLengthLimiter::Delegate
{
37 SessionLengthLimiterDelegateImpl();
38 virtual ~SessionLengthLimiterDelegateImpl();
40 virtual const base::TimeTicks
GetCurrentTime() const OVERRIDE
;
41 virtual void StopSession() OVERRIDE
;
44 DISALLOW_COPY_AND_ASSIGN(SessionLengthLimiterDelegateImpl
);
47 SessionLengthLimiterDelegateImpl::SessionLengthLimiterDelegateImpl() {
50 SessionLengthLimiterDelegateImpl::~SessionLengthLimiterDelegateImpl() {
53 const base::TimeTicks
SessionLengthLimiterDelegateImpl::GetCurrentTime() const {
54 return base::TimeTicks::Now();
57 void SessionLengthLimiterDelegateImpl::StopSession() {
58 chrome::AttemptUserExit();
63 SessionLengthLimiter::Delegate::~Delegate() {
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
,
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());
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)));
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();
101 if (!user_activity_seen_
&& ash::Shell::HasInstance())
102 ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this);
105 SessionLengthLimiter::~SessionLengthLimiter() {
106 if (!user_activity_seen_
&& ash::Shell::HasInstance())
107 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
110 void SessionLengthLimiter::OnUserActivity(const ui::Event
* event
) {
111 if (user_activity_seen_
)
113 if (ash::Shell::HasInstance())
114 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
115 user_activity_seen_
= true;
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());
127 local_state
->CommitPendingWrite();
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()) {
142 session_start_time_
= session_start_time
;
143 user_activity_seen_
=
144 local_state
->GetBoolean(prefs::kSessionUserActivitySeen
);
150 void SessionLengthLimiter::UpdateSessionStartTime() {
151 DCHECK(thread_checker_
.CalledOnValidThread());
153 if (user_activity_seen_
)
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
);
161 session_start_time_
= delegate_
->GetCurrentTime();
162 local_state
->SetInt64(prefs::kSessionStartTime
,
163 session_start_time_
.ToInternalValue());
165 local_state
->CommitPendingWrite();
170 void SessionLengthLimiter::UpdateLimit() {
171 DCHECK(thread_checker_
.CalledOnValidThread());
173 // Stop any currently running timer.
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())
181 // If no session length limit is set, do not start a timer.
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
)) {
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
));
196 // Calculate the remaining session time.
197 const base::TimeDelta remaining
= session_length_limit
-
198 (delegate_
->GetCurrentTime() - session_start_time_
);
200 // Log out the user immediately if the session length limit has been reached
202 if (remaining
<= base::TimeDelta()) {
203 delegate_
->StopSession();
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
);
213 } // namespace chromeos