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::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
ConstructProxyServer(ProxyServer::Scheme scheme,const std::string & proxy_host,const std::string & proxy_port)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
LookupProxy(const std::string & prefix,const GetPropertyCallback & get_property,ProxyServer::Scheme scheme)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
LookupSocksProxy(const GetPropertyCallback & get_property)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
AddBypassRules(const std::string & scheme,const GetPropertyCallback & get_property,ProxyBypassRules * bypass_rules)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.
GetProxyRules(const GetPropertyCallback & get_property,ProxyConfig::ProxyRules * rules)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
GetLatestProxyConfigInternal(const GetPropertyCallback & get_property,ProxyConfig * config)145 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
146 ProxyConfig* config) {
147 if (!GetProxyRules(get_property, &config->proxy_rules()))
148 *config = ProxyConfig::CreateDirect();
149 }
150
GetJavaProperty(const std::string & property)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
CreateStaticProxyConfig(const std::string & host,int port,ProxyConfig * config)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:
Delegate(base::SequencedTaskRunner * network_task_runner,base::SequencedTaskRunner * jni_task_runner,const GetPropertyCallback & get_property_callback)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
SetupJNI()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
FetchInitialConfig()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
Shutdown()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.
AddObserver(Observer * observer)221 void AddObserver(Observer* observer) {
222 DCHECK(OnNetworkThread());
223 observers_.AddObserver(observer);
224 }
225
RemoveObserver(Observer * observer)226 void RemoveObserver(Observer* observer) {
227 DCHECK(OnNetworkThread());
228 observers_.RemoveObserver(observer);
229 }
230
GetLatestProxyConfig(ProxyConfig * config)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.
ProxySettingsChanged()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.
ProxySettingsChangedTo(const std::string & host,int port)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:
JNIDelegateImpl(Delegate * delegate)266 explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
267
268 // ProxyConfigServiceAndroid::JNIDelegate overrides.
ProxySettingsChangedTo(JNIEnv * env,jobject jself,jstring jhost,jint jport)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
ProxySettingsChanged(JNIEnv * env,jobject self)275 virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
276 delegate_->ProxySettingsChanged();
277 }
278
279 private:
280 Delegate* const delegate_;
281 };
282
~Delegate()283 virtual ~Delegate() {}
284
ShutdownOnJNIThread()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.
SetNewConfigOnNetworkThread(const ProxyConfig & proxy_config)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
OnJNIThread() const301 bool OnJNIThread() const {
302 return jni_task_runner_->RunsTasksOnCurrentThread();
303 }
304
OnNetworkThread() const305 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
ProxyConfigServiceAndroid(base::SequencedTaskRunner * network_task_runner,base::SequencedTaskRunner * jni_task_runner)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
~ProxyConfigServiceAndroid()330 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
331 delegate_->Shutdown();
332 }
333
334 // static
Register(JNIEnv * env)335 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
336 return RegisterNativesImpl(env);
337 }
338
AddObserver(Observer * observer)339 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
340 delegate_->AddObserver(observer);
341 }
342
RemoveObserver(Observer * observer)343 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
344 delegate_->RemoveObserver(observer);
345 }
346
347 ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(ProxyConfig * config)348 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
349 return delegate_->GetLatestProxyConfig(config);
350 }
351
ProxyConfigServiceAndroid(base::SequencedTaskRunner * network_task_runner,base::SequencedTaskRunner * jni_task_runner,GetPropertyCallback get_property_callback)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
ProxySettingsChanged()362 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
363 delegate_->ProxySettingsChanged();
364 }
365
366 } // namespace net
367