chromium/chrome/browser/ash/app_mode/kiosk_chrome_app_manager.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 CHROME_BROWSER_ASH_APP_MODE_KIOSK_CHROME_APP_MANAGER_H_
#define CHROME_BROWSER_ASH_APP_MODE_KIOSK_CHROME_APP_MANAGER_H_

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/time/time.h"
#include "chrome/browser/ash/app_mode/kiosk_app_manager_base.h"
#include "chrome/browser/ash/app_mode/kiosk_app_types.h"
#include "chrome/browser/ash/extensions/external_cache.h"
#include "chrome/browser/ash/extensions/external_cache_delegate.h"
#include "chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.h"
#include "chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom.h"
#include "components/account_id/account_id.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "extensions/browser/updater/extension_downloader_delegate.h"
#include "extensions/common/extension_id.h"
#include "net/base/backoff_entry.h"

class GURL;
class PrefRegistrySimple;
class Profile;

namespace base {
class CommandLine;
}

namespace extensions {
class Extension;
}

namespace ash {

class KioskAppData;
class KioskExternalUpdater;

extern const char kKioskPrimaryAppInstallErrorHistogram[];
extern const char kKioskPrimaryAppUpdateResultHistogram[];
extern const char kKioskExternalUpdateSuccessHistogram[];

// KioskChromeAppManager manages cached app data.
class KioskChromeAppManager : public KioskAppManagerBase,
                              public chromeos::ExternalCacheDelegate {
 public:
  // Result of downloading primary app from ExternalCache. Should be in sync
  // with extensions::ExtensionDownloaderDelegate::Error. Used in UMA metrics.
  enum class PrimaryAppDownloadResult {
    // Successful update.
    kSuccess,
    // Background networking is disabled.
    kDisabled,
    // Failed to fetch the manifest for this extension.
    kManifestFetchFailed,
    // The manifest couldn't be parsed.
    kManifestInvalid,
    // The manifest was fetched and parsed, and there are no updates for
    // this extension.
    kNoUpdateAvailable,
    // The update entry for the extension contained no fetch URL.
    kCrxFetchUrlEmpty,
    // The update entry for the extension contained invalid fetch URL.
    kCrxFetchUrlInvalid,
    // There was an update for this extension but the download of the crx
    // failed.
    kCrxFetchFailed,
    kMaxValue = kCrxFetchFailed,
  };

  typedef std::vector<App> Apps;

  // Interface that can be used to override default KioskChromeAppManager
  // behavior. For example, it can be used in tests to inject test components
  // implementations.
  class Overrides {
   public:
    virtual ~Overrides() = default;

    // Creates the external cache that should be used by the
    // KioskChromeAppManager. It should always return a valid object.
    virtual std::unique_ptr<chromeos::ExternalCache> CreateExternalCache(
        chromeos::ExternalCacheDelegate* delegate,
        bool always_check_updates) = 0;
  };

  // Name of a dictionary that holds kiosk app info in Local State.
  // Sample layout:
  //   "kiosk": {
  //     "auto_login_enabled": true  //
  //   }
  static const char kKioskDictionaryName[];
  static const char kKeyAutoLoginState[];

  // Returns the manager instance. Crashes if it is not yet initialized.
  static KioskChromeAppManager* Get();

  static bool IsInitialized();

  // Initializes KioskChromeAppManager for testing. An `overrides` can be given
  // to customize behavior in tests, or `null` to use the default behavior.
  static void InitializeForTesting(Overrides* overrides);

  // Registers kiosk app entries in local state.
  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);

  // Registers kiosk app prefs that will be attached to a user profile. It would
  // be applied to Kiosk, because a Kiosk session has a special user profile.
  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);

  KioskChromeAppManager();
  KioskChromeAppManager(const KioskChromeAppManager&) = delete;
  KioskChromeAppManager& operator=(const KioskChromeAppManager&) = delete;
  ~KioskChromeAppManager() override;

  // Returns auto launcher app id or an empty string if there is none.
  std::string GetAutoLaunchApp() const;

  // Returns the cached required platform version of the auto launch with
  // zero delay kiosk app.
  std::string GetAutoLaunchAppRequiredPlatformVersion() const;

  // `KioskAppManagerBase` implementation:
  // Gets info of all apps that have no meta data load error.
  std::vector<App> GetApps() const override;

  // Gets app data for the given app id. Returns true if `app_id` is known and
  // `app` is populated. Otherwise, return false.
  bool GetApp(const std::string& app_id, App* app) const;

  // Clears locally cached Gdata.
  void ClearAppData(const std::string& app_id);

  // Updates app data from the `app` in `profile`. `app` is provided to cover
  // the case of app update case where `app` is the new version and is not
  // finished installing (e.g. because old version is still running). Otherwise,
  // `app` could be NULL and the current installed app in `profile` will be
  // used.
  void UpdateAppDataFromProfile(const std::string& app_id,
                                Profile* profile,
                                const extensions::Extension* app);

  void RetryFailedAppDataFetch();

  // Returns true if the app is found in cache.
  bool HasCachedCrx(const std::string& app_id) const;

  // Gets the path and version of the cached crx with `app_id`.
  // Returns true if the app is found in cache.
  bool GetCachedCrx(const std::string& app_id,
                    base::FilePath* file_path,
                    std::string* version) const;

  crosapi::mojom::AppInstallParams CreatePrimaryAppInstallData(
      const std::string& id) const;

  void UpdateExternalCache();

  // Monitors kiosk external update from usb stick.
  void MonitorKioskExternalUpdate();

  // Notify this manager that a Kiosk session started with the given `app_id`.
  void OnKioskSessionStarted(const KioskAppId& app_id);

  // Invoked when kiosk app cache has been updated.
  void OnKioskAppCacheUpdated(const std::string& app_id);

  // Invoked when kiosk app updating from usb stick has been completed.
  // `success` indicates if all the updates are completed successfully.
  void OnKioskAppExternalUpdateComplete(bool success);

  // Installs the validated external extension into cache.
  void PutValidatedExternalExtension(
      const std::string& app_id,
      const base::FilePath& crx_path,
      const std::string& version,
      chromeos::ExternalCache::PutExternalExtensionCallback callback);

  // Whether the current platform is compliant with the given required
  // platform version.
  bool IsPlatformCompliant(const std::string& required_platform_version) const;

  // Whether the platform is compliant for the given app.
  bool IsPlatformCompliantWithApp(const extensions::Extension* app) const;

  // Notifies the KioskChromeAppManager that a given app was auto-launched
  // automatically with no delay on startup. Certain privacy-sensitive
  // kiosk-mode behavior (such as network reporting) is only enabled for
  // kiosk apps that are immediately auto-launched on startup.
  void SetAppWasAutoLaunchedWithZeroDelay(const std::string& app_id);

  // Sets retry backoff policy of extension downloader. Set `std::nullopt` to
  // restore to the default. Used to reduce backoff while Kiosk is launching.
  void SetExtensionDownloaderBackoffPolicy(
      std::optional<net::BackoffEntry::Policy> backoff_policy);

  // Adds an app with the given meta data directly and skips meta data fetching
  // for test.
  void AddAppForTest(const std::string& app_id,
                     const AccountId& account_id,
                     const GURL& update_url,
                     const std::string& required_platform_version);

 private:
  friend class GlobalManager;
  friend class ChromeAppKioskAppManagerTest;
  friend class KioskAutoLaunchViewsTest;
  friend class KioskBaseTest;

  // Gets KioskAppData for the given app id.
  const KioskAppData* GetAppData(const std::string& app_id) const;
  KioskAppData* GetAppDataMutable(const std::string& app_id);

  // KioskAppManagerBase:
  // Updates app data `apps_` based on CrosSettings.
  void UpdateAppsFromPolicy() override;

  // Updates the prefs of `external_cache_` from `apps_`.
  void UpdateExternalCachePrefs();

  // chromeos::ExternalCacheDelegate:
  void OnExtensionLoadedInCache(const extensions::ExtensionId& id,
                                bool is_updated) override;
  void OnExtensionDownloadFailed(
      const extensions::ExtensionId& id,
      extensions::ExtensionDownloaderDelegate::Error error) override;

  // Returns the auto launch delay.
  base::TimeDelta GetAutoLaunchDelay() const;

  // Gets list of user switches that should be passed to Chrome in case current
  // session has to be restored, e.g. in case of a crash. The switches will be
  // returned as `switches` command line arguments.
  // Returns whether the set of switches would have to be changed in respect to
  // the current set of switches - if that is not the case `switches` might not
  // get populated.
  bool GetSwitchesForSessionRestore(const std::string& app_id,
                                    base::CommandLine* switches);

  // KioskAppDataDelegate:
  void OnExternalCacheDamaged(const std::string& app_id) override;

  // Converts kiosk app data from internal representation KioskAppData to
  // App.
  App ConstructApp(const KioskAppData& data) const;

  std::vector<std::unique_ptr<KioskAppData>> apps_;
  std::string auto_launch_app_id_;
  std::string currently_auto_launched_with_zero_delay_app_;

  std::unique_ptr<chromeos::ExternalCache> external_cache_;

  std::unique_ptr<KioskExternalUpdater> usb_stick_updater_;
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_APP_MODE_KIOSK_CHROME_APP_MANAGER_H_