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 #include <utility>
10
11 #include "ash/accessibility/accessibility_controller_impl.h"
12 #include "ash/cancel_mode.h"
13 #include "ash/public/cpp/shell_window_ids.h"
14 #include "ash/public/cpp/shutdown_controller.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/session/session_controller_impl.h"
17 #include "ash/shell.h"
18 #include "ash/shell_delegate.h"
19 #include "ash/shutdown_reason.h"
20 #include "ash/wallpaper/wallpaper_controller_impl.h"
21 #include "ash/wallpaper/wallpaper_widget_controller.h"
22 #include "ash/wm/session_state_animator.h"
23 #include "ash/wm/session_state_animator_impl.h"
24 #include "base/bind.h"
25 #include "base/callback_helpers.h"
26 #include "base/command_line.h"
27 #include "base/location.h"
28 #include "base/logging.h"
29 #include "base/metrics/histogram_macros.h"
30 #include "base/metrics/user_metrics.h"
31 #include "base/strings/string_util.h"
32 #include "base/system/sys_info.h"
33 #include "base/timer/timer.h"
34 #include "ui/aura/window_tree_host.h"
35 #include "ui/views/controls/menu/menu_controller.h"
36 #include "ui/wm/core/compound_event_filter.h"
37 #include "ui/wm/core/cursor_manager.h"
38
39 #define UMA_HISTOGRAM_LOCK_TIMES(name, sample) \
40 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \
41 base::TimeDelta::FromMilliseconds(1), \
42 base::TimeDelta::FromSeconds(50), 100)
43
44 namespace ash {
45
46 namespace {
47
48 // ASan/TSan/MSan instrument each memory access. This may slow the execution
49 // down significantly.
50 #if defined(MEMORY_SANITIZER)
51 // For MSan the slowdown depends heavily on the value of msan_track_origins GYP
52 // flag. The multiplier below corresponds to msan_track_origins=1.
53 constexpr int kTimeoutMultiplier = 6;
54 #elif defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
55 constexpr int kTimeoutMultiplier = 2;
56 #else
57 constexpr int kTimeoutMultiplier = 1;
58 #endif
59
60 constexpr int kMaxShutdownSoundDurationMs = 1500;
61
62 // Amount of time to wait for our lock requests to be honored before giving up.
63 constexpr base::TimeDelta kLockFailTimeout =
64 base::TimeDelta::FromSeconds(8 * kTimeoutMultiplier);
65
66 // Additional time to wait after starting the fast-close shutdown animation
67 // before actually requesting shutdown, to give the animation time to finish.
68 constexpr base::TimeDelta kShutdownRequestDelay =
69 base::TimeDelta::FromMilliseconds(50);
70
71 } // namespace
72
73 // static
74 const int LockStateController::kPreLockContainersMask =
75 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS |
76 SessionStateAnimator::SHELF;
77
LockStateController(ShutdownController * shutdown_controller)78 LockStateController::LockStateController(
79 ShutdownController* shutdown_controller)
80 : animator_(new SessionStateAnimatorImpl()),
81 shutdown_controller_(shutdown_controller),
82 scoped_session_observer_(this) {
83 DCHECK(shutdown_controller_);
84 Shell::GetPrimaryRootWindow()->GetHost()->AddObserver(this);
85 }
86
~LockStateController()87 LockStateController::~LockStateController() {
88 Shell::GetPrimaryRootWindow()->GetHost()->RemoveObserver(this);
89 }
90
AddObserver(LockStateObserver * observer)91 void LockStateController::AddObserver(LockStateObserver* observer) {
92 observers_.AddObserver(observer);
93 }
94
RemoveObserver(LockStateObserver * observer)95 void LockStateController::RemoveObserver(LockStateObserver* observer) {
96 observers_.RemoveObserver(observer);
97 }
98
StartLockAnimation()99 void LockStateController::StartLockAnimation() {
100 if (animating_lock_)
101 return;
102
103 animating_lock_ = true;
104 StoreUnlockedProperties();
105 VLOG(1) << "StartLockAnimation";
106 PreLockAnimation(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, true);
107 DispatchCancelMode();
108 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED);
109 }
110
StartShutdownAnimation(ShutdownReason reason)111 void LockStateController::StartShutdownAnimation(ShutdownReason reason) {
112 shutdown_reason_ = reason;
113
114 Shell* shell = Shell::Get();
115 // Hide cursor, but let it reappear if the mouse moves.
116 if (shell->cursor_manager())
117 shell->cursor_manager()->HideCursor();
118
119 animator_->StartAnimation(
120 SessionStateAnimator::ROOT_CONTAINER,
121 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
122 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
123 StartPreShutdownAnimationTimer();
124 }
125
LockWithoutAnimation()126 void LockStateController::LockWithoutAnimation() {
127 if (animating_lock_)
128 return;
129 animating_lock_ = true;
130 post_lock_immediate_animation_ = true;
131 animator_->StartAnimation(kPreLockContainersMask,
132 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
133 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
134 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED);
135 Shell::Get()->session_controller()->LockScreen();
136 }
137
LockRequested()138 bool LockStateController::LockRequested() {
139 return lock_fail_timer_.IsRunning();
140 }
141
ShutdownRequested()142 bool LockStateController::ShutdownRequested() {
143 return shutting_down_;
144 }
145
CancelLockAnimation()146 void LockStateController::CancelLockAnimation() {
147 VLOG(1) << "CancelLockAnimation";
148 animating_lock_ = false;
149 Shell::Get()->wallpaper_controller()->RestoreWallpaperBlurForLockState(
150 saved_blur_);
151 base::OnceClosure next_animation_starter =
152 base::BindOnce(&LockStateController::LockAnimationCancelled,
153 weak_ptr_factory_.GetWeakPtr());
154 SessionStateAnimator::AnimationSequence* animation_sequence =
155 animator_->BeginAnimationSequence(std::move(next_animation_starter));
156
157 animation_sequence->StartAnimation(
158 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
159 SessionStateAnimator::ANIMATION_UNDO_LIFT,
160 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS);
161 animation_sequence->StartAnimation(
162 SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN,
163 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS);
164 AnimateWallpaperHidingIfNecessary(
165 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
166 animation_sequence);
167
168 animation_sequence->EndSequence();
169 }
170
CanCancelShutdownAnimation()171 bool LockStateController::CanCancelShutdownAnimation() {
172 return pre_shutdown_timer_.IsRunning();
173 }
174
CancelShutdownAnimation()175 void LockStateController::CancelShutdownAnimation() {
176 if (!CanCancelShutdownAnimation())
177 return;
178
179 animator_->StartAnimation(
180 SessionStateAnimator::ROOT_CONTAINER,
181 SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS,
182 SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN);
183 pre_shutdown_timer_.Stop();
184 }
185
RequestShutdown(ShutdownReason reason)186 void LockStateController::RequestShutdown(ShutdownReason reason) {
187 if (shutting_down_)
188 return;
189
190 shutting_down_ = true;
191 shutdown_reason_ = reason;
192
193 ::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
194 cursor_manager->HideCursor();
195 cursor_manager->LockCursor();
196
197 animator_->StartAnimation(
198 SessionStateAnimator::ROOT_CONTAINER,
199 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
200 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
201 StartRealShutdownTimer(true);
202 }
203
OnLockScreenHide(base::OnceClosure callback)204 void LockStateController::OnLockScreenHide(base::OnceClosure callback) {
205 StartUnlockAnimationBeforeUIDestroyed(std::move(callback));
206 }
207
SetLockScreenDisplayedCallback(base::OnceClosure callback)208 void LockStateController::SetLockScreenDisplayedCallback(
209 base::OnceClosure callback) {
210 DCHECK(lock_screen_displayed_callback_.is_null());
211 if (system_is_locked_ && !animating_lock_)
212 std::move(callback).Run();
213 else
214 lock_screen_displayed_callback_ = std::move(callback);
215 }
216
OnHostCloseRequested(aura::WindowTreeHost * host)217 void LockStateController::OnHostCloseRequested(aura::WindowTreeHost* host) {
218 Shell::Get()->session_controller()->RequestSignOut();
219 }
220
OnChromeTerminating()221 void LockStateController::OnChromeTerminating() {
222 // If we hear that Chrome is exiting but didn't request it ourselves, all we
223 // can really hope for is that we'll have time to clear the screen.
224 // This is also the case when the user signs off.
225 if (!shutting_down_) {
226 shutting_down_ = true;
227 ::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
228 cursor_manager->HideCursor();
229 cursor_manager->LockCursor();
230 animator_->StartAnimation(SessionStateAnimator::kAllNonRootContainersMask,
231 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
232 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
233 }
234 }
235
OnLockStateChanged(bool locked)236 void LockStateController::OnLockStateChanged(bool locked) {
237 DCHECK((lock_fail_timer_.IsRunning() && lock_duration_timer_ != nullptr) ||
238 (!lock_fail_timer_.IsRunning() && lock_duration_timer_ == nullptr));
239 VLOG(1) << "OnLockStateChanged called with locked: " << locked
240 << ", shutting_down_: " << shutting_down_
241 << ", system_is_locked_: " << system_is_locked_
242 << ", lock_fail_timer_.IsRunning(): " << lock_fail_timer_.IsRunning();
243
244 if (shutting_down_ || (system_is_locked_ == locked))
245 return;
246
247 system_is_locked_ = locked;
248
249 if (locked) {
250 StartPostLockAnimation();
251
252 lock_fail_timer_.Stop();
253
254 if (lock_duration_timer_) {
255 UMA_HISTOGRAM_LOCK_TIMES("Ash.WindowManager.Lock.Success",
256 lock_duration_timer_->Elapsed());
257 lock_duration_timer_.reset();
258 }
259 } else {
260 StartUnlockAnimationAfterUIDestroyed();
261 }
262 }
263
OnLockFailTimeout()264 void LockStateController::OnLockFailTimeout() {
265 UMA_HISTOGRAM_LOCK_TIMES("Ash.WindowManager.Lock.Timeout",
266 lock_duration_timer_->Elapsed());
267 lock_duration_timer_.reset();
268 DCHECK(!system_is_locked_);
269
270 LOG(FATAL) << "Screen lock took too long; crashing intentionally";
271 }
272
StartPreShutdownAnimationTimer()273 void LockStateController::StartPreShutdownAnimationTimer() {
274 pre_shutdown_timer_.Stop();
275 pre_shutdown_timer_.Start(
276 FROM_HERE,
277 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN),
278 this, &LockStateController::OnPreShutdownAnimationTimeout);
279 }
280
OnPreShutdownAnimationTimeout()281 void LockStateController::OnPreShutdownAnimationTimeout() {
282 VLOG(1) << "OnPreShutdownAnimationTimeout";
283 shutting_down_ = true;
284
285 Shell* shell = Shell::Get();
286 if (shell->cursor_manager())
287 shell->cursor_manager()->HideCursor();
288
289 StartRealShutdownTimer(false);
290 }
291
StartRealShutdownTimer(bool with_animation_time)292 void LockStateController::StartRealShutdownTimer(bool with_animation_time) {
293 base::TimeDelta duration = kShutdownRequestDelay;
294 if (with_animation_time) {
295 duration +=
296 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
297 }
298 // Play and get shutdown sound duration from chrome in |sound_duration|. And
299 // start real shutdown after a delay of |duration|.
300 base::TimeDelta sound_duration =
301 std::min(Shell::Get()->accessibility_controller()->PlayShutdownSound(),
302 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs));
303 duration = std::max(duration, sound_duration);
304 real_shutdown_timer_.Start(FROM_HERE, duration, this,
305 &LockStateController::OnRealPowerTimeout);
306 }
307
OnRealPowerTimeout()308 void LockStateController::OnRealPowerTimeout() {
309 VLOG(1) << "OnRealPowerTimeout";
310 DCHECK(shutting_down_);
311 DCHECK(shutdown_reason_);
312 // Shut down or reboot based on device policy.
313 shutdown_controller_->ShutDownOrReboot(*shutdown_reason_);
314 }
315
PreLockAnimation(SessionStateAnimator::AnimationSpeed speed,bool request_lock_on_completion)316 void LockStateController::PreLockAnimation(
317 SessionStateAnimator::AnimationSpeed speed,
318 bool request_lock_on_completion) {
319 saved_blur_ = Shell::GetPrimaryRootWindowController()
320 ->wallpaper_widget_controller()
321 ->GetWallpaperBlur();
322 Shell::Get()->wallpaper_controller()->UpdateWallpaperBlurForLockState(true);
323 base::OnceClosure next_animation_starter = base::BindOnce(
324 &LockStateController::PreLockAnimationFinished,
325 weak_ptr_factory_.GetWeakPtr(), request_lock_on_completion);
326 SessionStateAnimator::AnimationSequence* animation_sequence =
327 animator_->BeginAnimationSequence(std::move(next_animation_starter));
328
329 animation_sequence->StartAnimation(
330 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
331 SessionStateAnimator::ANIMATION_LIFT, speed);
332 animation_sequence->StartAnimation(SessionStateAnimator::SHELF,
333 SessionStateAnimator::ANIMATION_FADE_OUT,
334 speed);
335 // Hide the screen locker containers so we can raise them later.
336 animator_->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
337 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
338 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
339 AnimateWallpaperAppearanceIfNecessary(speed, animation_sequence);
340
341 animation_sequence->EndSequence();
342 }
343
StartPostLockAnimation()344 void LockStateController::StartPostLockAnimation() {
345 VLOG(1) << "StartPostLockAnimation";
346 base::OnceClosure next_animation_starter =
347 base::BindOnce(&LockStateController::PostLockAnimationFinished,
348 weak_ptr_factory_.GetWeakPtr());
349 SessionStateAnimator::AnimationSequence* animation_sequence =
350 animator_->BeginAnimationSequence(std::move(next_animation_starter));
351
352 animation_sequence->StartAnimation(
353 SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
354 SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN,
355 post_lock_immediate_animation_
356 ? SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
357 : SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
358 // Show the lock screen shelf. This is a no-op if views-based shelf is
359 // disabled, since shelf is in NonLockScreenContainersContainer.
360 animation_sequence->StartAnimation(
361 SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN,
362 post_lock_immediate_animation_
363 ? SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE
364 : SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
365 animation_sequence->EndSequence();
366 }
367
StartUnlockAnimationBeforeUIDestroyed(base::OnceClosure callback)368 void LockStateController::StartUnlockAnimationBeforeUIDestroyed(
369 base::OnceClosure callback) {
370 VLOG(1) << "StartUnlockAnimationBeforeUIDestroyed";
371 // Hide the lock screen shelf. This is a no-op if views-based shelf is
372 // disabled, since shelf is in NonLockScreenContainersContainer.
373 animator_->StartAnimation(SessionStateAnimator::SHELF,
374 SessionStateAnimator::ANIMATION_FADE_OUT,
375 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
376 animator_->StartAnimationWithCallback(
377 SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
378 SessionStateAnimator::ANIMATION_LIFT,
379 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS, std::move(callback));
380 }
381
StartUnlockAnimationAfterUIDestroyed()382 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
383 VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
384 base::OnceClosure next_animation_starter = base::BindOnce(
385 &LockStateController::UnlockAnimationAfterUIDestroyedFinished,
386 weak_ptr_factory_.GetWeakPtr());
387 SessionStateAnimator::AnimationSequence* animation_sequence =
388 animator_->BeginAnimationSequence(std::move(next_animation_starter));
389
390 animation_sequence->StartAnimation(
391 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
392 SessionStateAnimator::ANIMATION_DROP,
393 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
394 animation_sequence->StartAnimation(
395 SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN,
396 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
397 AnimateWallpaperHidingIfNecessary(
398 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS, animation_sequence);
399 animation_sequence->EndSequence();
400 }
401
LockAnimationCancelled()402 void LockStateController::LockAnimationCancelled() {
403 RestoreUnlockedProperties();
404 }
405
PreLockAnimationFinished(bool request_lock)406 void LockStateController::PreLockAnimationFinished(bool request_lock) {
407 VLOG(1) << "PreLockAnimationFinished";
408
409 // Don't do anything (including starting the lock-fail timer) if the screen
410 // was already locked while the animation was going.
411 if (system_is_locked_) {
412 DCHECK(!request_lock) << "Got request to lock already-locked system "
413 << "at completion of pre-lock animation";
414 return;
415 }
416
417 if (request_lock) {
418 base::RecordAction(base::UserMetricsAction("Accel_LockScreen_LockButton"));
419 Shell::Get()->session_controller()->LockScreen();
420 }
421
422 lock_fail_timer_.Start(FROM_HERE, kLockFailTimeout, this,
423 &LockStateController::OnLockFailTimeout);
424
425 lock_duration_timer_.reset(new base::ElapsedTimer());
426 }
427
PostLockAnimationFinished()428 void LockStateController::PostLockAnimationFinished() {
429 animating_lock_ = false;
430 post_lock_immediate_animation_ = false;
431 VLOG(1) << "PostLockAnimationFinished";
432 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED);
433 if (!lock_screen_displayed_callback_.is_null())
434 std::move(lock_screen_displayed_callback_).Run();
435
436 CHECK(!views::MenuController::GetActiveInstance());
437 }
438
UnlockAnimationAfterUIDestroyedFinished()439 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() {
440 Shell::Get()->wallpaper_controller()->UpdateWallpaperBlurForLockState(false);
441 RestoreUnlockedProperties();
442 }
443
StoreUnlockedProperties()444 void LockStateController::StoreUnlockedProperties() {
445 if (!unlocked_properties_) {
446 unlocked_properties_.reset(new UnlockedStateProperties());
447 unlocked_properties_->wallpaper_is_hidden = animator_->IsWallpaperHidden();
448 }
449 if (unlocked_properties_->wallpaper_is_hidden) {
450 // Hide wallpaper so that it can be animated later.
451 animator_->StartAnimation(SessionStateAnimator::WALLPAPER,
452 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
453 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
454 animator_->ShowWallpaper();
455 }
456 }
457
RestoreUnlockedProperties()458 void LockStateController::RestoreUnlockedProperties() {
459 if (!unlocked_properties_)
460 return;
461 if (unlocked_properties_->wallpaper_is_hidden) {
462 animator_->HideWallpaper();
463 // Restore wallpaper visibility.
464 animator_->StartAnimation(SessionStateAnimator::WALLPAPER,
465 SessionStateAnimator::ANIMATION_FADE_IN,
466 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
467 }
468 unlocked_properties_.reset();
469 }
470
AnimateWallpaperAppearanceIfNecessary(SessionStateAnimator::AnimationSpeed speed,SessionStateAnimator::AnimationSequence * animation_sequence)471 void LockStateController::AnimateWallpaperAppearanceIfNecessary(
472 SessionStateAnimator::AnimationSpeed speed,
473 SessionStateAnimator::AnimationSequence* animation_sequence) {
474 if (unlocked_properties_.get() && unlocked_properties_->wallpaper_is_hidden) {
475 animation_sequence->StartAnimation(SessionStateAnimator::WALLPAPER,
476 SessionStateAnimator::ANIMATION_FADE_IN,
477 speed);
478 }
479 }
480
AnimateWallpaperHidingIfNecessary(SessionStateAnimator::AnimationSpeed speed,SessionStateAnimator::AnimationSequence * animation_sequence)481 void LockStateController::AnimateWallpaperHidingIfNecessary(
482 SessionStateAnimator::AnimationSpeed speed,
483 SessionStateAnimator::AnimationSequence* animation_sequence) {
484 if (unlocked_properties_.get() && unlocked_properties_->wallpaper_is_hidden) {
485 animation_sequence->StartAnimation(SessionStateAnimator::WALLPAPER,
486 SessionStateAnimator::ANIMATION_FADE_OUT,
487 speed);
488 }
489 }
490
OnLockStateEvent(LockStateObserver::EventType event)491 void LockStateController::OnLockStateEvent(LockStateObserver::EventType event) {
492 for (auto& observer : observers_)
493 observer.OnLockStateEvent(event);
494 }
495
496 } // namespace ash
497