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. 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 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. 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 146 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, 147 ProxyConfig* config) { 148 if (!GetProxyRules(get_property, &config->proxy_rules())) 149 *config = ProxyConfig::CreateDirect(); 150 } 151 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 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: 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 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 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 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. 222 void AddObserver(Observer* observer) { 223 DCHECK(OnNetworkThread()); 224 observers_.AddObserver(observer); 225 } 226 227 void RemoveObserver(Observer* observer) { 228 DCHECK(OnNetworkThread()); 229 observers_.RemoveObserver(observer); 230 } 231 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. 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. 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: 267 explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {} 268 269 // ProxyConfigServiceAndroid::JNIDelegate overrides. 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 276 virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE { 277 delegate_->ProxySettingsChanged(); 278 } 279 280 private: 281 Delegate* const delegate_; 282 }; 283 284 virtual ~Delegate() {} 285 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. 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 302 bool OnJNIThread() const { 303 return jni_task_runner_->RunsTasksOnCurrentThread(); 304 } 305 306 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 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 331 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { 332 delegate_->Shutdown(); 333 } 334 335 // static 336 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { 337 return RegisterNativesImpl(env); 338 } 339 340 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { 341 delegate_->AddObserver(observer); 342 } 343 344 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { 345 delegate_->RemoveObserver(observer); 346 } 347 348 ProxyConfigService::ConfigAvailability 349 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { 350 return delegate_->GetLatestProxyConfig(config); 351 } 352 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 363 void ProxyConfigServiceAndroid::ProxySettingsChanged() { 364 delegate_->ProxySettingsChanged(); 365 } 366 367 } // namespace net 368