chromium/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc

// Copyright 2020 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/phonehub/phone_hub_manager_factory.h"

#include "ash/constants/ash_features.h"
#include "ash/public/cpp/system_tray.h"
#include "chrome/browser/ash/attestation/soft_bind_attestation_flow_impl.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/device_sync/device_sync_client_factory.h"
#include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h"
#include "chrome/browser/ash/phonehub/attestation_certificate_generator_impl.h"
#include "chrome/browser/ash/phonehub/browser_tabs_metadata_fetcher_impl.h"
#include "chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.h"
#include "chrome/browser/ash/phonehub/camera_roll_download_manager_impl.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/secure_channel/nearby_connector_factory.h"
#include "chrome/browser/ash/secure_channel/secure_channel_client_provider.h"
#include "chrome/browser/ash/sync/sync_mojo_service_ash.h"
#include "chrome/browser/ash/sync/sync_mojo_service_factory_ash.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/session_sync_service_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
#include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h"
#include "chromeos/ash/components/phonehub/camera_roll_manager_impl.h"
#include "chromeos/ash/components/phonehub/multidevice_feature_access_manager_impl.h"
#include "chromeos/ash/components/phonehub/multidevice_setup_state_updater.h"
#include "chromeos/ash/components/phonehub/onboarding_ui_tracker_impl.h"
#include "chromeos/ash/components/phonehub/phone_hub_manager.h"
#include "chromeos/ash/components/phonehub/phone_hub_manager_impl.h"
#include "chromeos/ash/components/phonehub/phone_hub_structured_metrics_logger.h"
#include "chromeos/ash/components/phonehub/recent_apps_interaction_handler_impl.h"
#include "chromeos/ash/components/phonehub/screen_lock_manager_impl.h"
#include "chromeos/ash/components/phonehub/user_action_recorder_impl.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/prefs.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/sync/base/features.h"
#include "components/user_manager/user_manager.h"

namespace ash::phonehub {

namespace {

content::BrowserContext* g_context_for_service = nullptr;

bool IsProhibitedByPolicy(Profile* profile) {
  return !multidevice_setup::IsFeatureAllowed(
      multidevice_setup::mojom::Feature::kPhoneHub, profile->GetPrefs());
}

bool IsLoggedInAsPrimaryUser(Profile* profile) {
  // Guest/incognito/signin profiles cannot use Phone Hub.
  if (ash::ProfileHelper::IsSigninProfile(profile) ||
      profile->IsOffTheRecord()) {
    return false;
  }

  // Likewise, kiosk users are ineligible.
  if (user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) {
    return false;
  }

  return ProfileHelper::IsPrimaryProfile(profile);
}

}  // namespace

// static
PhoneHubManager* PhoneHubManagerFactory::GetForProfile(Profile* profile) {
  return static_cast<PhoneHubManagerImpl*>(
      PhoneHubManagerFactory::GetInstance()->GetServiceForBrowserContext(
          profile, /*create=*/true));
}

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

PhoneHubManagerFactory::PhoneHubManagerFactory()
    : ProfileKeyedServiceFactory(
          "PhoneHubManager",
          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(device_sync::DeviceSyncClientFactory::GetInstance());
  if (features::IsPhoneHubCameraRollEnabled()) {
    DependsOn(HoldingSpaceKeyedServiceFactory::GetInstance());
  }
  DependsOn(multidevice_setup::MultiDeviceSetupClientFactory::GetInstance());
  DependsOn(secure_channel::NearbyConnectorFactory::GetInstance());
  DependsOn(SessionSyncServiceFactory::GetInstance());
  DependsOn(HistoryUiFaviconRequestHandlerFactory::GetInstance());
  DependsOn(SyncServiceFactory::GetInstance());

  // We typically also check crosapi::browser_util::IsAshWebBrowserEnabled() in
  // relation to this feature flag but this relies on UserManager which is not
  // initialized at this point. Since this is just a service dependency simply
  // checking the flag itself is fine.
  if (base::FeatureList::IsEnabled(syncer::kChromeOSSyncedSessionSharing)) {
    DependsOn(SyncMojoServiceFactoryAsh::GetInstance());
  }
}

PhoneHubManagerFactory::~PhoneHubManagerFactory() = default;

std::unique_ptr<KeyedService>
PhoneHubManagerFactory::BuildServiceInstanceForBrowserContext(
    content::BrowserContext* context) const {
  if (!features::IsPhoneHubEnabled()) {
    return nullptr;
  }

  Profile* profile = Profile::FromBrowserContext(context);

  // Only available to the primary profile.
  if (!IsLoggedInAsPrimaryUser(profile)) {
    return nullptr;
  }

  if (IsProhibitedByPolicy(profile)) {
    return nullptr;
  }

  if (!features::IsCrossDeviceFeatureSuiteAllowed()) {
    return nullptr;
  }

  std::unique_ptr<AttestationCertificateGeneratorImpl>
      attestation_certificate_generator = nullptr;

  if (features::IsEcheSWAEnabled()) {
    auto soft_bind_attestation_flow =
        std::make_unique<attestation::SoftBindAttestationFlowImpl>();

    attestation_certificate_generator =
        std::make_unique<AttestationCertificateGeneratorImpl>(
            profile, std::move(soft_bind_attestation_flow));
  }

  SyncedSessionClientAsh* synced_session_client = nullptr;
  if (BrowserTabsModelProviderImpl::IsLacrosSessionSyncFeatureEnabled()) {
    SyncMojoServiceAsh* sync_mojo_service =
        SyncMojoServiceFactoryAsh::GetForProfile(profile);
    if (sync_mojo_service) {
      synced_session_client = sync_mojo_service->GetSyncedSessionClientAsh();
    }
  }

  auto phone_hub_manager = std::make_unique<PhoneHubManagerImpl>(
      profile->GetPrefs(),
      device_sync::DeviceSyncClientFactory::GetForProfile(profile),
      multidevice_setup::MultiDeviceSetupClientFactory::GetForProfile(profile),
      secure_channel::SecureChannelClientProvider::GetInstance()->GetClient(),
      std::make_unique<BrowserTabsModelProviderImpl>(
          multidevice_setup::MultiDeviceSetupClientFactory::GetForProfile(
              profile),
          synced_session_client,
          SyncServiceFactory::GetInstance()->GetForProfile(profile),
          SessionSyncServiceFactory::GetInstance()->GetForProfile(profile),
          std::make_unique<BrowserTabsMetadataFetcherImpl>(
              HistoryUiFaviconRequestHandlerFactory::GetInstance()
                  ->GetForBrowserContext(context))),
      features::IsPhoneHubCameraRollEnabled()
          ? std::make_unique<CameraRollDownloadManagerImpl>(
                DownloadPrefs::FromDownloadManager(
                    profile->GetDownloadManager())
                    ->DownloadPath(),
                ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(
                    profile))
          : nullptr,
      base::BindRepeating(&multidevice_setup::MultiDeviceSetupDialog::Show),
      std::move(attestation_certificate_generator));

  // Provide |phone_hub_manager| to the system tray so that it can be used by
  // the UI.
  SystemTray::Get()->SetPhoneHubManager(phone_hub_manager.get());

  DCHECK(!g_context_for_service);
  g_context_for_service = context;

  return phone_hub_manager;
}

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

bool PhoneHubManagerFactory::ServiceIsCreatedWithBrowserContext() const {
  // We do want the service to be created with the BrowserContext, but returning
  // true here causes issues when opting into Chrome Sync in OOBE because it
  // causes SyncService to be created before SyncConsentScreen. Instead,
  // we return false here and initialize PhoneHubManager within
  // UserSessionInitializer.
  return false;
}

void PhoneHubManagerFactory::BrowserContextShutdown(
    content::BrowserContext* context) {
  // If the primary Profile is being deleted, notify SystemTray that
  // PhoneHubManager is being deleted. Note that the SystemTray is normally
  // expected to be deleted *before* the Profile, so this check is only expected
  // to be necessary in tests.
  if (g_context_for_service == context) {
    auto* system_tray = SystemTray::Get();
    if (system_tray) {
      system_tray->SetPhoneHubManager(nullptr);
    }

    g_context_for_service = nullptr;
  }

  BrowserContextKeyedServiceFactory::BrowserContextShutdown(context);
}

void PhoneHubManagerFactory::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  MultideviceSetupStateUpdater::RegisterPrefs(registry);
  MultideviceFeatureAccessManagerImpl::RegisterPrefs(registry);
  OnboardingUiTrackerImpl::RegisterPrefs(registry);
  ScreenLockManagerImpl::RegisterPrefs(registry);
  RecentAppsInteractionHandlerImpl::RegisterPrefs(registry);
  PhoneHubStructuredMetricsLogger::RegisterPrefs(registry);
}

}  // namespace ash::phonehub