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

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: Add proxy_test_cases.py Created 8 years, 8 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 "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/string_tokenizer.h"
14 #include "base/string_util.h"
15 #include "jni/proxy_change_listener_jni.h"
16 #include "net/proxy/proxy_config.h"
17
18 #include <sys/system_properties.h>
19
20 using base::android::AttachCurrentThread;
21 using base::android::ConvertUTF8ToJavaString;
22 using base::android::ConvertJavaStringToUTF8;
23 using base::android::CheckException;
24 using base::android::ClearException;
25 using base::android::GetMethodID;
26
27 namespace net {
28
29 namespace {
30 // This class implements the link to Android's proxy broadcast mechanism.
Ryan Sleevi 2012/04/24 18:20:04 nit: Line break between 29 & 30
Philippe 2012/05/09 11:48:39 Done.
31 class DelegateImpl : public ProxyConfigServiceAndroid::Delegate {
32 public:
33 DelegateImpl();
Ryan Sleevi 2012/04/24 18:20:04 nit: Declare a destructor, add a line break betwee
Philippe 2012/05/09 11:48:39 Declaring the destructor here won't serve us in th
34 // ProxyConfigServiceAndroid::Delegate:
35 virtual void Start(ProxyConfigServiceAndroid* service) OVERRIDE;
36 virtual void Stop() OVERRIDE;
37 virtual std::string GetProperty(const std::string& property) OVERRIDE;
38
39 private:
40 ProxyConfigServiceAndroid* service_;
41 base::android::ScopedJavaGlobalRef<jobject>
42 java_proxy_change_listener_android_;
43 };
44
45 DelegateImpl::DelegateImpl() : service_(NULL) {}
46
47 void DelegateImpl::Start(ProxyConfigServiceAndroid* service) {
48 DCHECK(!service_);
49 JNIEnv* env = AttachCurrentThread();
50 service_ = service;
51 if (java_proxy_change_listener_android_.is_null()) {
52 java_proxy_change_listener_android_.Reset(
53 Java_ProxyChangeListener_create(
54 env, base::android::GetApplicationContext()));
55 CHECK(!java_proxy_change_listener_android_.is_null());
56 }
57 Java_ProxyChangeListener_start(
58 env,
59 java_proxy_change_listener_android_.obj(),
60 reinterpret_cast<jint>(service));
61 }
62
63 void DelegateImpl::Stop() {
64 // The java object will be NULL if Stop() was already called (e.g. after a
65 // Start()), or if Start() was never called. Stop() is called from the
66 // destructor of ProxyConfigServiceAndroid.
67 if (java_proxy_change_listener_android_.is_null())
68 return;
69 JNIEnv* env = AttachCurrentThread();
70 Java_ProxyChangeListener_stop(env,
71 java_proxy_change_listener_android_.obj());
72 }
73
74 std::string DelegateImpl::GetProperty(const std::string& property) {
75 // Use Java System.getProperty to get configuration information.
76 // Question: Conversion to/from UTF8 ok here?
77 JNIEnv* env = AttachCurrentThread();
78 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
79 ScopedJavaLocalRef<jstring> result =
80 Java_ProxyChangeListener_getProperty(env, str.obj());
81 return result.is_null() ? "" : ConvertJavaStringToUTF8(env, result.obj());
82 }
83 } // namespace
Ryan Sleevi 2012/04/24 18:20:04 nit: Line break between 82 & 83
Philippe 2012/05/09 11:48:39 Done.
84
85 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid()
86 : delegate_(new DelegateImpl),
87 notify_task_(NULL) {
88 }
89
90 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
91 ProxyConfigServiceAndroid::Delegate* delegate)
92 : delegate_(delegate),
93 notify_task_(NULL) {
94 }
95
96 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
97 DCHECK(OnObserverThread());
Ryan Sleevi 2012/04/24 18:20:04 This is very surprising, and I think dangerously s
Philippe 2012/05/09 11:48:39 I agree. Unfortunately I can't change that.
98 if (delegate_.get())
99 delegate_->Stop();
100 // Cancel the notify task last, as the java side may have been mid-way through
101 // posting a new notify task when we requested it stop
102 CancelNotifyTask();
103 }
104
105 bool ProxyConfigServiceAndroid::Init(JNIEnv* env) {
106 return RegisterNativesImpl(env);
107 }
108
109 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
110 DCHECK(OnObserverThread());
111 if (observers_.size() == 0) {
112 AssignObserverThread();
113 delegate_->Start(this);
114 }
115 // Note that any callbacks from the delegate will result in a scheduled task
116 // on this same thread, so there is no race between delegate_->Start() and
117 // AddObserver().
118 observers_.AddObserver(observer);
119 }
120
121 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
122 DCHECK(OnObserverThread());
123 observers_.RemoveObserver(observer);
124 if (observers_.size() == 0) {
125 delegate_->Stop();
126 }
127 }
128
129 namespace {
Ryan Sleevi 2012/04/24 18:20:04 This is very weird to read that you jump from an u
Philippe 2012/05/09 11:48:39 Done.
130
131 ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme,
132 const std::string& proxyHost,
Ryan Sleevi 2012/04/24 18:20:04 Style: Please go through these methods and make su
Philippe 2012/05/09 11:48:39 Done.
133 const std::string& proxyPort) {
134 DCHECK(!proxyHost.empty());
135 if (proxyPort.empty())
136 return ProxyServer::FromURI(proxyHost, scheme);
137 return ProxyServer::FromURI(proxyHost + ":" + proxyPort, scheme);
138 }
139
140 ProxyServer LookupProxy(
141 ProxyConfigServiceAndroid::Delegate& delegate,
142 const std::string& prefix,
143 ProxyServer::Scheme scheme) {
144 std::string proxyHost = delegate.GetProperty(prefix + ".proxyHost");
145 if (!proxyHost.empty()) {
146 std::string proxyPort = delegate.GetProperty(prefix + ".proxyPort");
147 return ConstructProxyServer(scheme, proxyHost, proxyPort);
148 }
149 // Fall back to default proxy, if any.
150 proxyHost = delegate.GetProperty("proxyHost");
151 if (!proxyHost.empty()) {
152 std::string proxyPort = delegate.GetProperty("proxyPort");
153 return ConstructProxyServer(scheme, proxyHost, proxyPort);
154 }
155 return ProxyServer();
156 }
157
158 ProxyServer LookupSocksProxy(
159 ProxyConfigServiceAndroid::Delegate& delegate) {
160 std::string proxyHost = delegate.GetProperty("socksProxyHost");
161 if (!proxyHost.empty()) {
162 std::string proxyPort = delegate.GetProperty("socksProxyPort");
163 return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxyHost,
164 proxyPort);
165 }
166 return ProxyServer();
167 }
168
169 void AddBypassRules(
170 ProxyConfigServiceAndroid::Delegate& delegate,
171 const std::string& scheme,
172 ProxyBypassRules* bypass_rules) {
173 // The format of a hostname pattern is a list of hostnames that are separated
174 // by | and that use * as a wildcard. For example, setting the
175 // http.nonProxyHosts property to *.android.com|*.kernel.org will cause
176 // requests to http://developer.android.com to be made without a proxy.
177 std::string nonProxyHosts = delegate.GetProperty(scheme + ".nonProxyHosts");
178 if (nonProxyHosts.empty())
179 return;
180 StringTokenizer tokenizer(nonProxyHosts, "|");
181 while (tokenizer.GetNext()) {
182 std::string token = tokenizer.token();
183 std::string pattern;
184 TrimWhitespaceASCII(token, TRIM_ALL, &pattern);
185 if (pattern.empty())
186 continue;
187 // '?' is not one of the specified pattern characters above.
188 DCHECK_EQ(std::string::npos, pattern.find('?'));
189 bypass_rules->AddRuleForHostname(scheme, pattern, -1);
190 }
191 }
192
193 // returns true iff a valid proxy was found.
194 bool GetProxyRules(
195 ProxyConfigServiceAndroid::Delegate& delegate,
196 ProxyConfig::ProxyRules* rules) {
197 // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the
198 // semantics we're trying to match.
199 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
200 rules->proxy_for_http = LookupProxy(delegate, "http",
201 ProxyServer::SCHEME_HTTP);
202 rules->proxy_for_https = LookupProxy(delegate, "https",
203 ProxyServer::SCHEME_HTTPS);
204 rules->proxy_for_ftp = LookupProxy(delegate, "ftp", ProxyServer::SCHEME_HTTP);
205 rules->fallback_proxy = LookupSocksProxy(delegate);
206 rules->bypass_rules.Clear();
207 AddBypassRules(delegate, "ftp", &rules->bypass_rules);
208 AddBypassRules(delegate, "http", &rules->bypass_rules);
209 AddBypassRules(delegate, "https", &rules->bypass_rules);
210 return rules->proxy_for_http.is_valid() ||
211 rules->proxy_for_https.is_valid() ||
212 rules->proxy_for_ftp.is_valid() ||
213 rules->fallback_proxy.is_valid();
214 };
215
216 void GetLatestProxyConfigInternal(
217 ProxyConfigServiceAndroid::Delegate& delegate,
218 ProxyConfig* config) {
219 if (!GetProxyRules(delegate, &config->proxy_rules()))
220 *config = ProxyConfig::CreateDirect();
221 }
222
223 } // namespace
224
225 ProxyConfigService::ConfigAvailability
226 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
227 DCHECK(OnObserverThread());
228 if (!config)
229 return ProxyConfigService::CONFIG_UNSET;
230 GetLatestProxyConfigInternal(*delegate_.get(), config);
231 return ProxyConfigService::CONFIG_VALID;
232 }
233
234 // Posted on observer_loop_. See header file for intended behaviour.
235 class ProxyConfigServiceAndroid::NotifyTask {
236 public:
237 NotifyTask(ProxyConfigServiceAndroid* service);
238 ~NotifyTask();
239 void Cancel();
240 void Run();
241
242 private:
243 ProxyConfigServiceAndroid* service_;
244 };
245
246 ProxyConfigServiceAndroid::NotifyTask::NotifyTask(
247 ProxyConfigServiceAndroid* service)
248 : service_(service) {
249 }
250
251 ProxyConfigServiceAndroid::NotifyTask::~NotifyTask() {
252 // Should be called from IO thread.
Ryan Sleevi 2012/04/24 18:20:04 network thread
Philippe 2012/05/09 11:48:39 I removed NotifyTask.
253 if (service_) {
254 // This could happen if the notify task is removed from the message loop
255 // before it has been run (e.g. if the message loop is destroyed).
256 service_->ClearNotifyTask();
257 }
258 }
259
260 void ProxyConfigServiceAndroid::NotifyTask::Cancel() {
261 // Should be called from IO thread.
Ryan Sleevi 2012/04/24 18:20:04 This doesn't seem to be met - ProxyConfigServiceAn
Philippe 2012/05/09 11:48:39 I removed NotifyTask.
262 service_ = NULL;
263 }
264
265 void ProxyConfigServiceAndroid::NotifyTask::Run() {
266 // Should be called from IO thread.
267 if (service_) {
268 service_->ProxySettingsChangedCallback();
269 service_ = NULL;
270 }
271 }
272
273 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
274 // Called on any thread.
275 DCHECK(observer_loop_); // Should have been assigned in AddObserver().
276 NotifyTask* task = NULL;
277 {
278 base::AutoLock lock(notify_task_lock_);
279 // If there is already a task scheduled, don't schedule another one.
280 if (notify_task_)
281 return;
282 notify_task_ = task = new NotifyTask(this);
283 }
284 observer_loop_->PostTask(FROM_HERE,
Ryan Sleevi 2012/04/24 18:20:04 Possible crash here, AFAICT Two threads: T1, T2 T
Philippe 2012/05/09 11:48:39 This code is now much simpler.
285 base::Bind(&NotifyTask::Run, base::Owned(task)));
286 }
287
288 void ProxyConfigServiceAndroid::ProxySettingsChangedCallback() {
289 DCHECK(OnObserverThread());
290 // Allow another task to be scheduled (before running observers).
291 ClearNotifyTask();
292 ProxyConfig config;
293 GetLatestProxyConfigInternal(*delegate_.get(), &config);
294 FOR_EACH_OBSERVER(Observer, observers_,
295 OnProxyConfigChanged(config,
296 ProxyConfigService::CONFIG_VALID));
297 }
298
299 void ProxyConfigServiceAndroid::ClearNotifyTask() {
300 base::AutoLock lock(notify_task_lock_);
301 notify_task_ = NULL;
302 }
303
304 void ProxyConfigServiceAndroid::CancelNotifyTask() {
305 base::AutoLock lock(notify_task_lock_);
306 if (notify_task_) {
307 notify_task_->Cancel();
308 notify_task_ = NULL;
309 }
310 }
311
312 bool ProxyConfigServiceAndroid::OnObserverThread() {
313 // Check that we're on the observer thread or the observer thread hasn't been
314 // assigned yet. The observer thread is only assigned in AddObserver(), which,
315 // in theory, might not be called.
316 return !observer_loop_ || observer_loop_->BelongsToCurrentThread();
Ryan Sleevi 2012/04/24 18:20:04 This is equally subtle and surprising. I think th
Philippe 2012/05/09 11:48:39 Since the message loop proxy is now injected in th
317 }
318
319 void ProxyConfigServiceAndroid::AssignObserverThread() {
320 // Called from AddObserver. We assign observer_loop_ here under a lock so that
321 // it is guaranteed that ProxySettingsChanged(), which is called from another
322 // thread, will see the value of observer_loop_.
Ryan Sleevi 2012/04/24 18:20:04 Lock? I don't see any lock.
Philippe 2012/05/09 11:48:39 Indeed. I removed this method since the observer l
323 if (!observer_loop_) {
324 observer_loop_ = base::MessageLoopProxy::current();
325 } else {
326 DCHECK(OnObserverThread());
Ryan Sleevi 2012/04/24 18:20:04 It seems like this could be written: if (!observer
Philippe 2012/05/09 11:48:39 Done.
327 }
328 }
329
330 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698