chromium/chromeos/ash/services/multidevice_setup/global_state_feature_manager_impl.h

// Copyright 2021 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_SERVICES_MULTIDEVICE_SETUP_GLOBAL_STATE_FEATURE_MANAGER_IMPL_H_
#define CHROMEOS_ASH_SERVICES_MULTIDEVICE_SETUP_GLOBAL_STATE_FEATURE_MANAGER_IMPL_H_

#include <memory>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chromeos/ash/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/ash/services/device_sync/public/mojom/device_sync.mojom.h"
#include "chromeos/ash/services/multidevice_setup/global_state_feature_manager.h"
#include "chromeos/ash/services/multidevice_setup/host_status_provider.h"
#include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"

class PrefRegistrySimple;
class PrefService;

namespace ash {

namespace multidevice {
enum class SoftwareFeature;
}

namespace multidevice_setup {

// Concrete GlobalStateFeatureManager implementation, which utilizes
// DeviceSyncClient to communicate with the back-end.
//
// This toggles the managed feature's host state between enabled/supported on
// cryptauth for a synced phone, where the supported state is considered
// disabled by user.
//
// Toggling the feature state is a global action, so it will be reflected on all
// synced devices.
class GlobalStateFeatureManagerImpl
    : public GlobalStateFeatureManager,
      public HostStatusProvider::Observer,
      public device_sync::DeviceSyncClient::Observer {
 public:
  class Factory {
   public:
    enum class Option {
      // Corresponds to |mojom::Feature::kWifiSync|.
      kWifiSync
    };

    static std::unique_ptr<GlobalStateFeatureManager> Create(
        Option option,
        HostStatusProvider* host_status_provider,
        PrefService* pref_service,
        device_sync::DeviceSyncClient* device_sync_client,
        std::unique_ptr<base::OneShotTimer> timer =
            std::make_unique<base::OneShotTimer>());
    static void SetFactoryForTesting(Factory* test_factory);

   protected:
    virtual ~Factory();
    virtual std::unique_ptr<GlobalStateFeatureManager> CreateInstance(
        Option option,
        HostStatusProvider* host_status_provider,
        PrefService* pref_service,
        device_sync::DeviceSyncClient* device_sync_client,
        std::unique_ptr<base::OneShotTimer> timer) = 0;

   private:
    static Factory* test_factory_;
  };

  static void RegisterPrefs(PrefRegistrySimple* registry);

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

 private:
  GlobalStateFeatureManagerImpl(
      mojom::Feature managed_feature,
      multidevice::SoftwareFeature managed_host_feature,
      const std::string& pending_state_pref_name,
      HostStatusProvider* host_status_provider,
      PrefService* pref_service,
      device_sync::DeviceSyncClient* device_sync_client,
      std::unique_ptr<base::OneShotTimer> timer);

  // HostStatusProvider::Observer,
  void OnHostStatusChange(const HostStatusProvider::HostStatusWithDevice&
                              host_status_with_device) override;

  // DeviceSyncClient::Observer:
  void OnNewDevicesSynced() override;

  // GlobalStateFeatureManager:

  // Attempts to enable/disable the managed feature on the backend for the host
  // device that is synced at the time SetIsFeatureEnabled is called.
  //
  // If a the request fails (e.g., the device is offline or the server is down),
  // this object will continue to attempt the request until one of the following
  // happens: the
  // request succeeds, SetIsFeatureEnabled() called is with different value, or
  // the synced host device changes.
  //
  // If there is already a pending request and this function is called with the
  // same request, a retry will be attempted immediately.
  void SetIsFeatureEnabled(bool enabled) override;

  // Returns whether the managed feature is enabled/disabled. If there is a
  // pending request to enable or disable the feature, the state that the
  // pending request is intending to set to is returned, otherwise the state on
  // the backend is returned.
  bool IsFeatureEnabled() override;

  // Numerical values cannot be changed because they map to integers that are
  // stored persistently in prefs.
  enum class PendingState {
    kPendingNone = 0,
    kPendingEnable = 1,
    kPendingDisable = 2,
    kSetPendingEnableOnVerify = 3
  };

  enum class CurrentState {
    kNoVerifiedHost,
    kNoPendingRequest,
    kPendingMatchesBackend,
    kValidPendingRequest
  };

  PendingState GetPendingState();
  CurrentState GetCurrentState();

  void ResetPendingNetworkRequest();
  void SetPendingState(PendingState pending_state);
  void AttemptSetHostStateNetworkRequest(bool is_retry);
  void OnSetHostStateNetworkRequestFinished(
      bool attempted_to_enable,
      device_sync::mojom::NetworkRequestResult result_code);
  bool ShouldEnableOnVerify();
  void ProcessEnableOnVerifyAttempt();
  bool ShouldAttemptToEnableAfterHostVerified();

  // The feature being managed.
  mojom::Feature managed_feature_;
  // Corresponding CryptAuth host feature for |managed_feature_|.
  multidevice::SoftwareFeature managed_host_feature_;
  const std::string pending_state_pref_name_;
  raw_ptr<HostStatusProvider> host_status_provider_;
  raw_ptr<PrefService> pref_service_;
  raw_ptr<device_sync::DeviceSyncClient> device_sync_client_;
  std::unique_ptr<base::OneShotTimer> timer_;

  bool network_request_in_flight_ = false;

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

}  // namespace multidevice_setup

}  // namespace ash

#endif  // CHROMEOS_ASH_SERVICES_MULTIDEVICE_SETUP_GLOBAL_STATE_FEATURE_MANAGER_IMPL_H_