| Index: net/proxy/proxy_config_service_android.cc
|
| diff --git a/net/proxy/proxy_config_service_android.cc b/net/proxy/proxy_config_service_android.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a73109361e5af4e6e4ec7bf36cedd068dfbb3067
|
| --- /dev/null
|
| +++ b/net/proxy/proxy_config_service_android.cc
|
| @@ -0,0 +1,287 @@
|
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/proxy/proxy_config_service_android.h"
|
| +
|
| +#include <sys/system_properties.h>
|
| +
|
| +#include "base/android/jni_android.h"
|
| +#include "base/android/jni_string.h"
|
| +#include "base/android/scoped_java_ref.h"
|
| +#include "base/basictypes.h"
|
| +#include "base/bind.h"
|
| +#include "base/compiler_specific.h"
|
| +#include "base/location.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/observer_list.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/string_tokenizer.h"
|
| +#include "base/string_util.h"
|
| +#include "googleurl/src/url_parse.h"
|
| +#include "jni/proxy_change_listener_jni.h"
|
| +#include "net/base/host_port_pair.h"
|
| +#include "net/proxy/proxy_config.h"
|
| +
|
| +using base::android::AttachCurrentThread;
|
| +using base::android::ConvertUTF8ToJavaString;
|
| +using base::android::ConvertJavaStringToUTF8;
|
| +using base::android::CheckException;
|
| +using base::android::ClearException;
|
| +using base::android::GetMethodID;
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +// Returns whether the provided string was successfully converted to a port.
|
| +bool ConvertStringToPort(const std::string& port, int* output) {
|
| + url_parse::Component component(0, port.size());
|
| + int result = url_parse::ParsePort(port.c_str(), component);
|
| + if (result == url_parse::PORT_INVALID ||
|
| + result == url_parse::PORT_UNSPECIFIED)
|
| + return false;
|
| + *output = result;
|
| + return true;
|
| +}
|
| +
|
| +ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme,
|
| + const std::string& proxy_host,
|
| + const std::string& proxy_port) {
|
| + DCHECK(!proxy_host.empty());
|
| + int port_as_int = 0;
|
| + if (proxy_port.empty())
|
| + port_as_int = ProxyServer::GetDefaultPortForScheme(scheme);
|
| + else if (!ConvertStringToPort(proxy_port, &port_as_int))
|
| + return ProxyServer();
|
| + DCHECK(port_as_int > 0);
|
| + return ProxyServer(
|
| + scheme,
|
| + HostPortPair(proxy_host, static_cast<uint16>(port_as_int)));
|
| +}
|
| +
|
| +ProxyServer LookupProxy(
|
| + const std::string& prefix,
|
| + ProxyConfigServiceAndroid::Delegate* delegate,
|
| + ProxyServer::Scheme scheme) {
|
| + DCHECK(!prefix.empty());
|
| + std::string proxy_host = delegate->GetProperty(prefix + ".proxyHost");
|
| + if (!proxy_host.empty()) {
|
| + std::string proxy_port = delegate->GetProperty(prefix + ".proxyPort");
|
| + return ConstructProxyServer(scheme, proxy_host, proxy_port);
|
| + }
|
| + // Fall back to default proxy, if any.
|
| + proxy_host = delegate->GetProperty("proxyHost");
|
| + if (!proxy_host.empty()) {
|
| + std::string proxy_port = delegate->GetProperty("proxyPort");
|
| + return ConstructProxyServer(scheme, proxy_host, proxy_port);
|
| + }
|
| + return ProxyServer();
|
| +}
|
| +
|
| +ProxyServer LookupSocksProxy(
|
| + ProxyConfigServiceAndroid::Delegate* delegate) {
|
| + std::string proxy_host = delegate->GetProperty("socksProxyHost");
|
| + if (!proxy_host.empty()) {
|
| + std::string proxy_port = delegate->GetProperty("socksProxyPort");
|
| + return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxy_host,
|
| + proxy_port);
|
| + }
|
| + return ProxyServer();
|
| +}
|
| +
|
| +void AddBypassRules(
|
| + const std::string& scheme,
|
| + ProxyConfigServiceAndroid::Delegate* delegate,
|
| + ProxyBypassRules* bypass_rules) {
|
| + // The format of a hostname pattern is a list of hostnames that are separated
|
| + // by | and that use * as a wildcard. For example, setting the
|
| + // http.nonProxyHosts property to *.android.com|*.kernel.org will cause
|
| + // requests to http://developer.android.com to be made without a proxy.
|
| + std::string non_proxy_hosts =
|
| + delegate->GetProperty(scheme + ".nonProxyHosts");
|
| + if (non_proxy_hosts.empty())
|
| + return;
|
| + StringTokenizer tokenizer(non_proxy_hosts, "|");
|
| + while (tokenizer.GetNext()) {
|
| + std::string token = tokenizer.token();
|
| + std::string pattern;
|
| + TrimWhitespaceASCII(token, TRIM_ALL, &pattern);
|
| + if (pattern.empty())
|
| + continue;
|
| + // '?' is not one of the specified pattern characters above.
|
| + DCHECK_EQ(std::string::npos, pattern.find('?'));
|
| + bypass_rules->AddRuleForHostname(scheme, pattern, -1);
|
| + }
|
| +}
|
| +
|
| +// Returns true if a valid proxy was found.
|
| +bool GetProxyRules(
|
| + ProxyConfigServiceAndroid::Delegate* delegate,
|
| + ProxyConfig::ProxyRules* rules) {
|
| + // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the
|
| + // equivalent Android implementation.
|
| + rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
|
| + rules->proxy_for_http = LookupProxy("http", delegate,
|
| + ProxyServer::SCHEME_HTTP);
|
| + rules->proxy_for_https = LookupProxy("https", delegate,
|
| + ProxyServer::SCHEME_HTTPS);
|
| + rules->proxy_for_ftp = LookupProxy("ftp", delegate, ProxyServer::SCHEME_HTTP);
|
| + rules->fallback_proxy = LookupSocksProxy(delegate);
|
| + rules->bypass_rules.Clear();
|
| + AddBypassRules("ftp", delegate, &rules->bypass_rules);
|
| + AddBypassRules("http", delegate, &rules->bypass_rules);
|
| + AddBypassRules("https", delegate, &rules->bypass_rules);
|
| + return rules->proxy_for_http.is_valid() ||
|
| + rules->proxy_for_https.is_valid() ||
|
| + rules->proxy_for_ftp.is_valid() ||
|
| + rules->fallback_proxy.is_valid();
|
| +};
|
| +
|
| +void GetLatestProxyConfigInternal(
|
| + ProxyConfigServiceAndroid::Delegate* delegate,
|
| + ProxyConfig* config) {
|
| + if (!GetProxyRules(delegate, &config->proxy_rules()))
|
| + *config = ProxyConfig::CreateDirect();
|
| +}
|
| +
|
| +// This class implements the link to Android's proxy broadcast mechanism.
|
| +class DelegateImpl : public ProxyConfigServiceAndroid::Delegate {
|
| + public:
|
| + virtual void Start(ProxyConfigServiceAndroid* service) OVERRIDE {
|
| + JNIEnv* env = AttachCurrentThread();
|
| + if (java_proxy_change_listener_android_.is_null()) {
|
| + java_proxy_change_listener_android_.Reset(
|
| + Java_ProxyChangeListener_create(
|
| + env, base::android::GetApplicationContext()));
|
| + CHECK(!java_proxy_change_listener_android_.is_null());
|
| + }
|
| + Java_ProxyChangeListener_start(
|
| + env,
|
| + java_proxy_change_listener_android_.obj(),
|
| + reinterpret_cast<jint>(service));
|
| + }
|
| +
|
| + virtual void Stop() OVERRIDE {
|
| + // The java object will be NULL if Stop() was already called (e.g. after a
|
| + // Start()), or if Start() was never called. Stop() is called from the
|
| + // destructor of ProxyConfigServiceAndroid.
|
| + if (java_proxy_change_listener_android_.is_null())
|
| + return;
|
| + JNIEnv* env = AttachCurrentThread();
|
| + Java_ProxyChangeListener_stop(env,
|
| + java_proxy_change_listener_android_.obj());
|
| + }
|
| +
|
| + virtual std::string GetProperty(const std::string& property) OVERRIDE {
|
| + // Use Java System.getProperty to get configuration information.
|
| + // TODO(pliard): Conversion to/from UTF8 ok here?
|
| + JNIEnv* env = AttachCurrentThread();
|
| + ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
|
| + ScopedJavaLocalRef<jstring> result =
|
| + Java_ProxyChangeListener_getProperty(env, str.obj());
|
| + return result.is_null() ?
|
| + std::string() : ConvertJavaStringToUTF8(env, result.obj());
|
| + }
|
| +
|
| + private:
|
| + base::android::ScopedJavaGlobalRef<jobject>
|
| + java_proxy_change_listener_android_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class ProxyConfigServiceAndroid::SharedObserverList
|
| + : public base::RefCountedThreadSafe<SharedObserverList> {
|
| + public:
|
| + SharedObserverList() {}
|
| +
|
| + ObserverList<Observer> value;
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<SharedObserverList>;
|
| + ~SharedObserverList() {}
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SharedObserverList);
|
| +};
|
| +
|
| +ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
|
| + base::SingleThreadTaskRunner* network_task_runner,
|
| + base::SingleThreadTaskRunner* jni_task_runner)
|
| + : delegate_(new DelegateImpl()),
|
| + observers_(new SharedObserverList()),
|
| + network_task_runner_(network_task_runner),
|
| + jni_task_runner_(jni_task_runner) {
|
| + DCHECK(network_task_runner_.get());
|
| + DCHECK(jni_task_runner_.get());
|
| +}
|
| +
|
| +ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
|
| + jni_task_runner_->PostTask(FROM_HERE, base::Bind(&Delegate::Stop, delegate_));
|
| +}
|
| +
|
| +// static
|
| +bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
|
| + return RegisterNativesImpl(env);
|
| +}
|
| +
|
| +void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
|
| + DCHECK(OnNetworkThread());
|
| + if (observers_->value.size() == 0)
|
| + jni_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&Delegate::Start, delegate_, this));
|
| + observers_->value.AddObserver(observer);
|
| +}
|
| +
|
| +void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
|
| + DCHECK(OnNetworkThread());
|
| + observers_->value.RemoveObserver(observer);
|
| + if (observers_->value.size() == 0)
|
| + jni_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&Delegate::Stop, delegate_));
|
| +}
|
| +
|
| +ProxyConfigService::ConfigAvailability
|
| +ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
|
| + if (!config)
|
| + return ProxyConfigService::CONFIG_UNSET;
|
| + base::AutoLock lock(lock_);
|
| + *config = latest_proxy_config_;
|
| + return ProxyConfigService::CONFIG_VALID;
|
| +}
|
| +
|
| +void ProxyConfigServiceAndroid::ProxySettingsChangedInternal() {
|
| + ProxyConfig proxy_config;
|
| + GetLatestProxyConfigInternal(delegate_.get(), &proxy_config);
|
| + network_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ProxySettingsChangedCallback, observers_, proxy_config));
|
| + base::AutoLock lock(lock_);
|
| + latest_proxy_config_ = proxy_config;
|
| +}
|
| +
|
| +ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
|
| + base::SingleThreadTaskRunner* network_task_runner,
|
| + base::SingleThreadTaskRunner* jni_task_runner,
|
| + ProxyConfigServiceAndroid::Delegate* delegate)
|
| + : delegate_(delegate),
|
| + observers_(new SharedObserverList()),
|
| + network_task_runner_(network_task_runner),
|
| + jni_task_runner_(jni_task_runner) {
|
| +}
|
| +
|
| +// static
|
| +void ProxyConfigServiceAndroid::ProxySettingsChangedCallback(
|
| + const scoped_refptr<SharedObserverList>& observers,
|
| + const ProxyConfig& proxy_config) {
|
| + FOR_EACH_OBSERVER(Observer, observers->value,
|
| + OnProxyConfigChanged(proxy_config,
|
| + ProxyConfigService::CONFIG_VALID));
|
| +}
|
| +
|
| +bool ProxyConfigServiceAndroid::OnNetworkThread() const {
|
| + return network_task_runner_->BelongsToCurrentThread();
|
| +}
|
| +
|
| +} // namespace net
|
|
|