chromium/chromeos/ash/components/power/dark_resume_controller.h

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

#ifndef CHROMEOS_ASH_COMPONENTS_POWER_DARK_RESUME_CONTROLLER_H_
#define CHROMEOS_ASH_COMPONENTS_POWER_DARK_RESUME_CONTROLLER_H_

#include "base/component_export.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/unguessable_token.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/wake_lock.mojom.h"
#include "services/device/public/mojom/wake_lock_provider.mojom.h"

namespace ash::system {

// This class listens to dark resume events from the power manager and makes
// decisions on whether to re-suspend the device or keep the device in dark
// resume based on the wake locks activated at the time of a dark resume. It -
//
// 1. Starts a timer to check for any activated app suspension wake locks. This
// gives services time to do work in dark resume. They can activate a wake lock
// to indicate to the system that they are still doing work.
//
// 2. After the timer in 1 expires, an observer for wake lock deactivation is
// set. Also a hard timeout timer is scheduled.
//
// 3. If no wake lock is held then the observer gets notified immediately and
// the power manager is requested to re-suspend the system.
//
// 4. If an app suspension wake lock is acquired then either -
// - The observer from 2 is notified when the wake lock is deactivated and the
// power manager is requested to re-suspend the system.
// Or
// - The hard timeout timer from 2 fires and the power manager is requested to
// re-suspend the system.
//
// 5. If the system transitions to a full resume all dark resume related state
// and timers are cleared as the system wakes up.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_POWER) DarkResumeController
    : public chromeos::PowerManagerClient::Observer,
      public device::mojom::WakeLockObserver {
 public:
  explicit DarkResumeController(
      mojo::PendingRemote<device::mojom::WakeLockProvider> wake_lock_provider);

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

  ~DarkResumeController() override;

  // Time after a dark resume when wake lock count is checked and a decision is
  // made to re-suspend or wait for wake lock release.
  static constexpr base::TimeDelta kDarkResumeWakeLockCheckTimeout =
      base::Seconds(3);

  // chromeos::PowerManagerClient::Observer overrides.
  void PowerManagerInitialized() override;
  void DarkSuspendImminent() override;
  void SuspendDone(base::TimeDelta sleep_duration) override;

  // mojom::WakeLockObserver overrides.
  void OnWakeLockDeactivated(device::mojom::WakeLockType type) override;

  // Return true iff all dark resume related state is set i.e the suspend
  // readiness token is set and wake lock release event has observers.
  bool IsDarkResumeStateSetForTesting() const;

  // Return true iff all dark resume related state is reset i.e. suspend
  // readiness token is empty, wake lock release event has no observers,
  // wake lock check timer is reset, hard timeout timer is reset and there are
  // no in flight tasks. This should be true when device exits dark resume
  // either by re-suspending or transitioning to full resume.
  bool IsDarkResumeStateClearedForTesting() const;

  // Returns |dark_resume_hard_timeout_|.
  base::TimeDelta GetHardTimeoutForTesting() const;

 private:
  // Called |kDarkResumeWakeLockCheckTimeout| after a dark resume. Checks if
  // app suspension wake locks are held. If no wake locks are held then
  // re-suspends the device else schedules HandleDarkResumeHardTimeout.
  void HandleDarkResumeWakeLockCheckTimeout();

  // Called |kDarkResumeHardTimeout| after a
  // HandleDarkResumeWakeLockCheckTimeout. Clears all dark resume state and
  // re-suspends the device.
  void HandleDarkResumeHardTimeout();

  // Clears all state associated with dark resume.
  void ClearDarkResumeState();

  // Used for acquiring, releasing and observing wake locks.
  mojo::Remote<device::mojom::WakeLockProvider> wake_lock_provider_;

  // Used when system is ready to suspend after a DarkSupendImminent i.e.
  // after a dark resume.
  base::UnguessableToken block_suspend_token_;

  // The receiver used to implement device::mojom::WakeLockObserver.
  mojo::Receiver<device::mojom::WakeLockObserver> wake_lock_observer_receiver_{
      this};

  // Timer used to schedule HandleDarkResumeWakeLockCheckTimeout.
  base::OneShotTimer wake_lock_check_timer_;

  // Timer used to schedule HandleDarkResumeHardTimeout.
  base::OneShotTimer hard_timeout_timer_;

  // Max time to wait for wake lock release after a wake lock check after a dark
  // resume. After this time the system is asked to re-suspend. This is
  // initialized via PowerManagerClient when it's initialization is complete in
  // |PowerManagerInitialized|. Till then there may be a very small window after
  // booth when it takes a default value.
  base::TimeDelta dark_resume_hard_timeout_;

  // Used for checking if HandleDarkResumeWakeLockCheckTimeout and
  // HandleDarkResumeHardTimeout run on the same sequence.
  SEQUENCE_CHECKER(dark_resume_tasks_sequence_checker_);

  // This is invalidated in ClearDarkResumeState as a fail safe measure to clear
  // any lingering timer callbacks or wake lock observer callbacks. This is a
  // good to have but not a necessity as ClearDarkResumeState cancels any dark
  // resume state machine related tasks via other means. In the future if other
  // tasks or callbacks need to be added separate from the dark resume state
  // machine lifetime then a separate factory needs to be created and used.
  base::WeakPtrFactory<DarkResumeController> weak_ptr_factory_{this};
};

}  // namespace ash::system

#endif  // CHROMEOS_ASH_COMPONENTS_POWER_DARK_RESUME_CONTROLLER_H_