chromium/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.h

// Copyright 2019 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_CHILD_ACCOUNTS_TIME_LIMITS_APP_ACTIVITY_REGISTRY_H_
#define CHROME_BROWSER_ASH_CHILD_ACCOUNTS_TIME_LIMITS_APP_ACTIVITY_REGISTRY_H_

#include <map>
#include <memory>
#include <optional>
#include <set>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_activity_report_interface.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_service_wrapper.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_types.h"

namespace base {
class UnguessableToken;
}  // namespace base

namespace enterprise_management {
class ChildStatusReportRequest;
}  // namespace enterprise_management

class PrefRegistrySimple;
class PrefService;

namespace ash {
namespace app_time {

class AppTimeLimitsAllowlistPolicyWrapper;
class AppTimeNotificationDelegate;
class PersistedAppInfo;

// Keeps track of app activity and time limits information.
// Stores app activity between user session. Information about uninstalled apps
// are removed from the registry after activity was uploaded to server or after
// 30 days if upload did not happen.
class AppActivityRegistry : public AppServiceWrapper::EventListener {
 public:
  // Used for tests to get internal implementation details.
  class TestApi {
   public:
    explicit TestApi(AppActivityRegistry* registry);
    ~TestApi();

    const std::optional<AppLimit>& GetAppLimit(const AppId& app_id) const;
    std::optional<base::TimeDelta> GetTimeLeft(const AppId& app_id) const;
    void SaveAppActivity();

   private:
    const raw_ptr<AppActivityRegistry, DanglingUntriaged> registry_;
  };

  // Interface for the observers interested in the changes of apps state.
  class AppStateObserver : public base::CheckedObserver {
   public:
    AppStateObserver() = default;
    AppStateObserver(const AppStateObserver&) = delete;
    AppStateObserver& operator=(const AppStateObserver&) = delete;
    ~AppStateObserver() override = default;

    // Called when state of the app with |app_id| changed to |kLimitReached|.
    // |was_active| indicates whether the app was active before reaching the
    // limit.
    virtual void OnAppLimitReached(const AppId& app_id,
                                   base::TimeDelta time_limit,
                                   bool was_active) = 0;

    // Called when state of the app with |app_id| is no longer |kLimitReached|.
    virtual void OnAppLimitRemoved(const AppId& app_id) = 0;

    // Called when new app was installed.
    virtual void OnAppInstalled(const AppId& app_id) = 0;
  };

  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  AppActivityRegistry(AppServiceWrapper* app_service_wrapper,
                      AppTimeNotificationDelegate* notification_delegate,
                      PrefService* pref_service);
  AppActivityRegistry(const AppActivityRegistry&) = delete;
  AppActivityRegistry& operator=(const AppActivityRegistry&) = delete;
  ~AppActivityRegistry() override;

  // AppServiceWrapper::EventListener:
  void OnAppInstalled(const AppId& app_id) override;
  void OnAppUninstalled(const AppId& app_id) override;
  void OnAppAvailable(const AppId& app_id) override;
  void OnAppBlocked(const AppId& app_id) override;
  void OnAppActive(const AppId& app_id,
                   const base::UnguessableToken& instance_id,
                   base::Time timestamp) override;
  void OnAppInactive(const AppId& app_id,
                     const base::UnguessableToken& instance_id,
                     base::Time timestamp) override;
  void OnAppDestroyed(const AppId& app_id,
                      const base::UnguessableToken& instance_id,
                      base::Time timestamp) override;

  bool IsAppInstalled(const AppId& app_id) const;
  bool IsAppAvailable(const AppId& app_id) const;
  bool IsAppBlocked(const AppId& app_id) const;
  bool IsAppTimeLimitReached(const AppId& app_id) const;
  bool IsAppActive(const AppId& app_id) const;
  bool IsAllowlistedApp(const AppId& app_id) const;

  // Manages AppStateObservers.
  void AddAppStateObserver(AppStateObserver* observer);
  void RemoveAppStateObserver(AppStateObserver* observer);

  // Called from AppTimeController to notify AppActivityRegistry about installed
  // apps which AppActivityRegistry may have missed.
  void SetInstalledApps(const std::vector<AppId>& installed_apps);

  // Returns the total active time for the application since the last time limit
  // reset.
  base::TimeDelta GetActiveTime(const AppId& app_id) const;

  // Web time limit is the time limit set for Chrome browser. It is shared
  // between Chrome and Web apps.
  const std::optional<AppLimit>& GetWebTimeLimit() const;

  AppState GetAppState(const AppId& app_id) const;

  // Returns current time limit for the app identified by |app_id|.
  // Will return nullopt if there is no limit set.
  std::optional<base::TimeDelta> GetTimeLimit(const AppId& app_id) const;

  // Reporting enablement is set if |enabled| has value.
  void SetReportingEnabled(std::optional<bool> enabled);

  void GenerateHiddenApps(
      enterprise_management::ChildStatusReportRequest* report);

  // Populates |report| with collected app activity. Returns whether any data
  // were reported.
  AppActivityReportInterface::ReportParams GenerateAppActivityReport(
      enterprise_management::ChildStatusReportRequest* report);

  // Application activities earlier than |timestamp| have been reported. Clear
  // entries earlier than |timestamp|.
  void OnSuccessfullyReported(base::Time timestamp);

  // Updates time limits for all installed apps.
  // Apps not present in |app_limits| are treated as they do not have limit set.
  // Returns true if a new app limit is observed in any of the applications.
  bool UpdateAppLimits(const std::map<AppId, AppLimit>& app_limits);

  // Sets time limit for app identified with |app_id|.
  // Does not affect limits of any other app. Not specified |app_limit| means
  // that app does not have limit set. Does not affect limits of any other app.
  // Returns true if a new app limit is observed.
  bool SetAppLimit(const AppId& app_id,
                   const std::optional<AppLimit>& app_limit);

  // Sets the app identified with |app_id| as being always available.
  void SetAppAllowlisted(const AppId& app_id);

  // Reset time has been reached at |timestamp|.
  void OnResetTimeReached(base::Time timestamp);

  // Called from WebTimeActivityProvider to update chrome app state.
  void OnChromeAppActivityChanged(ChromeAppActivityState state,
                                  base::Time timestamp);

  // Allowlisted applications changed. Called by AppTimeController.
  void OnTimeLimitAllowlistChanged(
      const AppTimeLimitsAllowlistPolicyWrapper& wrapper);

  // Saves app activity into user preference.
  void SaveAppActivity();

  std::vector<AppId> GetAppsWithAppRestriction(
      AppRestriction restriction) const;

 private:
  struct SystemNotification {
    SystemNotification(std::optional<base::TimeDelta> app_time_limit,
                       AppNotification app_notification);
    SystemNotification(const SystemNotification&);
    SystemNotification& operator=(const SystemNotification&);
    std::optional<base::TimeDelta> time_limit = std::nullopt;
    AppNotification notification = AppNotification::kUnknown;
  };

  // Bundles detailed data stored for a specific app.
  struct AppDetails {
    AppDetails();
    explicit AppDetails(const AppActivity& activity);
    AppDetails(const AppDetails&) = delete;
    AppDetails& operator=(const AppDetails&) = delete;
    ~AppDetails();

    // Resets the time limit check timer.
    void ResetTimeCheck();

    // Checks |limit| and |activity| to determine if the limit was reached.
    bool IsLimitReached() const;

    // Checks if |limit| is equal to |another_limit| with exception for the
    // timestamp (that does not indicate that limit changed).
    bool IsLimitEqual(const std::optional<AppLimit>& another_limit) const;

    // Contains information about current app state and logged activity.
    AppActivity activity{AppState::kAvailable};

    // Contains the set of active instances for the application.
    std::set<base::UnguessableToken> active_instances;

    // The set of instances AppActivityRegistry has requested to be paused, but
    // which have not been paused yet.
    std::set<base::UnguessableToken> paused_instances;

    // Contains information about restriction set for the app.
    std::optional<AppLimit> limit;

    // Timer set up for when the app time limit is expected to be reached and
    // preceding notifications.
    std::unique_ptr<base::OneShotTimer> app_limit_timer;

    // Boolean to specify if OnAppInstalled call has been received for this
    // particular application.
    bool received_app_installed_ = false;

    // At the beginning of a session, we may want to send system notifications
    // for applications. This may happen if there is an update in
    // PerAppTimeLimits policy while the user was logged out. In these
    // scenarios, we have to wait until the application is installed.
    std::vector<SystemNotification> pending_notifications_;
  };

  // OnAppReinstalled is called when an application has been uninstalled and
  // then installed again before being removed from app registry.
  void OnAppReinstalled(const AppId& app_id);

  // Removes data older than |timestamp| from the registry.
  // Removes entries for uninstalled apps if there is no more relevant activity
  // data left.
  void CleanRegistry(base::Time timestamp);

  // Adds an ap to the registry if it does not exist.
  void Add(const AppId& app_id);

  // Convenience methods to access state of the app identified by |app_id|.
  // Should only be called if app exists in the registry.
  void SetAppState(const AppId& app_id, AppState app_state);

  // Notifies state observers the application identified by |app_id| has reached
  // its set time limit.
  void NotifyLimitReached(const AppId& app_id, bool was_active);

  // Methods to set the application as active and inactive respectively.
  void SetAppActive(const AppId& app_id, base::Time timestamp);
  void SetAppInactive(const AppId& app_id, base::Time timestamp);

  std::optional<base::TimeDelta> GetTimeLeftForApp(const AppId& app_id) const;

  // Schedules a time limit check for application when it becomes active.
  void ScheduleTimeLimitCheckForApp(const AppId& app_id);

  // Checks the limit and shows notification if needed.
  void CheckTimeLimitForApp(const AppId& app_id);

  // Shows notification about time limit updates for the app if there were
  // relevant changes between |old_limit| and |new_limit|. Returns true if a
  // notification has been made.
  bool ShowLimitUpdatedNotificationIfNeeded(
      const AppId& app_id,
      const std::optional<AppLimit>& old_limit,
      const std::optional<AppLimit>& new_limit);

  base::TimeDelta GetWebActiveRunningTime() const;

  void WebTimeLimitReached(base::Time timestamp);

  // Initializes |activity_registry_| from the stored values in user pref.
  // Installed applications, their AppStates and their running active times will
  // be restored.
  void InitializeRegistryFromPref();
  void InitializeAppActivities();

  // Updates |AppActivity::active_times_| to include the current activity up to
  // |timestamp| then creates the most up to date instance of PersistedAppInfo.
  PersistedAppInfo GetPersistedAppInfoForApp(const AppId& app_id,
                                             base::Time timestamp);

  // Returns true if the last successfully reported time is earlier than 30 days
  // from base::Time::Now();
  bool ShouldCleanUpStoredPref();

  // Sends system notification for the application.
  void SendSystemNotificationsForApp(const AppId& app_id);

  // Shows notification or queues it to be shown later.
  void MaybeShowSystemNotification(const AppId& app_id,
                                   const SystemNotification& notification);

  // Called by AppActivityRegistry::SetAppLimit after the application's limit
  // has been updated.
  void AppLimitUpdated(const AppId& app_id);

  const raw_ptr<PrefService> pref_service_;

  // Owned by AppTimeController.
  const raw_ptr<AppServiceWrapper> app_service_wrapper_;

  // Notification delegate.
  const raw_ptr<AppTimeNotificationDelegate> notification_delegate_;

  // Observers to be notified about app state changes.
  base::ObserverList<AppStateObserver> app_state_observers_;

  std::map<AppId, AppDetails> activity_registry_;

  // Newly installed applications which have not yet been added to the user
  // pref.
  std::vector<AppId> newly_installed_apps_;

  // Repeating timer to trigger saving app activity to pref service.
  base::RepeatingTimer save_data_to_pref_service_;

  // This records the timestamp of the latest set app limit.
  base::Time latest_app_limit_update_;

  // Boolean to capture if app activity data reporting is enabled.
  bool activity_reporting_enabled_ = true;
};

}  // namespace app_time
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_CHILD_ACCOUNTS_TIME_LIMITS_APP_ACTIVITY_REGISTRY_H_