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(ShutdownMode mode
) {
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, mode
);
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, POWER_OFF
);
277 void LockStateController::StartRealShutdownTimer(bool with_animation_time
,
278 ShutdownMode shutdown_mode
) {
279 base::TimeDelta duration
=
280 base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs
);
281 if (with_animation_time
) {
283 animator_
->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
286 #if defined(OS_CHROMEOS)
287 const AccessibilityDelegate
* const delegate
=
288 Shell::GetInstance()->accessibility_delegate();
289 base::TimeDelta sound_duration
= delegate
->PlayShutdownSound();
291 std::min(sound_duration
,
292 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs
));
293 duration
= std::max(duration
, sound_duration
);
296 real_shutdown_timer_
.Start(
297 FROM_HERE
, duration
, base::Bind(&LockStateController::OnRealPowerTimeout
,
298 base::Unretained(this), shutdown_mode
));
301 void LockStateController::OnRealPowerTimeout(ShutdownMode shutdown_mode
) {
302 VLOG(1) << "OnRealPowerTimeout";
303 DCHECK(shutting_down_
);
304 #if defined(OS_CHROMEOS)
305 if (!base::SysInfo::IsRunningOnChromeOS()) {
306 ShellDelegate
* delegate
= Shell::GetInstance()->delegate();
313 if (shutdown_mode
== POWER_OFF
) {
314 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
315 UMA_ACCEL_SHUT_DOWN_POWER_BUTTON
);
316 delegate_
->RequestShutdown();
319 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
320 UMA_ACCEL_RESTART_POWER_BUTTON
);
321 delegate_
->RequestRestart();
324 void LockStateController::StartCancellableShutdownAnimation() {
325 Shell
* shell
= ash::Shell::GetInstance();
326 // Hide cursor, but let it reappear if the mouse moves.
327 shell
->cursor_manager()->HideCursor();
329 animator_
->StartAnimation(
330 SessionStateAnimator::ROOT_CONTAINER
,
331 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS
,
332 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN
);
333 StartPreShutdownAnimationTimer();
336 void LockStateController::StartImmediatePreLockAnimation(
337 bool request_lock_on_completion
) {
338 VLOG(1) << "StartImmediatePreLockAnimation " << request_lock_on_completion
;
339 animating_lock_
= true;
340 StoreUnlockedProperties();
342 base::Closure next_animation_starter
=
343 base::Bind(&LockStateController::PreLockAnimationFinished
,
344 weak_ptr_factory_
.GetWeakPtr(),
345 request_lock_on_completion
);
346 SessionStateAnimator::AnimationSequence
* animation_sequence
=
347 animator_
->BeginAnimationSequence(next_animation_starter
);
349 animation_sequence
->StartAnimation(
350 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
351 SessionStateAnimator::ANIMATION_LIFT
,
352 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
353 animation_sequence
->StartAnimation(
354 SessionStateAnimator::LAUNCHER
,
355 SessionStateAnimator::ANIMATION_FADE_OUT
,
356 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
357 // Hide the screen locker containers so we can raise them later.
358 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
359 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
360 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
361 AnimateBackgroundAppearanceIfNecessary(
362 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
364 animation_sequence
->EndSequence();
366 DispatchCancelMode();
367 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
368 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED
));
371 void LockStateController::StartCancellablePreLockAnimation() {
372 animating_lock_
= true;
373 StoreUnlockedProperties();
374 VLOG(1) << "StartCancellablePreLockAnimation";
375 base::Closure next_animation_starter
=
376 base::Bind(&LockStateController::PreLockAnimationFinished
,
377 weak_ptr_factory_
.GetWeakPtr(),
378 true /* request_lock */);
379 SessionStateAnimator::AnimationSequence
* animation_sequence
=
380 animator_
->BeginAnimationSequence(next_animation_starter
);
382 animation_sequence
->StartAnimation(
383 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
384 SessionStateAnimator::ANIMATION_LIFT
,
385 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
386 animation_sequence
->StartAnimation(
387 SessionStateAnimator::LAUNCHER
,
388 SessionStateAnimator::ANIMATION_FADE_OUT
,
389 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
);
390 // Hide the screen locker containers so we can raise them later.
391 animator_
->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
392 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
393 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
394 AnimateBackgroundAppearanceIfNecessary(
395 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE
, animation_sequence
);
397 DispatchCancelMode();
398 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
399 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED
));
400 animation_sequence
->EndSequence();
403 void LockStateController::CancelPreLockAnimation() {
404 VLOG(1) << "CancelPreLockAnimation";
405 base::Closure next_animation_starter
=
406 base::Bind(&LockStateController::LockAnimationCancelled
,
407 weak_ptr_factory_
.GetWeakPtr());
408 SessionStateAnimator::AnimationSequence
* animation_sequence
=
409 animator_
->BeginAnimationSequence(next_animation_starter
);
411 animation_sequence
->StartAnimation(
412 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
413 SessionStateAnimator::ANIMATION_UNDO_LIFT
,
414 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
415 animation_sequence
->StartAnimation(
416 SessionStateAnimator::LAUNCHER
,
417 SessionStateAnimator::ANIMATION_FADE_IN
,
418 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
);
419 AnimateBackgroundHidingIfNecessary(
420 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS
,
423 animation_sequence
->EndSequence();
426 void LockStateController::StartPostLockAnimation() {
427 VLOG(1) << "StartPostLockAnimation";
428 base::Closure next_animation_starter
=
429 base::Bind(&LockStateController::PostLockAnimationFinished
,
430 weak_ptr_factory_
.GetWeakPtr());
431 SessionStateAnimator::AnimationSequence
* animation_sequence
=
432 animator_
->BeginAnimationSequence(next_animation_starter
);
434 animation_sequence
->StartAnimation(
435 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
436 SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN
,
437 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
438 animation_sequence
->EndSequence();
441 void LockStateController::StartUnlockAnimationBeforeUIDestroyed(
442 base::Closure
& callback
) {
443 VLOG(1) << "StartUnlockAnimationBeforeUIDestroyed";
444 animator_
->StartAnimationWithCallback(
445 SessionStateAnimator::LOCK_SCREEN_CONTAINERS
,
446 SessionStateAnimator::ANIMATION_LIFT
,
447 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
,
451 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
452 VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
453 base::Closure next_animation_starter
=
454 base::Bind(&LockStateController::UnlockAnimationAfterUIDestroyedFinished
,
455 weak_ptr_factory_
.GetWeakPtr());
456 SessionStateAnimator::AnimationSequence
* animation_sequence
=
457 animator_
->BeginAnimationSequence(next_animation_starter
);
459 animation_sequence
->StartAnimation(
460 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS
,
461 SessionStateAnimator::ANIMATION_DROP
,
462 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
463 animation_sequence
->StartAnimation(
464 SessionStateAnimator::LAUNCHER
,
465 SessionStateAnimator::ANIMATION_FADE_IN
,
466 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
);
467 AnimateBackgroundHidingIfNecessary(
468 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS
, animation_sequence
);
469 animation_sequence
->EndSequence();
472 void LockStateController::LockAnimationCancelled() {
473 can_cancel_lock_animation_
= false;
474 RestoreUnlockedProperties();
477 void LockStateController::PreLockAnimationFinished(bool request_lock
) {
478 VLOG(1) << "PreLockAnimationFinished";
479 can_cancel_lock_animation_
= false;
481 // Don't do anything (including starting the lock-fail timer) if the screen
482 // was already locked while the animation was going.
483 if (system_is_locked_
) {
484 DCHECK(!request_lock
) << "Got request to lock already-locked system "
485 << "at completion of pre-lock animation";
490 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
491 shutdown_after_lock_
?
492 UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON
:
493 UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON
);
494 delegate_
->RequestLockScreen();
497 base::TimeDelta timeout
=
498 base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs
);
499 #if defined(OS_CHROMEOS)
500 // Increase lock timeout for slower hardware, see http://crbug.com/350628
501 const std::string board
= base::SysInfo::GetLsbReleaseBoard();
502 if (board
== "x86-mario" ||
503 StartsWithASCII(board
, "x86-alex", true /* case_sensitive */) ||
504 StartsWithASCII(board
, "x86-zgb", true /* case_sensitive */) ||
505 StartsWithASCII(board
, "daisy", true /* case_sensitive */)) {
509 lock_fail_timer_
.Start(
510 FROM_HERE
, timeout
, this, &LockStateController::OnLockFailTimeout
);
513 void LockStateController::PostLockAnimationFinished() {
514 animating_lock_
= false;
515 VLOG(1) << "PostLockAnimationFinished";
516 FOR_EACH_OBSERVER(LockStateObserver
, observers_
,
517 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED
));
518 if (!lock_screen_displayed_callback_
.is_null()) {
519 lock_screen_displayed_callback_
.Run();
520 lock_screen_displayed_callback_
.Reset();
522 CHECK(!views::MenuController::GetActiveInstance());
523 if (shutdown_after_lock_
) {
524 shutdown_after_lock_
= false;
525 StartLockToShutdownTimer();
529 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() {
530 RestoreUnlockedProperties();
533 void LockStateController::StoreUnlockedProperties() {
534 if (!unlocked_properties_
) {
535 unlocked_properties_
.reset(new UnlockedStateProperties());
536 unlocked_properties_
->background_is_hidden
=
537 animator_
->IsBackgroundHidden();
539 if (unlocked_properties_
->background_is_hidden
) {
540 // Hide background so that it can be animated later.
541 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
542 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY
,
543 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
544 animator_
->ShowBackground();
548 void LockStateController::RestoreUnlockedProperties() {
549 if (!unlocked_properties_
)
551 if (unlocked_properties_
->background_is_hidden
) {
552 animator_
->HideBackground();
553 // Restore background visibility.
554 animator_
->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND
,
555 SessionStateAnimator::ANIMATION_FADE_IN
,
556 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
);
558 unlocked_properties_
.reset();
561 void LockStateController::AnimateBackgroundAppearanceIfNecessary(
562 SessionStateAnimator::AnimationSpeed speed
,
563 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
564 if (unlocked_properties_
.get() &&
565 unlocked_properties_
->background_is_hidden
) {
566 animation_sequence
->StartAnimation(
567 SessionStateAnimator::DESKTOP_BACKGROUND
,
568 SessionStateAnimator::ANIMATION_FADE_IN
,
573 void LockStateController::AnimateBackgroundHidingIfNecessary(
574 SessionStateAnimator::AnimationSpeed speed
,
575 SessionStateAnimator::AnimationSequence
* animation_sequence
) {
576 if (unlocked_properties_
.get() &&
577 unlocked_properties_
->background_is_hidden
) {
578 animation_sequence
->StartAnimation(
579 SessionStateAnimator::DESKTOP_BACKGROUND
,
580 SessionStateAnimator::ANIMATION_FADE_OUT
,