blob: 7a9d66a9dc540a5534c091fd107275259e2be6a6 [file]
// 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)