blob: 04938dd8dc3621d091ddae0c4f127471d2b71afc [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/ios_chrome_main_parts.h"
#include "base/base_switches.h"
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/ios/ios_util.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/user_metrics.h"
#include "base/path_service.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/time/default_tick_clock.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/crash/core/common/reporter_running_ios.h"
#include "components/flags_ui/pref_service_flags_storage.h"
#include "components/heap_profiling/in_process/heap_profiler_controller.h"
#include "components/language/core/browser/language_usage_metrics.h"
#include "components/language/core/browser/pref_names.h"
#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/metrics/expired_histogram_util.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/open_from_clipboard/clipboard_recent_content.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_service.h"
#include "components/translate/core/browser/translate_download_manager.h"
#include "components/ukm/ios/features.h"
#include "components/variations/field_trial_config/field_trial_util.h"
#include "components/variations/service/variations_service.h"
#include "components/variations/synthetic_trials_active_group_id_provider.h"
#include "components/variations/variations_crash_keys.h"
#include "components/variations/variations_ids_provider.h"
#include "ios/chrome/browser/application_context_impl.h"
#include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
#include "ios/chrome/browser/chrome_paths.h"
#include "ios/chrome/browser/crash_report/crash_helper.h"
#import "ios/chrome/browser/first_run/first_run.h"
#include "ios/chrome/browser/flags/about_flags.h"
#include "ios/chrome/browser/install_time_util.h"
#include "ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h"
#include "ios/chrome/browser/metrics/ios_expired_histograms_array.h"
#include "ios/chrome/browser/open_from_clipboard/create_clipboard_recent_content.h"
#include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
#include "ios/chrome/browser/pref_names.h"
#include "ios/chrome/browser/safe_browsing/safe_browsing_service.h"
#include "ios/chrome/browser/translate/translate_service_ios.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#include "ios/web/public/thread/web_task_traits.h"
#include "ios/web/public/thread/web_thread.h"
#include "net/base/network_change_notifier.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_stream_factory.h"
#include "net/url_request/url_request.h"
#include "rlz/buildflags/buildflags.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h"
#if BUILDFLAG(ENABLE_RLZ)
#include "components/rlz/rlz_tracker.h" // nogncheck
#include "ios/chrome/browser/rlz/rlz_tracker_delegate_impl.h" // nogncheck
#endif
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
#include "base/allocator/allocator_shim.h"
#endif
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Sets |level| value for NSURLFileProtectionKey key for the URL with given
// |local_state_path|.
void SetProtectionLevel(const base::FilePath& file_path, id level) {
NSString* file_path_string = base::SysUTF8ToNSString(file_path.value());
NSURL* file_path_url = [NSURL fileURLWithPath:file_path_string
isDirectory:NO];
NSError* error = nil;
BOOL protection_set = [file_path_url setResourceValue:level
forKey:NSURLFileProtectionKey
error:&error];
DCHECK(protection_set) << base::SysNSStringToUTF8(error.localizedDescription);
}
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
// Do not install allocator shim on iOS 13.4 due to high crash volume on this
// particular version of OS. TODO(crbug.com/1108219): Remove this workaround
// when/if the bug gets fixed.
//
// Do not install allocator shim for now, until it's clear why Chrome crashes
// on iOS 14.3+ on startup.
// TODO(crbug.com/1150599): Remove this workaround when/if the bug gets fixed.
bool ShouldInstallAllocatorShim() {
return false;
}
#endif
// If enabled, always pass |true| to MetricsServicesManager
// UpdateUploadPermissions. Once impact of the UmaCellular logic to check
// cellular is determined, this flag and the incorrect logic can be removed.
const base::Feature kFixUmaCellularMetricsRecording{
"FixUmaCellularMetricsRecording", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace
IOSChromeMainParts::IOSChromeMainParts(
const base::CommandLine& parsed_command_line)
: parsed_command_line_(parsed_command_line), local_state_(nullptr) {
// Chrome disallows cookies by default. All code paths that want to use
// cookies need to go through one of Chrome's URLRequestContexts which have
// a ChromeNetworkDelegate attached that selectively allows cookies again.
net::URLRequest::SetDefaultCookiePolicyToBlock();
}
IOSChromeMainParts::~IOSChromeMainParts() {}
void IOSChromeMainParts::PreEarlyInitialization() {
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
if (ShouldInstallAllocatorShim()) {
base::allocator::InitializeAllocatorShim();
}
#endif
}
void IOSChromeMainParts::PreMainMessageLoopStart() {
l10n_util::OverrideLocaleWithCocoaLocale();
const std::string loaded_locale =
ui::ResourceBundle::InitSharedInstanceWithLocale(
std::string(), nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES);
CHECK(!loaded_locale.empty());
base::FilePath resources_pack_path;
base::PathService::Get(ios::FILE_RESOURCES_PACK, &resources_pack_path);
ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
resources_pack_path, ui::SCALE_FACTOR_100P);
}
void IOSChromeMainParts::PreCreateThreads() {
// IMPORTANT
// Calls in this function should not post tasks or create threads as
// components used to handle those tasks are not yet available. This work
// should be deferred to PreMainMessageLoopRunImpl.
// The initial read is done synchronously, the TaskPriority is thus only used
// for flushes to disks and BACKGROUND is therefore appropriate. Priority of
// remaining BACKGROUND+BLOCK_SHUTDOWN tasks is bumped by the ThreadPool on
// shutdown.
scoped_refptr<base::SequencedTaskRunner> local_state_task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
base::FilePath local_state_path;
CHECK(base::PathService::Get(ios::FILE_LOCAL_STATE, &local_state_path));
application_context_.reset(new ApplicationContextImpl(
local_state_task_runner.get(), parsed_command_line_,
l10n_util::GetLocaleOverride()));
DCHECK_EQ(application_context_.get(), GetApplicationContext());
// Check the first run state early; this must be done before IO is disallowed
// so that later calls can use the cached value. (The return value is ignored
// because this is only to trigger the internal lookup and caching for later
// use.)
FirstRun::IsChromeFirstRun();
// Convert freeform experimental settings into switches before initializing
// local state, in case any of the settings affect policy.
AppendSwitchesFromExperimentalSettings(
base::CommandLine::ForCurrentProcess());
// Initialize local state.
local_state_ = application_context_->GetLocalState();
DCHECK(local_state_);
flags_ui::PrefServiceFlagsStorage flags_storage(
application_context_->GetLocalState());
ConvertFlagsToSwitches(&flags_storage,
base::CommandLine::ForCurrentProcess());
// Now that the command line has been mutated based on about:flags, we can
// initialize field trials. The field trials are needed by IOThread's
// initialization which happens in BrowserProcess:PreCreateThreads. Metrics
// initialization is handled in PreMainMessageLoopRun since it posts tasks.
SetupFieldTrials();
// Sync the crashpad field tral state to NSUserDefaults. Called immediately
// after setting up field trials.
crash_helper::SyncCrashpadEnabledOnNextRun();
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
// Do not install allocator shim on iOS 13.4 due to high crash volume on this
// particular version of OS. TODO(crbug.com/1108219): Remove this workaround
// when/if the bug gets fixed.
if (ShouldInstallAllocatorShim()) {
// Start heap profiling as early as possible so it can start recording
// memory allocations. Requires the allocator shim to be enabled.
heap_profiler_controller_ = std::make_unique<HeapProfilerController>();
metrics::CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
base::BindRepeating(
&metrics::CallStackProfileMetricsProvider::ReceiveProfile));
heap_profiler_controller_->Start();
}
#endif
variations::InitCrashKeys();
metrics::EnableExpiryChecker(::kExpiredHistogramsHashes,
::kNumExpiredHistograms);
// TODO(crbug.com/1164533): Remove code below some time after February 2021.
NSString* const kRemoveProtectionFromPrefFileKey =
@"RemoveProtectionFromPrefKey";
if ([NSUserDefaults.standardUserDefaults
boolForKey:kRemoveProtectionFromPrefFileKey]) {
// Restore default protection level when user is no longer in the
// experimental group.
SetProtectionLevel(local_state_path,
NSFileProtectionCompleteUntilFirstUserAuthentication);
[NSUserDefaults.standardUserDefaults
removeObjectForKey:kRemoveProtectionFromPrefFileKey];
}
application_context_->PreCreateThreads();
}
void IOSChromeMainParts::PreMainMessageLoopRun() {
application_context_->PreMainMessageLoopRun();
// ContentSettingsPattern need to be initialized before creating the
// ChromeBrowserState.
ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(nullptr, 0);
// Ensure ClipboadRecentContentIOS is created.
ClipboardRecentContent::SetInstance(CreateClipboardRecentContentIOS());
// Ensure that the browser state is initialized.
EnsureBrowserStateKeyedServiceFactoriesBuilt();
ios::ChromeBrowserStateManager* browser_state_manager =
application_context_->GetChromeBrowserStateManager();
ChromeBrowserState* last_used_browser_state =
browser_state_manager->GetLastUsedBrowserState();
// This must occur at PreMainMessageLoopRun because |SetupMetrics()| uses the
// blocking pool, which is disabled until the CreateThreads phase of startup.
// TODO(crbug.com/786494): Investigate whether metrics recording can be
// initialized consistently across iOS and non-iOS platforms
SetupMetrics();
// Now that the file thread has been started, start recording.
StartMetricsRecording();
// Because the crashpad flag takes 2 restarts to take effect, register a
// synthetic field try when crashpad is actually running. Called immediately
// after starting metrics recording.
IOSChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
"CrashpadIOS",
crash_reporter::IsCrashpadRunning() ? "Enabled" : "Disabled");
#if BUILDFLAG(ENABLE_RLZ)
// Init the RLZ library. This just schedules a task on the file thread to be
// run sometime later. If this is the first run we record the installation
// event.
int ping_delay = last_used_browser_state->GetPrefs()->GetInteger(
FirstRun::GetPingDelayPrefName());
// Negative ping delay means to send ping immediately after a first search is
// recorded.
rlz::RLZTracker::SetRlzDelegate(base::WrapUnique(new RLZTrackerDelegateImpl));
rlz::RLZTracker::InitRlzDelayed(
FirstRun::IsChromeFirstRun(), ping_delay < 0,
base::TimeDelta::FromMilliseconds(abs(ping_delay)),
RLZTrackerDelegateImpl::IsGoogleDefaultSearch(last_used_browser_state),
RLZTrackerDelegateImpl::IsGoogleHomepage(last_used_browser_state),
RLZTrackerDelegateImpl::IsGoogleInStartpages(last_used_browser_state));
#endif // BUILDFLAG(ENABLE_RLZ)
TranslateServiceIOS::Initialize();
language::LanguageUsageMetrics::RecordAcceptLanguages(
last_used_browser_state->GetPrefs()->GetString(
language::prefs::kAcceptLanguages));
language::LanguageUsageMetrics::RecordApplicationLanguage(
application_context_->GetApplicationLocale());
// Request new variations seed information from server.
variations::VariationsService* variations_service =
application_context_->GetVariationsService();
if (variations_service) {
variations_service->set_policy_pref_service(
last_used_browser_state->GetPrefs());
variations_service->PerformPreMainMessageLoopStartup();
}
// Initialize Chrome Browser Cloud Management.
auto* policy_connector = application_context_->GetBrowserPolicyConnector();
if (policy_connector) {
policy_connector->chrome_browser_cloud_management_controller()->Init(
application_context_->GetLocalState(),
application_context_->GetSharedURLLoaderFactory());
}
// Ensure that Safe Browsing is initialized.
SafeBrowsingService* safe_browsing_service =
application_context_->GetSafeBrowsingService();
base::FilePath user_data_path;
CHECK(base::PathService::Get(ios::DIR_USER_DATA, &user_data_path));
safe_browsing_service->Initialize(last_used_browser_state->GetPrefs(),
user_data_path);
}
void IOSChromeMainParts::PostMainMessageLoopRun() {
TranslateServiceIOS::Shutdown();
#if BUILDFLAG(ENABLE_RLZ)
rlz::RLZTracker::CleanupRlz();
#endif // BUILDFLAG(ENABLE_RLZ)
application_context_->StartTearDown();
}
void IOSChromeMainParts::PostDestroyThreads() {
application_context_->PostDestroyThreads();
}
// This will be called after the command-line has been mutated by about:flags
void IOSChromeMainParts::SetupFieldTrials() {
base::SetRecordActionTaskRunner(
base::CreateSingleThreadTaskRunner({web::WebThread::UI}));
// Initialize FieldTrialList to support FieldTrials that use one-time
// randomization.
DCHECK(!field_trial_list_);
field_trial_list_.reset(
new base::FieldTrialList(application_context_->GetMetricsServicesManager()
->CreateEntropyProvider()));
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
// Associate parameters chosen in about:flags and create trial/group for them.
flags_ui::PrefServiceFlagsStorage flags_storage(
application_context_->GetLocalState());
std::vector<std::string> variation_ids =
RegisterAllFeatureVariationParameters(&flags_storage, feature_list.get());
// On iOS, GPU benchmarking is not supported. So, pass in a dummy value for
// the name of the switch that enables gpu benchmarking.
// TODO(crbug.com/988603): This should also set up extra switch-dependent
// feature overrides.
application_context_->GetVariationsService()->SetupFieldTrials(
"dummy-enable-gpu-benchmarking", switches::kEnableFeatures,
switches::kDisableFeatures, variation_ids,
std::vector<base::FeatureList::FeatureOverrideInfo>(),
std::move(feature_list), &ios_field_trials_);
}
void IOSChromeMainParts::SetupMetrics() {
metrics::MetricsService* metrics = application_context_->GetMetricsService();
metrics->synthetic_trial_registry()->AddSyntheticTrialObserver(
variations::VariationsIdsProvider::GetInstance());
metrics->synthetic_trial_registry()->AddSyntheticTrialObserver(
variations::SyntheticTrialsActiveGroupIdProvider::GetInstance());
// Now that field trials have been created, initializes metrics recording.
metrics->InitializeMetricsRecordingState();
}
void IOSChromeMainParts::StartMetricsRecording() {
bool isConnectionCellular = net::NetworkChangeNotifier::IsConnectionCellular(
net::NetworkChangeNotifier::GetConnectionType());
bool mayUpload = false;
if (base::FeatureList::IsEnabled(kUmaCellular)) {
if (base::FeatureList::IsEnabled(kFixUmaCellularMetricsRecording)) {
mayUpload = true;
} else {
// This is wrong and should be removed, but the fix is gated by the
// feature flag above to measure impact.
mayUpload = !isConnectionCellular;
}
} else {
// TODO(crbug.com/1179809): Now that kUmaCellular is default, all references
// to kUmaCellular and kMetricsReportingWifiOnly should be removed, but only
// after a study of kFixUmaCellularMetricsRecording is completed.
bool wifiOnly = local_state_->GetBoolean(prefs::kMetricsReportingWifiOnly);
mayUpload = !wifiOnly || !isConnectionCellular;
}
application_context_->GetMetricsServicesManager()->UpdateUploadPermissions(
mayUpload);
}