Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |