1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/proxy/proxy_config_service_android.h"
7 #include <sys/system_properties.h>
9 #include "base/android/jni_array.h"
10 #include "base/android/jni_string.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/compiler_specific.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/observer_list.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/strings/string_tokenizer.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "jni/ProxyChangeListener_jni.h"
24 #include "net/base/host_port_pair.h"
25 #include "net/proxy/proxy_config.h"
26 #include "url/third_party/mozilla/url_parse.h"
28 using base::android::AttachCurrentThread
;
29 using base::android::ConvertUTF8ToJavaString
;
30 using base::android::ConvertJavaStringToUTF8
;
31 using base::android::CheckException
;
32 using base::android::ClearException
;
33 using base::android::ScopedJavaGlobalRef
;
39 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback
;
41 // Returns whether the provided string was successfully converted to a port.
42 bool ConvertStringToPort(const std::string
& port
, int* output
) {
43 url::Component
component(0, port
.size());
44 int result
= url::ParsePort(port
.c_str(), component
);
45 if (result
== url::PORT_INVALID
|| result
== url::PORT_UNSPECIFIED
)
51 ProxyServer
ConstructProxyServer(ProxyServer::Scheme scheme
,
52 const std::string
& proxy_host
,
53 const std::string
& proxy_port
) {
54 DCHECK(!proxy_host
.empty());
56 if (proxy_port
.empty())
57 port_as_int
= ProxyServer::GetDefaultPortForScheme(scheme
);
58 else if (!ConvertStringToPort(proxy_port
, &port_as_int
))
60 DCHECK(port_as_int
> 0);
63 HostPortPair(proxy_host
, static_cast<uint16
>(port_as_int
)));
66 ProxyServer
LookupProxy(const std::string
& prefix
,
67 const GetPropertyCallback
& get_property
,
68 ProxyServer::Scheme scheme
) {
69 DCHECK(!prefix
.empty());
70 std::string proxy_host
= get_property
.Run(prefix
+ ".proxyHost");
71 if (!proxy_host
.empty()) {
72 std::string proxy_port
= get_property
.Run(prefix
+ ".proxyPort");
73 return ConstructProxyServer(scheme
, proxy_host
, proxy_port
);
75 // Fall back to default proxy, if any.
76 proxy_host
= get_property
.Run("proxyHost");
77 if (!proxy_host
.empty()) {
78 std::string proxy_port
= get_property
.Run("proxyPort");
79 return ConstructProxyServer(scheme
, proxy_host
, proxy_port
);
84 ProxyServer
LookupSocksProxy(const GetPropertyCallback
& get_property
) {
85 std::string proxy_host
= get_property
.Run("socksProxyHost");
86 if (!proxy_host
.empty()) {
87 std::string proxy_port
= get_property
.Run("socksProxyPort");
88 return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5
, proxy_host
,
94 void AddBypassRules(const std::string
& scheme
,
95 const GetPropertyCallback
& get_property
,
96 ProxyBypassRules
* bypass_rules
) {
97 // The format of a hostname pattern is a list of hostnames that are separated
98 // by | and that use * as a wildcard. For example, setting the
99 // http.nonProxyHosts property to *.android.com|*.kernel.org will cause
100 // requests to http://developer.android.com to be made without a proxy.
102 // Force localhost to be on the proxy exclusion list;
103 // otherwise all localhost traffic is routed through
104 // the proxy which is not desired.
105 bypass_rules
->AddRuleToBypassLocal();
107 std::string non_proxy_hosts
=
108 get_property
.Run(scheme
+ ".nonProxyHosts");
109 if (non_proxy_hosts
.empty())
111 base::StringTokenizer
tokenizer(non_proxy_hosts
, "|");
112 while (tokenizer
.GetNext()) {
113 std::string token
= tokenizer
.token();
115 base::TrimWhitespaceASCII(token
, base::TRIM_ALL
, &pattern
);
118 // '?' is not one of the specified pattern characters above.
119 DCHECK_EQ(std::string::npos
, pattern
.find('?'));
120 bypass_rules
->AddRuleForHostname(scheme
, pattern
, -1);
124 // Returns true if a valid proxy was found.
125 bool GetProxyRules(const GetPropertyCallback
& get_property
,
126 ProxyConfig::ProxyRules
* rules
) {
127 // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the
128 // mostly equivalent Android implementation. There is one intentional
129 // difference: by default Chromium uses the HTTP port (80) for HTTPS
130 // connections via proxy. This default is identical on other platforms.
131 // On the opposite, Java spec suggests to use HTTPS port (443) by default (the
132 // default value of https.proxyPort).
133 rules
->type
= ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME
;
134 rules
->proxies_for_http
.SetSingleProxyServer(
135 LookupProxy("http", get_property
, ProxyServer::SCHEME_HTTP
));
136 rules
->proxies_for_https
.SetSingleProxyServer(
137 LookupProxy("https", get_property
, ProxyServer::SCHEME_HTTP
));
138 rules
->proxies_for_ftp
.SetSingleProxyServer(
139 LookupProxy("ftp", get_property
, ProxyServer::SCHEME_HTTP
));
140 rules
->fallback_proxies
.SetSingleProxyServer(LookupSocksProxy(get_property
));
141 rules
->bypass_rules
.Clear();
142 AddBypassRules("ftp", get_property
, &rules
->bypass_rules
);
143 AddBypassRules("http", get_property
, &rules
->bypass_rules
);
144 AddBypassRules("https", get_property
, &rules
->bypass_rules
);
145 // We know a proxy was found if not all of the proxy lists are empty.
146 return !(rules
->proxies_for_http
.IsEmpty() &&
147 rules
->proxies_for_https
.IsEmpty() &&
148 rules
->proxies_for_ftp
.IsEmpty() &&
149 rules
->fallback_proxies
.IsEmpty());
152 void GetLatestProxyConfigInternal(const GetPropertyCallback
& get_property
,
153 ProxyConfig
* config
) {
154 if (!GetProxyRules(get_property
, &config
->proxy_rules()))
155 *config
= ProxyConfig::CreateDirect();
158 std::string
GetJavaProperty(const std::string
& property
) {
159 // Use Java System.getProperty to get configuration information.
160 // TODO(pliard): Conversion to/from UTF8 ok here?
161 JNIEnv
* env
= AttachCurrentThread();
162 ScopedJavaLocalRef
<jstring
> str
= ConvertUTF8ToJavaString(env
, property
);
163 ScopedJavaLocalRef
<jstring
> result
=
164 Java_ProxyChangeListener_getProperty(env
, str
.obj());
165 return result
.is_null() ?
166 std::string() : ConvertJavaStringToUTF8(env
, result
.obj());
169 void CreateStaticProxyConfig(const std::string
& host
,
171 const std::string
& pac_url
,
172 const std::vector
<std::string
>& exclusion_list
,
173 ProxyConfig
* config
) {
174 if (!pac_url
.empty()) {
175 config
->set_pac_url(GURL(pac_url
));
176 config
->set_pac_mandatory(false);
177 } else if (port
!= 0) {
178 std::string rules
= base::StringPrintf("%s:%d", host
.c_str(), port
);
179 config
->proxy_rules().ParseFromString(rules
);
180 config
->proxy_rules().bypass_rules
.Clear();
182 std::vector
<std::string
>::const_iterator it
;
183 for (it
= exclusion_list
.begin(); it
!= exclusion_list
.end(); ++it
) {
185 base::TrimWhitespaceASCII(*it
, base::TRIM_ALL
, &pattern
);
188 config
->proxy_rules().bypass_rules
.AddRuleForHostname("", pattern
, -1);
191 *config
= ProxyConfig::CreateDirect();
197 class ProxyConfigServiceAndroid::Delegate
198 : public base::RefCountedThreadSafe
<Delegate
> {
200 Delegate(const scoped_refptr
<base::SequencedTaskRunner
>& network_task_runner
,
201 const scoped_refptr
<base::SequencedTaskRunner
>& jni_task_runner
,
202 const GetPropertyCallback
& get_property_callback
)
203 : jni_delegate_(this),
204 network_task_runner_(network_task_runner
),
205 jni_task_runner_(jni_task_runner
),
206 get_property_callback_(get_property_callback
),
207 exclude_pac_url_(false) {
211 DCHECK(OnJNIThread());
212 JNIEnv
* env
= AttachCurrentThread();
213 if (java_proxy_change_listener_
.is_null()) {
214 java_proxy_change_listener_
.Reset(
215 Java_ProxyChangeListener_create(
216 env
, base::android::GetApplicationContext()));
217 CHECK(!java_proxy_change_listener_
.is_null());
219 Java_ProxyChangeListener_start(
221 java_proxy_change_listener_
.obj(),
222 reinterpret_cast<intptr_t>(&jni_delegate_
));
225 void FetchInitialConfig() {
226 DCHECK(OnJNIThread());
227 ProxyConfig proxy_config
;
228 GetLatestProxyConfigInternal(get_property_callback_
, &proxy_config
);
229 network_task_runner_
->PostTask(
231 base::Bind(&Delegate::SetNewConfigOnNetworkThread
, this, proxy_config
));
236 ShutdownOnJNIThread();
238 jni_task_runner_
->PostTask(
240 base::Bind(&Delegate::ShutdownOnJNIThread
, this));
244 // Called only on the network thread.
245 void AddObserver(Observer
* observer
) {
246 DCHECK(OnNetworkThread());
247 observers_
.AddObserver(observer
);
250 void RemoveObserver(Observer
* observer
) {
251 DCHECK(OnNetworkThread());
252 observers_
.RemoveObserver(observer
);
255 ConfigAvailability
GetLatestProxyConfig(ProxyConfig
* config
) {
256 DCHECK(OnNetworkThread());
258 return ProxyConfigService::CONFIG_UNSET
;
259 *config
= proxy_config_
;
260 return ProxyConfigService::CONFIG_VALID
;
263 // Called on the JNI thread.
264 void ProxySettingsChanged() {
265 DCHECK(OnJNIThread());
266 ProxyConfig proxy_config
;
267 GetLatestProxyConfigInternal(get_property_callback_
, &proxy_config
);
268 network_task_runner_
->PostTask(
271 &Delegate::SetNewConfigOnNetworkThread
, this, proxy_config
));
274 // Called on the JNI thread.
275 void ProxySettingsChangedTo(const std::string
& host
,
277 const std::string
& pac_url
,
278 const std::vector
<std::string
>& exclusion_list
) {
279 DCHECK(OnJNIThread());
280 ProxyConfig proxy_config
;
281 if (exclude_pac_url_
) {
282 CreateStaticProxyConfig(host
, port
, "", exclusion_list
, &proxy_config
);
284 CreateStaticProxyConfig(host
, port
, pac_url
, exclusion_list
,
287 network_task_runner_
->PostTask(
290 &Delegate::SetNewConfigOnNetworkThread
, this, proxy_config
));
293 void set_exclude_pac_url(bool enabled
) {
294 exclude_pac_url_
= enabled
;
298 friend class base::RefCountedThreadSafe
<Delegate
>;
300 class JNIDelegateImpl
: public ProxyConfigServiceAndroid::JNIDelegate
{
302 explicit JNIDelegateImpl(Delegate
* delegate
) : delegate_(delegate
) {}
304 // ProxyConfigServiceAndroid::JNIDelegate overrides.
305 void ProxySettingsChangedTo(JNIEnv
* env
,
310 jobjectArray jexclusion_list
) override
{
311 std::string host
= ConvertJavaStringToUTF8(env
, jhost
);
314 ConvertJavaStringToUTF8(env
, jpac_url
, &pac_url
);
315 std::vector
<std::string
> exclusion_list
;
316 base::android::AppendJavaStringArrayToStringVector(
317 env
, jexclusion_list
, &exclusion_list
);
318 delegate_
->ProxySettingsChangedTo(host
, jport
, pac_url
, exclusion_list
);
321 void ProxySettingsChanged(JNIEnv
* env
, jobject self
) override
{
322 delegate_
->ProxySettingsChanged();
326 Delegate
* const delegate_
;
329 virtual ~Delegate() {}
331 void ShutdownOnJNIThread() {
332 if (java_proxy_change_listener_
.is_null())
334 JNIEnv
* env
= AttachCurrentThread();
335 Java_ProxyChangeListener_stop(env
, java_proxy_change_listener_
.obj());
338 // Called on the network thread.
339 void SetNewConfigOnNetworkThread(const ProxyConfig
& proxy_config
) {
340 DCHECK(OnNetworkThread());
341 proxy_config_
= proxy_config
;
342 FOR_EACH_OBSERVER(Observer
, observers_
,
343 OnProxyConfigChanged(proxy_config
,
344 ProxyConfigService::CONFIG_VALID
));
347 bool OnJNIThread() const {
348 return jni_task_runner_
->RunsTasksOnCurrentThread();
351 bool OnNetworkThread() const {
352 return network_task_runner_
->RunsTasksOnCurrentThread();
355 ScopedJavaGlobalRef
<jobject
> java_proxy_change_listener_
;
357 JNIDelegateImpl jni_delegate_
;
358 ObserverList
<Observer
> observers_
;
359 scoped_refptr
<base::SequencedTaskRunner
> network_task_runner_
;
360 scoped_refptr
<base::SequencedTaskRunner
> jni_task_runner_
;
361 GetPropertyCallback get_property_callback_
;
362 ProxyConfig proxy_config_
;
363 bool exclude_pac_url_
;
365 DISALLOW_COPY_AND_ASSIGN(Delegate
);
368 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
369 const scoped_refptr
<base::SequencedTaskRunner
>& network_task_runner
,
370 const scoped_refptr
<base::SequencedTaskRunner
>& jni_task_runner
)
371 : delegate_(new Delegate(
372 network_task_runner
, jni_task_runner
, base::Bind(&GetJavaProperty
))) {
373 delegate_
->SetupJNI();
374 delegate_
->FetchInitialConfig();
377 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
378 delegate_
->Shutdown();
382 bool ProxyConfigServiceAndroid::Register(JNIEnv
* env
) {
383 return RegisterNativesImpl(env
);
386 void ProxyConfigServiceAndroid::set_exclude_pac_url(bool enabled
) {
387 delegate_
->set_exclude_pac_url(enabled
);
390 void ProxyConfigServiceAndroid::AddObserver(Observer
* observer
) {
391 delegate_
->AddObserver(observer
);
394 void ProxyConfigServiceAndroid::RemoveObserver(Observer
* observer
) {
395 delegate_
->RemoveObserver(observer
);
398 ProxyConfigService::ConfigAvailability
399 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig
* config
) {
400 return delegate_
->GetLatestProxyConfig(config
);
403 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
404 const scoped_refptr
<base::SequencedTaskRunner
>& network_task_runner
,
405 const scoped_refptr
<base::SequencedTaskRunner
>& jni_task_runner
,
406 GetPropertyCallback get_property_callback
)
407 : delegate_(new Delegate(
408 network_task_runner
, jni_task_runner
, get_property_callback
)) {
409 delegate_
->SetupJNI();
410 delegate_
->FetchInitialConfig();
413 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
414 delegate_
->ProxySettingsChanged();