chromium/chrome/browser/ash/device_sync/device_sync_client_factory.cc

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

#include "chrome/browser/ash/device_sync/device_sync_client_factory.h"

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/task/single_thread_task_runner.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/attestation/soft_bind_attestation_flow_impl.h"
#include "chrome/browser/ash/cryptauth/client_app_metadata_provider_service.h"
#include "chrome/browser/ash/cryptauth/client_app_metadata_provider_service_factory.h"
#include "chrome/browser/ash/cryptauth/gcm_device_info_provider_impl.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/gcm/instance_id/instance_id_profile_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chromeos/ash/components/multidevice/stub_multidevice_util.h"
#include "chromeos/ash/services/device_sync/device_sync_impl.h"
#include "chromeos/ash/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/ash/services/device_sync/public/cpp/device_sync_client_impl.h"
#include "chromeos/ash/services/device_sync/stub_device_sync.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/prefs.h"
#include "components/account_id/account_id.h"
#include "components/gcm_driver/instance_id/instance_id_profile_service.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

namespace ash {
namespace device_sync {

namespace {

// CryptAuth enrollment is allowed only if at least one multi-device feature is
// enabled. This ensures that we do not unnecessarily register devices on the
// CryptAuth back-end when the registration would never actually be used.
bool IsEnrollmentAllowedByPolicy(content::BrowserContext* context) {
  return multidevice_setup::AreAnyMultiDeviceFeaturesAllowed(
      Profile::FromBrowserContext(context)->GetPrefs());
}

}  // namespace

// Class that wraps DeviceSyncClient in a KeyedService.
class DeviceSyncClientHolder : public KeyedService {
 public:
  explicit DeviceSyncClientHolder(content::BrowserContext* context)
      : soft_bind_attestation_flow_(
            std::make_unique<attestation::SoftBindAttestationFlowImpl>()),
        device_sync_(CreateDeviceSyncImplForProfile(
            Profile::FromBrowserContext(context))),
        device_sync_client_(DeviceSyncClientImpl::Factory::Create()) {
    // Connect the client's mojo remote to the implementation.
    device_sync_->BindReceiver(device_sync_client_->GetDeviceSyncRemote()
                                   ->BindNewPipeAndPassReceiver());
    // Finish client initialization.
    device_sync_client_->Initialize(
        base::SingleThreadTaskRunner::GetCurrentDefault());
  }

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

  DeviceSyncClient* device_sync_client() { return device_sync_client_.get(); }

 private:
  // KeyedService:
  void Shutdown() override {
    device_sync_client_.reset();
    device_sync_->CloseAllReceivers();
    device_sync_.reset();
    soft_bind_attestation_flow_.reset();
  }

  std::unique_ptr<DeviceSyncBase> CreateDeviceSyncImplForProfile(
      Profile* profile) {
    return DeviceSyncImpl::Factory::Create(
        IdentityManagerFactory::GetForProfile(profile),
        instance_id::InstanceIDProfileServiceFactory::GetForProfile(profile)
            ->driver(),
        profile->GetPrefs(), GcmDeviceInfoProviderImpl::GetInstance(),
        ClientAppMetadataProviderServiceFactory::GetForProfile(profile),
        profile->GetURLLoaderFactory(), std::make_unique<base::OneShotTimer>(),
        base::BindRepeating(&DeviceSyncClientHolder::GetAttestationCertificates,
                            base::Unretained(this), profile));
  }

  void GetAttestationCertificates(
      Profile* profile,
      attestation::SoftBindAttestationFlow::Callback notify_callback,
      const std::string& user_key) {
    const user_manager::User* user =
        ProfileHelper::Get()->GetUserByProfile(profile);
    soft_bind_attestation_flow_->GetCertificate(
        std::move(notify_callback),
        user ? user->GetAccountId() : EmptyAccountId(), user_key);
  }

  std::unique_ptr<attestation::SoftBindAttestationFlowImpl>
      soft_bind_attestation_flow_;

  std::unique_ptr<DeviceSyncBase> device_sync_;
  std::unique_ptr<DeviceSyncClient> device_sync_client_;
};

DeviceSyncClientFactory::DeviceSyncClientFactory()
    : ProfileKeyedServiceFactory(
          "DeviceSyncClient",
          ProfileSelections::Builder()
              .WithRegular(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/40257657): Check if this service is needed in
              // Guest mode.
              .WithGuest(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/41488885): Check if this service is needed for
              // Ash Internals.
              .WithAshInternals(ProfileSelection::kOriginalOnly)
              .Build()) {
  DependsOn(ClientAppMetadataProviderServiceFactory::GetInstance());
  DependsOn(IdentityManagerFactory::GetInstance());
  DependsOn(instance_id::InstanceIDProfileServiceFactory::GetInstance());

  // If ShouldUseMultideviceStubs() is true, set a stub factory to facilitate
  // fake devices for testing in the Linux Chrome OS build. Note that this is
  // not done when a custom factory has already been set.
  if (multidevice::ShouldUseMultideviceStubs() &&
      !DeviceSyncImpl::Factory::IsCustomFactorySet()) {
    SetStubDeviceSyncFactory();
  }
}

DeviceSyncClientFactory::~DeviceSyncClientFactory() = default;

// static
DeviceSyncClient* DeviceSyncClientFactory::GetForProfile(Profile* profile) {
  DeviceSyncClientHolder* holder = static_cast<DeviceSyncClientHolder*>(
      GetInstance()->GetServiceForBrowserContext(profile, true));

  return holder ? holder->device_sync_client() : nullptr;
}

// static
DeviceSyncClientFactory* DeviceSyncClientFactory::GetInstance() {
  static base::NoDestructor<DeviceSyncClientFactory> instance;
  return instance.get();
}

std::unique_ptr<KeyedService>
DeviceSyncClientFactory::BuildServiceInstanceForBrowserContext(
    content::BrowserContext* context) const {
  // TODO(crbug.com/848347): Check prohibited by policy in services that depend
  // on this Factory, not here.
  if (IsEnrollmentAllowedByPolicy(context))
    return std::make_unique<DeviceSyncClientHolder>(context);

  return nullptr;
}

bool DeviceSyncClientFactory::ServiceIsNULLWhileTesting() const {
  return true;
}

}  // namespace device_sync
}  // namespace ash