1 // Copyright 2013 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 "ash/wm/lock_state_controller.h"
10 #include "ash/accessibility_delegate.h"
11 #include "ash/ash_switches.h"
12 #include "ash/cancel_mode.h"
13 #include "ash/metrics/user_metrics_recorder.h"
14 #include "ash/shell.h"
15 #include "ash/shell_delegate.h"
16 #include "ash/shell_window_ids.h"
17 #include "ash/wm/session_state_animator.h"
18 #include "ash/wm/session_state_animator_impl.h"
19 #include "base/bind.h"
20 #include "base/bind_helpers.h"
21 #include "base/command_line.h"
22 #include "base/location.h"
23 #include "base/logging.h"
24 #include "base/strings/string_util.h"
25 #include "base/timer/timer.h"
26 #include "ui/aura/window_tree_host.h"
27 #include "ui/views/controls/menu/menu_controller.h"
28 #include "ui/wm/core/compound_event_filter.h"
30 #if defined(OS_CHROMEOS)
31 #include "base/sys_info.h"
32 #include "media/audio/sounds/sounds_manager.h"
35 #if defined(OS_CHROMEOS)
36 using media::SoundsManager
;
43 #if defined(OS_CHROMEOS)
44 const int kMaxShutdownSoundDurationMs
= 1500;
49 const int LockStateController::kLockTimeoutMs
= 400;
50 const int LockStateController::kShutdownTimeoutMs
= 400;
51 const int LockStateController::kLockFailTimeoutMs
= 8000;
52 const int LockStateController::kLockToShutdownTimeoutMs
= 150;
53 const int LockStateController::kShutdownRequestDelayMs
= 50;
55 LockStateController::TestApi::TestApi(LockStateController
* controller
)
56 : controller_(controller
) {
59 LockStateController::TestApi::~TestApi() {
62 LockStateController::LockStateController()
63 : animator_(new SessionStateAnimatorImpl()),
64 login_status_(user::LOGGED_IN_NONE
),
65 system_is_locked_(false),
66 shutting_down_(false),
67 shutdown_after_lock_(false),
68 animating_lock_(false),
69 can_cancel_lock_animation_(false),
70 weak_ptr_factory_(this) {
71 Shell::GetPrimaryRootWindow()->GetHost()->AddObserver(this);
74 LockStateController::~LockStateController() {
75 Shell::GetPrimaryRootWindow()->GetHost()->RemoveObserver(this);
78 void LockStateController::SetDelegate(
79 scoped_ptr
<LockStateControllerDelegate
> delegate
) {
80 delegate_
= delegate
.Pass();
83 void LockStateController::AddObserver(LockStateObserver
* observer
) {
84 observers_
.AddObserver(observer
);
87 void LockStateController::RemoveObserver(LockStateObserver
* observer
) {
88 observers_
.RemoveObserver(observer
);
91 bool LockStateController::HasObserver(const LockStateObserver
* observer
) const {
92 return observers_
.HasObserver(observer
);
95 void LockStateController::StartLockAnimation(
96 bool shutdown_after_lock
) {
99 shutdown_after_lock_
= shutdown_after_lock
;
100 can_cancel_lock_animation_
= true;
102 StartCancellablePreLockAnimation();
105 void LockStateController::StartShutdownAnimation() {
106 StartCancellableShutdownAnimation();
109 void LockStateController::StartLockAnimationAndLockImmediately(
110 bool shutdown_after_lock
) {
113 shutdown_after_lock_
= shutdown_after_lock
;
114 StartImmediatePreLockAnimation(true /* request_lock_on_completion */);
117 bool LockStateController::LockRequested() {
118 return lock_fail_timer_
.IsRunning();
121 bool LockStateController::ShutdownRequested() {
122 return shutting_down_
;
125 bool LockStateController::CanCancelLockAnimation() {
126 return can_cancel_lock_animation_
;
129 void LockStateController::CancelLockAnimation() {
130 if (!CanCancelLockAnimation())
132 shutdown_after_lock_
= false;
133 animating_lock_
= false;
134 CancelPreLockAnimation();
137 bool LockStateController::CanCancelShutdownAnimation() {
138 return pre_shutdown_timer_
.IsRunning() ||
139 shutdown_after_lock_
||
140 lock_to_shutdown_timer_
.IsRunning();
143 void LockStateController::CancelShutdownAnimation() {
144 if (!CanCancelShutdownAnimation())
146 if (lock_to_shutdown_timer_
.IsRunning()) {
147 lock_to_shutdown_timer_
.Stop();
150 if (shutdown_after_lock_
) {
151 shutdown_after_lock_
= false;
155 animator_
->StartAnimation(
156 SessionStateAnimator::ROOT_CONTAINER
,
157 SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS
,
158 SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN
);
159 pre_shutdown_timer_
.Stop();
162 void LockStateController::OnStartingLock() {
163 if (shutting_down_
|| system_is_locked_
)
167 StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
170 void LockStateController::RequestShutdown() {
174 shutting_down_
= true;
176 Shell
* shell
= ash::Shell::GetInstance();
177 shell
->cursor_manager()->HideCursor();
178 shell
->cursor_manager()->LockCursor();
180 animator_
->StartAnimation(
181 SessionStateAnimator::ROOT_CONTAINER
,
182 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
183 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
184 StartRealShutdownTimer(true);
187 void LockStateController::OnLockScreenHide(
188 base::Callback
<void(void)>& callback
) {
189 StartUnlockAnimationBeforeUIDestroyed(callback
);
192 void LockStateController::SetLockScreenDisplayedCallback(
193 const base::Closure
& callback
) {
194 lock_screen_displayed_callback_
= callback
;
197 void LockStateController::OnHostCloseRequested(
198 const aura::WindowTreeHost
* host
) {
199 Shell::GetInstance()->delegate()->Exit();
202 void LockStateController::OnLoginStateChanged(
203 user::LoginStatus status
) {
204 if (status
!= user::LOGGED_IN_LOCKED
)
205 login_status_
= status
;
206 system_is_locked_
= (status
== user::LOGGED_IN_LOCKED
);
209 void LockStateController::OnAppTerminating() {
210 // If we hear that Chrome is exiting but didn't request it ourselves, all we
211 // can really hope for is that we'll have time to clear the screen.
212 // This is also the case when the user signs off.
213 if (!shutting_down_
) {
214 shutting_down_
= true;
215 Shell
* shell
= ash::Shell::GetInstance();
216 shell
->cursor_manager()->HideCursor();
217 shell
->cursor_manager()->LockCursor();
218 animator_
->StartAnimation(SessionStateAnimator::kAllNonRootContainersMask
,
219 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
220 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
224 void LockStateController::OnLockStateChanged(bool locked
) {
225 VLOG(1) << "OnLockStateChanged " << locked
;
226 if (shutting_down_
|| (system_is_locked_
== locked
))
229 system_is_locked_
= locked
;
232 StartPostLockAnimation();
233 lock_fail_timer_
.Stop();
235 StartUnlockAnimationAfterUIDestroyed();
239 void LockStateController::OnLockFailTimeout() {
240 DCHECK(!system_is_locked_
);
241 LOG(FATAL
) << "Screen lock took too long; crashing intentionally";
244 void LockStateController::StartLockToShutdownTimer() {
245 shutdown_after_lock_
= false;
246 lock_to_shutdown_timer_
.Stop();
247 lock_to_shutdown_timer_
.Start(
249 base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs
),
250 this, &LockStateController::OnLockToShutdownTimeout
);
253 void LockStateController::OnLockToShutdownTimeout() {
254 DCHECK(system_is_locked_
);
255 StartCancellableShutdownAnimation();
258 void LockStateController::StartPreShutdownAnimationTimer() {
259 pre_shutdown_timer_
.Stop();
260 pre_shutdown_timer_
.Start(
262 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
),
264 &LockStateController::OnPreShutdownAnimationTimeout
);
267 void LockStateController::OnPreShutdownAnimationTimeout() {
268 VLOG(1) << "OnPreShutdownAnimationTimeout";
269 shutting_down_
= true;
271 Shell
* shell
= ash::Shell::GetInstance();
272 shell
->cursor_manager()->HideCursor();
274 StartRealShutdownTimer(false);
277 void LockStateController::StartRealShutdownTimer(bool with_animation_time
) {
278 base::TimeDelta duration
=
279 base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs
);
280 if (with_animation_time
) {
282 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
285 #if defined(OS_CHROMEOS)
286 const AccessibilityDelegate
* const delegate
=
287 Shell::GetInstance()->accessibility_delegate();
288 base::TimeDelta sound_duration
= delegate
->PlayShutdownSound();
290 std::min(sound_duration
,
291 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs
));
292 duration
= std::max(duration
, sound_duration
);
295 real_shutdown_timer_
.Start(
296 FROM_HERE
, duration
, base::Bind(&LockStateController::OnRealPowerTimeout
,
297 base::Unretained(this)));
300 void LockStateController::OnRealPowerTimeout() {
301 VLOG(1) << "OnRealPowerTimeout";
302 DCHECK(shutting_down_
);
303 #if defined(OS_CHROMEOS)
304 if (!base::SysInfo::IsRunningOnChromeOS()) {
305 ShellDelegate
* delegate
= Shell::GetInstance()->delegate();
312 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
313 UMA_ACCEL_SHUT_DOWN_POWER_BUTTON
);
314 delegate_
->RequestShutdown();
317 void LockStateController::StartCancellableShutdownAnimation() {
318 Shell
* shell
= ash::Shell::GetInstance();
319 // Hide cursor, but let it reappear if the mouse moves.
320 shell
->cursor_manager()->HideCursor();
322 animator_
->StartAnimation(
323 SessionStateAnimator::ROOT_CONTAINER
,
324 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
325 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
326 StartPreShutdownAnimationTimer();
329 void LockStateController::StartImmediatePreLockAnimation(
330 bool request_lock_on_completion
) {
331 VLOG(1) << "StartImmediatePreLockAnimation " << request_lock_on_completion
;
332 animating_lock_
= true;
333 StoreUnlockedProperties();
335 base::Closure next_animation_starter
=
336 base::Bind(&LockStateController::PreLockAnimationFinished
,
337 weak_ptr_factory_
.GetWeakPtr(),
338 request_lock_on_completion
);
339 SessionStateAnimator::AnimationSequence
* animation_sequence
=
340 animator_
->BeginAnimationSequence(next_animation_starter
);
342 animation_sequence
->StartAnimation(
343 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
344 SessionStateAnimator::ANIMATION_LIFT
,
345 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
346 animation_sequence
->StartAnimation(
347 SessionStateAnimator::LAUNCHER
,
348 SessionStateAnimator::ANIMATION_FADE_OUT
,
349 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
350 // Hide the screen locker containers so we can raise them later.
351 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
352 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
353 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
354 AnimateBackgroundAppearanceIfNecessary(
355 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
357 animation_sequence
->EndSequence();
359 DispatchCancelMode();
360 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
361 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED
));
364 void LockStateController::StartCancellablePreLockAnimation() {
365 animating_lock_
= true;
366 StoreUnlockedProperties();
367 VLOG(1) << "StartCancellablePreLockAnimation";
368 base::Closure next_animation_starter
=
369 base::Bind(&LockStateController::PreLockAnimationFinished
,
370 weak_ptr_factory_
.GetWeakPtr(),
371 true /* request_lock */);
372 SessionStateAnimator::AnimationSequence
* animation_sequence
=
373 animator_
->BeginAnimationSequence(next_animation_starter
);
375 animation_sequence
->StartAnimation(
376 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
377 SessionStateAnimator::ANIMATION_LIFT
,
378 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
379 animation_sequence
->StartAnimation(
380 SessionStateAnimator::LAUNCHER
,
381 SessionStateAnimator::ANIMATION_FADE_OUT
,
382 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
383 // Hide the screen locker containers so we can raise them later.
384 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
385 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
386 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
387 AnimateBackgroundAppearanceIfNecessary(
388 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
, animation_sequence
);
390 DispatchCancelMode();
391 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
392 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED
));
393 animation_sequence
->EndSequence();
396 void LockStateController::CancelPreLockAnimation() {
397 VLOG(1) << "CancelPreLockAnimation";
398 base::Closure next_animation_starter
=
399 base::Bind(&LockStateController::LockAnimationCancelled
,
400 weak_ptr_factory_
.GetWeakPtr());
401 SessionStateAnimator::AnimationSequence
* animation_sequence
=
402 animator_
->BeginAnimationSequence(next_animation_starter
);
404 animation_sequence
->StartAnimation(
405 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
406 SessionStateAnimator::ANIMATION_UNDO_LIFT
,
407 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
408 animation_sequence
->StartAnimation(
409 SessionStateAnimator::LAUNCHER
,
410 SessionStateAnimator::ANIMATION_FADE_IN
,
411 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
412 AnimateBackgroundHidingIfNecessary(
413 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
,
416 animation_sequence
->EndSequence();
419 void LockStateController::StartPostLockAnimation() {
420 VLOG(1) << "StartPostLockAnimation";
421 base::Closure next_animation_starter
=
422 base::Bind(&LockStateController::PostLockAnimationFinished
,
423 weak_ptr_factory_
.GetWeakPtr());
424 SessionStateAnimator::AnimationSequence
* animation_sequence
=
425 animator_
->BeginAnimationSequence(next_animation_starter
);
427 animation_sequence
->StartAnimation(
428 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
429 SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN
,
430 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
431 animation_sequence
->EndSequence();
434 void LockStateController::StartUnlockAnimationBeforeUIDestroyed(
435 base::Closure
& callback
) {
436 VLOG(1) << "StartUnlockAnimationBeforeUIDestroyed";
437 animator_
->StartAnimationWithCallback(
438 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
439 SessionStateAnimator::ANIMATION_LIFT
,
440 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
,
444 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
445 VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
446 base::Closure next_animation_starter
=
447 base::Bind(&LockStateController::UnlockAnimationAfterUIDestroyedFinished
,
448 weak_ptr_factory_
.GetWeakPtr());
449 SessionStateAnimator::AnimationSequence
* animation_sequence
=
450 animator_
->BeginAnimationSequence(next_animation_starter
);
452 animation_sequence
->StartAnimation(
453 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
454 SessionStateAnimator::ANIMATION_DROP
,
455 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
456 animation_sequence
->StartAnimation(
457 SessionStateAnimator::LAUNCHER
,
458 SessionStateAnimator::ANIMATION_FADE_IN
,
459 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
460 AnimateBackgroundHidingIfNecessary(
461 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
462 animation_sequence
->EndSequence();
465 void LockStateController::LockAnimationCancelled() {
466 can_cancel_lock_animation_
= false;
467 RestoreUnlockedProperties();
470 void LockStateController::PreLockAnimationFinished(bool request_lock
) {
471 VLOG(1) << "PreLockAnimationFinished";
472 can_cancel_lock_animation_
= false;
474 // Don't do anything (including starting the lock-fail timer) if the screen
475 // was already locked while the animation was going.
476 if (system_is_locked_
) {
477 DCHECK(!request_lock
) << "Got request to lock already-locked system "
478 << "at completion of pre-lock animation";
483 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
484 shutdown_after_lock_
?
485 UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON
:
486 UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON
);
487 delegate_
->RequestLockScreen();
490 base::TimeDelta timeout
=
491 base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs
);
492 #if defined(OS_CHROMEOS)
493 // Increase lock timeout for slower hardware, see http://crbug.com/350628
494 const std::string board
= base::SysInfo::GetLsbReleaseBoard();
495 if (board
== "x86-mario" ||
496 StartsWithASCII(board
, "x86-alex", true /* case_sensitive */) ||
497 StartsWithASCII(board
, "x86-zgb", true /* case_sensitive */) ||
498 StartsWithASCII(board
, "daisy", true /* case_sensitive */)) {
502 lock_fail_timer_
.Start(
503 FROM_HERE
, timeout
, this, &LockStateController::OnLockFailTimeout
);
506 void LockStateController::PostLockAnimationFinished() {
507 animating_lock_
= false;
508 VLOG(1) << "PostLockAnimationFinished";
509 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
510 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED
));
511 if (!lock_screen_displayed_callback_
.is_null()) {
512 lock_screen_displayed_callback_
.Run();
513 lock_screen_displayed_callback_
.Reset();
515 CHECK(!views::MenuController::GetActiveInstance());
516 if (shutdown_after_lock_
) {
517 shutdown_after_lock_
= false;
518 StartLockToShutdownTimer();
522 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() {
523 RestoreUnlockedProperties();
526 void LockStateController::StoreUnlockedProperties() {
527 if (!unlocked_properties_
) {
528 unlocked_properties_
.reset(new UnlockedStateProperties());
529 unlocked_properties_
->background_is_hidden
=
530 animator_
->IsBackgroundHidden();
532 if (unlocked_properties_
->background_is_hidden
) {
533 // Hide background so that it can be animated later.
534 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
535 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
536 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
537 animator_
->ShowBackground();
541 void LockStateController::RestoreUnlockedProperties() {
542 if (!unlocked_properties_
)
544 if (unlocked_properties_
->background_is_hidden
) {
545 animator_
->HideBackground();
546 // Restore background visibility.
547 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
548 SessionStateAnimator::ANIMATION_FADE_IN
,
549 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
551 unlocked_properties_
.reset();
554 void LockStateController::AnimateBackgroundAppearanceIfNecessary(
555 SessionStateAnimator::AnimationSpeed speed
,
556 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
557 if (unlocked_properties_
.get() &&
558 unlocked_properties_
->background_is_hidden
) {
559 animation_sequence
->StartAnimation(
560 SessionStateAnimator::DESKTOP_BACKGROUND
,
561 SessionStateAnimator::ANIMATION_FADE_IN
,
566 void LockStateController::AnimateBackgroundHidingIfNecessary(
567 SessionStateAnimator::AnimationSpeed speed
,
568 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
569 if (unlocked_properties_
.get() &&
570 unlocked_properties_
->background_is_hidden
) {
571 animation_sequence
->StartAnimation(
572 SessionStateAnimator::DESKTOP_BACKGROUND
,
573 SessionStateAnimator::ANIMATION_FADE_OUT
,