chromium/ash/wm/lock_state_controller.h

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_WM_LOCK_STATE_CONTROLLER_H_
#define ASH_WM_LOCK_STATE_CONTROLLER_H_

#include <memory>
#include <optional>

#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/shutdown_reason.h"
#include "ash/wallpaper/wallpaper_constants.h"
#include "ash/wm/lock_state_observer.h"
#include "ash/wm/session_state_animator.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "third_party/cros_system_api/dbus/power_manager/dbus-constants.h"
#include "ui/aura/window_tree_host_observer.h"
#include "ui/gfx/image/image.h"

namespace ash {

class ShutdownController;
enum class ShutdownReason;

// Displays onscreen animations and locks or suspends the system in response to
// the power button being pressed or released.
// Lock workflow:
// Entry point:
//  * StartLockAnimation (bool shutdown after lock) - starts lock that can be
//    cancelled.
// Once it completes, PreLockAnimationFinished is called, and system lock is
// requested. Once system locks and lock UI is created, OnLockStateChanged is
// called, and StartPostLockAnimation is called. In PostLockAnimationFinished
// two things happen : EVENT_LOCK_ANIMATION_FINISHED notification is sent (it
// triggers third part of animation within lock UI), and check for continuing to
// shutdown is made.
//
// Unlock workflow:
// WebUI does first part of animation, and calls OnLockScreenHide(callback) that
// triggers StartUnlockAnimationBeforeUIDestroyed(callback). Once callback is
// called at the end of the animation, lock UI is deleted, system unlocks, and
// OnLockStateChanged is called. It leads to
// StartUnlockAnimationAfterLockUIDestroyed.
class ASH_EXPORT LockStateController : public aura::WindowTreeHostObserver,
                                       public SessionObserver {
 public:
  // A bitfield mask including NON_LOCK_SCREEN_CONTAINERS and LAUNCHER, used for
  // pre-lock hiding animation.
  static const int kPreLockContainersMask;

  LockStateController(ShutdownController* shutdown_controller,
                      PrefService* local_state);

  LockStateController(const LockStateController&) = delete;
  LockStateController& operator=(const LockStateController&) = delete;

  ~LockStateController() override;

  static void RegisterPrefs(PrefRegistrySimple* registry);

  void AddObserver(LockStateObserver* observer);
  void RemoveObserver(LockStateObserver* observer);

  // Starts locking (with slow pre-lock animation) that can be cancelled.
  void StartLockAnimation();

  // Starts locking without slow animation.
  void LockWithoutAnimation();

  // Returns true if we have requested system to lock, but haven't received
  // confirmation yet.
  bool LockRequested();

  // Cancels locking and reverts lock animation.
  void CancelLockAnimation();

  // Called when ScreenLocker is ready to close, but not yet destroyed.
  // Can be used to display "hiding" animations on unlock.
  // |callback| will be called when all animations are done.
  void OnLockScreenHide(SessionStateAnimator::AnimationCallback callback);

  // Sets up the callback that should be called once lock animation is finished.
  // Callback is guaranteed to be called once and then discarded.
  void SetLockScreenDisplayedCallback(base::OnceClosure callback);

  // Displays the shutdown animation and requests a system shutdown or system
  // restart depending on the the state of the |RebootOnShutdown| device policy.
  void RequestShutdown(ShutdownReason reason);

  // The difference between this and `RequestShutdown` is that this one starts
  // the shutdown that can be canceled. Note, please use only when necessary and
  // together with `MaybeCancelShutdownAnimation`. E.g., requesting through the
  // physical power button, while pressing the power button with different
  // duration can lead to different shutdown states.
  void RequestCancelableShutdown(ShutdownReason reason);

  // True if the real non-cancelable shutting down started.
  bool ShutdownRequested() const;

  // Reverts the shutdown animation and updates the shutdown state to canceled.
  // Then the shutdown process will not move forward. Returns true if the
  // shutdown is canceled, otherwise false.
  bool MaybeCancelShutdownAnimation();

  // Requests restart with the same animation as `RequestShutdown` and take the
  // informed restore image if forest feature is enabled, restart directly
  // otherwise. `description` is a human-readable string describing the source
  // of request the restart.
  void RequestRestart(power_manager::RequestRestartReason reason,
                      const std::string& description);

  // Requests sign out with the same animation as `RequestShutdown` and take the
  // informed restore image if forest feature is enabled, sign out directly
  // otherwise.
  void RequestSignOut();

  // aura::WindowTreeHostObserver override:
  void OnHostCloseRequested(aura::WindowTreeHost* host) override;

  // SessionObserver overrides:
  void OnChromeTerminating() override;
  void OnLockStateChanged(bool locked) override;

  void set_animator_for_test(SessionStateAnimator* animator) {
    animator_.reset(animator);
  }
  bool animating_lock_for_test() const { return animating_lock_; }

 private:
  friend class LockStateControllerTestApi;

  struct UnlockedStateProperties {
    bool wallpaper_is_hidden;
  };

  // Specifies the requested session state.
  enum class RequestedSessionState {
    kShutdown = 0,
    kCancelableShutdown,
    kRestart,
    kSignOut,
  };

  // Cancels unlock animation.
  void CancelUnlockAnimation();

  // Reverts the pre-lock animation, reports the error.
  void OnLockFailTimeout();

  void PreLockAnimation(SessionStateAnimator::AnimationSpeed speed,
                        bool request_lock_on_completion);
  void StartPostLockAnimation();
  void OnPostLockFailTimeout();
  // This method calls |callback| when animation completes.
  void StartUnlockAnimationBeforeLockUIDestroyed(base::OnceClosure callback);
  void StartUnlockAnimationAfterLockUIDestroyed();

  // These methods are called when corresponding animation completes.
  void LockAnimationCancelled(bool aborted);
  void PreLockAnimationFinished(bool request_lock, bool aborted);
  void PostLockAnimationFinished(bool aborted);
  void UnlockAnimationAfterLockUIDestroyedFinished(bool aborted);

  // Stores properties of UI that have to be temporarily modified while locking.
  void StoreUnlockedProperties();
  void RestoreUnlockedProperties();

  // Fades in wallpaper layer with |speed| if it was hidden in unlocked state.
  void AnimateWallpaperAppearanceIfNecessary(
      SessionStateAnimator::AnimationSpeed speed,
      SessionStateAnimator::AnimationSequence* animation_sequence);

  // Fades out wallpaper layer with |speed| if it was hidden in unlocked state.
  void AnimateWallpaperHidingIfNecessary(
      SessionStateAnimator::AnimationSpeed speed,
      SessionStateAnimator::AnimationSequence* animation_sequence);

  // Passed as a callback to the animation sequence that runs as part of
  // StartUnlockAnimationBeforeLockUIDestroyed. The callback will be invoked
  // after the animations complete, it will then check if the power button was
  // pressed at all during the unlock animation, and if so, immediately revert
  // the animations and notify ScreenLocker that the unlock process is to be
  // aborted.
  void OnUnlockAnimationBeforeLockUIDestroyedFinished();

  // Notifies observers.
  void OnLockStateEvent(LockStateObserver::EventType event);

  // Starts timer for undoable shutdown animation.
  void StartPreShutdownAnimationTimer();

  // Calls `StartSessionStateChangeTimer()` with
  // `RequestedSessionState::kCancelableShutdown` when
  // `cancelable_shutdown_timer_` expires.
  void OnPreShutdownAnimationTimeout();

  // Starts timer for final session state change animation. If
  // `with_animation_time` is true, it will also include time of "fade to white"
  // shutdown animation (NOTE: we are using the same animation for restart and
  // signout as well). If `requested_session_state` is shutdown related,
  // shutdown sound duration will be included in the duration calculation as
  // well.
  void StartSessionStateChangeTimer(
      bool with_animation_time,
      RequestedSessionState requested_session_state);

  // Called by `session_state_change_timer_` to start the
  // `requested_session_state` change.
  void OnSessionStateChangeTimeout(
      RequestedSessionState requested_session_state);

  // Takes a screenshot for the informed restore dialog first and then starts
  // the session state change process. `requested_session_state` indicates the
  // requested session state.
  void SessionStateChangeWithInformedRestore(
      RequestedSessionState requested_session_state);

  // Binds to a callback that will be called by the DLP manager to let us know
  // whether capturing the screenshot should `proceed` or abort due to some
  // restricted contents on the screen. `requested_session_state` indicates the
  // requested session state change.
  void OnDlpRestrictionCheckedAtScreenCapture(
      RequestedSessionState requested_session_state,
      const base::FilePath& file_path,
      bool proceed);

  // Starts the session state change process with the given
  // `requested_session_state`.
  void StartSessionStateChange(RequestedSessionState requested_session_state);

  // Triggers the session state change process when the
  // `take_screenshot_fail_timer_` times out. `requested_session_state`
  // indicates the requested session state change.
  void OnTakeScreenshotFailTimeout(
      RequestedSessionState requested_session_state);

  // Callback invoked once the image is taken. `requested_session_state`
  // indicates the requested session state after the image had been taken.
  // `file_path` indicates the path to save the informed restore image. Note:
  // `gfx::Image` is cheap to pass by value.
  void OnInformedRestoreImageTaken(
      RequestedSessionState requested_session_state,
      const base::FilePath& file_path,
      base::TimeTicks start_time,
      gfx::Image informed_restore_image);

  // Callback invoked when the informed restore image was encoded and saved.
  // `file_path` is the file path to save the informed restore image.
  void OnInformedRestoreImageSaved(base::TimeTicks start_time,
                                   const base::FilePath& file_path);

  // Called when `session_state_change_timer_` times out with `kRestart`
  // requested.
  void DoRestart(power_manager::RequestRestartReason reason,
                 const std::string& description);

  std::unique_ptr<SessionStateAnimator> animator_;

  // Current lock status.
  bool system_is_locked_ = false;

  // True if the real non-cancelable shutting down process started.
  bool shutting_down_ = false;

  // True if the requested cancelable shutdown gets canceled.
  bool shutdown_canceled_ = false;

  // The reason (e.g. user action) for a pending shutdown.
  std::optional<ShutdownReason> shutdown_reason_;

  // Callback bound on restart requested and run when
  // `session_state_change_timer_` times out.
  base::OnceClosure restart_callback_;

  // Indicates whether controller should proceed to (cancellable) shutdown after
  // locking.
  bool shutdown_after_lock_ = false;

  // Indicates that controller displays lock animation.
  bool animating_lock_ = false;

  // Indicates that controller displays unlock animation.
  bool animating_unlock_ = false;

  // Indicates that the power button has been pressed during the unlock
  // animation
  bool pb_pressed_during_unlock_ = false;

  // Indicates whether post lock animation should be immediate.
  bool post_lock_immediate_animation_ = false;

  std::unique_ptr<UnlockedStateProperties> unlocked_properties_;

  // How long has it been since the request to lock the screen?
  std::unique_ptr<base::ElapsedTimer> lock_duration_timer_;

  // Controller used to trigger the actual shutdown.
  raw_ptr<ShutdownController, DanglingUntriaged> shutdown_controller_;

  // Started when we request that the screen be locked. When it fires, we
  // assume that our request got dropped.
  base::OneShotTimer lock_fail_timer_;

  // Started when we call StartPostLockAnimation. When it fires, we assume
  // that our request got dropped.
  base::OneShotTimer post_lock_fail_timer_;

  // Started when a cancelable shutdown requested and the shutdown animation
  // triggered. When it fires, the real non-cancelable shutdown will start.
  base::OneShotTimer cancelable_shutdown_timer_;

  // Started when we display the session state change animation (NOTE, shutdown,
  // restart and signout have the same animation). When it fires, we actually
  // request the session state change. Gives the animation time to complete
  // before Chrome etc are shut down.
  base::OneShotTimer session_state_change_timer_;

  base::OnceClosure lock_screen_displayed_callback_;

  base::OnceCallback<void(bool)> start_unlock_callback_;

  // A new layer that mirrors the wallpaper layer, which will be added to the
  // layer hierarchy and help include the wallpaper into the informed restore
  // screenshot.
  std::unique_ptr<ui::Layer> mirror_wallpaper_layer_;

  // A timer tracks the time duration it takes to take the informed restore
  // image. If this timer timeouts before taking the screenshot completes, the
  // shutdown process will be triggered immediately without the informed restore
  // image. This is done to avoid the shutdown process being blocked too long to
  // be noticed by the users.
  base::OneShotTimer take_screenshot_fail_timer_;

  ScopedSessionObserver scoped_session_observer_;

  // The wallpaper blur before entering lock state. Used to restore the
  // wallpaper blur after exiting lock state.
  float saved_blur_;

  base::ObserverList<LockStateObserver>::Unchecked observers_;

  // To access the pref kLoginShutdownTimestampPrefName
  raw_ptr<PrefService> local_state_;

  // If set, it will be called once the operation on the informed restore image
  // is completed, either it was deleted or saved to the disk.
  base::OnceClosure informed_restore_image_callback_for_test_;

  // Disables the `take_screenshot_fail_timer_` for test, which means the timer
  // will never start if this is set to true.
  bool disable_screenshot_timeout_for_test_ = false;

  base::WeakPtrFactory<LockStateController> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_WM_LOCK_STATE_CONTROLLER_H_