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/metrics/histogram_macros.h"
25 #include "base/strings/string_util.h"
26 #include "base/timer/timer.h"
27 #include "ui/aura/window_tree_host.h"
28 #include "ui/views/controls/menu/menu_controller.h"
29 #include "ui/wm/core/compound_event_filter.h"
31 #if defined(OS_CHROMEOS)
32 #include "base/sys_info.h"
33 #include "media/audio/sounds/sounds_manager.h"
36 #if defined(OS_CHROMEOS)
37 using media::SoundsManager
;
40 #define UMA_HISTOGRAM_LOCK_TIMES(name, sample) \
41 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \
42 base::TimeDelta::FromMilliseconds(1), \
43 base::TimeDelta::FromSeconds(50), 100)
49 #if defined(OS_CHROMEOS)
50 const int kMaxShutdownSoundDurationMs
= 1500;
55 // ASan/TSan/MSan instrument each memory access. This may slow the execution
56 // down significantly.
57 #if defined(MEMORY_SANITIZER)
58 // For MSan the slowdown depends heavily on the value of msan_track_origins GYP
59 // flag. The multiplier below corresponds to msan_track_origins=1.
60 static const int kTimeoutMultiplier
= 6;
61 #elif defined(ADDRESS_SANITIZER) && defined(OS_WIN)
62 // Asan/Win has not been optimized yet, give it a higher
63 // timeout multiplier. See http://crbug.com/412471
64 static const int kTimeoutMultiplier
= 3;
65 #elif defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
67 static const int kTimeoutMultiplier
= 2;
69 static const int kTimeoutMultiplier
= 1;
72 const int LockStateController::kLockFailTimeoutMs
= 8000 * kTimeoutMultiplier
;
73 const int LockStateController::kLockToShutdownTimeoutMs
= 150;
74 const int LockStateController::kShutdownRequestDelayMs
= 50;
76 LockStateController::TestApi::TestApi(LockStateController
* controller
)
77 : controller_(controller
) {
80 LockStateController::TestApi::~TestApi() {
83 LockStateController::LockStateController()
84 : animator_(new SessionStateAnimatorImpl()),
85 login_status_(user::LOGGED_IN_NONE
),
86 system_is_locked_(false),
87 shutting_down_(false),
88 shutdown_after_lock_(false),
89 animating_lock_(false),
90 can_cancel_lock_animation_(false),
91 weak_ptr_factory_(this) {
92 Shell::GetPrimaryRootWindow()->GetHost()->AddObserver(this);
95 LockStateController::~LockStateController() {
96 Shell::GetPrimaryRootWindow()->GetHost()->RemoveObserver(this);
99 void LockStateController::SetDelegate(
100 scoped_ptr
<LockStateControllerDelegate
> delegate
) {
101 delegate_
= delegate
.Pass();
104 void LockStateController::AddObserver(LockStateObserver
* observer
) {
105 observers_
.AddObserver(observer
);
108 void LockStateController::RemoveObserver(LockStateObserver
* observer
) {
109 observers_
.RemoveObserver(observer
);
112 bool LockStateController::HasObserver(const LockStateObserver
* observer
) const {
113 return observers_
.HasObserver(observer
);
116 void LockStateController::StartLockAnimation(
117 bool shutdown_after_lock
) {
120 shutdown_after_lock_
= shutdown_after_lock
;
121 can_cancel_lock_animation_
= true;
123 StartCancellablePreLockAnimation();
126 void LockStateController::StartShutdownAnimation() {
127 StartCancellableShutdownAnimation();
130 void LockStateController::StartLockAnimationAndLockImmediately(
131 bool shutdown_after_lock
) {
134 shutdown_after_lock_
= shutdown_after_lock
;
135 StartImmediatePreLockAnimation(true /* request_lock_on_completion */);
138 bool LockStateController::LockRequested() {
139 return lock_fail_timer_
.IsRunning();
142 bool LockStateController::ShutdownRequested() {
143 return shutting_down_
;
146 bool LockStateController::CanCancelLockAnimation() {
147 return can_cancel_lock_animation_
;
150 void LockStateController::CancelLockAnimation() {
151 if (!CanCancelLockAnimation())
153 shutdown_after_lock_
= false;
154 animating_lock_
= false;
155 CancelPreLockAnimation();
158 bool LockStateController::CanCancelShutdownAnimation() {
159 return pre_shutdown_timer_
.IsRunning() ||
160 shutdown_after_lock_
||
161 lock_to_shutdown_timer_
.IsRunning();
164 void LockStateController::CancelShutdownAnimation() {
165 if (!CanCancelShutdownAnimation())
167 if (lock_to_shutdown_timer_
.IsRunning()) {
168 lock_to_shutdown_timer_
.Stop();
171 if (shutdown_after_lock_
) {
172 shutdown_after_lock_
= false;
176 animator_
->StartAnimation(
177 SessionStateAnimator::ROOT_CONTAINER
,
178 SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS
,
179 SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN
);
180 pre_shutdown_timer_
.Stop();
183 void LockStateController::OnStartingLock() {
184 if (shutting_down_
|| system_is_locked_
)
188 StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
191 void LockStateController::RequestShutdown() {
195 shutting_down_
= true;
197 Shell
* shell
= ash::Shell::GetInstance();
198 shell
->cursor_manager()->HideCursor();
199 shell
->cursor_manager()->LockCursor();
201 animator_
->StartAnimation(
202 SessionStateAnimator::ROOT_CONTAINER
,
203 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
204 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
205 StartRealShutdownTimer(true);
208 void LockStateController::OnLockScreenHide(
209 base::Callback
<void(void)>& callback
) {
210 StartUnlockAnimationBeforeUIDestroyed(callback
);
213 void LockStateController::SetLockScreenDisplayedCallback(
214 const base::Closure
& callback
) {
215 lock_screen_displayed_callback_
= callback
;
218 void LockStateController::OnHostCloseRequested(
219 const aura::WindowTreeHost
* host
) {
220 Shell::GetInstance()->delegate()->Exit();
223 void LockStateController::OnLoginStateChanged(
224 user::LoginStatus status
) {
225 if (status
!= user::LOGGED_IN_LOCKED
)
226 login_status_
= status
;
227 system_is_locked_
= (status
== user::LOGGED_IN_LOCKED
);
230 void LockStateController::OnAppTerminating() {
231 // If we hear that Chrome is exiting but didn't request it ourselves, all we
232 // can really hope for is that we'll have time to clear the screen.
233 // This is also the case when the user signs off.
234 if (!shutting_down_
) {
235 shutting_down_
= true;
236 Shell
* shell
= ash::Shell::GetInstance();
237 shell
->cursor_manager()->HideCursor();
238 shell
->cursor_manager()->LockCursor();
239 animator_
->StartAnimation(SessionStateAnimator::kAllNonRootContainersMask
,
240 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
241 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
245 void LockStateController::OnLockStateChanged(bool locked
) {
246 DCHECK((lock_fail_timer_
.IsRunning() && lock_duration_timer_
!= nullptr) ||
247 (!lock_fail_timer_
.IsRunning() && lock_duration_timer_
== nullptr));
248 VLOG(1) << "OnLockStateChanged " << locked
;
249 if (shutting_down_
|| (system_is_locked_
== locked
))
252 system_is_locked_
= locked
;
255 StartPostLockAnimation();
256 lock_fail_timer_
.Stop();
257 if (lock_duration_timer_
) {
258 UMA_HISTOGRAM_LOCK_TIMES("Ash.WindowManager.Lock.Success",
259 lock_duration_timer_
->Elapsed());
260 lock_duration_timer_
.reset();
263 StartUnlockAnimationAfterUIDestroyed();
267 void LockStateController::OnLockFailTimeout() {
268 UMA_HISTOGRAM_LOCK_TIMES("Ash.WindowManager.Lock.Timeout",
269 lock_duration_timer_
->Elapsed());
270 lock_duration_timer_
.reset();
271 DCHECK(!system_is_locked_
);
272 LOG(FATAL
) << "Screen lock took too long; crashing intentionally";
275 void LockStateController::StartLockToShutdownTimer() {
276 shutdown_after_lock_
= false;
277 lock_to_shutdown_timer_
.Stop();
278 lock_to_shutdown_timer_
.Start(
280 base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs
),
281 this, &LockStateController::OnLockToShutdownTimeout
);
284 void LockStateController::OnLockToShutdownTimeout() {
285 DCHECK(system_is_locked_
);
286 StartCancellableShutdownAnimation();
289 void LockStateController::StartPreShutdownAnimationTimer() {
290 pre_shutdown_timer_
.Stop();
291 pre_shutdown_timer_
.Start(
293 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
),
295 &LockStateController::OnPreShutdownAnimationTimeout
);
298 void LockStateController::OnPreShutdownAnimationTimeout() {
299 VLOG(1) << "OnPreShutdownAnimationTimeout";
300 shutting_down_
= true;
302 Shell
* shell
= ash::Shell::GetInstance();
303 shell
->cursor_manager()->HideCursor();
305 StartRealShutdownTimer(false);
308 void LockStateController::StartRealShutdownTimer(bool with_animation_time
) {
309 base::TimeDelta duration
=
310 base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs
);
311 if (with_animation_time
) {
313 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
316 #if defined(OS_CHROMEOS)
317 const AccessibilityDelegate
* const delegate
=
318 Shell::GetInstance()->accessibility_delegate();
319 base::TimeDelta sound_duration
= delegate
->PlayShutdownSound();
321 std::min(sound_duration
,
322 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs
));
323 duration
= std::max(duration
, sound_duration
);
326 real_shutdown_timer_
.Start(
327 FROM_HERE
, duration
, base::Bind(&LockStateController::OnRealPowerTimeout
,
328 base::Unretained(this)));
331 void LockStateController::OnRealPowerTimeout() {
332 VLOG(1) << "OnRealPowerTimeout";
333 DCHECK(shutting_down_
);
334 #if defined(OS_CHROMEOS)
335 if (!base::SysInfo::IsRunningOnChromeOS()) {
336 ShellDelegate
* delegate
= Shell::GetInstance()->delegate();
343 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
344 UMA_ACCEL_SHUT_DOWN_POWER_BUTTON
);
345 delegate_
->RequestShutdown();
348 void LockStateController::StartCancellableShutdownAnimation() {
349 Shell
* shell
= ash::Shell::GetInstance();
350 // Hide cursor, but let it reappear if the mouse moves.
351 shell
->cursor_manager()->HideCursor();
353 animator_
->StartAnimation(
354 SessionStateAnimator::ROOT_CONTAINER
,
355 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
356 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
357 StartPreShutdownAnimationTimer();
360 void LockStateController::StartImmediatePreLockAnimation(
361 bool request_lock_on_completion
) {
362 VLOG(1) << "StartImmediatePreLockAnimation " << request_lock_on_completion
;
363 animating_lock_
= true;
364 StoreUnlockedProperties();
366 base::Closure next_animation_starter
=
367 base::Bind(&LockStateController::PreLockAnimationFinished
,
368 weak_ptr_factory_
.GetWeakPtr(),
369 request_lock_on_completion
);
370 SessionStateAnimator::AnimationSequence
* animation_sequence
=
371 animator_
->BeginAnimationSequence(next_animation_starter
);
373 animation_sequence
->StartAnimation(
374 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
375 SessionStateAnimator::ANIMATION_LIFT
,
376 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
377 animation_sequence
->StartAnimation(
378 SessionStateAnimator::LAUNCHER
,
379 SessionStateAnimator::ANIMATION_FADE_OUT
,
380 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
381 // Hide the screen locker containers so we can raise them later.
382 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
383 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
384 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
385 AnimateBackgroundAppearanceIfNecessary(
386 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
388 animation_sequence
->EndSequence();
390 DispatchCancelMode();
391 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
392 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED
));
395 void LockStateController::StartCancellablePreLockAnimation() {
396 animating_lock_
= true;
397 StoreUnlockedProperties();
398 VLOG(1) << "StartCancellablePreLockAnimation";
399 base::Closure next_animation_starter
=
400 base::Bind(&LockStateController::PreLockAnimationFinished
,
401 weak_ptr_factory_
.GetWeakPtr(),
402 true /* request_lock */);
403 SessionStateAnimator::AnimationSequence
* animation_sequence
=
404 animator_
->BeginAnimationSequence(next_animation_starter
);
406 animation_sequence
->StartAnimation(
407 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
408 SessionStateAnimator::ANIMATION_LIFT
,
409 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
410 animation_sequence
->StartAnimation(
411 SessionStateAnimator::LAUNCHER
,
412 SessionStateAnimator::ANIMATION_FADE_OUT
,
413 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
414 // Hide the screen locker containers so we can raise them later.
415 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
416 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
417 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
418 AnimateBackgroundAppearanceIfNecessary(
419 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
, animation_sequence
);
421 DispatchCancelMode();
422 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
423 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED
));
424 animation_sequence
->EndSequence();
427 void LockStateController::CancelPreLockAnimation() {
428 VLOG(1) << "CancelPreLockAnimation";
429 base::Closure next_animation_starter
=
430 base::Bind(&LockStateController::LockAnimationCancelled
,
431 weak_ptr_factory_
.GetWeakPtr());
432 SessionStateAnimator::AnimationSequence
* animation_sequence
=
433 animator_
->BeginAnimationSequence(next_animation_starter
);
435 animation_sequence
->StartAnimation(
436 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
437 SessionStateAnimator::ANIMATION_UNDO_LIFT
,
438 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
439 animation_sequence
->StartAnimation(
440 SessionStateAnimator::LAUNCHER
,
441 SessionStateAnimator::ANIMATION_FADE_IN
,
442 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
443 AnimateBackgroundHidingIfNecessary(
444 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
,
447 animation_sequence
->EndSequence();
450 void LockStateController::StartPostLockAnimation() {
451 VLOG(1) << "StartPostLockAnimation";
452 base::Closure next_animation_starter
=
453 base::Bind(&LockStateController::PostLockAnimationFinished
,
454 weak_ptr_factory_
.GetWeakPtr());
455 SessionStateAnimator::AnimationSequence
* animation_sequence
=
456 animator_
->BeginAnimationSequence(next_animation_starter
);
458 animation_sequence
->StartAnimation(
459 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
460 SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN
,
461 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
462 animation_sequence
->EndSequence();
465 void LockStateController::StartUnlockAnimationBeforeUIDestroyed(
466 base::Closure
& callback
) {
467 VLOG(1) << "StartUnlockAnimationBeforeUIDestroyed";
468 animator_
->StartAnimationWithCallback(
469 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
470 SessionStateAnimator::ANIMATION_LIFT
,
471 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
,
475 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
476 VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
477 base::Closure next_animation_starter
=
478 base::Bind(&LockStateController::UnlockAnimationAfterUIDestroyedFinished
,
479 weak_ptr_factory_
.GetWeakPtr());
480 SessionStateAnimator::AnimationSequence
* animation_sequence
=
481 animator_
->BeginAnimationSequence(next_animation_starter
);
483 animation_sequence
->StartAnimation(
484 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
485 SessionStateAnimator::ANIMATION_DROP
,
486 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
487 animation_sequence
->StartAnimation(
488 SessionStateAnimator::LAUNCHER
,
489 SessionStateAnimator::ANIMATION_FADE_IN
,
490 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
491 AnimateBackgroundHidingIfNecessary(
492 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
493 animation_sequence
->EndSequence();
496 void LockStateController::LockAnimationCancelled() {
497 can_cancel_lock_animation_
= false;
498 RestoreUnlockedProperties();
501 void LockStateController::PreLockAnimationFinished(bool request_lock
) {
502 VLOG(1) << "PreLockAnimationFinished";
503 can_cancel_lock_animation_
= false;
505 // Don't do anything (including starting the lock-fail timer) if the screen
506 // was already locked while the animation was going.
507 if (system_is_locked_
) {
508 DCHECK(!request_lock
) << "Got request to lock already-locked system "
509 << "at completion of pre-lock animation";
514 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
515 shutdown_after_lock_
?
516 UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON
:
517 UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON
);
518 delegate_
->RequestLockScreen();
521 base::TimeDelta timeout
=
522 base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs
);
523 #if defined(OS_CHROMEOS)
524 // Increase lock timeout for slower hardware, see http://crbug.com/350628
525 const std::string board
= base::SysInfo::GetLsbReleaseBoard();
526 if (board
== "x86-mario" ||
527 base::StartsWith(board
, "x86-alex", base::CompareCase::SENSITIVE
) ||
528 base::StartsWith(board
, "x86-zgb", base::CompareCase::SENSITIVE
) ||
529 base::StartsWith(board
, "daisy", base::CompareCase::SENSITIVE
)) {
533 lock_fail_timer_
.Start(
534 FROM_HERE
, timeout
, this, &LockStateController::OnLockFailTimeout
);
535 lock_duration_timer_
.reset(new base::ElapsedTimer());
538 void LockStateController::PostLockAnimationFinished() {
539 animating_lock_
= false;
540 VLOG(1) << "PostLockAnimationFinished";
541 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
542 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED
));
543 if (!lock_screen_displayed_callback_
.is_null()) {
544 lock_screen_displayed_callback_
.Run();
545 lock_screen_displayed_callback_
.Reset();
547 CHECK(!views::MenuController::GetActiveInstance());
548 if (shutdown_after_lock_
) {
549 shutdown_after_lock_
= false;
550 StartLockToShutdownTimer();
554 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() {
555 RestoreUnlockedProperties();
558 void LockStateController::StoreUnlockedProperties() {
559 if (!unlocked_properties_
) {
560 unlocked_properties_
.reset(new UnlockedStateProperties());
561 unlocked_properties_
->background_is_hidden
=
562 animator_
->IsBackgroundHidden();
564 if (unlocked_properties_
->background_is_hidden
) {
565 // Hide background so that it can be animated later.
566 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
567 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
568 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
569 animator_
->ShowBackground();
573 void LockStateController::RestoreUnlockedProperties() {
574 if (!unlocked_properties_
)
576 if (unlocked_properties_
->background_is_hidden
) {
577 animator_
->HideBackground();
578 // Restore background visibility.
579 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
580 SessionStateAnimator::ANIMATION_FADE_IN
,
581 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
583 unlocked_properties_
.reset();
586 void LockStateController::AnimateBackgroundAppearanceIfNecessary(
587 SessionStateAnimator::AnimationSpeed speed
,
588 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
589 if (unlocked_properties_
.get() &&
590 unlocked_properties_
->background_is_hidden
) {
591 animation_sequence
->StartAnimation(
592 SessionStateAnimator::DESKTOP_BACKGROUND
,
593 SessionStateAnimator::ANIMATION_FADE_IN
,
598 void LockStateController::AnimateBackgroundHidingIfNecessary(
599 SessionStateAnimator::AnimationSpeed speed
,
600 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
601 if (unlocked_properties_
.get() &&
602 unlocked_properties_
->background_is_hidden
) {
603 animation_sequence
->StartAnimation(
604 SessionStateAnimator::DESKTOP_BACKGROUND
,
605 SessionStateAnimator::ANIMATION_FADE_OUT
,