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