1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <jni.h>
6 #include <memory>
7 #include <string>
8 #include <utility>
9
10 #include "base/android/base_jni_onload.h"
11 #include "base/android/build_info.h"
12 #include "base/android/jni_android.h"
13 #include "base/android/jni_array.h"
14 #include "base/android/jni_registrar.h"
15 #include "base/android/jni_string.h"
16 #include "base/android/jni_utils.h"
17 #include "base/android/library_loader/library_loader_hooks.h"
18 #include "base/check_op.h"
19 #include "base/command_line.h"
20 #include "base/feature_list.h"
21 #include "base/message_loop/message_pump_type.h"
22 #include "base/metrics/field_trial_params.h"
23 #include "base/synchronization/waitable_event.h"
24 #include "base/task/current_thread.h"
25 #include "base/task/sequenced_task_runner.h"
26 #include "base/task/single_thread_task_executor.h"
27 #include "base/task/thread_pool/thread_pool_instance.h"
28 #include "build/build_config.h"
29 #include "components/cronet/android/cronet_base_feature.h"
30 #include "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h"
31 #include "components/cronet/android/cronet_jni_registration_generated.h"
32 #include "components/cronet/android/cronet_library_loader.h"
33 #include "components/cronet/android/proto/base_feature_overrides.pb.h"
34 #include "components/cronet/cronet_global_state.h"
35 #include "components/cronet/version.h"
36 #include "net/android/network_change_notifier_factory_android.h"
37 #include "net/base/network_change_notifier.h"
38 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
39 #include "net/proxy_resolution/proxy_config_service_android.h"
40 #include "third_party/zlib/zlib.h"
41 #include "url/buildflags.h"
42
43 #if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
44 #include "base/i18n/icu_util.h" // nogncheck
45 #endif
46
47 using base::android::JavaParamRef;
48 using base::android::ScopedJavaLocalRef;
49
50 namespace cronet {
51 namespace {
52
53 // This feature flag can be used to make Cronet log a message from native code
54 // on library initialization. This is useful for testing that the Cronet
55 // base::Feature integration works.
56 BASE_FEATURE(kLogMe, "CronetLogMe", base::FEATURE_DISABLED_BY_DEFAULT);
57 constexpr base::FeatureParam<std::string> kLogMeMessage{&kLogMe, "message", ""};
58
59 // SingleThreadTaskExecutor on the init thread, which is where objects that
60 // receive Java notifications generally live.
61 base::SingleThreadTaskExecutor* g_init_task_executor = nullptr;
62
63 std::unique_ptr<net::NetworkChangeNotifier> g_network_change_notifier;
64 base::WaitableEvent g_init_thread_init_done(
65 base::WaitableEvent::ResetPolicy::MANUAL,
66 base::WaitableEvent::InitialState::NOT_SIGNALED);
67
GetBaseFeatureOverrides(JNIEnv * env)68 ::org::chromium::net::httpflags::BaseFeatureOverrides GetBaseFeatureOverrides(
69 JNIEnv* env) {
70 const auto serializedProto =
71 cronet::Java_CronetLibraryLoader_getBaseFeatureOverrides(env);
72 CHECK(serializedProto);
73
74 const auto serializedProtoSize =
75 base::android::SafeGetArrayLength(env, serializedProto);
76 ::org::chromium::net::httpflags::BaseFeatureOverrides overrides;
77 void* const serializedProtoArray =
78 env->GetPrimitiveArrayCritical(serializedProto.obj(), /*isCopy=*/nullptr);
79 CHECK(serializedProtoArray != nullptr);
80 CHECK(overrides.ParseFromArray(serializedProtoArray, serializedProtoSize));
81 env->ReleasePrimitiveArrayCritical(serializedProto.obj(),
82 serializedProtoArray, JNI_ABORT);
83 return overrides;
84 }
85
86 } // namespace
87
JNI_CronetLibraryLoader_NativeInit(JNIEnv * env)88 void JNI_CronetLibraryLoader_NativeInit(JNIEnv* env) {
89 // Cronet doesn't currently provide any way of using a custom command line
90 // (see https://crbug.com/1488393). For now, initialize an empty command line
91 // so that code attempting to use the command line doesn't crash.
92 static const char* const argv[] = {"cronet", nullptr};
93 base::CommandLine::Init(sizeof(argv) / sizeof(*argv) - 1, argv);
94
95 logging::InitLogging(logging::LoggingSettings());
96
97 #if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
98 base::i18n::InitializeICU();
99 #endif
100
101 ApplyBaseFeatureOverrides(GetBaseFeatureOverrides(env));
102
103 if (base::FeatureList::IsEnabled(kLogMe)) {
104 LOG(/* Bypass log spam warning regex */ INFO)
105 << "CronetLogMe feature flag set, logging as instructed. Message: "
106 << kLogMeMessage.Get();
107 }
108
109 if (!base::ThreadPoolInstance::Get())
110 base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Cronet");
111 }
112
OnInitThread()113 bool OnInitThread() {
114 DCHECK(g_init_task_executor);
115 return g_init_task_executor->task_runner()->RunsTasksInCurrentSequence();
116 }
117
118 // Checks the available version of JNI. Also, caches Java reflection artifacts.
CronetOnLoad(JavaVM * vm,void * reserved)119 jint CronetOnLoad(JavaVM* vm, void* reserved) {
120 base::android::InitVM(vm);
121 JNIEnv* env = base::android::AttachCurrentThread();
122 if (!RegisterNatives(env)) {
123 return -1;
124 }
125 if (!base::android::OnJNIOnLoadInit())
126 return -1;
127 return JNI_VERSION_1_6;
128 }
129
CronetOnUnLoad(JavaVM * jvm,void * reserved)130 void CronetOnUnLoad(JavaVM* jvm, void* reserved) {
131 if (base::ThreadPoolInstance::Get())
132 base::ThreadPoolInstance::Get()->Shutdown();
133
134 base::android::LibraryLoaderExitHook();
135 }
136
JNI_CronetLibraryLoader_CronetInitOnInitThread(JNIEnv * env)137 void JNI_CronetLibraryLoader_CronetInitOnInitThread(JNIEnv* env) {
138 // Initialize SingleThreadTaskExecutor for init thread.
139 DCHECK(!base::CurrentThread::IsSet());
140 DCHECK(!g_init_task_executor);
141 g_init_task_executor =
142 new base::SingleThreadTaskExecutor(base::MessagePumpType::JAVA);
143
144 DCHECK(!g_network_change_notifier);
145 if (!net::NetworkChangeNotifier::GetFactory()) {
146 net::NetworkChangeNotifier::SetFactory(
147 new net::NetworkChangeNotifierFactoryAndroid());
148 }
149 g_network_change_notifier = net::NetworkChangeNotifier::CreateIfNeeded();
150 DCHECK(g_network_change_notifier);
151
152 g_init_thread_init_done.Signal();
153 }
154
JNI_CronetLibraryLoader_GetCronetVersion(JNIEnv * env)155 ScopedJavaLocalRef<jstring> JNI_CronetLibraryLoader_GetCronetVersion(
156 JNIEnv* env) {
157 #if defined(ARCH_CPU_ARM64)
158 // Attempt to avoid crashes on some ARM64 Marshmallow devices by
159 // prompting zlib ARM feature detection early on. https://crbug.com/853725
160 if (base::android::BuildInfo::GetInstance()->sdk_int() ==
161 base::android::SDK_VERSION_MARSHMALLOW) {
162 crc32(0, Z_NULL, 0);
163 }
164 #endif
165 return base::android::ConvertUTF8ToJavaString(env, CRONET_VERSION);
166 }
167
JNI_CronetLibraryLoader_SetMinLogLevel(JNIEnv * env,jint jlog_level)168 void JNI_CronetLibraryLoader_SetMinLogLevel(JNIEnv* env, jint jlog_level) {
169 logging::SetMinLogLevel(jlog_level);
170 }
171
PostTaskToInitThread(const base::Location & posted_from,base::OnceClosure task)172 void PostTaskToInitThread(const base::Location& posted_from,
173 base::OnceClosure task) {
174 g_init_thread_init_done.Wait();
175 g_init_task_executor->task_runner()->PostTask(posted_from, std::move(task));
176 }
177
EnsureInitialized()178 void EnsureInitialized() {
179 if (g_init_task_executor) {
180 // Ensure that init is done on the init thread.
181 g_init_thread_init_done.Wait();
182 return;
183 }
184
185 // The initialization can only be done once, so static |s_run_once| variable
186 // is used to do it in the constructor.
187 static class RunOnce {
188 public:
189 RunOnce() {
190 JNIEnv* env = base::android::AttachCurrentThread();
191 // Ensure initialized from Java side to properly create Init thread.
192 cronet::Java_CronetLibraryLoader_ensureInitializedFromNative(env);
193 }
194 } s_run_once;
195 }
196
CreateProxyConfigService(const scoped_refptr<base::SequencedTaskRunner> & io_task_runner)197 std::unique_ptr<net::ProxyConfigService> CreateProxyConfigService(
198 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
199 // Note: CreateSystemProxyConfigService internally assumes that
200 // base::SingleThreadTaskRunner::GetCurrentDefault() == JNI communication
201 // thread.
202 std::unique_ptr<net::ProxyConfigService> service =
203 net::ProxyConfigService::CreateSystemProxyConfigService(io_task_runner);
204 // If a PAC URL is present, ignore it and use the address and port of
205 // Android system's local HTTP proxy server. See: crbug.com/432539.
206 // TODO(csharrison) Architect the wrapper better so we don't need to cast for
207 // android ProxyConfigServices.
208 net::ProxyConfigServiceAndroid* android_proxy_config_service =
209 static_cast<net::ProxyConfigServiceAndroid*>(service.get());
210 android_proxy_config_service->set_exclude_pac_url(true);
211 return service;
212 }
213
214 // Creates a proxy resolution service appropriate for this platform.
CreateProxyResolutionService(std::unique_ptr<net::ProxyConfigService> proxy_config_service,net::NetLog * net_log)215 std::unique_ptr<net::ProxyResolutionService> CreateProxyResolutionService(
216 std::unique_ptr<net::ProxyConfigService> proxy_config_service,
217 net::NetLog* net_log) {
218 // Android provides a local HTTP proxy server that handles proxying when a PAC
219 // URL is present. Create a proxy service without a resolver and rely on this
220 // local HTTP proxy. See: crbug.com/432539.
221 return net::ConfiguredProxyResolutionService::CreateWithoutProxyResolver(
222 std::move(proxy_config_service), net_log);
223 }
224
225 // Creates default User-Agent request value, combining optional
226 // |partial_user_agent| with system-dependent values.
CreateDefaultUserAgent(const std::string & partial_user_agent)227 std::string CreateDefaultUserAgent(const std::string& partial_user_agent) {
228 // Cronet global state must be initialized to include application info
229 // into default user agent
230 cronet::EnsureInitialized();
231
232 JNIEnv* env = base::android::AttachCurrentThread();
233 std::string user_agent = base::android::ConvertJavaStringToUTF8(
234 cronet::Java_CronetLibraryLoader_getDefaultUserAgent(env));
235 if (!partial_user_agent.empty())
236 user_agent.insert(user_agent.size() - 1, "; " + partial_user_agent);
237 return user_agent;
238 }
239
SetNetworkThreadPriorityOnNetworkThread(double priority)240 void SetNetworkThreadPriorityOnNetworkThread(double priority) {
241 int priority_int = priority;
242 DCHECK_LE(priority_int, 19);
243 DCHECK_GE(priority_int, -20);
244 if (priority_int >= -20 && priority_int <= 19) {
245 JNIEnv* env = base::android::AttachCurrentThread();
246 cronet::Java_CronetLibraryLoader_setNetworkThreadPriorityOnNetworkThread(
247 env, priority_int);
248 }
249 }
250
251 } // namespace cronet
252