Permission message rules: Each rule must have >= 1 required permissions
[chromium-blink-merge.git] / ash / wm / lock_state_controller.cc
blobaa075c5be5190047ab27a61840736d0e6cdd3d56
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/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"
34 #endif
36 #if defined(OS_CHROMEOS)
37 using media::SoundsManager;
38 #endif
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)
45 namespace ash {
47 namespace {
49 #if defined(OS_CHROMEOS)
50 const int kMaxShutdownSoundDurationMs = 1500;
51 #endif
53 } // namespace
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) || \
66 defined(SYZYASAN)
67 static const int kTimeoutMultiplier = 2;
68 #else
69 static const int kTimeoutMultiplier = 1;
70 #endif
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) {
118 if (animating_lock_)
119 return;
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) {
132 if (animating_lock_)
133 return;
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())
152 return;
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())
166 return;
167 if (lock_to_shutdown_timer_.IsRunning()) {
168 lock_to_shutdown_timer_.Stop();
169 return;
171 if (shutdown_after_lock_) {
172 shutdown_after_lock_ = false;
173 return;
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_)
185 return;
186 if (animating_lock_)
187 return;
188 StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
191 void LockStateController::RequestShutdown() {
192 if (shutting_down_)
193 return;
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))
250 return;
252 system_is_locked_ = locked;
254 if (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();
262 } else {
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(
279 FROM_HERE,
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(
292 FROM_HERE,
293 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN),
294 this,
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) {
312 duration +=
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();
320 sound_duration =
321 std::min(sound_duration,
322 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs));
323 duration = std::max(duration, sound_duration);
324 #endif
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();
337 if (delegate) {
338 delegate->Exit();
339 return;
342 #endif
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,
445 animation_sequence);
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,
472 callback);
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";
510 return;
513 if (request_lock) {
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)) {
530 timeout *= 2;
532 #endif
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_)
575 return;
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,
594 speed);
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,
606 speed);
610 } // namespace ash