// 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.

#include "chrome/browser/browser_process_platform_part_ash.h"

#include <memory>
#include <utility>

#include "ash/constants/ash_features.h"
#include "base/check_deref.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "base/time/default_clock.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "chrome/browser/ash/app_list/search/essential_search/essential_search_manager.h"
#include "chrome/browser/ash/boot_times_recorder/boot_times_recorder.h"
#include "chrome/browser/ash/login/saml/in_session_password_change_manager.h"
#include "chrome/browser/ash/login/session/chrome_session_manager.h"
#include "chrome/browser/ash/login/session/session_manager_delegate_impl.h"
#include "chrome/browser/ash/login/users/avatar/user_image_manager_registry.h"
#include "chrome/browser/ash/login/users/policy_user_manager_controller.h"
#include "chrome/browser/ash/login/users/profile_user_manager_controller.h"
#include "chrome/browser/ash/login/users/user_manager_delegate_impl.h"
#include "chrome/browser/ash/net/ash_proxy_monitor.h"
#include "chrome/browser/ash/net/secure_dns_manager.h"
#include "chrome/browser/ash/net/system_proxy_manager.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/device_attributes_impl.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/settings/cros_settings_holder.h"
#include "chrome/browser/ash/system/automatic_reboot_manager.h"
#include "chrome/browser/ash/system/device_disabling_manager.h"
#include "chrome/browser/ash/system/device_disabling_manager_default_delegate.h"
#include "chrome/browser/ash/system/system_clock.h"
#include "chrome/browser/ash/system/timezone_resolver_manager.h"
#include "chrome/browser/ash/system/timezone_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/metadata_table_chromeos.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_service_utils.h"
#include "chrome/browser/sync/device_info_sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/navigator/browser_navigator.h"
#include "chrome/browser/ui/navigator/browser_navigator_params.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chromeos/ash/components/account_manager/account_manager_factory.h"
#include "chromeos/ash/components/auto_sign_out/auto_sign_out_service.h"
#include "chromeos/ash/components/browser_context_helper/annotated_account_id.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_flusher.h"
#include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
#include "chromeos/ash/components/geolocation/system_location_provider.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/policy/restriction_schedule/device_restriction_schedule_controller.h"
#include "chromeos/ash/components/scheduler_config/scheduler_configuration_manager.h"
#include "chromeos/ash/components/settings/cros_settings.h"
#include "chromeos/ash/components/timezone/timezone_resolver.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
#include "components/prefs/pref_service.h"
#include "components/session_manager/core/session.h"
#include "components/session_manager/core/session_manager.h"
#include "components/sync/base/command_line_switches.h"
#include "components/user_manager/multi_user/multi_user_sign_in_policy_controller.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_manager_impl.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/preferences/public/mojom/preferences.mojom.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "services/service_manager/public/cpp/service.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition.h"

namespace {

class PrimaryProfileServicesShutdownNotifierFactory
    : public BrowserContextKeyedServiceShutdownNotifierFactory {
 public:
  static PrimaryProfileServicesShutdownNotifierFactory* GetInstance() {
    static base::NoDestructor<PrimaryProfileServicesShutdownNotifierFactory>
        instance;
    return instance.get();
  }

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

 private:
  friend base::NoDestructor<PrimaryProfileServicesShutdownNotifierFactory>;

  PrimaryProfileServicesShutdownNotifierFactory()
      : BrowserContextKeyedServiceShutdownNotifierFactory(
            "PrimaryProfileServices") {
    DependsOn(DeviceInfoSyncServiceFactory::GetInstance());
  }
  ~PrimaryProfileServicesShutdownNotifierFactory() override = default;
};

}  // namespace

BrowserProcessPlatformPart::BrowserProcessPlatformPart()
    : created_profile_helper_(false),
      browser_context_flusher_(std::make_unique<ash::BrowserContextFlusher>()),
      account_manager_factory_(std::make_unique<ash::AccountManagerFactory>()) {
}

BrowserProcessPlatformPart::~BrowserProcessPlatformPart() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

void BrowserProcessPlatformPart::InitializeAutomaticRebootManager() {
  DCHECK(!automatic_reboot_manager_);

  automatic_reboot_manager_ =
      std::make_unique<ash::system::AutomaticRebootManager>(
          g_browser_process->local_state(), base::DefaultClock::GetInstance(),
          base::DefaultTickClock::GetInstance());
}

void BrowserProcessPlatformPart::ShutdownAutomaticRebootManager() {
  automatic_reboot_manager_.reset();
}

void BrowserProcessPlatformPart::InitializeUserManager() {
  DCHECK(!user_manager_);
  CHECK(session_manager_);
  auto* local_state = g_browser_process->local_state();
  user_manager_ = std::make_unique<user_manager::UserManagerImpl>(
      std::make_unique<ash::UserManagerDelegateImpl>(), local_state);
  profile_user_manager_controller_ =
      std::make_unique<ash::ProfileUserManagerController>(
          g_browser_process->profile_manager(), user_manager_.get());
  policy_user_manager_controller_ =
      std::make_unique<ash::PolicyUserManagerController>(
          user_manager_.get(), ash::CrosSettings::Get(),
          ash::DeviceSettingsService::Get(),
          browser_policy_connector_ash()->GetMinimumVersionPolicyHandler(),
          browser_policy_connector_ash()->GetDeviceLocalAccountPolicyService());
  user_image_manager_registry_ =
      std::make_unique<ash::UserImageManagerRegistry>(
          g_browser_process->local_state(),
          g_browser_process->shared_url_loader_factory(), user_manager_.get());
  multi_user_sign_in_policy_controller_ =
      std::make_unique<user_manager::MultiUserSignInPolicyController>(
          local_state, user_manager_.get());
  session_manager_->OnUserManagerCreated(user_manager_.get());
  chrome_session_manager_->OnUserManagerCreated(user_manager_.get());
  // LoginState and DeviceCloudPolicyManager outlives UserManager, so on
  // their initialization, there's no way to start observing UserManager.
  // This is the earliest timing to do so.
  // TODO(b/332481586): Consider move the initialization to the constructor
  // of each class.
  if (auto* login_state = ash::LoginState::Get()) {
    login_state->OnUserManagerCreated(user_manager_.get());
  }
  browser_policy_connector_ash()->OnUserManagerCreated(user_manager_.get());
  user_manager_->Initialize();
}

void BrowserProcessPlatformPart::ShutdownUserManager() {
  if (!user_manager_) {
    return;
  }
  user_image_manager_registry_->Shutdown();
  browser_policy_connector_ash()->OnUserManagerShutdown();
  policy_user_manager_controller_.reset();
  user_manager_->Shutdown();
}

void BrowserProcessPlatformPart::DestroyUserManager() {
  user_manager_->Destroy();
  browser_policy_connector_ash()->OnUserManagerWillBeDestroyed();
  if (auto* login_state = ash::LoginState::Get()) {
    login_state->OnUserManagerWillBeDestroyed(user_manager_.get());
  }

  multi_user_sign_in_policy_controller_.reset();
  user_image_manager_registry_.reset();
  profile_user_manager_controller_.reset();
  user_manager_.reset();
}

void BrowserProcessPlatformPart::
    InitializeDeviceRestrictionScheduleController() {
  device_restriction_schedule_controller_ =
      policy::DeviceRestrictionScheduleController::Create(
          CHECK_DEREF(g_browser_process->local_state()));
}

void BrowserProcessPlatformPart::ShutdownDeviceRestrictionScheduleController() {
  device_restriction_schedule_controller_.reset();
}

void BrowserProcessPlatformPart::InitializeDeviceDisablingManager() {
  DCHECK(!device_disabling_manager_);

  device_disabling_manager_delegate_ =
      std::make_unique<ash::system::DeviceDisablingManagerDefaultDelegate>();
  device_disabling_manager_ =
      std::make_unique<ash::system::DeviceDisablingManager>(
          g_browser_process->local_state(), browser_policy_connector_ash(),
          device_restriction_schedule_controller_.get(),
          device_disabling_manager_delegate_.get(), ash::CrosSettings::Get(),
          user_manager::UserManager::Get());
  device_disabling_manager_->Init();
}

void BrowserProcessPlatformPart::ShutdownDeviceDisablingManager() {
  device_disabling_manager_.reset();
  device_disabling_manager_delegate_.reset();
}

void BrowserProcessPlatformPart::InitializeSessionManager() {
  CHECK(ash::BootTimesRecorder::GetIfCreated());
  CHECK(!session_manager_);
  CHECK(!chrome_session_manager_);
  session_manager_ = std::make_unique<session_manager::SessionManager>(
      std::make_unique<ash::SessionManagerDelegateImpl>());
  chrome_session_manager_ = std::make_unique<ash::ChromeSessionManager>(
      g_browser_process->local_state(), browser_policy_connector_ash(),
      session_manager_.get());
  // Registers BootTimesRecorder as an observer *after* ChromeSessionManager
  // creation to include ChromeSessionManager operations in UserLoggedIn
  // metrics. ChromeSessionManager registers itself as an observer, too,
  // in its constructor. The order of observers guarantees the measurement.
  session_manager_->AddObserver(ash::BootTimesRecorder::Get());
}

void BrowserProcessPlatformPart::ShutdownSessionManager() {
  session_manager_->RemoveObserver(ash::BootTimesRecorder::Get());
  chrome_session_manager_.reset();
  session_manager_.reset();
}

void BrowserProcessPlatformPart::InitializeCrosSettings() {
  CHECK(!cros_settings_holder_);
  cros_settings_holder_ = std::make_unique<ash::CrosSettingsHolder>(
      ash::DeviceSettingsService::Get(), g_browser_process->local_state());
}

void BrowserProcessPlatformPart::ShutdownCrosSettings() {
  cros_settings_holder_.reset();
}

void BrowserProcessPlatformPart::InitializeComponentManager() {
  if (using_testing_component_manager_ash_) {
    return;
  }

  DCHECK(!component_manager_ash_);
  component_manager_ash_ =
      base::MakeRefCounted<component_updater::CrOSComponentInstaller>(
          std::make_unique<component_updater::MetadataTable>(
              g_browser_process->local_state()),
          g_browser_process->component_updater());

  // Register all installed components for regular update.
  component_manager_ash_->RegisterInstalled();
}

void BrowserProcessPlatformPart::ShutdownComponentManager() {
  if (using_testing_component_manager_ash_) {
    return;
  }

  component_manager_ash_.reset();
}

void BrowserProcessPlatformPart::InitializeSchedulerConfigurationManager() {
  DCHECK(!scheduler_configuration_manager_);
  scheduler_configuration_manager_ =
      std::make_unique<ash::SchedulerConfigurationManager>(
          ash::DebugDaemonClient::Get(), g_browser_process->local_state());
}

void BrowserProcessPlatformPart::ShutdownSchedulerConfigurationManager() {
  scheduler_configuration_manager_.reset();
}

void BrowserProcessPlatformPart::InitializeAshProxyMonitor() {
  DCHECK(!ash_proxy_monitor_);
  ash_proxy_monitor_ = std::make_unique<ash::AshProxyMonitor>(
      g_browser_process->local_state(), g_browser_process->profile_manager());
}

void BrowserProcessPlatformPart::ShutdownAshProxyMonitor() {
  ash_proxy_monitor_.reset();
}

void BrowserProcessPlatformPart::InitializePrimaryProfileServices(
    Profile* primary_profile) {
  DCHECK(primary_profile);

  DCHECK(!essential_search_manager_);
  essential_search_manager_ =
      app_list::EssentialSearchManager::Create(primary_profile);

  DCHECK(!in_session_password_change_manager_);
  in_session_password_change_manager_ =
      ash::InSessionPasswordChangeManager::CreateIfEnabled(
          g_browser_process->local_state(), primary_profile);

  primary_profile_shutdown_subscription_ =
      PrimaryProfileServicesShutdownNotifierFactory::GetInstance()
          ->Get(primary_profile)
          ->Subscribe(base::BindRepeating(
              &BrowserProcessPlatformPart::ShutdownPrimaryProfileServices,
              base::Unretained(this)));

  if (ash::SystemProxyManager::Get()) {
    ash::SystemProxyManager::Get()->StartObservingPrimaryProfilePrefs(
        primary_profile);
  }

  // The current sesison may be guest session, where the Profile is
  // an OTR one. Take the original profile for the case.
  auto* user = user_manager::UserManager::Get()->FindUserAndModify(CHECK_DEREF(
      ash::AnnotatedAccountId::Get(primary_profile->GetOriginalProfile())));
  secure_dns_manager_ = std::make_unique<ash::SecureDnsManager>(
      g_browser_process->local_state(),
      std::make_unique<policy::DeviceAttributesImpl>(
          browser_policy_connector_ash()),
      CHECK_DEREF(user),
      primary_profile->GetProfilePolicyConnector()->IsManaged());

  if (ash::features::IsAutoSignOutEnabled() &&
      primary_profile->IsRegularProfile() && syncer::IsSyncAllowedByFlag()) {
    PrefService* prefs = primary_profile->GetPrefs();
    // AutoSignOutService is tied to the primary user profile and it's
    // destroyed via the `primary_profile_shutdown_subscription_`. To ensure
    // a safe shutdown, the `PrimaryProfileServicesShutdownNotifierFactory`
    // uses a `DependsOn()` to guarantee that DeviceInfoSyncService outlives
    // AutoSignOutService.
    auto_sign_out_service_ = std::make_unique<ash::AutoSignOutService>(
        DeviceInfoSyncServiceFactory::GetForProfile(primary_profile),
        session_manager_.get(), prefs);
  }
}

void BrowserProcessPlatformPart::ShutdownPrimaryProfileServices() {
  auto_sign_out_service_.reset();
  secure_dns_manager_.reset();
  if (ash::SystemProxyManager::Get())
    ash::SystemProxyManager::Get()->StopObservingPrimaryProfilePrefs();
  essential_search_manager_.reset();
  in_session_password_change_manager_.reset();
}

void BrowserProcessPlatformPart::RegisterKeepAlive() {
  DCHECK(!keep_alive_);
  keep_alive_ = std::make_unique<ScopedKeepAlive>(
      KeepAliveOrigin::BROWSER_PROCESS_CHROMEOS,
      KeepAliveRestartOption::DISABLED);
}

void BrowserProcessPlatformPart::UnregisterKeepAlive() {
  keep_alive_.reset();
}

ash::ProfileHelper* BrowserProcessPlatformPart::profile_helper() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!created_profile_helper_)
    CreateProfileHelper();
  return profile_helper_.get();
}

policy::BrowserPolicyConnectorAsh*
BrowserProcessPlatformPart::browser_policy_connector_ash() {
  return static_cast<policy::BrowserPolicyConnectorAsh*>(
      g_browser_process->browser_policy_connector());
}

void BrowserProcessPlatformPart::InitializeTimezoneResolverManager() {
  CHECK(!timezone_resolver_manager_);
  timezone_resolver_manager_ =
      std::make_unique<ash::system::TimeZoneResolverManager>(
          g_browser_process->local_state(),
          g_browser_process->shared_url_loader_factory(),
          ash::SystemLocationProvider::GetInstance(),
          session_manager::SessionManager::Get());
}

ash::system::TimeZoneResolverManager*
BrowserProcessPlatformPart::GetTimezoneResolverManager() {
  CHECK(timezone_resolver_manager_);
  return timezone_resolver_manager_.get();
}

void BrowserProcessPlatformPart::StartTearDown() {
  // Some tests check for memory leaks before this object is
  // destroyed.  So we need to destroy |timezone_resolver_manager_| here.
  timezone_resolver_manager_.reset();
  profile_helper_.reset();
  browser_context_flusher_.reset();
}

ash::system::SystemClock* BrowserProcessPlatformPart::GetSystemClock() {
  if (!system_clock_.get())
    system_clock_ = std::make_unique<ash::system::SystemClock>();
  return system_clock_.get();
}

void BrowserProcessPlatformPart::DestroySystemClock() {
  system_clock_.reset();
}

void BrowserProcessPlatformPart::CreateProfileHelper() {
  DCHECK(!created_profile_helper_ && !profile_helper_);
  created_profile_helper_ = true;
  profile_helper_ = ash::ProfileHelper::CreateInstance();
}

// static
void BrowserProcessPlatformPart::EnsureFactoryBuilt() {
  PrimaryProfileServicesShutdownNotifierFactory::GetInstance();
}
