Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: net/proxy/proxy_config_service_android.cc

Issue 10206014: Upstream Android proxy config service. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Make Java methods non-synchronized Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/location.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/observer_list.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/string_tokenizer.h"
19 #include "base/string_util.h"
20 #include "googleurl/src/url_parse.h"
21 #include "jni/proxy_change_listener_jni.h"
22 #include "net/base/host_port_pair.h"
23 #include "net/proxy/proxy_config.h"
24
25 using base::android::AttachCurrentThread;
26 using base::android::ConvertUTF8ToJavaString;
27 using base::android::ConvertJavaStringToUTF8;
28 using base::android::CheckException;
29 using base::android::ClearException;
30 using base::android::GetMethodID;
31 using base::android::ScopedJavaGlobalRef;
32
33 namespace net {
34
35 namespace {
36
37 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback;
38
39 // Returns whether the provided string was successfully converted to a port.
40 bool ConvertStringToPort(const std::string& port, int* output) {
41 url_parse::Component component(0, port.size());
42 int result = url_parse::ParsePort(port.c_str(), component);
43 if (result == url_parse::PORT_INVALID ||
44 result == url_parse::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 StringTokenizer tokenizer(non_proxy_hosts, "|");
105 while (tokenizer.GetNext()) {
106 std::string token = tokenizer.token();
107 std::string pattern;
108 TrimWhitespaceASCII(token, 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 // equivalent Android implementation.
122 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
123 rules->proxy_for_http = LookupProxy("http", get_property,
124 ProxyServer::SCHEME_HTTP);
125 rules->proxy_for_https = LookupProxy("https", get_property,
126 ProxyServer::SCHEME_HTTPS);
127 rules->proxy_for_ftp = LookupProxy("ftp", get_property,
128 ProxyServer::SCHEME_HTTP);
129 rules->fallback_proxy = LookupSocksProxy(get_property);
130 rules->bypass_rules.Clear();
131 AddBypassRules("ftp", get_property, &rules->bypass_rules);
132 AddBypassRules("http", get_property, &rules->bypass_rules);
133 AddBypassRules("https", get_property, &rules->bypass_rules);
134 return rules->proxy_for_http.is_valid() ||
135 rules->proxy_for_https.is_valid() ||
136 rules->proxy_for_ftp.is_valid() ||
137 rules->fallback_proxy.is_valid();
138 };
139
140 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
141 ProxyConfig* config) {
142 if (!GetProxyRules(get_property, &config->proxy_rules()))
143 *config = ProxyConfig::CreateDirect();
144 }
145
146 std::string GetJavaProperty(const std::string& property) {
147 // Use Java System.getProperty to get configuration information.
148 // TODO(pliard): Conversion to/from UTF8 ok here?
149 JNIEnv* env = AttachCurrentThread();
150 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
151 ScopedJavaLocalRef<jstring> result =
152 Java_ProxyChangeListener_getProperty(env, str.obj());
153 return result.is_null() ?
154 std::string() : ConvertJavaStringToUTF8(env, result.obj());
155 }
156
157 } // namespace
158
159 class ProxyConfigServiceAndroid::Delegate
160 : public base::RefCountedThreadSafe<Delegate> {
161 public:
162 Delegate(base::SingleThreadTaskRunner* network_task_runner,
163 base::SingleThreadTaskRunner* jni_task_runner,
164 const GetPropertyCallback& get_property_callback)
165 : network_task_runner_(network_task_runner),
166 jni_task_runner_(jni_task_runner),
167 get_property_callback_(get_property_callback) {}
168
169 void SetupJNI(const ProxyConfigServiceAndroid* service) {
170 if (OnJNIThread()) {
171 SetupOnJNIThread(service);
172 } else {
173 jni_task_runner_->PostTask(
174 FROM_HERE,
175 base::Bind(&Delegate::SetupOnJNIThread, this, service));
Ryan Sleevi 2012/05/29 17:34:43 There is a slight timing/use-after-free issue here
Philippe 2012/06/01 13:06:37 Good catch! I got your point for the first issue.
Philippe 2012/06/01 14:11:21 Ok, I think that I got your second point after I t
176 }
177 }
178
179 void FetchInitialConfig() {
180 if (OnJNIThread()) {
181 FetchInitialConfigOnJNIThread();
182 } else {
183 jni_task_runner_->PostTask(
184 FROM_HERE,
185 base::Bind(&Delegate::FetchInitialConfigOnJNIThread, this));
186 }
187 }
188
189 // Called only on the network thread.
190 void AddObserver(Observer* observer) {
191 DCHECK(OnNetworkThread());
192 observers_.AddObserver(observer);
193 }
194
195 void RemoveObserver(Observer* observer) {
196 DCHECK(OnNetworkThread());
197 observers_.RemoveObserver(observer);
198 }
199
200 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
201 DCHECK(OnNetworkThread());
202 if (!config)
203 return ProxyConfigService::CONFIG_UNSET;
204 *config = proxy_config_;
205 return ProxyConfigService::CONFIG_VALID;
206 }
207
208 // Called on the JNI thread.
209 void ProxySettingsChanged() {
210 DCHECK(OnJNIThread());
211 ProxyConfig proxy_config;
212 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
213 network_task_runner_->PostTask(
214 FROM_HERE,
215 base::Bind(
216 &Delegate::SetNewConfigAndNotifyObservers, this, proxy_config));
217 }
218
219 private:
220 friend class base::RefCountedThreadSafe<Delegate>;
221
222 virtual ~Delegate() {
223 Shutdown();
224 }
225
226 void SetupOnJNIThread(const ProxyConfigServiceAndroid* service) {
227 JNIEnv* env = AttachCurrentThread();
228 if (java_proxy_change_listener_.is_null()) {
229 java_proxy_change_listener_.Reset(
230 Java_ProxyChangeListener_create(
231 env, base::android::GetApplicationContext()));
232 CHECK(!java_proxy_change_listener_.is_null());
233 }
234 Java_ProxyChangeListener_start(
235 env,
236 java_proxy_change_listener_.obj(),
237 reinterpret_cast<jint>(service));
238 }
239
240 void FetchInitialConfigOnJNIThread() {
241 ProxyConfig proxy_config;
242 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
243 SetNewConfigAndNotifyObservers(proxy_config);
244 }
245
246 static void ShutdownOnJNIThread(
247 const ScopedJavaGlobalRef<jobject>& java_proxy_change_listener) {
248 if (java_proxy_change_listener.is_null())
249 return;
250 JNIEnv* env = AttachCurrentThread();
251 Java_ProxyChangeListener_stop(env, java_proxy_change_listener.obj());
252 }
253
254 void Shutdown() {
255 if (OnJNIThread()) {
256 ShutdownOnJNIThread(java_proxy_change_listener_);
257 } else {
258 jni_task_runner_->PostTask(
259 FROM_HERE,
260 base::Bind(&Delegate::ShutdownOnJNIThread,
261 java_proxy_change_listener_));
262 }
263 }
264
265 // Called on the network thread.
266 void SetNewConfigAndNotifyObservers(const ProxyConfig& proxy_config) {
267 DCHECK(OnNetworkThread());
268 proxy_config_ = proxy_config;
269 FOR_EACH_OBSERVER(Observer, observers_,
270 OnProxyConfigChanged(proxy_config,
271 ProxyConfigService::CONFIG_VALID));
272 }
273
274 bool OnJNIThread() const {
275 return jni_task_runner_->BelongsToCurrentThread();
276 }
277
278 bool OnNetworkThread() const {
279 return network_task_runner_->BelongsToCurrentThread();
280 }
281
282 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
283
284 ObserverList<Observer> observers_;
285 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
286 scoped_refptr<base::SingleThreadTaskRunner> jni_task_runner_;
287 GetPropertyCallback get_property_callback_;
288 ProxyConfig proxy_config_;
289
290 DISALLOW_COPY_AND_ASSIGN(Delegate);
291 };
292
293 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
294 base::SingleThreadTaskRunner* network_task_runner,
295 base::SingleThreadTaskRunner* jni_task_runner)
296 : delegate_(new Delegate(
297 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
298 delegate_->SetupJNI(this);
299 delegate_->FetchInitialConfig();
300 }
301
302 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {}
303
304 // static
305 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
306 return RegisterNativesImpl(env);
307 }
308
309 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
310 delegate_->AddObserver(observer);
311 }
312
313 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
314 delegate_->RemoveObserver(observer);
315 }
316
317 ProxyConfigService::ConfigAvailability
318 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
319 return delegate_->GetLatestProxyConfig(config);
320 }
321
322 void ProxyConfigServiceAndroid::ProxySettingsChanged(JNIEnv*, jobject) {
323 delegate_->ProxySettingsChanged();
324 }
325
326 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
327 base::SingleThreadTaskRunner* network_task_runner,
328 base::SingleThreadTaskRunner* jni_task_runner,
329 GetPropertyCallback get_property_callback)
330 : delegate_(new Delegate(
331 network_task_runner, jni_task_runner, get_property_callback)) {
332 delegate_->FetchInitialConfig();
333 }
334
335 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698