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
27using base::android::AttachCurrentThread;
28using base::android::ConvertUTF8ToJavaString;
29using base::android::ConvertJavaStringToUTF8;
30using base::android::CheckException;
31using base::android::ClearException;
32using base::android::ScopedJavaGlobalRef;
33
34namespace net {
35
36namespace {
37
38typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback;
39
40// Returns whether the provided string was successfully converted to a port.
41bool 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
51ProxyServer 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
66ProxyServer 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
84ProxyServer 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
94void 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.
119bool 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
146void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
147                                  ProxyConfig* config) {
148  if (!GetProxyRules(get_property, &config->proxy_rules()))
149    *config = ProxyConfig::CreateDirect();
150}
151
152std::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
163void 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
175class ProxyConfigServiceAndroid::Delegate
176    : public base::RefCountedThreadSafe<Delegate> {
177 public:
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
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
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
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.
222  void AddObserver(Observer* observer) {
223    DCHECK(OnNetworkThread());
224    observers_.AddObserver(observer);
225  }
226
227  void RemoveObserver(Observer* observer) {
228    DCHECK(OnNetworkThread());
229    observers_.RemoveObserver(observer);
230  }
231
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.
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.
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:
267    explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
268
269    // ProxyConfigServiceAndroid::JNIDelegate overrides.
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
276    virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
277      delegate_->ProxySettingsChanged();
278    }
279
280   private:
281    Delegate* const delegate_;
282  };
283
284  virtual ~Delegate() {}
285
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.
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
302  bool OnJNIThread() const {
303    return jni_task_runner_->RunsTasksOnCurrentThread();
304  }
305
306  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
322ProxyConfigServiceAndroid::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
331ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
332  delegate_->Shutdown();
333}
334
335// static
336bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
337  return RegisterNativesImpl(env);
338}
339
340void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
341  delegate_->AddObserver(observer);
342}
343
344void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
345  delegate_->RemoveObserver(observer);
346}
347
348ProxyConfigService::ConfigAvailability
349ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
350  return delegate_->GetLatestProxyConfig(config);
351}
352
353ProxyConfigServiceAndroid::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
363void ProxyConfigServiceAndroid::ProxySettingsChanged() {
364  delegate_->ProxySettingsChanged();
365}
366
367} // namespace net
368