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

#import "ios/chrome/browser/application_context/model/application_context_impl.h"

#import <algorithm>
#import <vector>

#import "base/base_paths.h"
#import "base/check_op.h"
#import "base/command_line.h"
#import "base/feature_list.h"
#import "base/files/file_path.h"
#import "base/functional/bind.h"
#import "base/functional/callback_helpers.h"
#import "base/memory/ptr_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/path_service.h"
#import "base/strings/sys_string_conversions.h"
#import "base/task/bind_post_task.h"
#import "base/task/sequenced_task_runner.h"
#import "base/task/thread_pool.h"
#import "base/time/default_clock.h"
#import "base/time/default_tick_clock.h"
#import "build/blink_buildflags.h"
#import "components/activity_reporter/activity_reporter.h"
#import "components/application_locale_storage/application_locale_storage.h"
#import "components/breadcrumbs/core/breadcrumbs_status.h"
#import "components/breadcrumbs/core/crash_reporter_breadcrumb_observer.h"
#import "components/component_updater/component_updater_service.h"
#import "components/component_updater/timer_update_scheduler.h"
#import "components/gcm_driver/gcm_client_factory.h"
#import "components/gcm_driver/gcm_desktop_utils.h"
#import "components/gcm_driver/gcm_driver.h"
#import "components/history/core/browser/history_service.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/metrics/metrics_features.h"
#import "components/metrics/metrics_service.h"
#import "components/metrics_services_manager/metrics_services_manager.h"
#import "components/net_log/net_export_file_writer.h"
#import "components/network_time/network_time_tracker.h"
#import "components/optimization_guide/optimization_guide_buildflags.h"
#import "components/os_crypt/async/browser/keychain_key_provider.h"
#import "components/os_crypt/async/browser/os_crypt_async.h"
#import "components/prefs/pref_registry_simple.h"
#import "components/prefs/pref_service.h"
#import "components/sessions/core/session_id_generator.h"
#import "components/signin/core/browser/active_primary_accounts_metrics_recorder.h"
#import "components/supervised_user/core/browser/device_parental_controls.h"
#import "components/supervised_user/core/browser/device_parental_controls_noop_impl.h"
#import "components/translate/core/browser/translate_download_manager.h"
#import "components/ukm/ukm_service.h"
#import "components/update_client/configurator.h"
#import "components/update_client/net/network_chromium.h"
#import "components/update_client/update_query_params.h"
#import "components/variations/service/variations_service.h"
#import "components/version_info/channel.h"
#import "ios/chrome/app/tests_hook.h"
#import "ios/chrome/browser/component_updater/model/ios_component_updater_configurator.h"
#import "ios/chrome/browser/crash_report/model/breadcrumbs/application_breadcrumbs_logger.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/download/model/auto_deletion/auto_deletion_service.h"
#import "ios/chrome/browser/gcm/model/ios_chrome_gcm_profile_service_factory.h"
#import "ios/chrome/browser/history/model/history_service_factory.h"
#import "ios/chrome/browser/metrics/model/ios_chrome_metrics_services_manager_client.h"
#import "ios/chrome/browser/optimization_guide/model/optimization_guide_global_state.h"
#import "ios/chrome/browser/policy/model/browser_policy_connector_ios.h"
#import "ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.h"
#import "ios/chrome/browser/prefs/model/ios_chrome_pref_service_factory.h"
#import "ios/chrome/browser/profile/model/ios_chrome_io_thread.h"
#import "ios/chrome/browser/profile/model/profile_manager_ios_impl.h"
#import "ios/chrome/browser/push_notification/model/push_notification_service.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/paths/paths.h"
#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/incognito_session_tracker.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/signin/model/account_profile_mapper.h"
#import "ios/chrome/browser/signin/model/avatar/avatar_provider.h"
#import "ios/chrome/browser/update_client/model/ios_chrome_update_query_params_delegate.h"
#import "ios/chrome/common/channel_info.h"
#import "ios/components/security_interstitials/safe_browsing/safe_browsing_service_impl.h"
#import "ios/public/provider/chrome/browser/additional_features/additional_features_api.h"
#import "ios/public/provider/chrome/browser/additional_features/additional_features_controller.h"
#import "ios/public/provider/chrome/browser/app_distribution/app_distribution_api.h"
#import "ios/public/provider/chrome/browser/push_notification/push_notification_api.h"
#import "ios/public/provider/chrome/browser/signin/signin_identity_api.h"
#import "ios/public/provider/chrome/browser/signin/signin_sso_api.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
#import "mojo/public/cpp/bindings/pending_receiver.h"
#import "net/log/net_log.h"
#import "net/log/net_log_capture_mode.h"
#import "net/url_request/url_request_context_getter.h"
#import "services/metrics/public/cpp/ukm_recorder.h"
#import "services/network/network_change_manager.h"
#import "services/network/public/cpp/network_connection_tracker.h"
#import "services/network/public/mojom/network_service.mojom.h"
#import "ui/base/resource/resource_bundle.h"

#if !BUILDFLAG(USE_BLINK)
#import "base/memory/memory_pressure_listener_registry.h"
#endif

ApplicationContextImpl::ApplicationContextImpl(
    base::SequencedTaskRunner* local_state_task_runner,
    const base::CommandLine& command_line,
    const std::string& locale,
    const std::string& country)
    : application_locale_storage_(std::make_unique<ApplicationLocaleStorage>()),
      local_state_task_runner_(local_state_task_runner)
#if !BUILDFLAG(USE_BLINK)
      ,
      memory_pressure_listener_registry_(
          std::make_unique<base::MemoryPressureListenerRegistry>())
#endif
{
  DCHECK(!GetApplicationContext());
  SetApplicationContext(this);

  application_locale_storage_->Set(locale);
  application_country_ = country;

  update_client::UpdateQueryParams::SetDelegate(
      IOSChromeUpdateQueryParamsDelegate::GetInstance());

  translate::TranslateDownloadManager::GetInstance()->set_application_locale(
      locale);
}

ApplicationContextImpl::~ApplicationContextImpl() {
  DCHECK_EQ(this, GetApplicationContext());
  SetApplicationContext(nullptr);
}

void ApplicationContextImpl::PreCreateThreads() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  ios_chrome_io_thread_.reset(
      new IOSChromeIOThread(GetLocalState(), GetNetLog()));
}

void ApplicationContextImpl::PostCreateThreads() {
  // Initialize OSCryptAsync with a KeychainKeyProvider.
  auto key_provider = std::make_unique<os_crypt_async::KeychainKeyProvider>();
  std::vector<std::pair<size_t, std::unique_ptr<os_crypt_async::KeyProvider>>>
      key_providers;
  key_providers.emplace_back(/*precedence=*/10u, std::move(key_provider));
  os_crypt_async_ =
      std::make_unique<os_crypt_async::OSCryptAsync>(std::move(key_providers));

  // Trigger an instance grab on a background thread if necessary.
  os_crypt_async_->GetInstance(base::DoNothing());

  web::GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(&IOSChromeIOThread::InitOnIO,
                                base::Unretained(ios_chrome_io_thread_.get())));
}

void ApplicationContextImpl::PreMainMessageLoopRun() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // BrowserPolicyConnectorIOS is created very early because local_state()
  // needs policy to be initialized with the managed preference values.
  // However, policy fetches from the network and loading of disk caches
  // requires that threads are running; this Init() call lets the connector
  // resume its initialization now that the loops are spinning and the
  // system request context is available for the fetchers.
  BrowserPolicyConnectorIOS* browser_policy_connector =
      GetBrowserPolicyConnector();
  if (browser_policy_connector) {
    browser_policy_connector->Init(GetLocalState(),
                                   GetSharedURLLoaderFactory());
  }

  if (breadcrumbs::MaybeEnableBasedOnChannel(GetLocalState(), ::GetChannel())) {
    // Start crash reporter listening for breadcrumb events. Collected
    // breadcrumbs will be attached to crash reports.
    breadcrumbs::CrashReporterBreadcrumbObserver::GetInstance();

    base::FilePath storage_dir;
    bool result = base::PathService::Get(ios::DIR_USER_DATA, &storage_dir);
    DCHECK(result);
    application_breadcrumbs_logger_ =
        std::make_unique<ApplicationBreadcrumbsLogger>(storage_dir);
  }
}

void ApplicationContextImpl::StartTearDown() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  tearing_down_ = true;

  // We need to destroy the MetricsServicesManager and NetworkTimeTracker before
  // the IO thread gets destroyed, since the destructor can call the URLFetcher
  // destructor, which does a PostDelayedTask operation on the IO thread. (The
  // IO thread will handle that URLFetcher operation before going away.)
  metrics::MetricsService* metrics_service = GetMetricsService();
  if (metrics_service) {
    metrics_service->LogCleanShutdown();
  }
  metrics_services_manager_.reset();
  network_time_tracker_.reset();

  net_export_file_writer_.reset();

  if (safe_browsing_service_) {
    safe_browsing_service_->ShutDown();
  }

  // Ensure that the profiles have all be unloaded. This is required because
  // the profiles' KeyedService may use the AccountProfileMapper, and thus
  // they have to be destroyed before the ProfileManagerIOS. However since
  // the AccountProfileMapper depends on the ProfileManagerIOS, it must be
  // destroyed before.
  profile_manager_->PrepareForDestruction();
  CHECK_EQ(profile_manager_->GetLoadedProfiles().size(), 0u);

  account_profile_mapper_.reset();
  profile_manager_.reset();

  // The policy providers managed by `browser_policy_connector_` need to shut
  // down while the IO threads is still alive. The monitoring framework owned by
  // `browser_policy_connector_` relies on `gcm_driver_`, so this must be
  // shutdown before `gcm_driver_` below.
  if (browser_policy_connector_) {
    browser_policy_connector_->Shutdown();
  }

  // The GCMDriver must shut down while the IO thread is still alive.
  if (gcm_driver_) {
    gcm_driver_->Shutdown();
  }

  if (local_state_) {
    local_state_->CommitPendingWrite();
    sessions::SessionIdGenerator::GetInstance()->Shutdown();
  }

  // The ApplicationBreadcrumbsLogger tries to log event via a task when it
  // is destroyed, so it needs to be notified of the app tear down now when
  // the task tracker is still valid (will be destroyed after StartTearDown
  // returns).
  application_breadcrumbs_logger_.reset();

  ios_chrome_io_thread_->NetworkTearDown();

  additional_features_controller_->ShutDown();
}

void ApplicationContextImpl::PostDestroyThreads() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // Resets associated state right after actual thread is stopped as
  // IOSChromeIOThread::Globals cleanup happens in CleanUp on the IO
  // thread, i.e. as the thread exits its message loop.
  //
  // This is important because in various places, the IOSChromeIOThread
  // object being NULL is considered synonymous with the IO thread
  // having stopped.
  ios_chrome_io_thread_.reset();
}

void ApplicationContextImpl::OnAppEnterForeground() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!tearing_down_);

  OnAppEnterState(AppState::kForeground);
}

void ApplicationContextImpl::OnAppEnterBackground() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!tearing_down_);

  OnAppEnterState(AppState::kBackgroundFromActive);
}

void ApplicationContextImpl::OnAppStartedBackgroundProcessing() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!tearing_down_);

  OnAppEnterState(AppState::kBackgroundProcessing);
}

void ApplicationContextImpl::OnAppFinishedBackgroundProcessing() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!tearing_down_);

  OnAppEnterState(AppState::kBackgroundIdle);
}

bool ApplicationContextImpl::WasLastShutdownClean() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  metrics::MetricsService* metrics_service = GetMetricsService();
  if (metrics_service) {
    return metrics_service->WasLastShutdownClean();
  }
  return true;
}

PrefService* ApplicationContextImpl::GetLocalState() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!local_state_) {
    CreateLocalState();
  }
  return local_state_.get();
}

net::URLRequestContextGetter*
ApplicationContextImpl::GetSystemURLRequestContext() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return ios_chrome_io_thread_->system_url_request_context_getter();
}

scoped_refptr<network::SharedURLLoaderFactory>
ApplicationContextImpl::GetSharedURLLoaderFactory() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return ios_chrome_io_thread_->GetSharedURLLoaderFactory();
}

network::mojom::NetworkContext*
ApplicationContextImpl::GetSystemNetworkContext() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return ios_chrome_io_thread_->GetSystemNetworkContext();
}

ApplicationLocaleStorage*
ApplicationContextImpl::GetApplicationLocaleStorage() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  CHECK(application_locale_storage_);
  return application_locale_storage_.get();
}

const std::string& ApplicationContextImpl::GetApplicationCountry() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!application_country_.empty());
  return application_country_;
}

ProfileManagerIOS* ApplicationContextImpl::GetProfileManager() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!profile_manager_) {
    profile_manager_ = std::make_unique<ProfileManagerIOSImpl>(
        GetLocalState(), base::PathService::CheckedGet(ios::DIR_USER_DATA));
  }
  return profile_manager_.get();
}

metrics_services_manager::MetricsServicesManager*
ApplicationContextImpl::GetMetricsServicesManager() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // Only create the objects if teardown hasn't started yet, as otherwise these
  // may have already been destroyed.
  if (!metrics_services_manager_ && !tearing_down_) {
    metrics_services_manager_.reset(
        new metrics_services_manager::MetricsServicesManager(
            std::make_unique<IOSChromeMetricsServicesManagerClient>(
                GetLocalState())));
  }
  return metrics_services_manager_.get();
}

metrics::MetricsService* ApplicationContextImpl::GetMetricsService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto* metrics_services_manager = GetMetricsServicesManager();
  if (metrics_services_manager_) {
    return metrics_services_manager->GetMetricsService();
  }
  return nullptr;
}

signin::ActivePrimaryAccountsMetricsRecorder*
ApplicationContextImpl::GetActivePrimaryAccountsMetricsRecorder() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!active_primary_accounts_metrics_recorder_) {
    active_primary_accounts_metrics_recorder_ =
        std::make_unique<signin::ActivePrimaryAccountsMetricsRecorder>(
            *GetLocalState());
  }
  return active_primary_accounts_metrics_recorder_.get();
}

ukm::UkmRecorder* ApplicationContextImpl::GetUkmRecorder() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto* metrics_services_manager = GetMetricsServicesManager();
  if (metrics_services_manager_) {
    return metrics_services_manager->GetUkmService();
  }
  return nullptr;
}

variations::VariationsService* ApplicationContextImpl::GetVariationsService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto* metrics_services_manager = GetMetricsServicesManager();
  if (metrics_services_manager_) {
    return metrics_services_manager->GetVariationsService();
  }
  return nullptr;
}

net::NetLog* ApplicationContextImpl::GetNetLog() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return net::NetLog::Get();
}

net_log::NetExportFileWriter* ApplicationContextImpl::GetNetExportFileWriter() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!net_export_file_writer_) {
    net_export_file_writer_ = std::make_unique<net_log::NetExportFileWriter>();
  }
  return net_export_file_writer_.get();
}

network_time::NetworkTimeTracker*
ApplicationContextImpl::GetNetworkTimeTrackerMaybeUninitialized() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!network_time_tracker_) {
    network_time_tracker_ = std::make_unique<network_time::NetworkTimeTracker>(
        std::make_unique<base::DefaultClock>(),
        std::make_unique<base::DefaultTickClock>(),
        /*pref_service=*/nullptr,
        /*url_loader_factory=*/nullptr,
        /*fetch_behavior=*/std::nullopt);
    CHECK(!network_time_tracker_->is_initialized());
  }
  return network_time_tracker_.get();
}

network_time::NetworkTimeTracker*
ApplicationContextImpl::GetNetworkTimeTracker() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto* network_time_tracker = GetNetworkTimeTrackerMaybeUninitialized();
  if (!network_time_tracker->is_initialized()) {
    network_time_tracker->Initialize(
        /*pref_service=*/GetLocalState(),
        /*url_loader_factory=*/GetSharedURLLoaderFactory());
    CHECK(network_time_tracker->is_initialized());
  }
  return network_time_tracker;
}

IOSChromeIOThread* ApplicationContextImpl::GetIOSChromeIOThread() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(ios_chrome_io_thread_.get());
  return ios_chrome_io_thread_.get();
}

gcm::GCMDriver* ApplicationContextImpl::GetGCMDriver() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!gcm_driver_) {
    CreateGCMDriver();
  }
  DCHECK(gcm_driver_);
  return gcm_driver_.get();
}

activity_reporter::ActivityReporter*
ApplicationContextImpl::GetActivityReporter() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!activity_reporter_) {
    activity_reporter_ = activity_reporter::CreateActivityReporter(
        base::BindRepeating(
            [](PrefService* pref_service) { return pref_service; },
            GetLocalState()),
        base::MakeRefCounted<update_client::NetworkFetcherChromiumFactory>(
            GetSharedURLLoaderFactory(),
            // Never send cookies for activity reports.
            base::BindRepeating([](const GURL& url) { return false; })),
        base::BindRepeating(&::GetChannel),
        base::BindRepeating(&ios::provider::GetBrandCode), base::DoNothing(),
        true);
  }
  return activity_reporter_.get();
}

component_updater::ComponentUpdateService*
ApplicationContextImpl::GetComponentUpdateService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!component_updater_) {
    // Creating the component updater does not do anything, components need to
    // be registered and Start() needs to be called.
    component_updater_ = component_updater::ComponentUpdateServiceFactory(
        component_updater::MakeIOSComponentUpdaterConfigurator(
            base::CommandLine::ForCurrentProcess()),
        std::make_unique<component_updater::TimerUpdateScheduler>(),
        ios::provider::GetBrandCode());
  }
  return component_updater_.get();
}

SafeBrowsingService* ApplicationContextImpl::GetSafeBrowsingService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!safe_browsing_service_) {
    safe_browsing_service_ = base::MakeRefCounted<SafeBrowsingServiceImpl>();
    safe_browsing_service_->Initialize(
        base::PathService::CheckedGet(ios::DIR_USER_DATA));
  }
  return safe_browsing_service_.get();
}

network::NetworkConnectionTracker*
ApplicationContextImpl::GetNetworkConnectionTracker() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!network_connection_tracker_) {
    DCHECK(!network_change_manager_);
    network_change_manager_ =
        std::make_unique<network::NetworkChangeManager>(nullptr);
    network_connection_tracker_ =
        std::make_unique<network::NetworkConnectionTracker>(base::BindRepeating(
            &network::NetworkChangeManager::AddReceiver,
            base::Unretained(network_change_manager_.get())));
  }
  return network_connection_tracker_.get();
}

BrowserPolicyConnectorIOS* ApplicationContextImpl::GetBrowserPolicyConnector() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!browser_policy_connector_.get()) {
#if !BUILDFLAG(USE_BLINK)
    // Ensure that the ResourceBundle has already been initialized. If this
    // DCHECK ever fails, a call to
    // BrowserPolicyConnector::OnResourceBundleCreated() will need to be added
    // later in the startup sequence, after the ResourceBundle is initialized.
    // Blink based startup will call OnResourceBundleCreated in
    // IOSChromeMainParts::PreCreateThreads.
    DCHECK(ui::ResourceBundle::HasSharedInstance());
#endif
    version_info::Channel channel = ::GetChannel();
    policy::ConfigurationPolicyProvider* test_policy_provider =
        tests_hook::GetOverriddenPlatformPolicyProvider();

    // If running under test (for example, if a mock platform policy provider
    // was provided), enable future policies without requiring them to be on
    // an allowlist. Otherwise, disable future policies on
    // externally-published channels, unless a domain administrator explicitly
    // adds them to the allowlist.
    bool enable_future_policies_without_allowlist =
        test_policy_provider != nullptr ||
        (channel != version_info::Channel::STABLE &&
         channel != version_info::Channel::BETA);
    browser_policy_connector_ =
        std::make_unique<BrowserPolicyConnectorIOS>(base::BindRepeating(
            &BuildPolicyHandlerList, enable_future_policies_without_allowlist));

    // Install a mock platform policy provider, if running under EG2 and one
    // is supplied.
    if (test_policy_provider) {
      browser_policy_connector_->SetPolicyProviderForTesting(  // IN-TEST
          test_policy_provider);
    }
  }
  return browser_policy_connector_.get();
}

id<SingleSignOnService> ApplicationContextImpl::GetSingleSignOnService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!single_sign_on_service_) {
    single_sign_on_service_ = ios::provider::CreateSSOService();
    DCHECK(single_sign_on_service_);
  }
  return single_sign_on_service_;
}

signin::AvatarProvider* ApplicationContextImpl::GetIdentityAvatarProvider() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!resized_avatar_caches_) {
    resized_avatar_caches_ = std::make_unique<signin::AvatarProvider>();
  }
  return resized_avatar_caches_.get();
}

SystemIdentityManager* ApplicationContextImpl::GetSystemIdentityManager() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!system_identity_manager_) {
    // Give the opportunity for the test hook to override the factory from
    // the provider (allowing EG tests to use a fake SystemIdentityManager).
    system_identity_manager_ = tests_hook::CreateSystemIdentityManager();
    if (!system_identity_manager_) {
      system_identity_manager_ =
          ios::provider::CreateSystemIdentityManager(GetSingleSignOnService());
    }
    DCHECK(system_identity_manager_);
  }
  return system_identity_manager_.get();
}

AccountProfileMapper* ApplicationContextImpl::GetAccountProfileMapper() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!account_profile_mapper_) {
    account_profile_mapper_ = std::make_unique<AccountProfileMapper>(
        GetSystemIdentityManager(), GetProfileManager(), GetLocalState());
  }
  return account_profile_mapper_.get();
}

IncognitoSessionTracker* ApplicationContextImpl::GetIncognitoSessionTracker() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!incognito_session_tracker_) {
    incognito_session_tracker_ =
        std::make_unique<IncognitoSessionTracker>(GetProfileManager());
  }
  return incognito_session_tracker_.get();
}

PushNotificationService* ApplicationContextImpl::GetPushNotificationService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!push_notification_service_) {
    push_notification_service_ = ios::provider::CreatePushNotificationService();
    DCHECK(push_notification_service_);
  }

  return push_notification_service_.get();
}

AdditionalFeaturesController*
ApplicationContextImpl::GetAdditionalFeaturesController() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!additional_features_controller_) {
    additional_features_controller_ =
        ios::provider::CreateAdditionalFeaturesController();
  }
  return additional_features_controller_.get();
}

auto_deletion::AutoDeletionService*
ApplicationContextImpl::GetAutoDeletionService() {
  if (!auto_deletion_service_) {
    auto_deletion_service_ =
        std::make_unique<auto_deletion::AutoDeletionService>(GetLocalState());
  }
  return auto_deletion_service_.get();
}

supervised_user::DeviceParentalControls&
ApplicationContextImpl::GetDeviceParentalControls() {
  if (!device_parental_controls_) {
    device_parental_controls_ =
        std::make_unique<supervised_user::DeviceParentalControlsNoOpImpl>();
  }
  return *device_parental_controls_;
}

optimization_guide::OptimizationGuideGlobalState*
ApplicationContextImpl::GetOptimizationGuideGlobalState() {
  if (!optimization_guide_global_state_) {
    optimization_guide_global_state_ =
        std::make_unique<optimization_guide::OptimizationGuideGlobalState>(
            GetLocalState(), GetProfileManager(), GetApplicationLocaleStorage(),
            GetSharedURLLoaderFactory());
  }
  return optimization_guide_global_state_.get();
}

os_crypt_async::OSCryptAsync* ApplicationContextImpl::GetOSCryptAsync() {
  return os_crypt_async_.get();
}

void ApplicationContextImpl::OnAppEnterState(AppState app_state) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!tearing_down_);

  // Tell the metrics services that the application state changes (taking
  // care not to create the services if they have not been created yet).
  if (metrics_services_manager_) {
    if (metrics::MetricsService* metrics_service =
            metrics_services_manager_->GetMetricsService()) {
      bool enable_background_metrics_work = base::FeatureList::IsEnabled(
          metrics::features::kIOSBackgroundMetrics);
      switch (app_state) {
        case AppState::kForeground:
          metrics_service->OnAppEnterForeground();
          break;

        case AppState::kBackgroundFromActive:
          metrics_service->OnAppEnterBackground(enable_background_metrics_work);
          break;
        case AppState::kBackgroundProcessing:
          // Background processing should be tracked in metrcis, including
          // specifically the clean exit beacon, as if it were foreground.
          metrics_service->OnAppEnterForeground();
          break;
        case AppState::kBackgroundIdle:
          // When background processing is complete, this state should be
          // treated like normal backgrounding, including specifically the
          // clean exit beacon.
          metrics_service->OnAppEnterBackground(enable_background_metrics_work);
          break;
      }
    }

    if (variations::VariationsService* variations_service =
            metrics_services_manager_->GetVariationsService()) {
      switch (app_state) {
        case AppState::kForeground:
          variations_service->OnAppEnterForeground();
          break;

        case AppState::kBackgroundFromActive:
        case AppState::kBackgroundProcessing:
        case AppState::kBackgroundIdle:
          // Nothing to do for VariationsService when entering background.
          break;
      }
    }

    if (ukm::UkmService* ukm_service =
            metrics_services_manager_->GetUkmService()) {
      switch (app_state) {
        case AppState::kForeground:
          ukm_service->OnAppEnterForeground();
          break;

        case AppState::kBackgroundFromActive:
          ukm_service->OnAppEnterBackground();
          break;
        case AppState::kBackgroundProcessing:
        case AppState::kBackgroundIdle:
          break;
      }
    }
  }

  // Request saving the local state prefs and all loaded ProfileIOS'
  // prefs (taking care not to create the objects if they have not been created
  // yet).
  if (profile_manager_) {
    for (ProfileIOS* profile : profile_manager_->GetLoadedProfiles()) {
      switch (app_state) {
        case AppState::kForeground:
          // Nothing extra to do when entering foreground.
          break;

        case AppState::kBackgroundFromActive:
          if (history::HistoryService* history_service =
                  ios::HistoryServiceFactory::GetForProfile(
                      profile, ServiceAccessType::EXPLICIT_ACCESS)) {
            history_service->HandleBackgrounding();
          }
          break;
        case AppState::kBackgroundProcessing:
        case AppState::kBackgroundIdle:
          break;
      }

      // No need to check that `GetPrefs()` returns non-null value since the
      // ProfileIOS owns its PrefService and thus the method cannot
      // return null.
      profile->GetPrefs()->CommitPendingWrite();
    }
  }

  if (local_state_) {
    local_state_->CommitPendingWrite();
  }

  // Persisting to disk is protected by a critical task, so no other special
  // handling is necessary on iOS.
}

void ApplicationContextImpl::CreateLocalState() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!local_state_);

  base::FilePath local_state_path;
  CHECK(base::PathService::Get(ios::FILE_LOCAL_STATE, &local_state_path));

  scoped_refptr<PrefRegistrySimple> pref_registry(new PrefRegistrySimple);

  // Register local state preferences.
  RegisterLocalStatePrefs(pref_registry.get());

  policy::BrowserPolicyConnector* browser_policy_connector =
      GetBrowserPolicyConnector();
  policy::PolicyService* policy_service =
      browser_policy_connector ? browser_policy_connector->GetPolicyService()
                               : nullptr;
  local_state_ = ::CreateLocalState(
      local_state_path, local_state_task_runner_.get(), pref_registry,
      policy_service, browser_policy_connector);
  DCHECK(local_state_);

  sessions::SessionIdGenerator::GetInstance()->Init(local_state_.get());

  // Cleanup obsolete preferences.
  MigrateObsoleteLocalStatePrefs(local_state_.get());

  // Delete obsolete data from NSUserDefaults.
  MigrateObsoleteUserDefault();
}

void ApplicationContextImpl::CreateGCMDriver() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!gcm_driver_);

  base::FilePath store_path;
  CHECK(base::PathService::Get(ios::DIR_GLOBAL_GCM_STORE, &store_path));

  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));

  gcm_driver_ = gcm::CreateGCMDriverDesktop(
      std::make_unique<gcm::GCMClientFactory>(), GetLocalState(), store_path,
      // This callback may be invoked on a background sequence, but it calls a
      // method of ApplicationContextImpl which is a sequence-affine object,
      // so wrap the call in BindPostTask(...) to ensure the method happens on
      // the correct sequence.
      base::BindPostTask(
          base::SequencedTaskRunner::GetCurrentDefault(),
          base::BindRepeating(
              &ApplicationContextImpl::RequestProxyResolvingSocketFactory,
              weak_ptr_factory_.GetWeakPtr())),
      GetSharedURLLoaderFactory(),
      GetApplicationContext()->GetNetworkConnectionTracker(), ::GetChannel(),
      IOSChromeGCMProfileServiceFactory::GetProductCategoryForSubtypes(),
      web::GetUIThreadTaskRunner({}), web::GetIOThreadTaskRunner({}),
      blocking_task_runner, GetApplicationContext()->GetOSCryptAsync());
}

void ApplicationContextImpl::RequestProxyResolvingSocketFactory(
    mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>
        receiver) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  network::mojom::NetworkContext* network_context = GetSystemNetworkContext();
  network_context->CreateProxyResolvingSocketFactory(std::move(receiver));
}
