| 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 <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/string_tokenizer.h" |
| 20 #include "base/string_util.h" |
| 21 #include "googleurl/src/url_parse.h" |
| 22 #include "jni/proxy_change_listener_jni.h" |
| 23 #include "net/base/host_port_pair.h" |
| 24 #include "net/proxy/proxy_config.h" |
| 25 |
| 26 using base::android::AttachCurrentThread; |
| 27 using base::android::ConvertUTF8ToJavaString; |
| 28 using base::android::ConvertJavaStringToUTF8; |
| 29 using base::android::CheckException; |
| 30 using base::android::ClearException; |
| 31 using base::android::GetMethodID; |
| 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. |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 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. |
| 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 // equivalent Android implementation. |
| 123 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; |
| 124 rules->proxy_for_http = LookupProxy("http", get_property, |
| 125 ProxyServer::SCHEME_HTTP); |
| 126 rules->proxy_for_https = LookupProxy("https", get_property, |
| 127 ProxyServer::SCHEME_HTTPS); |
| 128 rules->proxy_for_ftp = LookupProxy("ftp", get_property, |
| 129 ProxyServer::SCHEME_HTTP); |
| 130 rules->fallback_proxy = LookupSocksProxy(get_property); |
| 131 rules->bypass_rules.Clear(); |
| 132 AddBypassRules("ftp", get_property, &rules->bypass_rules); |
| 133 AddBypassRules("http", get_property, &rules->bypass_rules); |
| 134 AddBypassRules("https", get_property, &rules->bypass_rules); |
| 135 return rules->proxy_for_http.is_valid() || |
| 136 rules->proxy_for_https.is_valid() || |
| 137 rules->proxy_for_ftp.is_valid() || |
| 138 rules->fallback_proxy.is_valid(); |
| 139 }; |
| 140 |
| 141 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, |
| 142 ProxyConfig* config) { |
| 143 if (!GetProxyRules(get_property, &config->proxy_rules())) |
| 144 *config = ProxyConfig::CreateDirect(); |
| 145 } |
| 146 |
| 147 std::string GetJavaProperty(const std::string& property) { |
| 148 // Use Java System.getProperty to get configuration information. |
| 149 // TODO(pliard): Conversion to/from UTF8 ok here? |
| 150 JNIEnv* env = AttachCurrentThread(); |
| 151 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property); |
| 152 ScopedJavaLocalRef<jstring> result = |
| 153 Java_ProxyChangeListener_getProperty(env, str.obj()); |
| 154 return result.is_null() ? |
| 155 std::string() : ConvertJavaStringToUTF8(env, result.obj()); |
| 156 } |
| 157 |
| 158 } // namespace |
| 159 |
| 160 class ProxyConfigServiceAndroid::Delegate |
| 161 : public base::RefCountedThreadSafe<Delegate> { |
| 162 public: |
| 163 Delegate(base::SequencedTaskRunner* network_task_runner, |
| 164 base::SequencedTaskRunner* jni_task_runner, |
| 165 const GetPropertyCallback& get_property_callback) |
| 166 : ALLOW_THIS_IN_INITIALIZER_LIST(jni_delegate_(this)), |
| 167 network_task_runner_(network_task_runner), |
| 168 jni_task_runner_(jni_task_runner), |
| 169 get_property_callback_(get_property_callback) { |
| 170 } |
| 171 |
| 172 void SetupJNI() { |
| 173 DCHECK(OnJNIThread()); |
| 174 JNIEnv* env = AttachCurrentThread(); |
| 175 if (java_proxy_change_listener_.is_null()) { |
| 176 java_proxy_change_listener_.Reset( |
| 177 Java_ProxyChangeListener_create( |
| 178 env, base::android::GetApplicationContext())); |
| 179 CHECK(!java_proxy_change_listener_.is_null()); |
| 180 } |
| 181 Java_ProxyChangeListener_start( |
| 182 env, |
| 183 java_proxy_change_listener_.obj(), |
| 184 reinterpret_cast<jint>(&jni_delegate_)); |
| 185 } |
| 186 |
| 187 void FetchInitialConfig() { |
| 188 DCHECK(OnJNIThread()); |
| 189 ProxyConfig proxy_config; |
| 190 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); |
| 191 network_task_runner_->PostTask( |
| 192 FROM_HERE, |
| 193 base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); |
| 194 } |
| 195 |
| 196 void Shutdown() { |
| 197 if (OnJNIThread()) { |
| 198 ShutdownOnJNIThread(); |
| 199 } else { |
| 200 jni_task_runner_->PostTask( |
| 201 FROM_HERE, |
| 202 base::Bind(&Delegate::ShutdownOnJNIThread, this)); |
| 203 } |
| 204 } |
| 205 |
| 206 // Called only on the network thread. |
| 207 void AddObserver(Observer* observer) { |
| 208 DCHECK(OnNetworkThread()); |
| 209 observers_.AddObserver(observer); |
| 210 } |
| 211 |
| 212 void RemoveObserver(Observer* observer) { |
| 213 DCHECK(OnNetworkThread()); |
| 214 observers_.RemoveObserver(observer); |
| 215 } |
| 216 |
| 217 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { |
| 218 DCHECK(OnNetworkThread()); |
| 219 if (!config) |
| 220 return ProxyConfigService::CONFIG_UNSET; |
| 221 *config = proxy_config_; |
| 222 return ProxyConfigService::CONFIG_VALID; |
| 223 } |
| 224 |
| 225 // Called on the JNI thread. |
| 226 void ProxySettingsChanged() { |
| 227 DCHECK(OnJNIThread()); |
| 228 ProxyConfig proxy_config; |
| 229 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); |
| 230 network_task_runner_->PostTask( |
| 231 FROM_HERE, |
| 232 base::Bind( |
| 233 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); |
| 234 } |
| 235 |
| 236 private: |
| 237 friend class base::RefCountedThreadSafe<Delegate>; |
| 238 |
| 239 class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate { |
| 240 public: |
| 241 explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {} |
| 242 |
| 243 // ProxyConfigServiceAndroid::JNIDelegate overrides. |
| 244 virtual void ProxySettingsChanged(JNIEnv*, jobject) OVERRIDE { |
| 245 delegate_->ProxySettingsChanged(); |
| 246 } |
| 247 |
| 248 private: |
| 249 Delegate* const delegate_; |
| 250 }; |
| 251 |
| 252 virtual ~Delegate() {} |
| 253 |
| 254 void ShutdownOnJNIThread() { |
| 255 if (java_proxy_change_listener_.is_null()) |
| 256 return; |
| 257 JNIEnv* env = AttachCurrentThread(); |
| 258 Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj()); |
| 259 } |
| 260 |
| 261 // Called on the network thread. |
| 262 void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) { |
| 263 DCHECK(OnNetworkThread()); |
| 264 proxy_config_ = proxy_config; |
| 265 FOR_EACH_OBSERVER(Observer, observers_, |
| 266 OnProxyConfigChanged(proxy_config, |
| 267 ProxyConfigService::CONFIG_VALID)); |
| 268 } |
| 269 |
| 270 bool OnJNIThread() const { |
| 271 return jni_task_runner_->RunsTasksOnCurrentThread(); |
| 272 } |
| 273 |
| 274 bool OnNetworkThread() const { |
| 275 return network_task_runner_->RunsTasksOnCurrentThread(); |
| 276 } |
| 277 |
| 278 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_; |
| 279 |
| 280 JNIDelegateImpl jni_delegate_; |
| 281 ObserverList<Observer> observers_; |
| 282 scoped_refptr<base::SequencedTaskRunner> network_task_runner_; |
| 283 scoped_refptr<base::SequencedTaskRunner> jni_task_runner_; |
| 284 GetPropertyCallback get_property_callback_; |
| 285 ProxyConfig proxy_config_; |
| 286 |
| 287 DISALLOW_COPY_AND_ASSIGN(Delegate); |
| 288 }; |
| 289 |
| 290 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( |
| 291 base::SequencedTaskRunner* network_task_runner, |
| 292 base::SequencedTaskRunner* jni_task_runner) |
| 293 : delegate_(new Delegate( |
| 294 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) { |
| 295 delegate_->SetupJNI(); |
| 296 delegate_->FetchInitialConfig(); |
| 297 } |
| 298 |
| 299 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { |
| 300 delegate_->Shutdown(); |
| 301 } |
| 302 |
| 303 // static |
| 304 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { |
| 305 return RegisterNativesImpl(env); |
| 306 } |
| 307 |
| 308 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { |
| 309 delegate_->AddObserver(observer); |
| 310 } |
| 311 |
| 312 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { |
| 313 delegate_->RemoveObserver(observer); |
| 314 } |
| 315 |
| 316 ProxyConfigService::ConfigAvailability |
| 317 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { |
| 318 return delegate_->GetLatestProxyConfig(config); |
| 319 } |
| 320 |
| 321 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( |
| 322 base::SequencedTaskRunner* network_task_runner, |
| 323 base::SequencedTaskRunner* jni_task_runner, |
| 324 GetPropertyCallback get_property_callback) |
| 325 : delegate_(new Delegate( |
| 326 network_task_runner, jni_task_runner, get_property_callback)) { |
| 327 delegate_->FetchInitialConfig(); |
| 328 } |
| 329 |
| 330 void ProxyConfigServiceAndroid::ProxySettingsChanged() { |
| 331 delegate_->ProxySettingsChanged(); |
| 332 } |
| 333 |
| 334 } // namespace net |
| OLD | NEW |