chromium/chrome/browser/ash/policy/off_hours/device_off_hours_controller.h

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

#ifndef CHROME_BROWSER_ASH_POLICY_OFF_HOURS_DEVICE_OFF_HOURS_CONTROLLER_H_
#define CHROME_BROWSER_ASH_POLICY_OFF_HOURS_DEVICE_OFF_HOURS_CONTROLLER_H_

#include <memory>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/timer/wall_clock_timer.h"
#include "chromeos/ash/components/dbus/system_clock/system_clock_client.h"
#include "chromeos/ash/components/policy/weekly_time/weekly_time_interval.h"
#include "components/policy/proto/chrome_device_policy.pb.h"

namespace policy {
namespace off_hours {

// The main class for handling "OffHours" policy turns "OffHours" mode on and
// off, handles server and client time, timezone.
//
// DeviceSettingsService is owner of this object. Use to get a reference:
// DeviceSettingsService::Get()->device_off_hours_controller()
//
// Device setting proto is changed in DeviceSettingsService and doesn't contain
// ignored device policies from DeviceOffHoursProto during "OffHours" mode. It
// is changed exactly in DeviceSettingsService because it's late to change
// policies in PrefValueMap and PolicyMap. The system will revert to the default
// behavior for the removed policies. And behavior of policies is handled during
// decoding process from proto to PolicyMap.
//
// "OffHours" mode requires the system clock to be network synchronized.
// However, network synchronization happens only after the first policy parsing.
// We therefore have to assume that the clock is in fact synchronized at
// startup. Otherwise we would terminate off-hour-allowed guest sessions
// immediately.
class DeviceOffHoursController : public ash::SystemClockClient::Observer {
 public:
  // Observer interface.
  class Observer {
   public:
    // Gets called when "OffHours" end time is changed.
    virtual void OnOffHoursEndTimeChanged() {}

   protected:
    virtual ~Observer() {}
  };

  // Creates a device off hours controller instance.
  DeviceOffHoursController();

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

  ~DeviceOffHoursController() override;

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

  // Return current "OffHours" mode status.
  bool is_off_hours_mode() const { return off_hours_mode_; }

  // Return true if the current user session is allowed only during the
  // "OffHours" and will be signed out at the end of session. Always returns
  // false outside of "OffHours".
  bool IsCurrentSessionAllowedOnlyForOffHours() const;

  // Save actual "OffHours" intervals from |device_settings_proto| to
  // |off_hours_intervals_| and call "UpdateOffhoursMode()".
  void UpdateOffHoursPolicy(
      const enterprise_management::ChromeDeviceSettingsProto&
          device_settings_proto);

  // Return "OffHours" mode end time during "OffHours" mode is on. Return null
  // when "OffHours" mode is off.
  base::Time GetOffHoursEndTime() const { return off_hours_end_time_; }

  // ash::SystemClockClient::Observer:
  void SystemClockUpdated() override;

  // |timer_clock| is not owned and its lifetime should cover lifetime of
  // DeviceOffHoursContoller.
  void SetClockForTesting(base::Clock* clock,
                          const base::TickClock* timer_clock);

 private:
  // Run OnOffHoursEndTimeChanged() for observers.
  void NotifyOffHoursEndTimeChanged() const;

  // Called when "OffHours" mode is changed and ask DeviceSettingsService to
  // update current proto.
  void OffHoursModeIsChanged() const;

  // If device should be in "OffHours" mode based on the current time then apply
  // current "OffHours" interval and turn "OffHours" mode on otherwise turn
  // "OffHours" mode off and set timer to next update "OffHours" mode.
  void UpdateOffHoursMode();

  // Turn on and off "OffHours" mode and call "OffHoursModeIsChanged()" if
  // "OffHours" mode is changed.
  void SetOffHoursMode(bool off_hours_enabled);

  // Set "OffHours" mode end time.
  void SetOffHoursEndTime(base::Time off_hours_end_time);

  // Timer for update "OffHours" mode.
  void StartOffHoursTimer(base::Time update_time);
  void StopOffHoursTimer();

  // Called once when the system clock service initially becomes available (or
  // immediately if it's already available).
  void SystemClockInitiallyAvailable(bool service_is_available);

  // Called when the system clock synchronization status with network time is
  // changed.
  void NetworkSynchronizationUpdated(bool network_synchronized);

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

  // The main value of "OffHours" policy which indicates current "OffHours" mode
  // state.
  bool off_hours_mode_ = false;

  // "OffHours" mode end time. It is needed to show "OffHours" session limit
  // notification. When "OffHours" mode is off the value is base::Time{}.
  base::Time off_hours_end_time_;

  // Timer for updating device settings at the begin of next “OffHours” interval
  // or at the end of current "OffHours" interval.
  std::unique_ptr<base::WallClockTimer> timer_;

  // Used for testing purposes, otherwise it's an instance of
  // base::DefaultClock.
  raw_ptr<base::Clock> clock_;

  // We start with the assumption that the system clock is synchronized with the
  // network time. This will be updated once actual synchronization attempts are
  // made by the system.
  bool is_clock_network_synchronized_ = true;

  // Current "OffHours" time intervals.
  std::vector<WeeklyTimeInterval> off_hours_intervals_;

  // Non-"OffHours" device settings proto.
  // Needed to check policies outside of "OffHours" mode.
  enterprise_management::ChromeDeviceSettingsProto device_settings_proto_;

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

}  // namespace off_hours
}  // namespace policy

#endif  // CHROME_BROWSER_ASH_POLICY_OFF_HOURS_DEVICE_OFF_HOURS_CONTROLLER_H_