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

Unified 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 side-by-side diff with in-line comments
Download patch
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..6e8dc550b3f7dd98b3f592a176b8c52d3aa92bb9
--- /dev/null
+++ b/net/proxy/proxy_config_service_android.cc
@@ -0,0 +1,330 @@
+// 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 "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "jni/proxy_change_listener_jni.h"
+#include "net/proxy/proxy_config.h"
+
+#include <sys/system_properties.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 {
+// 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.
+class DelegateImpl : public ProxyConfigServiceAndroid::Delegate {
+ public:
+ 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
+ // ProxyConfigServiceAndroid::Delegate:
+ virtual void Start(ProxyConfigServiceAndroid* service) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+ virtual std::string GetProperty(const std::string& property) OVERRIDE;
+
+ private:
+ ProxyConfigServiceAndroid* service_;
+ base::android::ScopedJavaGlobalRef<jobject>
+ java_proxy_change_listener_android_;
+};
+
+DelegateImpl::DelegateImpl() : service_(NULL) {}
+
+void DelegateImpl::Start(ProxyConfigServiceAndroid* service) {
+ DCHECK(!service_);
+ JNIEnv* env = AttachCurrentThread();
+ service_ = service;
+ 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));
+}
+
+void DelegateImpl::Stop() {
+ // 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());
+}
+
+std::string DelegateImpl::GetProperty(const std::string& property) {
+ // Use Java System.getProperty to get configuration information.
+ // Question: 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() ? "" : ConvertJavaStringToUTF8(env, result.obj());
+}
+} // namespace
Ryan Sleevi 2012/04/24 18:20:04 nit: Line break between 82 & 83
Philippe 2012/05/09 11:48:39 Done.
+
+ProxyConfigServiceAndroid::ProxyConfigServiceAndroid()
+ : delegate_(new DelegateImpl),
+ notify_task_(NULL) {
+}
+
+ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
+ ProxyConfigServiceAndroid::Delegate* delegate)
+ : delegate_(delegate),
+ notify_task_(NULL) {
+}
+
+ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
+ 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.
+ if (delegate_.get())
+ delegate_->Stop();
+ // Cancel the notify task last, as the java side may have been mid-way through
+ // posting a new notify task when we requested it stop
+ CancelNotifyTask();
+}
+
+bool ProxyConfigServiceAndroid::Init(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
+ DCHECK(OnObserverThread());
+ if (observers_.size() == 0) {
+ AssignObserverThread();
+ delegate_->Start(this);
+ }
+ // Note that any callbacks from the delegate will result in a scheduled task
+ // on this same thread, so there is no race between delegate_->Start() and
+ // AddObserver().
+ observers_.AddObserver(observer);
+}
+
+void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
+ DCHECK(OnObserverThread());
+ observers_.RemoveObserver(observer);
+ if (observers_.size() == 0) {
+ delegate_->Stop();
+ }
+}
+
+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.
+
+ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme,
+ 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.
+ const std::string& proxyPort) {
+ DCHECK(!proxyHost.empty());
+ if (proxyPort.empty())
+ return ProxyServer::FromURI(proxyHost, scheme);
+ return ProxyServer::FromURI(proxyHost + ":" + proxyPort, scheme);
+}
+
+ProxyServer LookupProxy(
+ ProxyConfigServiceAndroid::Delegate& delegate,
+ const std::string& prefix,
+ ProxyServer::Scheme scheme) {
+ std::string proxyHost = delegate.GetProperty(prefix + ".proxyHost");
+ if (!proxyHost.empty()) {
+ std::string proxyPort = delegate.GetProperty(prefix + ".proxyPort");
+ return ConstructProxyServer(scheme, proxyHost, proxyPort);
+ }
+ // Fall back to default proxy, if any.
+ proxyHost = delegate.GetProperty("proxyHost");
+ if (!proxyHost.empty()) {
+ std::string proxyPort = delegate.GetProperty("proxyPort");
+ return ConstructProxyServer(scheme, proxyHost, proxyPort);
+ }
+ return ProxyServer();
+}
+
+ProxyServer LookupSocksProxy(
+ ProxyConfigServiceAndroid::Delegate& delegate) {
+ std::string proxyHost = delegate.GetProperty("socksProxyHost");
+ if (!proxyHost.empty()) {
+ std::string proxyPort = delegate.GetProperty("socksProxyPort");
+ return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxyHost,
+ proxyPort);
+ }
+ return ProxyServer();
+}
+
+void AddBypassRules(
+ ProxyConfigServiceAndroid::Delegate& delegate,
+ const std::string& scheme,
+ 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 nonProxyHosts = delegate.GetProperty(scheme + ".nonProxyHosts");
+ if (nonProxyHosts.empty())
+ return;
+ StringTokenizer tokenizer(nonProxyHosts, "|");
+ 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 iff 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
+ // semantics we're trying to match.
+ rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+ rules->proxy_for_http = LookupProxy(delegate, "http",
+ ProxyServer::SCHEME_HTTP);
+ rules->proxy_for_https = LookupProxy(delegate, "https",
+ ProxyServer::SCHEME_HTTPS);
+ rules->proxy_for_ftp = LookupProxy(delegate, "ftp", ProxyServer::SCHEME_HTTP);
+ rules->fallback_proxy = LookupSocksProxy(delegate);
+ rules->bypass_rules.Clear();
+ AddBypassRules(delegate, "ftp", &rules->bypass_rules);
+ AddBypassRules(delegate, "http", &rules->bypass_rules);
+ AddBypassRules(delegate, "https", &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();
+}
+
+} // namespace
+
+ProxyConfigService::ConfigAvailability
+ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
+ DCHECK(OnObserverThread());
+ if (!config)
+ return ProxyConfigService::CONFIG_UNSET;
+ GetLatestProxyConfigInternal(*delegate_.get(), config);
+ return ProxyConfigService::CONFIG_VALID;
+}
+
+// Posted on observer_loop_. See header file for intended behaviour.
+class ProxyConfigServiceAndroid::NotifyTask {
+ public:
+ NotifyTask(ProxyConfigServiceAndroid* service);
+ ~NotifyTask();
+ void Cancel();
+ void Run();
+
+ private:
+ ProxyConfigServiceAndroid* service_;
+};
+
+ProxyConfigServiceAndroid::NotifyTask::NotifyTask(
+ ProxyConfigServiceAndroid* service)
+ : service_(service) {
+}
+
+ProxyConfigServiceAndroid::NotifyTask::~NotifyTask() {
+ // 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.
+ if (service_) {
+ // This could happen if the notify task is removed from the message loop
+ // before it has been run (e.g. if the message loop is destroyed).
+ service_->ClearNotifyTask();
+ }
+}
+
+void ProxyConfigServiceAndroid::NotifyTask::Cancel() {
+ // 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.
+ service_ = NULL;
+}
+
+void ProxyConfigServiceAndroid::NotifyTask::Run() {
+ // Should be called from IO thread.
+ if (service_) {
+ service_->ProxySettingsChangedCallback();
+ service_ = NULL;
+ }
+}
+
+void ProxyConfigServiceAndroid::ProxySettingsChanged() {
+ // Called on any thread.
+ DCHECK(observer_loop_); // Should have been assigned in AddObserver().
+ NotifyTask* task = NULL;
+ {
+ base::AutoLock lock(notify_task_lock_);
+ // If there is already a task scheduled, don't schedule another one.
+ if (notify_task_)
+ return;
+ notify_task_ = task = new NotifyTask(this);
+ }
+ 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.
+ base::Bind(&NotifyTask::Run, base::Owned(task)));
+}
+
+void ProxyConfigServiceAndroid::ProxySettingsChangedCallback() {
+ DCHECK(OnObserverThread());
+ // Allow another task to be scheduled (before running observers).
+ ClearNotifyTask();
+ ProxyConfig config;
+ GetLatestProxyConfigInternal(*delegate_.get(), &config);
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnProxyConfigChanged(config,
+ ProxyConfigService::CONFIG_VALID));
+}
+
+void ProxyConfigServiceAndroid::ClearNotifyTask() {
+ base::AutoLock lock(notify_task_lock_);
+ notify_task_ = NULL;
+}
+
+void ProxyConfigServiceAndroid::CancelNotifyTask() {
+ base::AutoLock lock(notify_task_lock_);
+ if (notify_task_) {
+ notify_task_->Cancel();
+ notify_task_ = NULL;
+ }
+}
+
+bool ProxyConfigServiceAndroid::OnObserverThread() {
+ // Check that we're on the observer thread or the observer thread hasn't been
+ // assigned yet. The observer thread is only assigned in AddObserver(), which,
+ // in theory, might not be called.
+ 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
+}
+
+void ProxyConfigServiceAndroid::AssignObserverThread() {
+ // Called from AddObserver. We assign observer_loop_ here under a lock so that
+ // it is guaranteed that ProxySettingsChanged(), which is called from another
+ // 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
+ if (!observer_loop_) {
+ observer_loop_ = base::MessageLoopProxy::current();
+ } else {
+ 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.
+ }
+}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698