Make multiple-connections.html less aggressive.
[chromium-blink-merge.git] / ash / wm / lock_state_controller.cc
blob3b6c04bef12bd4b3bd790ac09f5fa3e7f0e33c1a
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"
7 #include <algorithm>
8 #include <string>
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"
33 #endif
35 #if defined(OS_CHROMEOS)
36 using media::SoundsManager;
37 #endif
39 namespace ash {
41 namespace {
43 #if defined(OS_CHROMEOS)
44 const int kMaxShutdownSoundDurationMs = 1500;
45 #endif
47 } // namespace
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) {
97 if (animating_lock_)
98 return;
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) {
111 if (animating_lock_)
112 return;
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())
131 return;
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())
145 return;
146 if (lock_to_shutdown_timer_.IsRunning()) {
147 lock_to_shutdown_timer_.Stop();
148 return;
150 if (shutdown_after_lock_) {
151 shutdown_after_lock_ = false;
152 return;
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_)
164 return;
165 if (animating_lock_)
166 return;
167 StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
170 void LockStateController::RequestShutdown() {
171 if (shutting_down_)
172 return;
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))
227 return;
229 system_is_locked_ = locked;
231 if (locked) {
232 StartPostLockAnimation();
233 lock_fail_timer_.Stop();
234 } else {
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(
248 FROM_HERE,
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(
261 FROM_HERE,
262 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN),
263 this,
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) {
281 duration +=
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();
289 sound_duration =
290 std::min(sound_duration,
291 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs));
292 duration = std::max(duration, sound_duration);
293 #endif
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();
306 if (delegate) {
307 delegate->Exit();
308 return;
311 #endif
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,
414 animation_sequence);
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,
441 callback);
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";
479 return;
482 if (request_lock) {
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 */)) {
499 timeout *= 2;
501 #endif
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_)
543 return;
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,
562 speed);
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,
574 speed);
578 } // namespace ash