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_helpers.h"
20 #include "base/command_line.h"
21 #include "base/strings/string_util.h"
22 #include "base/timer/timer.h"
23 #include "ui/aura/window_tree_host.h"
24 #include "ui/views/controls/menu/menu_controller.h"
25 #include "ui/wm/core/compound_event_filter.h"
27 #if defined(OS_CHROMEOS)
28 #include "base/sys_info.h"
29 #include "media/audio/sounds/sounds_manager.h"
32 #if defined(OS_CHROMEOS)
33 using media::SoundsManager
;
40 #if defined(OS_CHROMEOS)
41 const int kMaxShutdownSoundDurationMs
= 1500;
46 const int LockStateController::kLockTimeoutMs
= 400;
47 const int LockStateController::kShutdownTimeoutMs
= 400;
48 const int LockStateController::kLockFailTimeoutMs
= 8000;
49 const int LockStateController::kLockToShutdownTimeoutMs
= 150;
50 const int LockStateController::kShutdownRequestDelayMs
= 50;
52 LockStateController::TestApi::TestApi(LockStateController
* controller
)
53 : controller_(controller
) {
56 LockStateController::TestApi::~TestApi() {
59 LockStateController::LockStateController()
60 : animator_(new SessionStateAnimatorImpl()),
61 login_status_(user::LOGGED_IN_NONE
),
62 system_is_locked_(false),
63 shutting_down_(false),
64 shutdown_after_lock_(false),
65 animating_lock_(false),
66 can_cancel_lock_animation_(false),
67 weak_ptr_factory_(this) {
68 Shell::GetPrimaryRootWindow()->GetHost()->AddObserver(this);
71 LockStateController::~LockStateController() {
72 Shell::GetPrimaryRootWindow()->GetHost()->RemoveObserver(this);
75 void LockStateController::SetDelegate(
76 scoped_ptr
<LockStateControllerDelegate
> delegate
) {
77 delegate_
= delegate
.Pass();
80 void LockStateController::AddObserver(LockStateObserver
* observer
) {
81 observers_
.AddObserver(observer
);
84 void LockStateController::RemoveObserver(LockStateObserver
* observer
) {
85 observers_
.RemoveObserver(observer
);
88 bool LockStateController::HasObserver(const LockStateObserver
* observer
) const {
89 return observers_
.HasObserver(observer
);
92 void LockStateController::StartLockAnimation(
93 bool shutdown_after_lock
) {
96 shutdown_after_lock_
= shutdown_after_lock
;
97 can_cancel_lock_animation_
= true;
99 StartCancellablePreLockAnimation();
102 void LockStateController::StartShutdownAnimation() {
103 StartCancellableShutdownAnimation();
106 void LockStateController::StartLockAnimationAndLockImmediately(
107 bool shutdown_after_lock
) {
110 shutdown_after_lock_
= shutdown_after_lock
;
111 StartImmediatePreLockAnimation(true /* request_lock_on_completion */);
114 bool LockStateController::LockRequested() {
115 return lock_fail_timer_
.IsRunning();
118 bool LockStateController::ShutdownRequested() {
119 return shutting_down_
;
122 bool LockStateController::CanCancelLockAnimation() {
123 return can_cancel_lock_animation_
;
126 void LockStateController::CancelLockAnimation() {
127 if (!CanCancelLockAnimation())
129 shutdown_after_lock_
= false;
130 animating_lock_
= false;
131 CancelPreLockAnimation();
134 bool LockStateController::CanCancelShutdownAnimation() {
135 return pre_shutdown_timer_
.IsRunning() ||
136 shutdown_after_lock_
||
137 lock_to_shutdown_timer_
.IsRunning();
140 void LockStateController::CancelShutdownAnimation() {
141 if (!CanCancelShutdownAnimation())
143 if (lock_to_shutdown_timer_
.IsRunning()) {
144 lock_to_shutdown_timer_
.Stop();
147 if (shutdown_after_lock_
) {
148 shutdown_after_lock_
= false;
152 animator_
->StartAnimation(
153 SessionStateAnimator::ROOT_CONTAINER
,
154 SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS
,
155 SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN
);
156 pre_shutdown_timer_
.Stop();
159 void LockStateController::OnStartingLock() {
160 if (shutting_down_
|| system_is_locked_
)
164 StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
167 void LockStateController::RequestShutdown() {
171 shutting_down_
= true;
173 Shell
* shell
= ash::Shell::GetInstance();
174 shell
->cursor_manager()->HideCursor();
175 shell
->cursor_manager()->LockCursor();
177 animator_
->StartAnimation(
178 SessionStateAnimator::ROOT_CONTAINER
,
179 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
180 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
181 StartRealShutdownTimer(true);
184 void LockStateController::OnLockScreenHide(
185 base::Callback
<void(void)>& callback
) {
186 StartUnlockAnimationBeforeUIDestroyed(callback
);
189 void LockStateController::SetLockScreenDisplayedCallback(
190 const base::Closure
& callback
) {
191 lock_screen_displayed_callback_
= callback
;
194 void LockStateController::OnHostCloseRequested(
195 const aura::WindowTreeHost
* host
) {
196 Shell::GetInstance()->delegate()->Exit();
199 void LockStateController::OnLoginStateChanged(
200 user::LoginStatus status
) {
201 if (status
!= user::LOGGED_IN_LOCKED
)
202 login_status_
= status
;
203 system_is_locked_
= (status
== user::LOGGED_IN_LOCKED
);
206 void LockStateController::OnAppTerminating() {
207 // If we hear that Chrome is exiting but didn't request it ourselves, all we
208 // can really hope for is that we'll have time to clear the screen.
209 // This is also the case when the user signs off.
210 if (!shutting_down_
) {
211 shutting_down_
= true;
212 Shell
* shell
= ash::Shell::GetInstance();
213 shell
->cursor_manager()->HideCursor();
214 shell
->cursor_manager()->LockCursor();
215 animator_
->StartAnimation(SessionStateAnimator::kAllNonRootContainersMask
,
216 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
217 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
221 void LockStateController::OnLockStateChanged(bool locked
) {
222 VLOG(1) << "OnLockStateChanged " << locked
;
223 if (shutting_down_
|| (system_is_locked_
== locked
))
226 system_is_locked_
= locked
;
229 StartPostLockAnimation();
230 lock_fail_timer_
.Stop();
232 StartUnlockAnimationAfterUIDestroyed();
236 void LockStateController::OnLockFailTimeout() {
237 DCHECK(!system_is_locked_
);
238 CHECK(false) << "We can not be sure about the lock state. Crash and let the "
239 << "SessionManager end the session";
242 void LockStateController::StartLockToShutdownTimer() {
243 shutdown_after_lock_
= false;
244 lock_to_shutdown_timer_
.Stop();
245 lock_to_shutdown_timer_
.Start(
247 base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs
),
248 this, &LockStateController::OnLockToShutdownTimeout
);
251 void LockStateController::OnLockToShutdownTimeout() {
252 DCHECK(system_is_locked_
);
253 StartCancellableShutdownAnimation();
256 void LockStateController::StartPreShutdownAnimationTimer() {
257 pre_shutdown_timer_
.Stop();
258 pre_shutdown_timer_
.Start(
260 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
),
262 &LockStateController::OnPreShutdownAnimationTimeout
);
265 void LockStateController::OnPreShutdownAnimationTimeout() {
266 VLOG(1) << "OnPreShutdownAnimationTimeout";
267 shutting_down_
= true;
269 Shell
* shell
= ash::Shell::GetInstance();
270 shell
->cursor_manager()->HideCursor();
272 StartRealShutdownTimer(false);
275 void LockStateController::StartRealShutdownTimer(bool with_animation_time
) {
276 base::TimeDelta duration
=
277 base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs
);
278 if (with_animation_time
) {
280 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
283 #if defined(OS_CHROMEOS)
284 const AccessibilityDelegate
* const delegate
=
285 Shell::GetInstance()->accessibility_delegate();
286 base::TimeDelta sound_duration
= delegate
->PlayShutdownSound();
288 std::min(sound_duration
,
289 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs
));
290 duration
= std::max(duration
, sound_duration
);
293 real_shutdown_timer_
.Start(
294 FROM_HERE
, duration
, this, &LockStateController::OnRealShutdownTimeout
);
297 void LockStateController::OnRealShutdownTimeout() {
298 VLOG(1) << "OnRealShutdownTimeout";
299 DCHECK(shutting_down_
);
300 #if defined(OS_CHROMEOS)
301 if (!base::SysInfo::IsRunningOnChromeOS()) {
302 ShellDelegate
* delegate
= Shell::GetInstance()->delegate();
309 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
310 UMA_ACCEL_SHUT_DOWN_POWER_BUTTON
);
311 delegate_
->RequestShutdown();
314 void LockStateController::StartCancellableShutdownAnimation() {
315 Shell
* shell
= ash::Shell::GetInstance();
316 // Hide cursor, but let it reappear if the mouse moves.
317 shell
->cursor_manager()->HideCursor();
319 animator_
->StartAnimation(
320 SessionStateAnimator::ROOT_CONTAINER
,
321 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
322 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
323 StartPreShutdownAnimationTimer();
326 void LockStateController::StartImmediatePreLockAnimation(
327 bool request_lock_on_completion
) {
328 VLOG(1) << "StartImmediatePreLockAnimation " << request_lock_on_completion
;
329 animating_lock_
= true;
330 StoreUnlockedProperties();
332 base::Closure next_animation_starter
=
333 base::Bind(&LockStateController::PreLockAnimationFinished
,
334 weak_ptr_factory_
.GetWeakPtr(),
335 request_lock_on_completion
);
336 SessionStateAnimator::AnimationSequence
* animation_sequence
=
337 animator_
->BeginAnimationSequence(next_animation_starter
);
339 animation_sequence
->StartAnimation(
340 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
341 SessionStateAnimator::ANIMATION_LIFT
,
342 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
343 animation_sequence
->StartAnimation(
344 SessionStateAnimator::LAUNCHER
,
345 SessionStateAnimator::ANIMATION_FADE_OUT
,
346 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
347 // Hide the screen locker containers so we can raise them later.
348 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
349 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
350 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
351 AnimateBackgroundAppearanceIfNecessary(
352 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
354 animation_sequence
->EndSequence();
356 DispatchCancelMode();
357 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
358 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED
));
361 void LockStateController::StartCancellablePreLockAnimation() {
362 animating_lock_
= true;
363 StoreUnlockedProperties();
364 VLOG(1) << "StartCancellablePreLockAnimation";
365 base::Closure next_animation_starter
=
366 base::Bind(&LockStateController::PreLockAnimationFinished
,
367 weak_ptr_factory_
.GetWeakPtr(),
368 true /* request_lock */);
369 SessionStateAnimator::AnimationSequence
* animation_sequence
=
370 animator_
->BeginAnimationSequence(next_animation_starter
);
372 animation_sequence
->StartAnimation(
373 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
374 SessionStateAnimator::ANIMATION_LIFT
,
375 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
376 animation_sequence
->StartAnimation(
377 SessionStateAnimator::LAUNCHER
,
378 SessionStateAnimator::ANIMATION_FADE_OUT
,
379 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
380 // Hide the screen locker containers so we can raise them later.
381 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
382 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
383 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
384 AnimateBackgroundAppearanceIfNecessary(
385 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
, animation_sequence
);
387 DispatchCancelMode();
388 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
389 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED
));
390 animation_sequence
->EndSequence();
393 void LockStateController::CancelPreLockAnimation() {
394 VLOG(1) << "CancelPreLockAnimation";
395 base::Closure next_animation_starter
=
396 base::Bind(&LockStateController::LockAnimationCancelled
,
397 weak_ptr_factory_
.GetWeakPtr());
398 SessionStateAnimator::AnimationSequence
* animation_sequence
=
399 animator_
->BeginAnimationSequence(next_animation_starter
);
401 animation_sequence
->StartAnimation(
402 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
403 SessionStateAnimator::ANIMATION_UNDO_LIFT
,
404 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
405 animation_sequence
->StartAnimation(
406 SessionStateAnimator::LAUNCHER
,
407 SessionStateAnimator::ANIMATION_FADE_IN
,
408 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
409 AnimateBackgroundHidingIfNecessary(
410 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
,
413 animation_sequence
->EndSequence();
416 void LockStateController::StartPostLockAnimation() {
417 VLOG(1) << "StartPostLockAnimation";
418 base::Closure next_animation_starter
=
419 base::Bind(&LockStateController::PostLockAnimationFinished
,
420 weak_ptr_factory_
.GetWeakPtr());
421 SessionStateAnimator::AnimationSequence
* animation_sequence
=
422 animator_
->BeginAnimationSequence(next_animation_starter
);
424 animation_sequence
->StartAnimation(
425 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
426 SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN
,
427 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
428 animation_sequence
->EndSequence();
431 void LockStateController::StartUnlockAnimationBeforeUIDestroyed(
432 base::Closure
& callback
) {
433 VLOG(1) << "StartUnlockAnimationBeforeUIDestroyed";
434 animator_
->StartAnimationWithCallback(
435 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
436 SessionStateAnimator::ANIMATION_LIFT
,
437 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
,
441 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
442 VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
443 base::Closure next_animation_starter
=
444 base::Bind(&LockStateController::UnlockAnimationAfterUIDestroyedFinished
,
445 weak_ptr_factory_
.GetWeakPtr());
446 SessionStateAnimator::AnimationSequence
* animation_sequence
=
447 animator_
->BeginAnimationSequence(next_animation_starter
);
449 animation_sequence
->StartAnimation(
450 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
451 SessionStateAnimator::ANIMATION_DROP
,
452 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
453 animation_sequence
->StartAnimation(
454 SessionStateAnimator::LAUNCHER
,
455 SessionStateAnimator::ANIMATION_FADE_IN
,
456 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
457 AnimateBackgroundHidingIfNecessary(
458 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
459 animation_sequence
->EndSequence();
462 void LockStateController::LockAnimationCancelled() {
463 can_cancel_lock_animation_
= false;
464 RestoreUnlockedProperties();
467 void LockStateController::PreLockAnimationFinished(bool request_lock
) {
468 VLOG(1) << "PreLockAnimationFinished";
469 can_cancel_lock_animation_
= false;
471 // Don't do anything (including starting the lock-fail timer) if the screen
472 // was already locked while the animation was going.
473 if (system_is_locked_
) {
474 DCHECK(!request_lock
) << "Got request to lock already-locked system "
475 << "at completion of pre-lock animation";
480 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
481 shutdown_after_lock_
?
482 UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON
:
483 UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON
);
484 delegate_
->RequestLockScreen();
487 base::TimeDelta timeout
=
488 base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs
);
489 #if defined(OS_CHROMEOS)
490 // Increase lock timeout for slower hardware, see http://crbug.com/350628
491 const std::string board
= base::SysInfo::GetLsbReleaseBoard();
492 if (board
== "x86-mario" ||
493 StartsWithASCII(board
, "x86-alex", true /* case_sensitive */) ||
494 StartsWithASCII(board
, "x86-zgb", true /* case_sensitive */)) {
498 lock_fail_timer_
.Start(
499 FROM_HERE
, timeout
, this, &LockStateController::OnLockFailTimeout
);
502 void LockStateController::PostLockAnimationFinished() {
503 animating_lock_
= false;
504 VLOG(1) << "PostLockAnimationFinished";
505 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
506 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED
));
507 if (!lock_screen_displayed_callback_
.is_null()) {
508 lock_screen_displayed_callback_
.Run();
509 lock_screen_displayed_callback_
.Reset();
511 CHECK(!views::MenuController::GetActiveInstance());
512 if (shutdown_after_lock_
) {
513 shutdown_after_lock_
= false;
514 StartLockToShutdownTimer();
518 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() {
519 RestoreUnlockedProperties();
522 void LockStateController::StoreUnlockedProperties() {
523 if (!unlocked_properties_
) {
524 unlocked_properties_
.reset(new UnlockedStateProperties());
525 unlocked_properties_
->background_is_hidden
=
526 animator_
->IsBackgroundHidden();
528 if (unlocked_properties_
->background_is_hidden
) {
529 // Hide background so that it can be animated later.
530 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
531 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
532 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
533 animator_
->ShowBackground();
537 void LockStateController::RestoreUnlockedProperties() {
538 if (!unlocked_properties_
)
540 if (unlocked_properties_
->background_is_hidden
) {
541 animator_
->HideBackground();
542 // Restore background visibility.
543 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
544 SessionStateAnimator::ANIMATION_FADE_IN
,
545 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
547 unlocked_properties_
.reset();
550 void LockStateController::AnimateBackgroundAppearanceIfNecessary(
551 SessionStateAnimator::AnimationSpeed speed
,
552 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
553 if (unlocked_properties_
.get() &&
554 unlocked_properties_
->background_is_hidden
) {
555 animation_sequence
->StartAnimation(
556 SessionStateAnimator::DESKTOP_BACKGROUND
,
557 SessionStateAnimator::ANIMATION_FADE_IN
,
562 void LockStateController::AnimateBackgroundHidingIfNecessary(
563 SessionStateAnimator::AnimationSpeed speed
,
564 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
565 if (unlocked_properties_
.get() &&
566 unlocked_properties_
->background_is_hidden
) {
567 animation_sequence
->StartAnimation(
568 SessionStateAnimator::DESKTOP_BACKGROUND
,
569 SessionStateAnimator::ANIMATION_FADE_OUT
,