chromium/chrome/browser/ash/policy/handlers/minimum_version_policy_handler.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_HANDLERS_MINIMUM_VERSION_POLICY_HANDLER_H_
#define CHROME_BROWSER_ASH_POLICY_HANDLERS_MINIMUM_VERSION_POLICY_HANDLER_H_

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/wall_clock_timer.h"
#include "base/version.h"
#include "chrome/browser/upgrade_detector/build_state_observer.h"
#include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "chromeos/ash/components/settings/cros_settings.h"

class PrefRegistrySimple;

namespace ash {
class NetworkStateHandler;
class UpdateRequiredNotification;
}  // namespace ash

namespace base {
class Clock;
class Time;
}  // namespace base

namespace policy {

// This class observes the device setting |kDeviceMinimumVersion|, and
// checks if respective requirement is met. If an update is not required, all
// running timers are reset. If an update is required, it calculates the
// deadline using the warning period in the policy and restarts the timer. It
// also calls UpdateRequiredNotification to show in-session notifications if an
// update is required but it cannot be downloaded due to network limitations or
// Auto Update Expiration.
class MinimumVersionPolicyHandler : public BuildStateObserver,
                                    public ash::NetworkStateHandlerObserver,
                                    public ash::UpdateEngineClient::Observer {
 public:
  static const char kRequirements[];
  static const char kChromeOsVersion[];
  static const char kWarningPeriod[];
  static const char kEolWarningPeriod[];
  static const char kUnmanagedUserRestricted[];

  class Observer {
   public:
    virtual void OnMinimumVersionStateChanged() = 0;
    virtual ~Observer() = default;
  };

  // Delegate of MinimumVersionPolicyHandler to handle the external
  // dependencies.
  class Delegate {
   public:
    virtual ~Delegate() {}

    // Checks if the user is logged in as any kiosk app or this is an
    // auto-launch kiosk device.
    virtual bool IsKioskMode() const = 0;

    // Checks if the device is enterprise managed.
    virtual bool IsDeviceEnterpriseManaged() const = 0;

    // Checks if a user is logged in.
    virtual bool IsUserLoggedIn() const = 0;

    // Checks if the user logged in is managed and not a child user.
    virtual bool IsUserEnterpriseManaged() const = 0;

    // Checks if we are currently on the login screen.
    virtual bool IsLoginSessionState() const = 0;

    // Checks if login is in progress before starting user session.
    virtual bool IsLoginInProgress() const = 0;

    // Shows the update required screen.
    virtual void ShowUpdateRequiredScreen() = 0;

    // Terminates the current session and restarts Chrome to show the login
    // screen.
    virtual void RestartToLoginScreen() = 0;

    // Hides update required screen and shows the login screen.
    virtual void HideUpdateRequiredScreenIfShown() = 0;

    virtual base::Version GetCurrentVersion() const = 0;
  };

  class MinimumVersionRequirement {
   public:
    MinimumVersionRequirement(const base::Version version,
                              const base::TimeDelta warning,
                              const base::TimeDelta eol_warning);

    MinimumVersionRequirement(const MinimumVersionRequirement&) = delete;

    // Method used to create an instance of MinimumVersionRequirement from
    // dictionary if it contains valid version string.
    static std::unique_ptr<MinimumVersionRequirement> CreateInstanceIfValid(
        const base::Value::Dict& dict);

    // This is used to compare two MinimumVersionRequirement objects
    // and returns 1 if the first object has version or warning time
    // or eol warning time greater than that the second, -1 if the
    // its version or warning time or eol warning time is less than the second,
    // else 0.
    int Compare(const MinimumVersionRequirement* other) const;

    base::Version version() const { return minimum_version_; }
    base::TimeDelta warning() const { return warning_time_; }
    base::TimeDelta eol_warning() const { return eol_warning_time_; }

   private:
    base::Version minimum_version_;
    base::TimeDelta warning_time_;
    base::TimeDelta eol_warning_time_;
  };

  enum class NetworkStatus { kAllowed, kMetered, kOffline };

  enum class NotificationType {
    kMeteredConnection,
    kNoConnection,
    kEolReached
  };

  explicit MinimumVersionPolicyHandler(Delegate* delegate,
                                       ash::CrosSettings* cros_settings);

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

  ~MinimumVersionPolicyHandler() override;

  // BuildStateObserver:
  void OnUpdate(const BuildState* build_state) override;

  // NetworkStateHandlerObserver:
  void DefaultNetworkChanged(const ash::NetworkState* network) override;
  void OnShuttingDown() override;

  // UpdateEngineClient::Observer:
  void UpdateStatusChanged(const update_engine::StatusResult& status) override;

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);
  bool RequirementsAreSatisfied() const { return GetState() == nullptr; }

  const MinimumVersionRequirement* GetState() const { return state_.get(); }

  bool DeadlineReached() { return deadline_reached_; }

  static void RegisterPrefs(PrefRegistrySimple* registry);

  // Show notification on login if the user is managed or
  // |unmanaged_user_restricted_| is set to true if it is the last day to
  // deadline.
  void MaybeShowNotificationOnLogin();

  // Whether banner to return back the device should be visible in Settings. It
  // is true when an update is required on a device that has reached End Of Life
  // (Auto Update Expiration) and the currently signed in user is enterprise
  // managed or an unmanaged user restricted by DeviceMinimumVersion policy.
  bool ShouldShowUpdateRequiredEolBanner() const;

  // Returns the number of days to deadline if update is required and deadline
  // has not been reached. Returns null if update is not required.
  std::optional<int> GetTimeRemainingInDays();

  // Callback used in tests and invoked after end-of-life status has been
  // fetched from the update_engine.
  void set_fetch_eol_callback_for_testing(base::OnceClosure callback) {
    fetch_eol_callback_ = std::move(callback);
  }

  base::Time update_required_deadline_for_testing() const {
    return update_required_deadline_;
  }

  bool IsDeadlineTimerRunningForTesting() const;

 private:
  // Returns |true| if the current version satisfies the given requirement.
  bool CurrentVersionSatisfies(
      const MinimumVersionRequirement& requirement) const;

  // Whether the current user should receive update required notifications and
  // force signed out on reaching the deadline. Retuns true if the user is
  // enterprise managed or |unmanaged_user_restricted_| is true.
  bool IsPolicyRestrictionAppliedForUser() const;

  void OnPolicyChanged();
  bool IsPolicyApplicable();
  void Reset();

  // Handles post update completed actions like reset timers, hide update
  // required notification and stop observing build state.
  void ResetOnUpdateCompleted();

  // Handles the state when update is required as per the policy. If on the
  // login screen, update required screen is shown, else the user is logged out
  // if the device is not updated within the given |warning_time|. The
  // |warning_time| is supplied by the policy.
  void HandleUpdateRequired(base::TimeDelta warning_time);

  void HandleUpdateNotRequired();

  // Invokes update_engine_client to fetch the end-of-life status of the device.
  void FetchEolInfo();

  // Callback after fetching end-of-life info from the update_engine_client.
  void OnFetchEolInfo(ash::UpdateEngineClient::EolInfo info);

  // Called when the warning time to apply updates has expired. If the user on
  // the login screen, the update required screen is shown else the current user
  // session is terminated to bring the user back to the login screen.
  void OnDeadlineReached();

  // Starts the timer to expire when |deadline| is reached.
  void StartDeadlineTimer(base::Time deadline);

  // Starts observing the BuildState for any updates in Chrome OS and resets the
  // state if new version satisfies the minimum version requirement.
  void StartObservingUpdate();

  // Shows notification for a managed logged in user if update is required and
  // the device can not be updated due to end-of-life or network limitations.
  void MaybeShowNotification(base::TimeDelta warning);

  // Shows notification if required and starts a timer to expire when the next
  // notification is to be shown.
  void ShowAndScheduleNotification(base::Time deadline);

  void HideNotification() const;

  void StopObservingNetwork();

  // Start updating over metered network as user has given consent through
  // notification click.
  void UpdateOverMeteredPermssionGranted();

  // Tells whether starting an update check succeeded or not.
  void OnUpdateCheckStarted(ash::UpdateEngineClient::UpdateCheckResult result);

  // Callback from UpdateEngineClient::SetUpdateOverCellularOneTimePermission().
  void OnSetUpdateOverCellularOneTimePermission(bool success);

  // Updates pref |kUpdateRequiredWarningPeriod| in local state to
  // |warning_time|. If |kUpdateRequiredTimerStartTime| is not null, it means
  // update is already required and hence, the timer start time should not be
  // updated.
  void UpdateLocalState(base::TimeDelta warning_time);

  // Resets the local state prefs to default values.
  void ResetLocalState();

  // This delegate instance is owned by the owner of
  // MinimumVersionPolicyHandler. The owner is responsible to make sure that the
  // delegate lives throughout the life of the policy handler.
  raw_ptr<Delegate> delegate_;

  // This represents the current minimum version requirement.
  // It is chosen as one of the configurations specified in the policy. It is
  // set to nullptr if the current version is higher than the minimum required
  // version in all the configurations.
  std::unique_ptr<MinimumVersionRequirement> state_;

  // If this flag is true, unmanaged user sessions receive update required
  // notifications and are force logged out when deadline is reached.
  bool unmanaged_user_restricted_ = false;

  bool eol_reached_ = false;

  // If this flag is true, user should restricted to use the session by logging
  // out and/or showing update required screen.
  bool deadline_reached_ = false;

  // Time when the policy is applied and with respect to which the deadline to
  // update the device is calculated.
  base::Time update_required_time_;

  // Deadline for updating the device post which the user is restricted from
  // using the session by force log out if a session is active and then blocking
  // sign in at the login screen.
  base::Time update_required_deadline_;

  // Fires when the deadline to update the device has reached or passed.
  base::WallClockTimer update_required_deadline_timer_;

  // Fires when next update required notification is to be shown.
  base::WallClockTimer notification_timer_;

  // Non-owning reference to CrosSettings. This class have shorter lifetime than
  // CrosSettings.
  raw_ptr<ash::CrosSettings> cros_settings_;

  const raw_ptr<base::Clock> clock_;

  base::OnceClosure fetch_eol_callback_;

  base::CallbackListSubscription policy_subscription_;

  // Handles showing in-session update required notifications on the basis of
  // current network and time to reach the deadline.
  std::unique_ptr<ash::UpdateRequiredNotification> notification_handler_;

  base::ScopedObservation<ash::NetworkStateHandler,
                          ash::NetworkStateHandlerObserver>
      network_state_handler_observer_{this};

  // List of registered observers.
  base::ObserverList<Observer>::Unchecked observers_;

  base::WeakPtrFactory<MinimumVersionPolicyHandler> weak_factory_{this};
};

}  // namespace policy

#endif  // CHROME_BROWSER_ASH_POLICY_HANDLERS_MINIMUM_VERSION_POLICY_HANDLER_H_