• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ConvertStringToPort(const std::string & port,int * output)41 bool ConvertStringToPort(const std::string& port, int* output) {
42   url_parse::Component component(0, port.size());
43   int result = url_parse::ParsePort(port.c_str(), component);
44   if (result == url_parse::PORT_INVALID ||
45       result == url_parse::PORT_UNSPECIFIED)
46     return false;
47   *output = result;
48   return true;
49 }
50 
ConstructProxyServer(ProxyServer::Scheme scheme,const std::string & proxy_host,const std::string & proxy_port)51 ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme,
52                                  const std::string& proxy_host,
53                                  const std::string& proxy_port) {
54   DCHECK(!proxy_host.empty());
55   int port_as_int = 0;
56   if (proxy_port.empty())
57     port_as_int = ProxyServer::GetDefaultPortForScheme(scheme);
58   else if (!ConvertStringToPort(proxy_port, &port_as_int))
59     return ProxyServer();
60   DCHECK(port_as_int > 0);
61   return ProxyServer(
62       scheme,
63       HostPortPair(proxy_host, static_cast<uint16>(port_as_int)));
64 }
65 
LookupProxy(const std::string & prefix,const GetPropertyCallback & get_property,ProxyServer::Scheme scheme)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);
74   }
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);
80   }
81   return ProxyServer();
82 }
83 
LookupSocksProxy(const GetPropertyCallback & get_property)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,
89                                 proxy_port);
90   }
91   return ProxyServer();
92 }
93 
AddBypassRules(const std::string & scheme,const GetPropertyCallback & get_property,ProxyBypassRules * bypass_rules)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.
101   std::string non_proxy_hosts =
102       get_property.Run(scheme + ".nonProxyHosts");
103   if (non_proxy_hosts.empty())
104     return;
105   base::StringTokenizer tokenizer(non_proxy_hosts, "|");
106   while (tokenizer.GetNext()) {
107     std::string token = tokenizer.token();
108     std::string pattern;
109     TrimWhitespaceASCII(token, TRIM_ALL, &pattern);
110     if (pattern.empty())
111       continue;
112     // '?' is not one of the specified pattern characters above.
113     DCHECK_EQ(std::string::npos, pattern.find('?'));
114     bypass_rules->AddRuleForHostname(scheme, pattern, -1);
115   }
116 }
117 
118 // Returns true if a valid proxy was found.
GetProxyRules(const GetPropertyCallback & get_property,ProxyConfig::ProxyRules * rules)119 bool GetProxyRules(const GetPropertyCallback& get_property,
120                    ProxyConfig::ProxyRules* rules) {
121   // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the
122   // mostly equivalent Android implementation.  There is one intentional
123   // difference: by default Chromium uses the HTTP port (80) for HTTPS
124   // connections via proxy.  This default is identical on other platforms.
125   // On the opposite, Java spec suggests to use HTTPS port (443) by default (the
126   // default value of https.proxyPort).
127   rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
128   rules->proxies_for_http.SetSingleProxyServer(
129       LookupProxy("http", get_property, ProxyServer::SCHEME_HTTP));
130   rules->proxies_for_https.SetSingleProxyServer(
131       LookupProxy("https", get_property, ProxyServer::SCHEME_HTTP));
132   rules->proxies_for_ftp.SetSingleProxyServer(
133       LookupProxy("ftp", get_property, ProxyServer::SCHEME_HTTP));
134   rules->fallback_proxies.SetSingleProxyServer(LookupSocksProxy(get_property));
135   rules->bypass_rules.Clear();
136   AddBypassRules("ftp", get_property, &rules->bypass_rules);
137   AddBypassRules("http", get_property, &rules->bypass_rules);
138   AddBypassRules("https", get_property, &rules->bypass_rules);
139   // We know a proxy was found if not all of the proxy lists are empty.
140   return !(rules->proxies_for_http.IsEmpty() &&
141       rules->proxies_for_https.IsEmpty() &&
142       rules->proxies_for_ftp.IsEmpty() &&
143       rules->fallback_proxies.IsEmpty());
144 };
145 
GetLatestProxyConfigInternal(const GetPropertyCallback & get_property,ProxyConfig * config)146 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
147                                   ProxyConfig* config) {
148   if (!GetProxyRules(get_property, &config->proxy_rules()))
149     *config = ProxyConfig::CreateDirect();
150 }
151 
GetJavaProperty(const std::string & property)152 std::string GetJavaProperty(const std::string& property) {
153   // Use Java System.getProperty to get configuration information.
154   // TODO(pliard): Conversion to/from UTF8 ok here?
155   JNIEnv* env = AttachCurrentThread();
156   ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
157   ScopedJavaLocalRef<jstring> result =
158       Java_ProxyChangeListener_getProperty(env, str.obj());
159   return result.is_null() ?
160       std::string() : ConvertJavaStringToUTF8(env, result.obj());
161 }
162 
CreateStaticProxyConfig(const std::string & host,int port,ProxyConfig * config)163 void CreateStaticProxyConfig(const std::string& host, int port,
164                              ProxyConfig* config) {
165   if (port != 0) {
166     std::string rules = base::StringPrintf("%s:%d", host.c_str(), port);
167     config->proxy_rules().ParseFromString(rules);
168   } else {
169     *config = ProxyConfig::CreateDirect();
170   }
171 }
172 
173 }  // namespace
174 
175 class ProxyConfigServiceAndroid::Delegate
176     : public base::RefCountedThreadSafe<Delegate> {
177  public:
Delegate(base::SequencedTaskRunner * network_task_runner,base::SequencedTaskRunner * jni_task_runner,const GetPropertyCallback & get_property_callback)178   Delegate(base::SequencedTaskRunner* network_task_runner,
179            base::SequencedTaskRunner* jni_task_runner,
180            const GetPropertyCallback& get_property_callback)
181       : jni_delegate_(this),
182         network_task_runner_(network_task_runner),
183         jni_task_runner_(jni_task_runner),
184         get_property_callback_(get_property_callback) {
185   }
186 
SetupJNI()187   void SetupJNI() {
188     DCHECK(OnJNIThread());
189     JNIEnv* env = AttachCurrentThread();
190     if (java_proxy_change_listener_.is_null()) {
191       java_proxy_change_listener_.Reset(
192           Java_ProxyChangeListener_create(
193               env, base::android::GetApplicationContext()));
194       CHECK(!java_proxy_change_listener_.is_null());
195     }
196     Java_ProxyChangeListener_start(
197         env,
198         java_proxy_change_listener_.obj(),
199         reinterpret_cast<intptr_t>(&jni_delegate_));
200   }
201 
FetchInitialConfig()202   void FetchInitialConfig() {
203     DCHECK(OnJNIThread());
204     ProxyConfig proxy_config;
205     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
206     network_task_runner_->PostTask(
207         FROM_HERE,
208         base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
209   }
210 
Shutdown()211   void Shutdown() {
212     if (OnJNIThread()) {
213       ShutdownOnJNIThread();
214     } else {
215       jni_task_runner_->PostTask(
216           FROM_HERE,
217           base::Bind(&Delegate::ShutdownOnJNIThread, this));
218     }
219   }
220 
221   // Called only on the network thread.
AddObserver(Observer * observer)222   void AddObserver(Observer* observer) {
223     DCHECK(OnNetworkThread());
224     observers_.AddObserver(observer);
225   }
226 
RemoveObserver(Observer * observer)227   void RemoveObserver(Observer* observer) {
228     DCHECK(OnNetworkThread());
229     observers_.RemoveObserver(observer);
230   }
231 
GetLatestProxyConfig(ProxyConfig * config)232   ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
233     DCHECK(OnNetworkThread());
234     if (!config)
235       return ProxyConfigService::CONFIG_UNSET;
236     *config = proxy_config_;
237     return ProxyConfigService::CONFIG_VALID;
238   }
239 
240   // Called on the JNI thread.
ProxySettingsChanged()241   void ProxySettingsChanged() {
242     DCHECK(OnJNIThread());
243     ProxyConfig proxy_config;
244     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
245     network_task_runner_->PostTask(
246         FROM_HERE,
247         base::Bind(
248             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
249   }
250 
251   // Called on the JNI thread.
ProxySettingsChangedTo(const std::string & host,int port)252   void ProxySettingsChangedTo(const std::string& host, int port) {
253     DCHECK(OnJNIThread());
254     ProxyConfig proxy_config;
255     CreateStaticProxyConfig(host, port, &proxy_config);
256     network_task_runner_->PostTask(
257         FROM_HERE,
258         base::Bind(
259             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
260   }
261 
262  private:
263   friend class base::RefCountedThreadSafe<Delegate>;
264 
265   class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate {
266    public:
JNIDelegateImpl(Delegate * delegate)267     explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
268 
269     // ProxyConfigServiceAndroid::JNIDelegate overrides.
ProxySettingsChangedTo(JNIEnv * env,jobject jself,jstring jhost,jint jport)270     virtual void ProxySettingsChangedTo(JNIEnv* env, jobject jself,
271                                       jstring jhost, jint jport) OVERRIDE {
272       std::string host = ConvertJavaStringToUTF8(env, jhost);
273       delegate_->ProxySettingsChangedTo(host, jport);
274     }
275 
ProxySettingsChanged(JNIEnv * env,jobject self)276     virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
277       delegate_->ProxySettingsChanged();
278     }
279 
280    private:
281     Delegate* const delegate_;
282   };
283 
~Delegate()284   virtual ~Delegate() {}
285 
ShutdownOnJNIThread()286   void ShutdownOnJNIThread() {
287     if (java_proxy_change_listener_.is_null())
288       return;
289     JNIEnv* env = AttachCurrentThread();
290     Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj());
291   }
292 
293   // Called on the network thread.
SetNewConfigOnNetworkThread(const ProxyConfig & proxy_config)294   void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) {
295     DCHECK(OnNetworkThread());
296     proxy_config_ = proxy_config;
297     FOR_EACH_OBSERVER(Observer, observers_,
298                       OnProxyConfigChanged(proxy_config,
299                                            ProxyConfigService::CONFIG_VALID));
300   }
301 
OnJNIThread() const302   bool OnJNIThread() const {
303     return jni_task_runner_->RunsTasksOnCurrentThread();
304   }
305 
OnNetworkThread() const306   bool OnNetworkThread() const {
307     return network_task_runner_->RunsTasksOnCurrentThread();
308   }
309 
310   ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
311 
312   JNIDelegateImpl jni_delegate_;
313   ObserverList<Observer> observers_;
314   scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
315   scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
316   GetPropertyCallback get_property_callback_;
317   ProxyConfig proxy_config_;
318 
319   DISALLOW_COPY_AND_ASSIGN(Delegate);
320 };
321 
ProxyConfigServiceAndroid(base::SequencedTaskRunner * network_task_runner,base::SequencedTaskRunner * jni_task_runner)322 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
323     base::SequencedTaskRunner* network_task_runner,
324     base::SequencedTaskRunner* jni_task_runner)
325     : delegate_(new Delegate(
326         network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
327   delegate_->SetupJNI();
328   delegate_->FetchInitialConfig();
329 }
330 
~ProxyConfigServiceAndroid()331 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
332   delegate_->Shutdown();
333 }
334 
335 // static
Register(JNIEnv * env)336 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
337   return RegisterNativesImpl(env);
338 }
339 
AddObserver(Observer * observer)340 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
341   delegate_->AddObserver(observer);
342 }
343 
RemoveObserver(Observer * observer)344 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
345   delegate_->RemoveObserver(observer);
346 }
347 
348 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfig * config)349 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
350   return delegate_->GetLatestProxyConfig(config);
351 }
352 
ProxyConfigServiceAndroid(base::SequencedTaskRunner * network_task_runner,base::SequencedTaskRunner * jni_task_runner,GetPropertyCallback get_property_callback)353 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
354     base::SequencedTaskRunner* network_task_runner,
355     base::SequencedTaskRunner* jni_task_runner,
356     GetPropertyCallback get_property_callback)
357     : delegate_(new Delegate(
358         network_task_runner, jni_task_runner, get_property_callback)) {
359   delegate_->SetupJNI();
360   delegate_->FetchInitialConfig();
361 }
362 
ProxySettingsChanged()363 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
364   delegate_->ProxySettingsChanged();
365 }
366 
367 } // namespace net
368