Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / proxy / proxy_config_service_android.cc
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.
4
5 #include "net/proxy/proxy_config_service_android.h"
6
7 #include <sys/system_properties.h>
8
9 #include "base/android/jni_string.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/compiler_specific.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/observer_list.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "jni/ProxyChangeListener_jni.h"
23 #include "net/base/host_port_pair.h"
24 #include "net/proxy/proxy_config.h"
25 #include "url/url_parse.h"
26
27 using base::android::AttachCurrentThread;
28 using base::android::ConvertUTF8ToJavaString;
29 using base::android::ConvertJavaStringToUTF8;
30 using base::android::CheckException;
31 using base::android::ClearException;
32 using base::android::ScopedJavaGlobalRef;
33
34 namespace net {
35
36 namespace {
37
38 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback;
39
40 // Returns whether the provided string was successfully converted to a port.
41 bool ConvertStringToPort(const std::string& port, int* output) {
42   url::Component component(0, port.size());
43   int result = url::ParsePort(port.c_str(), component);
44   if (result == url::PORT_INVALID || result == url::PORT_UNSPECIFIED)
45     return false;
46   *output = result;
47   return true;
48 }
49
50 ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme,
51                                  const std::string& proxy_host,
52                                  const std::string& proxy_port) {
53   DCHECK(!proxy_host.empty());
54   int port_as_int = 0;
55   if (proxy_port.empty())
56     port_as_int = ProxyServer::GetDefaultPortForScheme(scheme);
57   else if (!ConvertStringToPort(proxy_port, &port_as_int))
58     return ProxyServer();
59   DCHECK(port_as_int > 0);
60   return ProxyServer(
61       scheme,
62       HostPortPair(proxy_host, static_cast<uint16>(port_as_int)));
63 }
64
65 ProxyServer LookupProxy(const std::string& prefix,
66                         const GetPropertyCallback& get_property,
67                         ProxyServer::Scheme scheme) {
68   DCHECK(!prefix.empty());
69   std::string proxy_host = get_property.Run(prefix + ".proxyHost");
70   if (!proxy_host.empty()) {
71     std::string proxy_port = get_property.Run(prefix + ".proxyPort");
72     return ConstructProxyServer(scheme, proxy_host, proxy_port);
73   }
74   // Fall back to default proxy, if any.
75   proxy_host = get_property.Run("proxyHost");
76   if (!proxy_host.empty()) {
77     std::string proxy_port = get_property.Run("proxyPort");
78     return ConstructProxyServer(scheme, proxy_host, proxy_port);
79   }
80   return ProxyServer();
81 }
82
83 ProxyServer LookupSocksProxy(const GetPropertyCallback& get_property) {
84   std::string proxy_host = get_property.Run("socksProxyHost");
85   if (!proxy_host.empty()) {
86     std::string proxy_port = get_property.Run("socksProxyPort");
87     return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxy_host,
88                                 proxy_port);
89   }
90   return ProxyServer();
91 }
92
93 void AddBypassRules(const std::string& scheme,
94                     const GetPropertyCallback& get_property,
95                     ProxyBypassRules* bypass_rules) {
96   // The format of a hostname pattern is a list of hostnames that are separated
97   // by | and that use * as a wildcard. For example, setting the
98   // http.nonProxyHosts property to *.android.com|*.kernel.org will cause
99   // requests to http://developer.android.com to be made without a proxy.
100   std::string non_proxy_hosts =
101       get_property.Run(scheme + ".nonProxyHosts");
102   if (non_proxy_hosts.empty())
103     return;
104   base::StringTokenizer tokenizer(non_proxy_hosts, "|");
105   while (tokenizer.GetNext()) {
106     std::string token = tokenizer.token();
107     std::string pattern;
108     base::TrimWhitespaceASCII(token, base::TRIM_ALL, &pattern);
109     if (pattern.empty())
110       continue;
111     // '?' is not one of the specified pattern characters above.
112     DCHECK_EQ(std::string::npos, pattern.find('?'));
113     bypass_rules->AddRuleForHostname(scheme, pattern, -1);
114   }
115 }
116
117 // Returns true if a valid proxy was found.
118 bool GetProxyRules(const GetPropertyCallback& get_property,
119                    ProxyConfig::ProxyRules* rules) {
120   // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the
121   // mostly equivalent Android implementation.  There is one intentional
122   // difference: by default Chromium uses the HTTP port (80) for HTTPS
123   // connections via proxy.  This default is identical on other platforms.
124   // On the opposite, Java spec suggests to use HTTPS port (443) by default (the
125   // default value of https.proxyPort).
126   rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
127   rules->proxies_for_http.SetSingleProxyServer(
128       LookupProxy("http", get_property, ProxyServer::SCHEME_HTTP));
129   rules->proxies_for_https.SetSingleProxyServer(
130       LookupProxy("https", get_property, ProxyServer::SCHEME_HTTP));
131   rules->proxies_for_ftp.SetSingleProxyServer(
132       LookupProxy("ftp", get_property, ProxyServer::SCHEME_HTTP));
133   rules->fallback_proxies.SetSingleProxyServer(LookupSocksProxy(get_property));
134   rules->bypass_rules.Clear();
135   AddBypassRules("ftp", get_property, &rules->bypass_rules);
136   AddBypassRules("http", get_property, &rules->bypass_rules);
137   AddBypassRules("https", get_property, &rules->bypass_rules);
138   // We know a proxy was found if not all of the proxy lists are empty.
139   return !(rules->proxies_for_http.IsEmpty() &&
140       rules->proxies_for_https.IsEmpty() &&
141       rules->proxies_for_ftp.IsEmpty() &&
142       rules->fallback_proxies.IsEmpty());
143 };
144
145 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
146                                   ProxyConfig* config) {
147   if (!GetProxyRules(get_property, &config->proxy_rules()))
148     *config = ProxyConfig::CreateDirect();
149 }
150
151 std::string GetJavaProperty(const std::string& property) {
152   // Use Java System.getProperty to get configuration information.
153   // TODO(pliard): Conversion to/from UTF8 ok here?
154   JNIEnv* env = AttachCurrentThread();
155   ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
156   ScopedJavaLocalRef<jstring> result =
157       Java_ProxyChangeListener_getProperty(env, str.obj());
158   return result.is_null() ?
159       std::string() : ConvertJavaStringToUTF8(env, result.obj());
160 }
161
162 void CreateStaticProxyConfig(const std::string& host,
163                              int port,
164                              const std::string& pac_url,
165                              ProxyConfig* config) {
166   if (!pac_url.empty()) {
167     config->set_pac_url(GURL(pac_url));
168     config->set_pac_mandatory(false);
169   } else if (port != 0) {
170     std::string rules = base::StringPrintf("%s:%d", host.c_str(), port);
171     config->proxy_rules().ParseFromString(rules);
172   } else {
173     *config = ProxyConfig::CreateDirect();
174   }
175 }
176
177 }  // namespace
178
179 class ProxyConfigServiceAndroid::Delegate
180     : public base::RefCountedThreadSafe<Delegate> {
181  public:
182   Delegate(const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
183            const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
184            const GetPropertyCallback& get_property_callback)
185       : jni_delegate_(this),
186         network_task_runner_(network_task_runner),
187         jni_task_runner_(jni_task_runner),
188         get_property_callback_(get_property_callback) {
189   }
190
191   void SetupJNI() {
192     DCHECK(OnJNIThread());
193     JNIEnv* env = AttachCurrentThread();
194     if (java_proxy_change_listener_.is_null()) {
195       java_proxy_change_listener_.Reset(
196           Java_ProxyChangeListener_create(
197               env, base::android::GetApplicationContext()));
198       CHECK(!java_proxy_change_listener_.is_null());
199     }
200     Java_ProxyChangeListener_start(
201         env,
202         java_proxy_change_listener_.obj(),
203         reinterpret_cast<intptr_t>(&jni_delegate_));
204   }
205
206   void FetchInitialConfig() {
207     DCHECK(OnJNIThread());
208     ProxyConfig proxy_config;
209     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
210     network_task_runner_->PostTask(
211         FROM_HERE,
212         base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
213   }
214
215   void Shutdown() {
216     if (OnJNIThread()) {
217       ShutdownOnJNIThread();
218     } else {
219       jni_task_runner_->PostTask(
220           FROM_HERE,
221           base::Bind(&Delegate::ShutdownOnJNIThread, this));
222     }
223   }
224
225   // Called only on the network thread.
226   void AddObserver(Observer* observer) {
227     DCHECK(OnNetworkThread());
228     observers_.AddObserver(observer);
229   }
230
231   void RemoveObserver(Observer* observer) {
232     DCHECK(OnNetworkThread());
233     observers_.RemoveObserver(observer);
234   }
235
236   ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
237     DCHECK(OnNetworkThread());
238     if (!config)
239       return ProxyConfigService::CONFIG_UNSET;
240     *config = proxy_config_;
241     return ProxyConfigService::CONFIG_VALID;
242   }
243
244   // Called on the JNI thread.
245   void ProxySettingsChanged() {
246     DCHECK(OnJNIThread());
247     ProxyConfig proxy_config;
248     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
249     network_task_runner_->PostTask(
250         FROM_HERE,
251         base::Bind(
252             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
253   }
254
255   // Called on the JNI thread.
256   void ProxySettingsChangedTo(const std::string& host,
257                               int port,
258                               const std::string& pac_url) {
259     DCHECK(OnJNIThread());
260     ProxyConfig proxy_config;
261     CreateStaticProxyConfig(host, port, pac_url, &proxy_config);
262     network_task_runner_->PostTask(
263         FROM_HERE,
264         base::Bind(
265             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
266   }
267
268  private:
269   friend class base::RefCountedThreadSafe<Delegate>;
270
271   class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate {
272    public:
273     explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
274
275     // ProxyConfigServiceAndroid::JNIDelegate overrides.
276     virtual void ProxySettingsChangedTo(JNIEnv* env,
277                                         jobject jself,
278                                         jstring jhost,
279                                         jint jport,
280                                         jstring jpac_url) OVERRIDE {
281       std::string host = ConvertJavaStringToUTF8(env, jhost);
282       std::string pac_url;
283       if (jpac_url)
284         ConvertJavaStringToUTF8(env, jpac_url, &pac_url);
285       delegate_->ProxySettingsChangedTo(host, jport, pac_url);
286     }
287
288     virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
289       delegate_->ProxySettingsChanged();
290     }
291
292    private:
293     Delegate* const delegate_;
294   };
295
296   virtual ~Delegate() {}
297
298   void ShutdownOnJNIThread() {
299     if (java_proxy_change_listener_.is_null())
300       return;
301     JNIEnv* env = AttachCurrentThread();
302     Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj());
303   }
304
305   // Called on the network thread.
306   void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) {
307     DCHECK(OnNetworkThread());
308     proxy_config_ = proxy_config;
309     FOR_EACH_OBSERVER(Observer, observers_,
310                       OnProxyConfigChanged(proxy_config,
311                                            ProxyConfigService::CONFIG_VALID));
312   }
313
314   bool OnJNIThread() const {
315     return jni_task_runner_->RunsTasksOnCurrentThread();
316   }
317
318   bool OnNetworkThread() const {
319     return network_task_runner_->RunsTasksOnCurrentThread();
320   }
321
322   ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
323
324   JNIDelegateImpl jni_delegate_;
325   ObserverList<Observer> observers_;
326   scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
327   scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
328   GetPropertyCallback get_property_callback_;
329   ProxyConfig proxy_config_;
330
331   DISALLOW_COPY_AND_ASSIGN(Delegate);
332 };
333
334 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
335     const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
336     const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner)
337     : delegate_(new Delegate(
338         network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
339   delegate_->SetupJNI();
340   delegate_->FetchInitialConfig();
341 }
342
343 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
344   delegate_->Shutdown();
345 }
346
347 // static
348 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
349   return RegisterNativesImpl(env);
350 }
351
352 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
353   delegate_->AddObserver(observer);
354 }
355
356 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
357   delegate_->RemoveObserver(observer);
358 }
359
360 ProxyConfigService::ConfigAvailability
361 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
362   return delegate_->GetLatestProxyConfig(config);
363 }
364
365 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
366     const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
367     const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
368     GetPropertyCallback get_property_callback)
369     : delegate_(new Delegate(
370         network_task_runner, jni_task_runner, get_property_callback)) {
371   delegate_->SetupJNI();
372   delegate_->FetchInitialConfig();
373 }
374
375 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
376   delegate_->ProxySettingsChanged();
377 }
378
379 } // namespace net