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. 4 5 #include "ash/wm/lock_state_controller.h" 6 7 #include <algorithm> 8 #include <string> 9 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" 26 27 #if defined(OS_CHROMEOS) 28 #include "base/sys_info.h" 29 #include "media/audio/sounds/sounds_manager.h" 30 #endif 31 32 #if defined(OS_CHROMEOS) 33 using media::SoundsManager; 34 #endif 35 36 namespace ash { 37 38 namespace { 39 40 #if defined(OS_CHROMEOS) 41 const int kMaxShutdownSoundDurationMs = 1500; 42 #endif 43 44 } // namespace 45 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; 51 52 LockStateController::TestApi::TestApi(LockStateController* controller) 53 : controller_(controller) { 54 } 55 56 LockStateController::TestApi::~TestApi() { 57 } 58 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); 69 } 70 71 LockStateController::~LockStateController() { 72 Shell::GetPrimaryRootWindow()->GetHost()->RemoveObserver(this); 73 } 74 75 void LockStateController::SetDelegate( 76 scoped_ptr<LockStateControllerDelegate> delegate) { 77 delegate_ = delegate.Pass(); 78 } 79 80 void LockStateController::AddObserver(LockStateObserver* observer) { 81 observers_.AddObserver(observer); 82 } 83 84 void LockStateController::RemoveObserver(LockStateObserver* observer) { 85 observers_.RemoveObserver(observer); 86 } 87 88 bool LockStateController::HasObserver(LockStateObserver* observer) { 89 return observers_.HasObserver(observer); 90 } 91 92 void LockStateController::StartLockAnimation( 93 bool shutdown_after_lock) { 94 if (animating_lock_) 95 return; 96 shutdown_after_lock_ = shutdown_after_lock; 97 can_cancel_lock_animation_ = true; 98 99 StartCancellablePreLockAnimation(); 100 } 101 102 void LockStateController::StartShutdownAnimation() { 103 StartCancellableShutdownAnimation(); 104 } 105 106 void LockStateController::StartLockAnimationAndLockImmediately( 107 bool shutdown_after_lock) { 108 if (animating_lock_) 109 return; 110 shutdown_after_lock_ = shutdown_after_lock; 111 StartImmediatePreLockAnimation(true /* request_lock_on_completion */); 112 } 113 114 bool LockStateController::LockRequested() { 115 return lock_fail_timer_.IsRunning(); 116 } 117 118 bool LockStateController::ShutdownRequested() { 119 return shutting_down_; 120 } 121 122 bool LockStateController::CanCancelLockAnimation() { 123 return can_cancel_lock_animation_; 124 } 125 126 void LockStateController::CancelLockAnimation() { 127 if (!CanCancelLockAnimation()) 128 return; 129 shutdown_after_lock_ = false; 130 animating_lock_ = false; 131 CancelPreLockAnimation(); 132 } 133 134 bool LockStateController::CanCancelShutdownAnimation() { 135 return pre_shutdown_timer_.IsRunning() || 136 shutdown_after_lock_ || 137 lock_to_shutdown_timer_.IsRunning(); 138 } 139 140 void LockStateController::CancelShutdownAnimation() { 141 if (!CanCancelShutdownAnimation()) 142 return; 143 if (lock_to_shutdown_timer_.IsRunning()) { 144 lock_to_shutdown_timer_.Stop(); 145 return; 146 } 147 if (shutdown_after_lock_) { 148 shutdown_after_lock_ = false; 149 return; 150 } 151 152 animator_->StartAnimation( 153 SessionStateAnimator::ROOT_CONTAINER, 154 SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS, 155 SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN); 156 pre_shutdown_timer_.Stop(); 157 } 158 159 void LockStateController::OnStartingLock() { 160 if (shutting_down_ || system_is_locked_) 161 return; 162 if (animating_lock_) 163 return; 164 StartImmediatePreLockAnimation(false /* request_lock_on_completion */); 165 } 166 167 void LockStateController::RequestShutdown() { 168 if (shutting_down_) 169 return; 170 171 shutting_down_ = true; 172 173 Shell* shell = ash::Shell::GetInstance(); 174 shell->cursor_manager()->HideCursor(); 175 shell->cursor_manager()->LockCursor(); 176 177 animator_->StartAnimation( 178 SessionStateAnimator::ROOT_CONTAINER, 179 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS, 180 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN); 181 StartRealShutdownTimer(true); 182 } 183 184 void LockStateController::OnLockScreenHide( 185 base::Callback<void(void)>& callback) { 186 StartUnlockAnimationBeforeUIDestroyed(callback); 187 } 188 189 void LockStateController::SetLockScreenDisplayedCallback( 190 const base::Closure& callback) { 191 lock_screen_displayed_callback_ = callback; 192 } 193 194 void LockStateController::OnHostCloseRequested( 195 const aura::WindowTreeHost* host) { 196 Shell::GetInstance()->delegate()->Exit(); 197 } 198 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); 204 } 205 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); 218 } 219 } 220 221 void LockStateController::OnLockStateChanged(bool locked) { 222 VLOG(1) << "OnLockStateChanged " << locked; 223 if (shutting_down_ || (system_is_locked_ == locked)) 224 return; 225 226 system_is_locked_ = locked; 227 228 if (locked) { 229 StartPostLockAnimation(); 230 lock_fail_timer_.Stop(); 231 } else { 232 StartUnlockAnimationAfterUIDestroyed(); 233 } 234 } 235 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"; 240 } 241 242 void LockStateController::StartLockToShutdownTimer() { 243 shutdown_after_lock_ = false; 244 lock_to_shutdown_timer_.Stop(); 245 lock_to_shutdown_timer_.Start( 246 FROM_HERE, 247 base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs), 248 this, &LockStateController::OnLockToShutdownTimeout); 249 } 250 251 void LockStateController::OnLockToShutdownTimeout() { 252 DCHECK(system_is_locked_); 253 StartCancellableShutdownAnimation(); 254 } 255 256 void LockStateController::StartPreShutdownAnimationTimer() { 257 pre_shutdown_timer_.Stop(); 258 pre_shutdown_timer_.Start( 259 FROM_HERE, 260 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN), 261 this, 262 &LockStateController::OnPreShutdownAnimationTimeout); 263 } 264 265 void LockStateController::OnPreShutdownAnimationTimeout() { 266 VLOG(1) << "OnPreShutdownAnimationTimeout"; 267 shutting_down_ = true; 268 269 Shell* shell = ash::Shell::GetInstance(); 270 shell->cursor_manager()->HideCursor(); 271 272 StartRealShutdownTimer(false); 273 } 274 275 void LockStateController::StartRealShutdownTimer(bool with_animation_time) { 276 base::TimeDelta duration = 277 base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs); 278 if (with_animation_time) { 279 duration += 280 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN); 281 } 282 283 #if defined(OS_CHROMEOS) 284 const AccessibilityDelegate* const delegate = 285 Shell::GetInstance()->accessibility_delegate(); 286 base::TimeDelta sound_duration = delegate->PlayShutdownSound(); 287 sound_duration = 288 std::min(sound_duration, 289 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs)); 290 duration = std::max(duration, sound_duration); 291 #endif 292 293 real_shutdown_timer_.Start( 294 FROM_HERE, duration, this, &LockStateController::OnRealShutdownTimeout); 295 } 296 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(); 303 if (delegate) { 304 delegate->Exit(); 305 return; 306 } 307 } 308 #endif 309 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 310 UMA_ACCEL_SHUT_DOWN_POWER_BUTTON); 311 delegate_->RequestShutdown(); 312 } 313 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(); 318 319 animator_->StartAnimation( 320 SessionStateAnimator::ROOT_CONTAINER, 321 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS, 322 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN); 323 StartPreShutdownAnimationTimer(); 324 } 325 326 void LockStateController::StartImmediatePreLockAnimation( 327 bool request_lock_on_completion) { 328 VLOG(1) << "StartImmediatePreLockAnimation " << request_lock_on_completion; 329 animating_lock_ = true; 330 StoreUnlockedProperties(); 331 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); 338 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); 353 354 animation_sequence->EndSequence(); 355 356 DispatchCancelMode(); 357 FOR_EACH_OBSERVER(LockStateObserver, observers_, 358 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED)); 359 } 360 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); 371 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); 386 387 DispatchCancelMode(); 388 FOR_EACH_OBSERVER(LockStateObserver, observers_, 389 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED)); 390 animation_sequence->EndSequence(); 391 } 392 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); 400 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, 411 animation_sequence); 412 413 animation_sequence->EndSequence(); 414 } 415 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); 423 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(); 429 } 430 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, 438 callback); 439 } 440 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); 448 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(); 460 } 461 462 void LockStateController::LockAnimationCancelled() { 463 can_cancel_lock_animation_ = false; 464 RestoreUnlockedProperties(); 465 } 466 467 void LockStateController::PreLockAnimationFinished(bool request_lock) { 468 VLOG(1) << "PreLockAnimationFinished"; 469 can_cancel_lock_animation_ = false; 470 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"; 476 return; 477 } 478 479 if (request_lock) { 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(); 485 } 486 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 */)) { 495 timeout *= 2; 496 } 497 #endif 498 lock_fail_timer_.Start( 499 FROM_HERE, timeout, this, &LockStateController::OnLockFailTimeout); 500 } 501 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(); 510 } 511 CHECK(!views::MenuController::GetActiveInstance()); 512 if (shutdown_after_lock_) { 513 shutdown_after_lock_ = false; 514 StartLockToShutdownTimer(); 515 } 516 } 517 518 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() { 519 RestoreUnlockedProperties(); 520 } 521 522 void LockStateController::StoreUnlockedProperties() { 523 if (!unlocked_properties_) { 524 unlocked_properties_.reset(new UnlockedStateProperties()); 525 unlocked_properties_->background_is_hidden = 526 animator_->IsBackgroundHidden(); 527 } 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(); 534 } 535 } 536 537 void LockStateController::RestoreUnlockedProperties() { 538 if (!unlocked_properties_) 539 return; 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); 546 } 547 unlocked_properties_.reset(); 548 } 549 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, 558 speed); 559 } 560 } 561 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, 570 speed); 571 } 572 } 573 574 } // namespace ash 575