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_impl2.h"
6
7#include "ash/ash_switches.h"
8#include "ash/cancel_mode.h"
9#include "ash/shell.h"
10#include "ash/shell_delegate.h"
11#include "ash/shell_window_ids.h"
12#include "ash/wm/session_state_animator.h"
13#include "base/bind_helpers.h"
14#include "base/command_line.h"
15#include "base/timer/timer.h"
16#include "ui/aura/root_window.h"
17#include "ui/compositor/layer_animation_sequence.h"
18#include "ui/compositor/scoped_layer_animation_settings.h"
19#include "ui/views/corewm/compound_event_filter.h"
20
21#if defined(OS_CHROMEOS)
22#include "base/chromeos/chromeos_version.h"
23#endif
24
25namespace ash {
26
27namespace {
28
29aura::Window* GetBackground() {
30  aura::RootWindow* root_window = Shell::GetPrimaryRootWindow();
31  return Shell::GetContainer(root_window,
32      internal::kShellWindowId_DesktopBackgroundContainer);
33}
34
35bool IsBackgroundHidden() {
36  return !GetBackground()->IsVisible();
37}
38
39void ShowBackground() {
40  ui::ScopedLayerAnimationSettings settings(
41      GetBackground()->layer()->GetAnimator());
42  settings.SetTransitionDuration(base::TimeDelta());
43  GetBackground()->Show();
44}
45
46void HideBackground() {
47  ui::ScopedLayerAnimationSettings settings(
48      GetBackground()->layer()->GetAnimator());
49  settings.SetTransitionDuration(base::TimeDelta());
50  GetBackground()->Hide();
51}
52
53// This observer is intended to use in cases when some action has to be taken
54// once some animation successfully completes (i.e. it was not aborted).
55// Observer will count a number of sequences it is attached to, and a number of
56// finished sequences (either Ended or Aborted). Once these two numbers are
57// equal, observer will delete itself, calling callback passed to constructor if
58// there were no aborted animations.
59// This way it can be either used to wait for some animation to be finished in
60// multiple layers, to wait once a sequence of animations is finished in one
61// layer or the mixture of both.
62class AnimationFinishedObserver : public ui::LayerAnimationObserver {
63 public:
64  explicit AnimationFinishedObserver(base::Closure &callback)
65      : callback_(callback),
66        sequences_attached_(0),
67        sequences_completed_(0),
68        paused_(false) {
69  }
70
71  // Pauses observer: no checks will be made while paused. It can be used when
72  // a sequence has some immediate animations in the beginning, and for
73  // animations that can be tested with flag that makes all animations
74  // immediate.
75  void Pause() {
76    paused_ = true;
77  }
78
79  // Unpauses observer. It does a check and calls callback if conditions are
80  // met.
81  void Unpause() {
82    if (!paused_)
83      return;
84    paused_ = false;
85    if (sequences_completed_ == sequences_attached_) {
86      callback_.Run();
87      delete this;
88    }
89  }
90
91 private:
92  virtual ~AnimationFinishedObserver() {
93  }
94
95  // LayerAnimationObserver implementation
96  virtual void OnLayerAnimationEnded(
97      ui::LayerAnimationSequence* sequence) OVERRIDE {
98    sequences_completed_++;
99    if ((sequences_completed_ == sequences_attached_) && !paused_) {
100      callback_.Run();
101      delete this;
102    }
103  }
104
105  virtual void OnLayerAnimationAborted(
106      ui::LayerAnimationSequence* sequence) OVERRIDE {
107    sequences_completed_++;
108    if ((sequences_completed_ == sequences_attached_) && !paused_)
109      delete this;
110  }
111
112  virtual void OnLayerAnimationScheduled(
113      ui::LayerAnimationSequence* sequence) OVERRIDE {
114  }
115
116  virtual void OnAttachedToSequence(
117      ui::LayerAnimationSequence* sequence) OVERRIDE {
118    LayerAnimationObserver::OnAttachedToSequence(sequence);
119    sequences_attached_++;
120  }
121
122  // Callback to be called.
123  base::Closure callback_;
124
125  // Number of sequences this observer was attached to.
126  int sequences_attached_;
127
128  // Number of sequences either ended or aborted.
129  int sequences_completed_;
130
131  bool paused_;
132
133  DISALLOW_COPY_AND_ASSIGN(AnimationFinishedObserver);
134};
135
136} // namespace
137
138LockStateControllerImpl2::TestApi::TestApi(
139    LockStateControllerImpl2* controller)
140    : controller_(controller) {
141}
142
143LockStateControllerImpl2::TestApi::~TestApi() {
144}
145
146LockStateControllerImpl2::LockStateControllerImpl2()
147    : login_status_(user::LOGGED_IN_NONE),
148      system_is_locked_(false),
149      shutting_down_(false),
150      shutdown_after_lock_(false),
151      animating_lock_(false),
152      can_cancel_lock_animation_(false) {
153  Shell::GetPrimaryRootWindow()->AddRootWindowObserver(this);
154}
155
156LockStateControllerImpl2::~LockStateControllerImpl2() {
157  Shell::GetPrimaryRootWindow()->RemoveRootWindowObserver(this);
158}
159
160void LockStateControllerImpl2::OnLoginStateChanged(
161    user::LoginStatus status) {
162  if (status != user::LOGGED_IN_LOCKED)
163    login_status_ = status;
164  system_is_locked_ = (status == user::LOGGED_IN_LOCKED);
165}
166
167void LockStateControllerImpl2::OnAppTerminating() {
168  // If we hear that Chrome is exiting but didn't request it ourselves, all we
169  // can really hope for is that we'll have time to clear the screen.
170  // This is also the case when the user signs off.
171  if (!shutting_down_) {
172    shutting_down_ = true;
173    Shell* shell = ash::Shell::GetInstance();
174    shell->env_filter()->set_cursor_hidden_by_filter(false);
175    shell->cursor_manager()->HideCursor();
176    animator_->StartAnimation(
177        internal::SessionStateAnimator::kAllContainersMask,
178        internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
179        internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
180  }
181}
182
183void LockStateControllerImpl2::OnLockStateChanged(bool locked) {
184  if (shutting_down_ || (system_is_locked_ == locked))
185    return;
186
187  system_is_locked_ = locked;
188
189  if (locked) {
190    StartPostLockAnimation();
191    lock_fail_timer_.Stop();
192  } else {
193    StartUnlockAnimationAfterUIDestroyed();
194  }
195}
196
197void LockStateControllerImpl2::SetLockScreenDisplayedCallback(
198    base::Closure& callback) {
199  lock_screen_displayed_callback_ = callback;
200}
201
202void LockStateControllerImpl2::OnStartingLock() {
203  if (shutting_down_ || system_is_locked_)
204    return;
205  if (animating_lock_)
206    return;
207  StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
208}
209
210void LockStateControllerImpl2::StartLockAnimationAndLockImmediately() {
211  if (animating_lock_)
212    return;
213  StartImmediatePreLockAnimation(true /* request_lock_on_completion */);
214}
215
216void LockStateControllerImpl2::StartLockAnimation(
217    bool shutdown_after_lock) {
218  if (animating_lock_)
219    return;
220  shutdown_after_lock_ = shutdown_after_lock;
221  can_cancel_lock_animation_ = true;
222
223  StartCancellablePreLockAnimation();
224}
225
226bool LockStateControllerImpl2::LockRequested() {
227  return lock_fail_timer_.IsRunning();
228}
229
230bool LockStateControllerImpl2::ShutdownRequested() {
231  return shutting_down_;
232}
233
234bool LockStateControllerImpl2::CanCancelLockAnimation() {
235  return can_cancel_lock_animation_;
236}
237
238void LockStateControllerImpl2::CancelLockAnimation() {
239  if (!CanCancelLockAnimation())
240    return;
241  shutdown_after_lock_ = false;
242  animating_lock_ = false;
243  CancelPreLockAnimation();
244}
245
246bool LockStateControllerImpl2::CanCancelShutdownAnimation() {
247  return pre_shutdown_timer_.IsRunning() ||
248         shutdown_after_lock_ ||
249         lock_to_shutdown_timer_.IsRunning();
250}
251
252void LockStateControllerImpl2::StartShutdownAnimation() {
253  StartCancellableShutdownAnimation();
254}
255
256void LockStateControllerImpl2::CancelShutdownAnimation() {
257  if (!CanCancelShutdownAnimation())
258    return;
259  if (lock_to_shutdown_timer_.IsRunning()) {
260    lock_to_shutdown_timer_.Stop();
261    return;
262  }
263  if (shutdown_after_lock_) {
264    shutdown_after_lock_ = false;
265    return;
266  }
267
268  animator_->StartGlobalAnimation(
269      internal::SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS,
270      internal::SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN);
271  pre_shutdown_timer_.Stop();
272}
273
274void LockStateControllerImpl2::RequestShutdown() {
275  if (!shutting_down_)
276    RequestShutdownImpl();
277}
278
279void LockStateControllerImpl2::RequestShutdownImpl() {
280  DCHECK(!shutting_down_);
281  shutting_down_ = true;
282
283  Shell* shell = ash::Shell::GetInstance();
284  shell->env_filter()->set_cursor_hidden_by_filter(false);
285  shell->cursor_manager()->HideCursor();
286
287  StartShutdownAnimationImpl();
288}
289
290void LockStateControllerImpl2::OnRootWindowHostCloseRequested(
291                                                const aura::RootWindow*) {
292  Shell::GetInstance()->delegate()->Exit();
293}
294
295void LockStateControllerImpl2::OnLockFailTimeout() {
296  DCHECK(!system_is_locked_);
297  // Undo lock animation.
298  StartUnlockAnimationAfterUIDestroyed();
299}
300
301void LockStateControllerImpl2::StartLockToShutdownTimer() {
302  shutdown_after_lock_ = false;
303  lock_to_shutdown_timer_.Stop();
304  lock_to_shutdown_timer_.Start(
305      FROM_HERE,
306      base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs),
307      this, &LockStateControllerImpl2::OnLockToShutdownTimeout);
308}
309
310void LockStateControllerImpl2::OnLockToShutdownTimeout() {
311  DCHECK(system_is_locked_);
312  StartCancellableShutdownAnimation();
313}
314
315void LockStateControllerImpl2::StartCancellableShutdownAnimation() {
316  Shell* shell = ash::Shell::GetInstance();
317  // Hide cursor, but let it reappear if the mouse moves.
318  shell->env_filter()->set_cursor_hidden_by_filter(true);
319  shell->cursor_manager()->HideCursor();
320
321  animator_->StartGlobalAnimation(
322      internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
323      internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
324  StartPreShutdownAnimationTimer();
325}
326
327void LockStateControllerImpl2::StartShutdownAnimationImpl() {
328  animator_->StartGlobalAnimation(
329      internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
330      internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
331  StartRealShutdownTimer(true);
332}
333
334void LockStateControllerImpl2::StartPreShutdownAnimationTimer() {
335  pre_shutdown_timer_.Stop();
336  pre_shutdown_timer_.Start(
337      FROM_HERE,
338      animator_->
339          GetDuration(internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN),
340      this,
341      &LockStateControllerImpl2::OnPreShutdownAnimationTimeout);
342}
343
344void LockStateControllerImpl2::OnPreShutdownAnimationTimeout() {
345  shutting_down_ = true;
346
347  Shell* shell = ash::Shell::GetInstance();
348  shell->env_filter()->set_cursor_hidden_by_filter(false);
349  shell->cursor_manager()->HideCursor();
350
351  StartRealShutdownTimer(false);
352}
353
354void LockStateControllerImpl2::StartRealShutdownTimer(
355    bool with_animation_time) {
356  base::TimeDelta duration =
357      base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs);
358  if (with_animation_time) {
359    duration += animator_->GetDuration(
360        internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
361  }
362  real_shutdown_timer_.Start(
363      FROM_HERE,
364      duration,
365      this,
366      &LockStateControllerImpl2::OnRealShutdownTimeout);
367}
368
369void LockStateControllerImpl2::OnRealShutdownTimeout() {
370  DCHECK(shutting_down_);
371#if defined(OS_CHROMEOS)
372  if (!base::chromeos::IsRunningOnChromeOS()) {
373    ShellDelegate* delegate = Shell::GetInstance()->delegate();
374    if (delegate) {
375      delegate->Exit();
376      return;
377    }
378  }
379#endif
380  Shell::GetInstance()->delegate()->RecordUserMetricsAction(
381      UMA_ACCEL_SHUT_DOWN_POWER_BUTTON);
382  delegate_->RequestShutdown();
383}
384
385void LockStateControllerImpl2::OnLockScreenHide(
386  base::Callback<void(void)>& callback) {
387  StartUnlockAnimationBeforeUIDestroyed(callback);
388}
389
390void LockStateControllerImpl2::LockAnimationCancelled() {
391  can_cancel_lock_animation_ = false;
392  RestoreUnlockedProperties();
393}
394
395void LockStateControllerImpl2::PreLockAnimationFinished(
396    bool request_lock) {
397  can_cancel_lock_animation_ = false;
398
399  if (request_lock) {
400    Shell::GetInstance()->delegate()->RecordUserMetricsAction(
401        shutdown_after_lock_ ?
402        UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON :
403        UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON);
404    delegate_->RequestLockScreen();
405  }
406
407  lock_fail_timer_.Start(
408      FROM_HERE,
409      base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs),
410      this,
411      &LockStateControllerImpl2::OnLockFailTimeout);
412}
413
414void LockStateControllerImpl2::PostLockAnimationFinished() {
415  animating_lock_ = false;
416
417  FOR_EACH_OBSERVER(LockStateObserver, observers_,
418      OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED));
419  if (!lock_screen_displayed_callback_.is_null()) {
420    lock_screen_displayed_callback_.Run();
421    lock_screen_displayed_callback_.Reset();
422  }
423  if (shutdown_after_lock_) {
424    shutdown_after_lock_ = false;
425    StartLockToShutdownTimer();
426  }
427}
428
429void LockStateControllerImpl2::
430UnlockAnimationAfterUIDestroyedFinished() {
431  RestoreUnlockedProperties();
432}
433
434void LockStateControllerImpl2::StartImmediatePreLockAnimation(
435    bool request_lock_on_completion) {
436  animating_lock_ = true;
437
438  StoreUnlockedProperties();
439
440  base::Closure next_animation_starter =
441      base::Bind(&LockStateControllerImpl2::PreLockAnimationFinished,
442      base::Unretained(this), request_lock_on_completion);
443  AnimationFinishedObserver* observer =
444      new AnimationFinishedObserver(next_animation_starter);
445
446  observer->Pause();
447
448  animator_->StartAnimationWithObserver(
449      internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
450      internal::SessionStateAnimator::ANIMATION_LIFT,
451      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
452      observer);
453  animator_->StartAnimationWithObserver(
454      internal::SessionStateAnimator::LAUNCHER,
455      internal::SessionStateAnimator::ANIMATION_FADE_OUT,
456      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
457      observer);
458  // Hide the screen locker containers so we can raise them later.
459  animator_->StartAnimation(
460      internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
461      internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
462      internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
463  AnimateBackgroundAppearanceIfNecessary(
464      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
465      observer);
466
467  observer->Unpause();
468
469  DispatchCancelMode();
470  FOR_EACH_OBSERVER(LockStateObserver, observers_,
471      OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED));
472}
473
474void LockStateControllerImpl2::StartCancellablePreLockAnimation() {
475  animating_lock_ = true;
476  StoreUnlockedProperties();
477
478  base::Closure next_animation_starter =
479      base::Bind(&LockStateControllerImpl2::PreLockAnimationFinished,
480      base::Unretained(this), true /* request_lock */);
481  AnimationFinishedObserver* observer =
482      new AnimationFinishedObserver(next_animation_starter);
483
484  observer->Pause();
485
486  animator_->StartAnimationWithObserver(
487      internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
488      internal::SessionStateAnimator::ANIMATION_LIFT,
489      internal::SessionStateAnimator::ANIMATION_SPEED_UNDOABLE,
490      observer);
491  animator_->StartAnimationWithObserver(
492      internal::SessionStateAnimator::LAUNCHER,
493      internal::SessionStateAnimator::ANIMATION_FADE_OUT,
494      internal::SessionStateAnimator::ANIMATION_SPEED_UNDOABLE,
495      observer);
496  // Hide the screen locker containers so we can raise them later.
497  animator_->StartAnimation(
498      internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
499      internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
500      internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
501  AnimateBackgroundAppearanceIfNecessary(
502      internal::SessionStateAnimator::ANIMATION_SPEED_UNDOABLE,
503      observer);
504
505  DispatchCancelMode();
506  FOR_EACH_OBSERVER(LockStateObserver, observers_,
507      OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED));
508  observer->Unpause();
509}
510
511void LockStateControllerImpl2::CancelPreLockAnimation() {
512  base::Closure next_animation_starter =
513      base::Bind(&LockStateControllerImpl2::LockAnimationCancelled,
514      base::Unretained(this));
515  AnimationFinishedObserver* observer =
516      new AnimationFinishedObserver(next_animation_starter);
517
518  observer->Pause();
519
520  animator_->StartAnimationWithObserver(
521      internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
522      internal::SessionStateAnimator::ANIMATION_UNDO_LIFT,
523      internal::SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
524      observer);
525  animator_->StartAnimationWithObserver(
526      internal::SessionStateAnimator::LAUNCHER,
527      internal::SessionStateAnimator::ANIMATION_FADE_IN,
528      internal::SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
529      observer);
530  AnimateBackgroundHidingIfNecessary(
531      internal::SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
532      observer);
533
534  observer->Unpause();
535}
536
537void LockStateControllerImpl2::StartPostLockAnimation() {
538  base::Closure next_animation_starter =
539      base::Bind(&LockStateControllerImpl2::PostLockAnimationFinished,
540      base::Unretained(this));
541
542  AnimationFinishedObserver* observer =
543      new AnimationFinishedObserver(next_animation_starter);
544
545  observer->Pause();
546  animator_->StartAnimationWithObserver(
547      internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
548      internal::SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN,
549      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
550      observer);
551  observer->Unpause();
552}
553
554void LockStateControllerImpl2::StartUnlockAnimationBeforeUIDestroyed(
555    base::Closure& callback) {
556  animator_->StartAnimationWithCallback(
557      internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
558      internal::SessionStateAnimator::ANIMATION_LIFT,
559      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
560      callback);
561}
562
563void LockStateControllerImpl2::StartUnlockAnimationAfterUIDestroyed() {
564  base::Closure next_animation_starter =
565      base::Bind(
566          &LockStateControllerImpl2::
567              UnlockAnimationAfterUIDestroyedFinished,
568          base::Unretained(this));
569
570  AnimationFinishedObserver* observer =
571      new AnimationFinishedObserver(next_animation_starter);
572
573  observer->Pause();
574
575  animator_->StartAnimationWithObserver(
576      internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
577      internal::SessionStateAnimator::ANIMATION_DROP,
578      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
579      observer);
580  animator_->StartAnimationWithObserver(
581      internal::SessionStateAnimator::LAUNCHER,
582      internal::SessionStateAnimator::ANIMATION_FADE_IN,
583      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
584      observer);
585  AnimateBackgroundHidingIfNecessary(
586      internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
587      observer);
588  observer->Unpause();
589}
590
591void LockStateControllerImpl2::StoreUnlockedProperties() {
592  if (!unlocked_properties_) {
593    unlocked_properties_.reset(new UnlockedStateProperties());
594    unlocked_properties_->background_is_hidden = IsBackgroundHidden();
595  }
596  if (unlocked_properties_->background_is_hidden) {
597    // Hide background so that it can be animated later.
598    animator_->StartAnimation(
599        internal::SessionStateAnimator::DESKTOP_BACKGROUND,
600        internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
601        internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
602    ShowBackground();
603  }
604}
605
606void LockStateControllerImpl2::RestoreUnlockedProperties() {
607  if (!unlocked_properties_)
608    return;
609  if (unlocked_properties_->background_is_hidden) {
610    HideBackground();
611    // Restore background visibility.
612    animator_->StartAnimation(
613        internal::SessionStateAnimator::DESKTOP_BACKGROUND,
614        internal::SessionStateAnimator::ANIMATION_FADE_IN,
615        internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
616  }
617  unlocked_properties_.reset();
618}
619
620void LockStateControllerImpl2::AnimateBackgroundAppearanceIfNecessary(
621    internal::SessionStateAnimator::AnimationSpeed speed,
622    ui::LayerAnimationObserver* observer) {
623  if (unlocked_properties_.get() &&
624      unlocked_properties_->background_is_hidden) {
625    animator_->StartAnimationWithObserver(
626        internal::SessionStateAnimator::DESKTOP_BACKGROUND,
627        internal::SessionStateAnimator::ANIMATION_FADE_IN,
628        speed,
629        observer);
630  }
631}
632
633void LockStateControllerImpl2::AnimateBackgroundHidingIfNecessary(
634    internal::SessionStateAnimator::AnimationSpeed speed,
635    ui::LayerAnimationObserver* observer) {
636  if (unlocked_properties_.get() &&
637      unlocked_properties_->background_is_hidden) {
638    animator_->StartAnimationWithObserver(
639        internal::SessionStateAnimator::DESKTOP_BACKGROUND,
640        internal::SessionStateAnimator::ANIMATION_FADE_OUT,
641        speed,
642        observer);
643  }
644}
645
646}  // namespace ash
647