Upstream version 7.36.149.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, int port,
163                              ProxyConfig* config) {
164   if (port != 0) {
165     std::string rules = base::StringPrintf("%s:%d", host.c_str(), port);
166     config->proxy_rules().ParseFromString(rules);
167   } else {
168     *config = ProxyConfig::CreateDirect();
169   }
170 }
171
172 }  // namespace
173
174 class ProxyConfigServiceAndroid::Delegate
175     : public base::RefCountedThreadSafe<Delegate> {
176  public:
177   Delegate(base::SequencedTaskRunner* network_task_runner,
178            base::SequencedTaskRunner* jni_task_runner,
179            const GetPropertyCallback& get_property_callback)
180       : jni_delegate_(this),
181         network_task_runner_(network_task_runner),
182         jni_task_runner_(jni_task_runner),
183         get_property_callback_(get_property_callback) {
184   }
185
186   void SetupJNI() {
187     DCHECK(OnJNIThread());
188     JNIEnv* env = AttachCurrentThread();
189     if (java_proxy_change_listener_.is_null()) {
190       java_proxy_change_listener_.Reset(
191           Java_ProxyChangeListener_create(
192               env, base::android::GetApplicationContext()));
193       CHECK(!java_proxy_change_listener_.is_null());
194     }
195     Java_ProxyChangeListener_start(
196         env,
197         java_proxy_change_listener_.obj(),
198         reinterpret_cast<intptr_t>(&jni_delegate_));
199   }
200
201   void FetchInitialConfig() {
202     DCHECK(OnJNIThread());
203     ProxyConfig proxy_config;
204     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
205     network_task_runner_->PostTask(
206         FROM_HERE,
207         base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
208   }
209
210   void Shutdown() {
211     if (OnJNIThread()) {
212       ShutdownOnJNIThread();
213     } else {
214       jni_task_runner_->PostTask(
215           FROM_HERE,
216           base::Bind(&Delegate::ShutdownOnJNIThread, this));
217     }
218   }
219
220   // Called only on the network thread.
221   void AddObserver(Observer* observer) {
222     DCHECK(OnNetworkThread());
223     observers_.AddObserver(observer);
224   }
225
226   void RemoveObserver(Observer* observer) {
227     DCHECK(OnNetworkThread());
228     observers_.RemoveObserver(observer);
229   }
230
231   ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
232     DCHECK(OnNetworkThread());
233     if (!config)
234       return ProxyConfigService::CONFIG_UNSET;
235     *config = proxy_config_;
236     return ProxyConfigService::CONFIG_VALID;
237   }
238
239   // Called on the JNI thread.
240   void ProxySettingsChanged() {
241     DCHECK(OnJNIThread());
242     ProxyConfig proxy_config;
243     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
244     network_task_runner_->PostTask(
245         FROM_HERE,
246         base::Bind(
247             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
248   }
249
250   // Called on the JNI thread.
251   void ProxySettingsChangedTo(const std::string& host, int port) {
252     DCHECK(OnJNIThread());
253     ProxyConfig proxy_config;
254     CreateStaticProxyConfig(host, port, &proxy_config);
255     network_task_runner_->PostTask(
256         FROM_HERE,
257         base::Bind(
258             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
259   }
260
261  private:
262   friend class base::RefCountedThreadSafe<Delegate>;
263
264   class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate {
265    public:
266     explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
267
268     // ProxyConfigServiceAndroid::JNIDelegate overrides.
269     virtual void ProxySettingsChangedTo(JNIEnv* env, jobject jself,
270                                       jstring jhost, jint jport) OVERRIDE {
271       std::string host = ConvertJavaStringToUTF8(env, jhost);
272       delegate_->ProxySettingsChangedTo(host, jport);
273     }
274
275     virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
276       delegate_->ProxySettingsChanged();
277     }
278
279    private:
280     Delegate* const delegate_;
281   };
282
283   virtual ~Delegate() {}
284
285   void ShutdownOnJNIThread() {
286     if (java_proxy_change_listener_.is_null())
287       return;
288     JNIEnv* env = AttachCurrentThread();
289     Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj());
290   }
291
292   // Called on the network thread.
293   void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) {
294     DCHECK(OnNetworkThread());
295     proxy_config_ = proxy_config;
296     FOR_EACH_OBSERVER(Observer, observers_,
297                       OnProxyConfigChanged(proxy_config,
298                                            ProxyConfigService::CONFIG_VALID));
299   }
300
301   bool OnJNIThread() const {
302     return jni_task_runner_->RunsTasksOnCurrentThread();
303   }
304
305   bool OnNetworkThread() const {
306     return network_task_runner_->RunsTasksOnCurrentThread();
307   }
308
309   ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
310
311   JNIDelegateImpl jni_delegate_;
312   ObserverList<Observer> observers_;
313   scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
314   scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
315   GetPropertyCallback get_property_callback_;
316   ProxyConfig proxy_config_;
317
318   DISALLOW_COPY_AND_ASSIGN(Delegate);
319 };
320
321 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
322     base::SequencedTaskRunner* network_task_runner,
323     base::SequencedTaskRunner* jni_task_runner)
324     : delegate_(new Delegate(
325         network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
326   delegate_->SetupJNI();
327   delegate_->FetchInitialConfig();
328 }
329
330 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
331   delegate_->Shutdown();
332 }
333
334 // static
335 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
336   return RegisterNativesImpl(env);
337 }
338
339 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
340   delegate_->AddObserver(observer);
341 }
342
343 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
344   delegate_->RemoveObserver(observer);
345 }
346
347 ProxyConfigService::ConfigAvailability
348 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
349   return delegate_->GetLatestProxyConfig(config);
350 }
351
352 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
353     base::SequencedTaskRunner* network_task_runner,
354     base::SequencedTaskRunner* jni_task_runner,
355     GetPropertyCallback get_property_callback)
356     : delegate_(new Delegate(
357         network_task_runner, jni_task_runner, get_property_callback)) {
358   delegate_->SetupJNI();
359   delegate_->FetchInitialConfig();
360 }
361
362 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
363   delegate_->ProxySettingsChanged();
364 }
365
366 } // namespace net