| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "android_webview/browser/aw_browser_main_parts.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "android_webview/browser/aw_browser_context.h" |
| #include "android_webview/browser/aw_browser_terminator.h" |
| #include "android_webview/browser/aw_content_browser_client.h" |
| #include "android_webview/browser/aw_web_ui_controller_factory.h" |
| #include "android_webview/browser/metrics/aw_metrics_service_accessor.h" |
| #include "android_webview/browser/metrics/aw_metrics_service_client.h" |
| #include "android_webview/browser/metrics/memory_metrics_logger.h" |
| #include "android_webview/browser/metrics/system_state_util.h" |
| #include "android_webview/browser/network_service/aw_network_change_notifier_factory.h" |
| #include "android_webview/common/aw_cached_flags.h" |
| #include "android_webview/common/aw_descriptors.h" |
| #include "android_webview/common/aw_features.h" |
| #include "android_webview/common/aw_paths.h" |
| #include "android_webview/common/aw_resource.h" |
| #include "android_webview/common/aw_switches.h" |
| #include "android_webview/common/crash_reporter/aw_crash_reporter_client.h" |
| #include "base/android/apk_assets.h" |
| #include "base/android/apk_info.h" |
| #include "base/android/bundle_utils.h" |
| #include "base/android/memory_pressure_listener_android.h" |
| #include "base/android/path_utils.h" |
| #include "base/base_paths_android.h" |
| #include "base/byte_size.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/i18n/rtl.h" |
| #include "base/logging.h" |
| #include "base/message_loop/message_pump_type.h" |
| #include "base/path_service.h" |
| #include "base/task/current_thread.h" |
| #include "base/task/thread_pool.h" |
| #include "components/crash/content/browser/child_exit_observer_android.h" |
| #include "components/crash/core/common/crash_key.h" |
| #include "components/embedder_support/origin_trials/component_updater_utils.h" |
| #include "components/embedder_support/origin_trials/origin_trials_settings_storage.h" |
| #include "components/heap_profiling/in_process/browser_process_snapshot_controller.h" |
| #include "components/heap_profiling/in_process/heap_profiler_controller.h" |
| #include "components/heap_profiling/in_process/mojom/snapshot_controller.mojom.h" |
| #include "components/metrics/android_metrics_helper.h" |
| #include "components/metrics/content/subprocess_metrics_provider.h" |
| #include "components/metrics/metrics_service.h" |
| #include "components/performance_manager/embedder/graph_features.h" |
| #include "components/performance_manager/embedder/performance_manager_lifetime.h" |
| #include "components/user_prefs/user_prefs.h" |
| #include "components/variations/synthetic_trials.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 "components/version_info/version_info.h" |
| #include "content/public/browser/android/synchronous_compositor.h" |
| #include "content/public/browser/browser_child_process_host.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_host.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/synthetic_trial_syncer.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/result_codes.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "net/android/network_change_notifier_factory_android.h" |
| #include "net/base/network_change_notifier.h" |
| #include "third_party/blink/public/common/origin_trials/origin_trials_settings_provider.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gl/gl_surface.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "android_webview/browser_jni_headers/AwInterfaceRegistrar_jni.h" |
| |
| namespace { |
| |
| void BindHeapSnapshotControllerToProcessHost( |
| int child_process_id, |
| mojo::PendingReceiver<heap_profiling::mojom::SnapshotController> receiver) { |
| if (auto* bcph = content::BrowserChildProcessHost::FromID(child_process_id)) { |
| bcph->GetHost()->BindReceiver(std::move(receiver)); |
| } else if (auto* rph = content::RenderProcessHost::FromID(child_process_id)) { |
| if (!rph->GetBrowserContext()->IsOffTheRecord()) { |
| rph->BindReceiver(std::move(receiver)); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| namespace android_webview { |
| |
| namespace { |
| |
| // Return true if the version code indicates the bundle is primarily 64-bit |
| // (even if it may have 32-bit bits). |
| bool Is64bitAccordingToVersionCode(const std::string& version_code) { |
| // Primary bitness of the bundle is encoded in the last digit of the version |
| // code. |
| // |
| // From build/util/android_chrome_version.py: |
| // 'arm': { |
| // '32': 0, |
| // '32_64': 1, |
| // '64_32': 2, |
| // '64_32_high': 3, |
| // '64': 4, |
| // }, |
| // 'intel': { |
| // '32': 6, |
| // '32_64': 7, |
| // '64_32': 8, |
| // '64': 9, |
| // }, |
| std::set<char> arch_codes_64bit = {'2', '3', '4', '8', '9'}; |
| char arch_code = version_code.back(); |
| return arch_codes_64bit.count(arch_code) > 0; |
| } |
| |
| bool IsBundleInterestingAccordingToVersionCode( |
| const std::string& version_code) { |
| // Primary bitness of the bundle is encoded in the last digit of the version |
| // code. And the variant (package name) is encoded in the second to last. |
| // |
| // From build/util/android_chrome_version.py: |
| // 'arm': { |
| // '32': 0, |
| // '32_64': 1, |
| // '64_32': 2, |
| // '64_32_high': 3, |
| // '64': 4, |
| // }, |
| // 'intel': { |
| // '32': 6, |
| // '32_64': 7, |
| // '64_32': 8, |
| // '64': 9, |
| // }, |
| // |
| // _PACKAGE_NAMES = { |
| // 'CHROME': 0, |
| // 'CHROME_MODERN': 10, |
| // 'MONOCHROME': 20, |
| // 'TRICHROME': 30, |
| // [...] |
| |
| if (version_code.length() != 9) { // Our scheme has exactly 9 digits. |
| return false; |
| } |
| |
| // '32' and '64' bundles go on 32bit-only and 64bit-only devices, so exclude |
| // them. |
| std::set<char> arch_codes_mixed = {'1', '2', '3', '7', '8'}; |
| char arch_code = version_code.back(); |
| |
| // Only 'TRICHROME' supports 64-bit. |
| constexpr char kTriChromeVariant = '3'; |
| char variant = version_code[version_code.length() - 2]; |
| |
| return arch_codes_mixed.count(arch_code) > 0 && variant == kTriChromeVariant; |
| } |
| |
| } // namespace |
| |
| AwBrowserMainParts::AwBrowserMainParts(AwContentBrowserClient* browser_client) |
| : browser_client_(browser_client) { |
| } |
| |
| AwBrowserMainParts::~AwBrowserMainParts() { |
| } |
| |
| void AwBrowserMainParts::PreCreateMainMessageLoop() { |
| // WebView should not override the main thread name. |
| // This switch is set here instead of |
| // `android_webview/lib/aw_main_delegate.cc` because it must be available in |
| // `BrowserMainLoop::CreateMainMessageLoop`. |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kDisableMainThreadNameOverride); |
| } |
| |
| int AwBrowserMainParts::PreEarlyInitialization() { |
| // Network change notifier factory must be singleton, only set factory |
| // instance while it is not been created. |
| // In most cases, this check is not necessary because SetFactory should be |
| // called only once, but both webview and native cronet calls this function, |
| // in case of building both webview and cronet to one app, it is required to |
| // avoid crashing the app. |
| if (!net::NetworkChangeNotifier::GetFactory()) { |
| net::NetworkChangeNotifier::SetFactory( |
| new AwNetworkChangeNotifierFactory()); |
| } |
| |
| // Creates a SingleThreadTaskExecutor for Android WebView if doesn't exist. |
| DCHECK(!main_task_executor_.get()); |
| if (!base::CurrentThread::IsSet()) { |
| main_task_executor_ = std::make_unique<base::SingleThreadTaskExecutor>( |
| base::MessagePumpType::UI); |
| } |
| |
| browser_process_ = std::make_unique<AwBrowserProcess>(browser_client_); |
| |
| auto* origin_trials_settings_storage = |
| browser_process_->GetOriginTrialsSettingsStorage(); |
| embedder_support::SetupOriginTrialsCommandLineAndSettings( |
| browser_process_->local_state(), origin_trials_settings_storage); |
| blink::OriginTrialsSettingsProvider::Get()->SetSettings( |
| origin_trials_settings_storage->GetSettings()); |
| |
| if (base::FeatureList::IsEnabled( |
| features::kWebViewCacheSizeLimitDerivedFromAppCacheQuota)) { |
| base::ThreadPool::PostTask( |
| FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()}, |
| base::BindOnce(&AwBrowserProcess::FetchHostAppCacheQuota, |
| base::Unretained(browser_process_.get()))); |
| } |
| |
| return content::RESULT_CODE_NORMAL_EXIT; |
| } |
| |
| int AwBrowserMainParts::PreCreateThreads() { |
| base::android::MemoryPressureListenerAndroid::Initialize( |
| base::android::AttachCurrentThread()); |
| child_exit_observer_ = |
| std::make_unique<::crash_reporter::ChildExitObserver>(); |
| |
| // We need to create the safe browsing specific directory even if the |
| // AwSafeBrowsingConfigHelper::GetSafeBrowsingEnabled() is false |
| // initially, because safe browsing can be enabled later at runtime |
| // on a per-webview basis. |
| base::FilePath safe_browsing_dir; |
| if (base::PathService::Get(android_webview::DIR_SAFE_BROWSING, |
| &safe_browsing_dir)) { |
| if (!base::PathExists(safe_browsing_dir)) |
| base::CreateDirectory(safe_browsing_dir); |
| } |
| |
| base::FilePath crash_dir; |
| if (base::PathService::Get(android_webview::DIR_CRASH_DUMPS, &crash_dir)) { |
| if (!base::PathExists(crash_dir)) { |
| base::CreateDirectory(crash_dir); |
| } |
| } |
| |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kWebViewSandboxedRenderer)) { |
| // Create the renderers crash manager on the UI thread. |
| child_exit_observer_->RegisterClient( |
| std::make_unique<AwBrowserTerminator>()); |
| } |
| |
| crash_reporter::InitializeCrashKeys(); |
| variations::InitCrashKeys(); |
| CHECK(metrics::SubprocessMetricsProvider::CreateInstance()); |
| |
| RegisterSyntheticTrials(); |
| |
| return content::RESULT_CODE_NORMAL_EXIT; |
| } |
| |
| void AwBrowserMainParts::RegisterSyntheticTrials() { |
| metrics::MetricsService* metrics = |
| AwMetricsServiceClient::GetInstance()->GetMetricsService(); |
| metrics->GetSyntheticTrialRegistry()->AddObserver( |
| variations::VariationsIdsProvider::GetInstance()); |
| metrics->GetSyntheticTrialRegistry()->AddObserver( |
| variations::SyntheticTrialsActiveGroupIdProvider::GetInstance()); |
| |
| synthetic_trial_syncer_ = content::SyntheticTrialSyncer::Create( |
| metrics->GetSyntheticTrialRegistry()); |
| |
| // We use 3393823 as an id reported for all WebView traffic to help analyse data on the |
| // server-side for WebView embedders. |
| std::vector<std::string> forced_variation_ids = {"3393823"}; |
| |
| // Configure experiment to measure impact of using a native renderer zygote. |
| std::string native_zygote_group; |
| if (AwBrowserProcess::IsNativeWebViewZygoteEnabled()) { |
| native_zygote_group = "Enabled"; |
| forced_variation_ids.emplace_back("101000092"); |
| } else { |
| native_zygote_group = "Disabled"; |
| forced_variation_ids.emplace_back("101000091"); |
| } |
| AwMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| metrics, "WebViewNativeZygote", native_zygote_group, |
| variations::SyntheticTrialAnnotationMode::kCurrentLog); |
| |
| variations::VariationsIdsProvider::GetInstance()->ForceVariationIds( |
| base::PassKey<AwBrowserMainParts>(), forced_variation_ids, |
| /*command_line_variation_ids=*/""); |
| |
| // Set up experiment for 64-bit WebView. |
| // |
| // We are specifically interested in devices that meet all of these criteria: |
| // 1) Devices with 4&6GB RAM, as we're launching the feature only for those |
| // (using (3.2;6.5) range to match RAM targeting in Play). |
| // 2) Devices with only one Android profile (work versus personal), as having |
| // multiple profiles is a source of a population bias (so is having |
| // multiple users, but that bias is known to be small, and they're hard to |
| // filter out). |
| // 3) Mixed 32-/64-bit devices, as non-mixed devices are forced to use |
| // a particular bitness, thus don't participate in the experiment. |
| // 4) Version code ends with 31/32/33/37/38. 3x represents Trichrome and the |
| // last digit represents mixed device (which streghtens the filter #3). In |
| // reality Stable is mostly represented by 31 and 33. |
| // (TMI, but Beta is a tad more complicated. What UMA sees as Beta |
| // includes, as expected, Chrome Beta channel and, less expected, Chrome |
| // Stable channel distributed via the Play Beta track. The latter is |
| // represented mainly by version codes ending with 41 and 42, which |
| // dominate, but we want to filter them out nonetheless because it's harder |
| // to set up experiment for them.) |
| std::string version_code = base::android::apk_info::package_version_code(); |
| size_t ram_mb = base::SysInfo::AmountOfTotalPhysicalMemory().InMiB(); |
| auto cpu_abi_bitness_support = |
| metrics::AndroidMetricsHelper::GetInstance()->cpu_abi_bitness_support(); |
| bool is_device_of_interest = |
| (3.2 * 1024 < ram_mb && ram_mb < 6.5 * 1024) && |
| (GetMultipleUserProfilesState() == |
| MultipleUserProfilesState::kSingleProfile) && |
| (cpu_abi_bitness_support == metrics::CpuAbiBitnessSupport::k32And64bit) && |
| IsBundleInterestingAccordingToVersionCode(version_code); |
| if (is_device_of_interest) { |
| std::string trial_group; |
| // We can't use defined(ARCH_CPU_64_BITS) on WebView, because bitness of |
| // Browser doesn't have to match the bitness of the bundle. Browser always |
| // follows bitness of the app, whereas Renderer follows bitness of the |
| // bundle. |
| if (Is64bitAccordingToVersionCode(version_code)) { |
| trial_group = "64bit"; |
| } else { |
| trial_group = "32bit"; |
| } |
| AwMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| metrics, "BitnessForMidRangeRAM", trial_group, |
| variations::SyntheticTrialAnnotationMode::kCurrentLog); |
| AwMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| metrics, "BitnessForMidRangeRAM_wVersion", |
| std::string(version_info::GetVersionNumber()) + "_" + trial_group, |
| variations::SyntheticTrialAnnotationMode::kCurrentLog); |
| } |
| AwMetricsServiceClient::GetInstance()->FlushPendingSyntheticTrialsFromJava(); |
| |
| bool in_seed_experiment = |
| android_webview::CachedFlags::IsCachedFeatureOverridden( |
| features::kWebViewReducedSeedExpiration) || |
| android_webview::CachedFlags::IsCachedFeatureOverridden( |
| features::kWebViewReducedSeedRequestPeriod); |
| bool reduced_seed_expiration = android_webview::CachedFlags::IsEnabled( |
| features::kWebViewReducedSeedExpiration); |
| bool reduced_seed_request_period = android_webview::CachedFlags::IsEnabled( |
| features::kWebViewReducedSeedRequestPeriod); |
| |
| std::string group = "Default"; |
| if (in_seed_experiment) { |
| if (reduced_seed_expiration && reduced_seed_request_period) { |
| group = "BothEnabled"; |
| } else if (reduced_seed_expiration) { |
| group = "ReducedSeedExpiration"; |
| } else if (reduced_seed_request_period) { |
| group = "ReducedSeedRequestPeriod"; |
| } else { |
| group = "Control"; |
| } |
| } |
| AwMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| metrics, "WebViewFasterFinchSeed", group, |
| variations::SyntheticTrialAnnotationMode::kCurrentLog); |
| |
| std::string trial_name, group_name; |
| if (const auto* heap_profiler_controller = |
| heap_profiling::HeapProfilerController::GetInstance(); |
| heap_profiler_controller && |
| heap_profiler_controller->GetSyntheticFieldTrial(trial_name, |
| group_name)) { |
| AwMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| metrics, trial_name, group_name, |
| variations::SyntheticTrialAnnotationMode::kCurrentLog); |
| } |
| } |
| |
| int AwBrowserMainParts::PreMainMessageLoopRun() { |
| TRACE_EVENT0("startup", "AwBrowserMainParts::PreMainMessageLoopRun"); |
| AwBrowserProcess::GetInstance()->PreMainMessageLoopRun(); |
| browser_client_->InitBrowserContextStore(); |
| content::WebUIControllerFactory::RegisterFactory( |
| AwWebUIControllerFactory::GetInstance()); |
| content::RenderFrameHost::AllowInjectingJavaScript(); |
| metrics_logger_ = std::make_unique<metrics::MemoryMetricsLogger>(); |
| |
| Java_AwInterfaceRegistrar_registerMojoInterfaces( |
| base::android::AttachCurrentThread()); |
| |
| // The global NetworkService instance (network::NetworkService) is profile |
| // agnostic and operates asynchronously on a dedicated IO/Network thread. |
| // Unlike initializing a full StoragePartition or NetworkContext, invoking |
| // content::GetNetworkService() does not touch profile storage paths, SQLite |
| // cookie databases, or app quota limits. Eagerly warming up the service here |
| // during native browser process startup spins up the background thread and |
| // IPC pipelines ahead of time, eliminating thread spin-up latency on the |
| // critical path of the first navigation or socket preconnect API call. |
| if (base::FeatureList::IsEnabled(features::kWebViewWarmupNetworkService)) { |
| TRACE_EVENT0("startup", "AwBrowserMainParts::WarmupNetworkService"); |
| content::GetNetworkService(); |
| } |
| |
| return content::RESULT_CODE_NORMAL_EXIT; |
| } |
| |
| void AwBrowserMainParts::WillRunMainMessageLoop( |
| std::unique_ptr<base::RunLoop>& run_loop) { |
| NOTREACHED(); |
| } |
| |
| int AwBrowserMainParts::PostCreateThreads() { |
| if (base::FeatureList::IsEnabled(features::kWebViewMemoryProfilingClient)) { |
| if (auto* snapshot_controller = |
| heap_profiling::BrowserProcessSnapshotController::GetInstance()) { |
| snapshot_controller->SetBindRemoteForChildProcessCallback( |
| base::BindRepeating(&BindHeapSnapshotControllerToProcessHost)); |
| } |
| } |
| |
| // TODO(crbug.com/524981399): Enable standard graph features. |
| performance_manager_lifetime_ = |
| std::make_unique<performance_manager::PerformanceManagerLifetime>( |
| performance_manager::GraphFeatures::WithNone(), base::DoNothing()); |
| return content::RESULT_CODE_NORMAL_EXIT; |
| } |
| |
| } // namespace android_webview |
| |
| DEFINE_JNI(AwInterfaceRegistrar) |