summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk3
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/ApplicationThreadNative.java7
-rw-r--r--core/java/android/app/IApplicationThread.java3
-rw-r--r--core/java/android/net/PacProxySelector.java80
-rw-r--r--core/java/android/net/Proxy.java48
-rw-r--r--core/java/android/net/ProxyProperties.java45
-rw-r--r--core/java/android/provider/Settings.java15
-rw-r--r--packages/services/PacProcessor/Android.mk41
-rw-r--r--packages/services/PacProcessor/IProxyService.cpp97
-rw-r--r--packages/services/PacProcessor/IProxyService.h59
-rw-r--r--packages/services/PacProcessor/ProxyService.cpp96
-rw-r--r--packages/services/PacProcessor/ProxyService.h33
-rw-r--r--packages/services/PacProcessor/main_pacserver.cpp43
-rw-r--r--packages/services/Proxy/Android.mk14
-rw-r--r--packages/services/Proxy/AndroidManifest.xml25
-rw-r--r--packages/services/Proxy/com/android/net/IProxyService.aidl16
-rw-r--r--packages/services/Proxy/res/values/strings.xml22
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java219
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java68
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/ProxyServiceReceiver.java22
-rw-r--r--packages/services/Proxy/src/com/android/proxyhandler/SocketConnect.java59
-rw-r--r--services/java/com/android/server/ConnectivityService.java40
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/java/com/android/server/connectivity/PacManager.java226
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java19
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java5
27 files changed, 1268 insertions, 45 deletions
diff --git a/Android.mk b/Android.mk
index 4b1fa58..7e34c84 100644
--- a/Android.mk
+++ b/Android.mk
@@ -257,7 +257,8 @@ LOCAL_SRC_FILES += \
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
- wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
+ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
+ packages/services/Proxy/com/android/net/IProxyService.aidl \
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9f9d61..5300eca 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -793,8 +793,8 @@ public final class ActivityThread {
InetAddress.clearDnsCache();
}
- public void setHttpProxy(String host, String port, String exclList) {
- Proxy.setHttpProxySystemProperty(host, port, exclList);
+ public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) {
+ Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
public void processInBackground() {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 6f18e84..1465de2 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -338,7 +338,8 @@ public abstract class ApplicationThreadNative extends Binder
final String proxy = data.readString();
final String port = data.readString();
final String exclList = data.readString();
- setHttpProxy(proxy, port, exclList);
+ final String pacFileUrl = data.readString();
+ setHttpProxy(proxy, port, exclList, pacFileUrl);
return true;
}
@@ -1001,12 +1002,14 @@ class ApplicationThreadProxy implements IApplicationThread {
data.recycle();
}
- public void setHttpProxy(String proxy, String port, String exclList) throws RemoteException {
+ public void setHttpProxy(String proxy, String port, String exclList,
+ String pacFileUrl) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(proxy);
data.writeString(port);
data.writeString(exclList);
+ data.writeString(pacFileUrl);
mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8e882df..61c499a 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -101,7 +101,8 @@ public interface IApplicationThread extends IInterface {
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
void updateTimeZone() throws RemoteException;
void clearDnsCache() throws RemoteException;
- void setHttpProxy(String proxy, String port, String exclList) throws RemoteException;
+ void setHttpProxy(String proxy, String port, String exclList,
+ String pacFileUrl) throws RemoteException;
void processInBackground() throws RemoteException;
void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
new file mode 100644
index 0000000..be3a31d
--- /dev/null
+++ b/core/java/android/net/PacProxySelector.java
@@ -0,0 +1,80 @@
+
+package android.net;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.net.IProxyService;
+import com.google.android.collect.Lists;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.Proxy.Type;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class PacProxySelector extends ProxySelector {
+ public static final String PROXY_SERVICE = "com.android.net.IProxyService";
+ private IProxyService mProxyService;
+
+ public PacProxySelector() {
+ mProxyService = IProxyService.Stub.asInterface(
+ ServiceManager.getService(PROXY_SERVICE));
+ }
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ String response = null;
+ String urlString;
+ try {
+ urlString = uri.toURL().toString();
+ } catch (MalformedURLException e) {
+ urlString = uri.getHost();
+ }
+ try {
+ response = mProxyService.resolvePacFile(uri.getHost(), urlString);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ return parseResponse(response);
+ }
+
+ private static List<Proxy> parseResponse(String response) {
+ String[] split = response.split(";");
+ List<Proxy> ret = Lists.newArrayList();
+ for (String s : split) {
+ String trimmed = s.trim();
+ if (trimmed.equals("DIRECT")) {
+ ret.add(java.net.Proxy.NO_PROXY);
+ } else if (trimmed.startsWith("PROXY ")) {
+ String[] hostPort = trimmed.substring(6).split(":");
+ String host = hostPort[0];
+ int port;
+ try {
+ port = Integer.parseInt(hostPort[1]);
+ } catch (Exception e) {
+ port = 8080;
+ }
+ ret.add(new Proxy(Type.HTTP, new InetSocketAddress(host, port)));
+ }
+ }
+ if (ret.size() == 0) {
+ ret.add(java.net.Proxy.NO_PROXY);
+ }
+ return ret;
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress address, IOException failure) {
+
+ }
+
+}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index a408ea0..5b38f57 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -18,38 +18,25 @@ package android.net;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
-import android.net.ProxyProperties;
-import android.os.Handler;
-import android.os.SystemProperties;
import android.text.TextUtils;
-import android.provider.Settings;
import android.util.Log;
-import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.protocol.HttpContext;
+
import java.net.InetSocketAddress;
import java.net.ProxySelector;
-import java.net.SocketAddress;
import java.net.URI;
-import java.net.UnknownHostException;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import junit.framework.Assert;
-
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.routing.HttpRoutePlanner;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
-import org.apache.http.protocol.HttpContext;
-
/**
* A convenience class for accessing the user and default proxy
* settings.
@@ -60,6 +47,9 @@ public final class Proxy {
private static final boolean DEBUG = false;
private static final String TAG = "Proxy";
+ private static final ProxySelector sDefaultProxySelector;
+ private static PacProxySelector sPacProxySelector;
+
/**
* Used to notify an app that's caching the default connection proxy
* that either the default connection or its proxy has changed.
@@ -96,6 +86,7 @@ public final class Proxy {
static {
HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
+ sDefaultProxySelector = ProxySelector.getDefault();
}
/**
@@ -325,16 +316,19 @@ public final class Proxy {
String host = null;
String port = null;
String exclList = null;
+ String pacFileUrl = null;
if (p != null) {
host = p.getHost();
port = Integer.toString(p.getPort());
exclList = p.getExclusionList();
+ pacFileUrl = p.getPacFileUrl();
}
- setHttpProxySystemProperty(host, port, exclList);
+ setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
/** @hide */
- public static final void setHttpProxySystemProperty(String host, String port, String exclList) {
+ public static final void setHttpProxySystemProperty(String host, String port, String exclList,
+ String pacFileUrl) {
if (exclList != null) exclList = exclList.replace(",", "|");
if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
if (host != null) {
@@ -358,5 +352,13 @@ public final class Proxy {
System.clearProperty("http.nonProxyHosts");
System.clearProperty("https.nonProxyHosts");
}
+ if ((pacFileUrl != null) && !TextUtils.isEmpty(pacFileUrl)) {
+ if (sPacProxySelector == null) {
+ sPacProxySelector = new PacProxySelector();
+ }
+ ProxySelector.setDefault(sPacProxySelector);
+ } else {
+ ProxySelector.setDefault(sDefaultProxySelector);
+ }
}
}
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
index 9c4772b..76aea9f 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyProperties.java
@@ -20,9 +20,7 @@ package android.net;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Log;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Locale;
@@ -38,17 +36,30 @@ public class ProxyProperties implements Parcelable {
private String mExclusionList;
private String[] mParsedExclusionList;
+ private String mPacFileUrl;
+ public static final String LOCAL_EXCL_LIST = "";
+ public static final int LOCAL_PORT = 8182;
+ public static final String LOCAL_HOST = "localhost";
+
public ProxyProperties(String host, int port, String exclList) {
mHost = host;
mPort = port;
setExclusionList(exclList);
}
+ public ProxyProperties(String pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ setExclusionList(LOCAL_EXCL_LIST);
+ mPacFileUrl = pacFileUrl;
+ }
+
private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
mHost = host;
mPort = port;
mExclusionList = exclList;
mParsedExclusionList = parsedExclList;
+ mPacFileUrl = null;
}
// copy constructor instead of clone
@@ -56,6 +67,7 @@ public class ProxyProperties implements Parcelable {
if (source != null) {
mHost = source.getHost();
mPort = source.getPort();
+ mPacFileUrl = source.getPacFileUrl();
mExclusionList = source.getExclusionList();
mParsedExclusionList = source.mParsedExclusionList;
}
@@ -69,6 +81,10 @@ public class ProxyProperties implements Parcelable {
return inetSocketAddress;
}
+ public String getPacFileUrl() {
+ return mPacFileUrl;
+ }
+
public String getHost() {
return mHost;
}
@@ -130,7 +146,10 @@ public class ProxyProperties implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (mHost != null) {
+ if (mPacFileUrl != null) {
+ sb.append("PAC Script: ");
+ sb.append(mPacFileUrl);
+ } else if (mHost != null) {
sb.append("[");
sb.append(mHost);
sb.append("] ");
@@ -148,6 +167,14 @@ public class ProxyProperties implements Parcelable {
public boolean equals(Object o) {
if (!(o instanceof ProxyProperties)) return false;
ProxyProperties p = (ProxyProperties)o;
+ // If PAC URL is present in either then they must be equal.
+ // Other parameters will only be for fall back.
+ if (!TextUtils.isEmpty(mPacFileUrl)) {
+ return mPacFileUrl.equals(p.getPacFileUrl());
+ }
+ if (!TextUtils.isEmpty(p.getPacFileUrl())) {
+ return false;
+ }
if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
return false;
@@ -181,6 +208,13 @@ public class ProxyProperties implements Parcelable {
* @hide
*/
public void writeToParcel(Parcel dest, int flags) {
+ if (mPacFileUrl != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mPacFileUrl);
+ return;
+ } else {
+ dest.writeByte((byte)0);
+ }
if (mHost != null) {
dest.writeByte((byte)1);
dest.writeString(mHost);
@@ -201,7 +235,10 @@ public class ProxyProperties implements Parcelable {
public ProxyProperties createFromParcel(Parcel in) {
String host = null;
int port = 0;
- if (in.readByte() == 1) {
+ if (in.readByte() != 0) {
+ return new ProxyProperties(in.readString());
+ }
+ if (in.readByte() != 0) {
host = in.readString();
port = in.readInt();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b252641..130123f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5292,6 +5292,14 @@ public final class Settings {
public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
/**
+ * The series of successively longer delays used in retrying to download PAC file.
+ * Last delay is used between successful PAC downloads.
+ *
+ * @hide
+ */
+ public static final String PAC_CHANGE_DELAY = "pac_change_delay";
+
+ /**
* Setting to turn off captive portal detection. Feature is enabled by
* default and the setting needs to be set to 0 to disable it.
*
@@ -5386,6 +5394,13 @@ public final class Settings {
GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list";
/**
+ * The location PAC File for the proxy.
+ * @hide
+ */
+ public static final String
+ GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
+
+ /**
* Enables the UI setting to allow the user to specify the global HTTP
* proxy and associated exclusion list.
*
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
new file mode 100644
index 0000000..e4afde6
--- /dev/null
+++ b/packages/services/PacProcessor/Android.mk
@@ -0,0 +1,41 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main_pacserver.cpp \
+ ProxyService.cpp \
+ IProxyService.cpp
+
+LOCAL_C_INCLUDES += \
+ external/chromium-libpac/src
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ liblog \
+ libpac \
+ libbinder \
+ libstlport
+
+LOCAL_MODULE := pacserver
+LOCAL_MODULE_TAGS := optional
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_EXECUTABLE)
diff --git a/packages/services/PacProcessor/IProxyService.cpp b/packages/services/PacProcessor/IProxyService.cpp
new file mode 100644
index 0000000..3707d85
--- /dev/null
+++ b/packages/services/PacProcessor/IProxyService.cpp
@@ -0,0 +1,97 @@
+#define LOG_TAG "ProxyTesting"
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <utils/Errors.h>
+#include "IProxyService.h"
+
+#include <utils/Log.h>
+
+#include <private/android_filesystem_config.h>
+
+using namespace android;
+
+String16 BpProxyService::resolveProxies(String16 host, String16 url) {
+ String16 ret;
+ return ret;
+}
+
+void BpProxyService::setPacFile(String16& scriptContents) {
+
+}
+
+void BpProxyService::startPacSystem() {
+
+}
+void BpProxyService::stopPacSystem() {
+
+}
+
+IMPLEMENT_META_INTERFACE(ProxyService, "com.android.net.IProxyService");
+
+status_t BnProxyService::onTransact(
+ uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ int returnInt = 0;
+ switch (code) {
+ case RESOLVE_PROXIES:
+ {
+ CHECK_INTERFACE(IProxyService, data, reply);
+ String16 host = data.readString16();
+ String16 url = data.readString16();
+ String16 response = resolveProxies(host, url);
+ reply->writeNoException();
+ reply->writeString16(response);
+ return NO_ERROR;
+ } break;
+ case SET_PAC:
+ {
+ CHECK_INTERFACE(IProxyService, data, reply);
+ if (notSystemUid()) {
+ returnInt = 1;
+ } else {
+ String16 pacFile = data.readString16();
+ setPacFile(pacFile);
+ }
+ reply->writeNoException();
+ reply->writeInt32(returnInt);
+ return NO_ERROR;
+ } break;
+ case START_PAC:
+ {
+ CHECK_INTERFACE(IProxyService, data, reply);
+ if (notSystemUid()) {
+ returnInt = 1;
+ } else {
+ startPacSystem();
+ }
+ reply->writeNoException();
+ reply->writeInt32(returnInt);
+ return NO_ERROR;
+ } break;
+ case STOP_PAC:
+ {
+ CHECK_INTERFACE(IProxyService, data, reply);
+ if (notSystemUid()) {
+ returnInt = 1;
+ } else {
+ stopPacSystem();
+ }
+ reply->writeNoException();
+ reply->writeInt32(returnInt);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+int BnProxyService::getCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+bool BnProxyService::notSystemUid() {
+ return getCallingUid() != AID_SYSTEM;
+}
diff --git a/packages/services/PacProcessor/IProxyService.h b/packages/services/PacProcessor/IProxyService.h
new file mode 100644
index 0000000..57c527b
--- /dev/null
+++ b/packages/services/PacProcessor/IProxyService.h
@@ -0,0 +1,59 @@
+#ifndef IPROXY_SERVICE_H
+#define IPROXY_SERVICE_H
+
+#include <binder/IInterface.h>
+#include <binder/IBinder.h>
+
+namespace android {
+class IProxyService : public IInterface {
+public:
+ /**
+ * Keep up-to-date with
+ * frameworks/base/packages/services/Proxy/com/android/net/IProxyService.aidl
+ */
+ enum {
+ RESOLVE_PROXIES = IBinder::FIRST_CALL_TRANSACTION,
+ SET_PAC,
+ START_PAC,
+ STOP_PAC,
+ };
+public:
+ DECLARE_META_INTERFACE(ProxyService);
+
+public:
+
+ virtual String16 resolveProxies(String16 host, String16 url) = 0;
+
+ virtual void setPacFile(String16& scriptContents) = 0;
+
+ virtual void startPacSystem() = 0;
+ virtual void stopPacSystem() = 0;
+private:
+};
+
+class BpProxyService : public BpInterface<IProxyService> {
+public:
+ BpProxyService(const sp<IBinder>& impl) : BpInterface<IProxyService>(impl) {}
+
+ virtual String16 resolveProxies(String16 host, String16 url);
+
+ virtual void setPacFile(String16& scriptContents);
+
+ virtual void startPacSystem();
+ virtual void stopPacSystem();
+};
+
+class BnProxyService : public BnInterface<IProxyService> {
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+
+private:
+ int getCallingUid();
+
+ bool notSystemUid();
+};
+}
+
+
+#endif //IPROXY_SERVICE_H
diff --git a/packages/services/PacProcessor/ProxyService.cpp b/packages/services/PacProcessor/ProxyService.cpp
new file mode 100644
index 0000000..7084a47
--- /dev/null
+++ b/packages/services/PacProcessor/ProxyService.cpp
@@ -0,0 +1,96 @@
+#define LOG_TAG "ProxyService"
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <utils/threads.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <sys/stat.h>
+#include <proxy_resolver_v8.h>
+#include <sstream>
+
+#include "ProxyService.h"
+
+using namespace net;
+
+using namespace android;
+
+class ProxyErrorLogger : public ProxyErrorListener {
+protected:
+ ~ProxyErrorLogger() {
+
+ }
+public:
+ void AlertMessage(String16 message) {
+ String8 str(message);
+ ALOGD("Alert: %s", str.string());
+ }
+ void ErrorMessage(String16 message) {
+ String8 str(message);
+ ALOGE("Error: %s", str.string());
+ }
+};
+
+void ProxyService::instantiate() {
+ ALOGV("instantiate");
+ defaultServiceManager()->addService(String16("com.android.net.IProxyService"),
+ new ProxyService());
+}
+
+ProxyService::ProxyService() {
+ hasSetScript = false;
+}
+
+ProxyService::~ProxyService() {
+ stopPacSystem();
+}
+
+String16 ProxyService::resolveProxies(String16 host, String16 url) {
+ ALOGV("resolve");
+ String16 blankRet;
+ if (proxyResolver != NULL) {
+ if (hasSetScript) {
+ String16 ret;
+ if (proxyResolver->GetProxyForURL(url, host, &ret) != OK) {
+ return blankRet;
+ }
+ return ret;
+ } else {
+ ALOGD("Unable to resolve PAC when no script is set!");
+ }
+ } else {
+ ALOGE("Cannot parse while resolver not initialized!");
+ }
+ return blankRet;
+}
+
+void ProxyService::setPacFile(String16& scriptContents) {
+ ALOGV("set");
+ if (proxyResolver != NULL) {
+ if (proxyResolver->SetPacScript(scriptContents) != OK) {
+ ALOGD("Unable to initialize PAC - Resolving will not work");
+ } else {
+ hasSetScript = true;
+ }
+ } else {
+ ALOGE("PAC script set while resolver not initialized!");
+ }
+}
+
+void ProxyService::startPacSystem() {
+ ALOGV("start");
+ // Stop in case redundant start call
+ stopPacSystem();
+
+ proxyResolver = new ProxyResolverV8(ProxyResolverJSBindings::CreateDefault(),
+ new ProxyErrorLogger());
+ hasSetScript = false;
+}
+
+void ProxyService::stopPacSystem() {
+ ALOGV("stop");
+ if (proxyResolver != NULL) {
+ delete proxyResolver;
+ proxyResolver = NULL;
+ }
+}
diff --git a/packages/services/PacProcessor/ProxyService.h b/packages/services/PacProcessor/ProxyService.h
new file mode 100644
index 0000000..a0861b2
--- /dev/null
+++ b/packages/services/PacProcessor/ProxyService.h
@@ -0,0 +1,33 @@
+#ifndef PROXY_SERVICE_H
+#define PROXY_SERVICE_H
+
+#include <binder/IInterface.h>
+#include "IProxyService.h"
+#include "proxy_resolver_v8.h"
+
+namespace android {
+
+class ProxyService : public BnProxyService {
+public:
+ static void instantiate();
+
+private:
+ ProxyService();
+ virtual ~ProxyService();
+
+public:
+ String16 resolveProxies(String16 host, String16 url);
+
+ void setPacFile(String16& scriptContents);
+
+ void startPacSystem();
+ void stopPacSystem();
+
+private:
+ net::ProxyResolverV8* proxyResolver;
+ bool hasSetScript;
+};
+
+}
+
+#endif //PROXY_SERVICE_H
diff --git a/packages/services/PacProcessor/main_pacserver.cpp b/packages/services/PacProcessor/main_pacserver.cpp
new file mode 100644
index 0000000..19588b5
--- /dev/null
+++ b/packages/services/PacProcessor/main_pacserver.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "pacserver"
+//#define LOG_NDEBUG 0
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include "ProxyService.h"
+#include "proxy_resolver_v8.h"
+#include <stdio.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ printf("1\n");
+ ALOGV("ServiceManager: %p", sm.get());
+ ProxyService::instantiate();
+ printf("1\n");
+
+ ProcessState::self()->startThreadPool();
+ printf("1\n");
+ IPCThreadState::self()->joinThreadPool();
+}
diff --git a/packages/services/Proxy/Android.mk b/packages/services/Proxy/Android.mk
new file mode 100644
index 0000000..d5546b2
--- /dev/null
+++ b/packages/services/Proxy/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ProxyHandler
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/services/Proxy/AndroidManifest.xml b/packages/services/Proxy/AndroidManifest.xml
new file mode 100644
index 0000000..02475c0
--- /dev/null
+++ b/packages/services/Proxy/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.android.proxyhandler"
+ coreApp="true">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:persistent="true"
+ android:label="@string/app_label"
+ android:process="com.android.proxyhandler">
+
+ <service android:name=".ProxyService"
+ android:exported="true">
+ </service>
+
+ <receiver android:name=".ProxyServiceReceiver">
+
+ <intent-filter>
+ <action android:name="android.intent.action.PROXY_CHANGE" />
+ </intent-filter>
+ </receiver>
+
+ </application>
+</manifest>
diff --git a/packages/services/Proxy/com/android/net/IProxyService.aidl b/packages/services/Proxy/com/android/net/IProxyService.aidl
new file mode 100644
index 0000000..7e9ed79
--- /dev/null
+++ b/packages/services/Proxy/com/android/net/IProxyService.aidl
@@ -0,0 +1,16 @@
+package com.android.net;
+
+/** @hide */
+interface IProxyService
+{
+ /**
+ * Keep up-to-date with
+ * frameworks/base/packages/services/PacProcessor/IProxyService.h
+ */
+ String resolvePacFile(String host, String url);
+
+ int setPacFile(String scriptContents);
+
+ int startPacSystem();
+ int stopPacSystem();
+}
diff --git a/packages/services/Proxy/res/values/strings.xml b/packages/services/Proxy/res/values/strings.xml
new file mode 100644
index 0000000..6188d79
--- /dev/null
+++ b/packages/services/Proxy/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label">ProxyHandler</string>
+
+</resources>
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
new file mode 100644
index 0000000..5d8689e
--- /dev/null
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -0,0 +1,219 @@
+
+package com.android.proxyhandler;
+
+import android.net.ProxyProperties;
+import android.util.Log;
+
+import com.google.android.collect.Lists;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @hide
+ */
+public class ProxyServer extends Thread {
+
+ private static final String CONNECT = "CONNECT";
+ private static final String HTTP_OK = "HTTP/1.1 200 OK\n";
+
+ private static final String TAG = "ProxyServer";
+
+ private ExecutorService threadExecutor;
+
+ public boolean mIsRunning = false;
+
+ private ServerSocket serverSocket;
+ private ProxyProperties mProxy;
+
+ private class ProxyConnection implements Runnable {
+ private Socket connection;
+
+ private ProxyConnection(Socket connection) {
+ this.connection = connection;
+ }
+
+ @Override
+ public void run() {
+ try {
+ android.net.Proxy.setHttpProxySystemProperty(mProxy);
+
+ String requestLine = getLine(connection.getInputStream());
+ if (requestLine == null) {
+ connection.close();
+ return;
+ }
+ String[] splitLine = requestLine.split(" ");
+ if (splitLine.length < 3) {
+ connection.close();
+ return;
+ }
+ String requestType = splitLine[0];
+ String urlString = splitLine[1];
+
+ String host = "";
+ int port = 80;
+
+ if (requestType.equals(CONNECT)) {
+ String[] hostPortSplit = urlString.split(":");
+ host = hostPortSplit[0];
+ try {
+ port = Integer.parseInt(hostPortSplit[1]);
+ } catch (NumberFormatException nfe) {
+ port = 443;
+ }
+ urlString = "Https://" + host + ":" + port;
+ } else {
+ try {
+ URI url = new URI(urlString);
+ host = url.getHost();
+ port = url.getPort();
+ if (port < 0) {
+ port = 80;
+ }
+ } catch (URISyntaxException e) {
+ connection.close();
+ return;
+ }
+ }
+
+ List<Proxy> list = Lists.newArrayList();
+ try {
+ list = ProxySelector.getDefault().select(new URI(urlString));
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+ Socket server = null;
+ for (Proxy proxy : list) {
+ try {
+ if (!proxy.equals(Proxy.NO_PROXY)) {
+ // Only Inets created by PacProxySelector.
+ InetSocketAddress inetSocketAddress =
+ (InetSocketAddress)list.get(0).address();
+ server = new Socket(inetSocketAddress.getAddress(),
+ inetSocketAddress.getPort());
+ sendLine(server, requestLine);
+ } else {
+ server = new Socket(host, port);
+ if (requestType.equals(CONNECT)) {
+ while (getLine(connection.getInputStream()).length() != 0);
+ // No proxy to respond so we must.
+ sendLine(connection, HTTP_OK);
+ } else {
+ sendLine(server, requestLine);
+ }
+ }
+ } catch (IOException ioe) {
+
+ }
+ if (server != null) {
+ break;
+ }
+ }
+ if (server == null) {
+ server = new Socket(host, port);
+ if (requestType.equals(CONNECT)) {
+ while (getLine(connection.getInputStream()).length() != 0);
+ // No proxy to respond so we must.
+ sendLine(connection, HTTP_OK);
+ } else {
+ sendLine(server, requestLine);
+ }
+ }
+ // Pass data back and forth until complete.
+ SocketConnect.connect(connection, server);
+ } catch (IOException e) {
+ Log.d(TAG, "Problem Proxying", e);
+ }
+ try {
+ connection.close();
+ } catch (IOException ioe) {
+
+ }
+ }
+
+ private String getLine(InputStream inputStream) throws IOException {
+ StringBuffer buffer = new StringBuffer();
+ int byteBuffer = inputStream.read();
+ if (byteBuffer < 0) return "";
+ do {
+ if (byteBuffer != '\r') {
+ buffer.append((char)byteBuffer);
+ }
+ byteBuffer = inputStream.read();
+ } while ((byteBuffer != '\n') && (byteBuffer >= 0));
+
+ return buffer.toString();
+ }
+
+ private void sendLine(Socket socket, String line) throws IOException {
+ OutputStream os = socket.getOutputStream();
+ os.write(line.getBytes());
+ os.write('\r');
+ os.write('\n');
+ os.flush();
+ }
+ }
+
+ public ProxyServer() {
+ threadExecutor = Executors.newCachedThreadPool();
+ }
+
+ @Override
+ public void run() {
+ try {
+ serverSocket = new ServerSocket(ProxyService.PORT);
+
+ serverSocket.setReuseAddress(true);
+
+ while (mIsRunning) {
+ try {
+ ProxyConnection parser = new ProxyConnection(serverSocket.accept());
+
+ threadExecutor.execute(parser);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ } catch (SocketException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ mIsRunning = false;
+ }
+
+ public synchronized void startServer() {
+ mIsRunning = true;
+ start();
+ }
+
+ public synchronized void stopServer() {
+ mIsRunning = false;
+ if (serverSocket != null) {
+ try {
+ serverSocket.close();
+ serverSocket = null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void setProxy(ProxyProperties proxy) {
+ mProxy = proxy;
+ }
+}
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
new file mode 100644
index 0000000..0aea5ee
--- /dev/null
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
@@ -0,0 +1,68 @@
+package com.android.proxyhandler;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.Proxy;
+import android.net.ProxyProperties;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+/**
+ * @hide
+ */
+public class ProxyService extends Service {
+
+ private static ProxyServer server = null;
+
+ /** Keep these values up-to-date with PacManager.java */
+ public static final String KEY_PROXY = "keyProxy";
+ public static final String HOST = "localhost";
+ public static final int PORT = 8182;
+ public static final String EXCL_LIST = "";
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null) {
+ handleCommand(intent);
+ }
+ return START_STICKY;
+ }
+
+ private void handleCommand(Intent intent) {
+ Bundle bundle = intent.getExtras();
+ ProxyProperties proxy = null;
+ if ((bundle != null) && bundle.containsKey(Proxy.EXTRA_PROXY_INFO)) {
+ proxy = bundle.getParcelable(Proxy.EXTRA_PROXY_INFO);
+ if ((proxy != null) && !TextUtils.isEmpty(proxy.getPacFileUrl())) {
+ startProxy(proxy);
+ } else {
+ stopSelf();
+ }
+ } else {
+ stopSelf();
+ }
+ }
+
+
+ private void startProxy(ProxyProperties proxy) {
+ if (server == null) {
+ server = new ProxyServer();
+ server.startServer();
+ }
+ server.setProxy(proxy);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (server != null) {
+ server.stopServer();
+ server = null;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServiceReceiver.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServiceReceiver.java
new file mode 100644
index 0000000..f5c2ca5
--- /dev/null
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServiceReceiver.java
@@ -0,0 +1,22 @@
+package com.android.proxyhandler;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Proxy;
+import android.os.Bundle;
+
+public class ProxyServiceReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent service = new Intent(context, ProxyService.class);
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ service.putExtra(Proxy.EXTRA_PROXY_INFO,
+ bundle.getParcelable(Proxy.EXTRA_PROXY_INFO));
+ }
+ context.startService(service);
+ }
+
+}
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/SocketConnect.java b/packages/services/Proxy/src/com/android/proxyhandler/SocketConnect.java
new file mode 100644
index 0000000..0d7df7f
--- /dev/null
+++ b/packages/services/Proxy/src/com/android/proxyhandler/SocketConnect.java
@@ -0,0 +1,59 @@
+package com.android.proxyhandler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * @hide
+ */
+public class SocketConnect extends Thread {
+
+ private InputStream from;
+ private OutputStream to;
+
+ public SocketConnect(Socket from, Socket to) throws IOException {
+ this.from = from.getInputStream();
+ this.to = to.getOutputStream();
+ start();
+ }
+
+ @Override
+ public void run() {
+ final byte[] buffer = new byte[512];
+
+ try {
+ while (true) {
+ int r = from.read(buffer);
+ if (r < 0) {
+ break;
+ }
+ to.write(buffer, 0, r);
+ }
+ from.close();
+ to.close();
+ } catch (IOException io) {
+
+ }
+ }
+
+ public static void connect(Socket first, Socket second) {
+ try {
+ SocketConnect sc1 = new SocketConnect(first, second);
+ SocketConnect sc2 = new SocketConnect(second, first);
+ try {
+ sc1.join();
+ } catch (InterruptedException e) {
+ }
+ try {
+ sc2.join();
+ } catch (InterruptedException e) {
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 17ae85f..3ae2eb5 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -56,13 +56,11 @@ import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.Uri;
import android.net.LinkProperties.CompareResult;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
import android.net.NetworkStateTracker;
@@ -70,6 +68,7 @@ import android.net.NetworkUtils;
import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.RouteInfo;
+import android.net.Uri;
import android.net.wifi.WifiStateTracker;
import android.net.wimax.WimaxManagerConstants;
import android.os.AsyncTask;
@@ -102,6 +101,7 @@ import android.util.SparseIntArray;
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
@@ -110,9 +110,11 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
+import com.android.net.IProxyService;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -120,8 +122,6 @@ import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
-import com.android.internal.annotations.GuardedBy;
-
import dalvik.system.DexClassLoader;
import org.xmlpull.v1.XmlPullParser;
@@ -370,6 +370,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// track the global proxy.
private ProxyProperties mGlobalProxy = null;
+ private PacManager mPacManager = null;
+
private SettingsObserver mSettingsObserver;
NetworkConfig[] mNetConfigs;
@@ -631,6 +633,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mDataConnectionStats = new DataConnectionStats(mContext);
mDataConnectionStats.startMonitoring();
+
+ mPacManager = new PacManager(mContext);
}
/**
@@ -3168,13 +3172,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// of proxy info to all the JVMs.
// enforceAccessPermission();
synchronized (mProxyLock) {
- if (mGlobalProxy != null) return mGlobalProxy;
- return (mDefaultProxyDisabled ? null : mDefaultProxy);
+ ProxyProperties ret = mGlobalProxy;
+ if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
+ return ret;
}
}
public void setGlobalProxy(ProxyProperties proxyProperties) {
enforceConnectivityInternalPermission();
+
synchronized (mProxyLock) {
if (proxyProperties == mGlobalProxy) return;
if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
@@ -3183,11 +3189,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
String host = "";
int port = 0;
String exclList = "";
- if (proxyProperties != null && !TextUtils.isEmpty(proxyProperties.getHost())) {
+ String pacFileUrl = "";
+ if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
+ !TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) {
mGlobalProxy = new ProxyProperties(proxyProperties);
host = mGlobalProxy.getHost();
port = mGlobalProxy.getPort();
exclList = mGlobalProxy.getExclusionList();
+ if (proxyProperties.getPacFileUrl() != null) {
+ pacFileUrl = proxyProperties.getPacFileUrl();
+ }
} else {
mGlobalProxy = null;
}
@@ -3198,6 +3209,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclList);
+ Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3215,8 +3227,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
String exclList = Settings.Global.getString(res,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
- if (!TextUtils.isEmpty(host)) {
- ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
+ String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
+ if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+ ProxyProperties proxyProperties;
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ proxyProperties = new ProxyProperties(pacFileUrl);
+ } else {
+ proxyProperties = new ProxyProperties(host, port, exclList);
+ }
synchronized (mProxyLock) {
mGlobalProxy = proxyProperties;
}
@@ -3234,7 +3252,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void handleApplyDefaultProxy(ProxyProperties proxy) {
- if (proxy != null && TextUtils.isEmpty(proxy.getHost())) {
+ if (proxy != null && TextUtils.isEmpty(proxy.getHost())
+ && TextUtils.isEmpty(proxy.getPacFileUrl())) {
proxy = null;
}
synchronized (mProxyLock) {
@@ -3276,6 +3295,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void sendProxyBroadcast(ProxyProperties proxy) {
if (proxy == null) proxy = new ProxyProperties("", 0, "");
+ mPacManager.setCurrentProxyScriptUrl(proxy);
if (DBG) log("sending Proxy Broadcast for " + proxy);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6fa1822..2d2d468 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1184,17 +1184,19 @@ public final class ActivityManagerService extends ActivityManagerNative
String host = "";
String port = "";
String exclList = "";
+ String pacFileUrl = null;
if (proxy != null) {
host = proxy.getHost();
port = Integer.toString(proxy.getPort());
exclList = proxy.getExclusionList();
+ pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null) {
try {
- r.thread.setHttpProxy(host, port, exclList);
+ r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: " +
r.info.processName);
diff --git a/services/java/com/android/server/connectivity/PacManager.java b/services/java/com/android/server/connectivity/PacManager.java
new file mode 100644
index 0000000..189c626
--- /dev/null
+++ b/services/java/com/android/server/connectivity/PacManager.java
@@ -0,0 +1,226 @@
+package com.android.server.connectivity;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ProxyProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.net.IProxyService;
+
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.params.ConnRouteParams;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.net.ProxySelector;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * @hide
+ */
+public class PacManager implements Runnable {
+ public static final int NO_ERROR = 0;
+ public static final int PERMISSION_DENIED = 1;
+ public static final String PROXY_SERVICE = "com.android.net.IProxyService";
+
+
+ private static final String TAG = "PACManager";
+
+ private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
+
+ private static final String DEFAULT_DELAYS = "8 32 120 14400 43200";
+ private static final int DELAY_1 = 0;
+ private static final int DELAY_4 = 3;
+ private static final int DELAY_LONG = 4;
+
+ /** Keep these values up-to-date with ProxyService.java */
+ public static final String KEY_PROXY = "keyProxy";
+ private String mCurrentPac;
+ private volatile String mPacUrl;
+
+ private AlarmManager mAlarmManager;
+ private IProxyService mProxyService;
+ private PendingIntent mPacRefreshIntent;
+ private Context mContext;
+
+ private int mCurrentDelay;
+
+ class PacRefreshIntentReceiver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ new Thread(PacManager.this).start();
+ }
+ }
+
+ public PacManager(Context context) {
+ mContext = context;
+ mProxyService = IProxyService.Stub.asInterface(
+ ServiceManager.getService(PROXY_SERVICE));
+
+ mPacRefreshIntent = PendingIntent.getBroadcast(
+ context, 0, new Intent(ACTION_PAC_REFRESH), 0);
+ context.registerReceiver(new PacRefreshIntentReceiver(),
+ new IntentFilter(ACTION_PAC_REFRESH));
+ }
+
+ private AlarmManager getAlarmManager() {
+ if (mAlarmManager == null) {
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+ return mAlarmManager;
+ }
+
+ public void setCurrentProxyScriptUrl(ProxyProperties proxy) {
+ if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
+ try {
+ mProxyService.startPacSystem();
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ new Thread(this).start();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
+ }
+ } else {
+ try {
+ mProxyService.stopPacSystem();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Does a post and reports back the status code.
+ *
+ * @throws IOException
+ */
+ public static String get(String urlString) throws IOException {
+ URL url = new URL(urlString);
+ URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ urlConnection.getInputStream()));
+ String inputLine;
+ String resp = "";
+ while ((inputLine = in.readLine()) != null) {
+ resp = resp + inputLine + "\n";
+ }
+ in.close();
+ return resp;
+ }
+
+ private static String toString(InputStream content) throws IOException {
+ StringBuffer buffer = new StringBuffer();
+ String line;
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(content));
+
+ while ((line = bufferedReader.readLine()) != null) {
+ if (buffer.length() != 0) {
+ buffer.append('\n');
+ }
+ buffer.append(line);
+ }
+
+ return buffer.toString();
+ }
+
+ @Override
+ public void run() {
+ String file;
+ try {
+ file = get(mPacUrl);
+ } catch (IOException ioe) {
+ file = null;
+ }
+ if (file != null) {
+ if (!file.equals(mCurrentPac)) {
+ setCurrentProxyScript(file);
+ }
+ longSchedule();
+ } else {
+ reschedule();
+ }
+ }
+
+ private int getNextDelay(int currentDelay) {
+ if (++currentDelay > DELAY_4) {
+ return DELAY_4;
+ }
+ return currentDelay;
+ }
+
+ private void longSchedule() {
+ mCurrentDelay = DELAY_1;
+ setDownloadIn(DELAY_LONG);
+ }
+
+ private void reschedule() {
+ mCurrentDelay = getNextDelay(mCurrentDelay);
+ setDownloadIn(mCurrentDelay);
+ }
+
+ private String getPacChangeDelay() {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ /** Check system properties for the default value then use secure settings value, if any. */
+ String defaultDelay = SystemProperties.get(
+ "conn." + Settings.Global.PAC_CHANGE_DELAY,
+ DEFAULT_DELAYS);
+ String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
+ return (val == null) ? defaultDelay : val;
+ }
+
+ private long getDownloadDelay(int delayIndex) {
+ String[] list = getPacChangeDelay().split(" ");
+ if (delayIndex < list.length) {
+ return Long.parseLong(list[delayIndex]);
+ }
+ return 0;
+ }
+
+ private void setDownloadIn(int delayIndex) {
+ long delay = getDownloadDelay(delayIndex);
+ long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
+ getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
+ }
+
+ private boolean setCurrentProxyScript(String script) {
+ try {
+ if (mProxyService.setPacFile(script) != NO_ERROR) {
+ Log.e(TAG, "Unable to parse proxy script.");
+ return false;
+ }
+ mCurrentPac = script;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to set PAC file", e);
+ }
+ return true;
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 9418de1..ad700aa 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -141,6 +141,7 @@ class WifiConfigStore {
private static final String PROXY_SETTINGS_KEY = "proxySettings";
private static final String PROXY_HOST_KEY = "proxyHost";
private static final String PROXY_PORT_KEY = "proxyPort";
+ private static final String PROXY_PAC_FILE = "proxyPac";
private static final String EXCLUSION_LIST_KEY = "exclusionList";
private static final String EOS = "eos";
@@ -771,6 +772,14 @@ class WifiConfigStore {
out.writeUTF(exclusionList);
writeToFile = true;
break;
+ case PAC:
+ ProxyProperties proxyPacProperties = linkProperties.getHttpProxy();
+ out.writeUTF(PROXY_SETTINGS_KEY);
+ out.writeUTF(config.proxySettings.toString());
+ out.writeUTF(PROXY_PAC_FILE);
+ out.writeUTF(proxyPacProperties.getPacFileUrl());
+ writeToFile = true;
+ break;
case NONE:
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.proxySettings.toString());
@@ -838,6 +847,7 @@ class WifiConfigStore {
ProxySettings proxySettings = ProxySettings.NONE;
LinkProperties linkProperties = new LinkProperties();
String proxyHost = null;
+ String pacFileUrl = null;
int proxyPort = -1;
String exclusionList = null;
String key;
@@ -879,6 +889,8 @@ class WifiConfigStore {
proxyHost = in.readUTF();
} else if (key.equals(PROXY_PORT_KEY)) {
proxyPort = in.readInt();
+ } else if (key.equals(PROXY_PAC_FILE)) {
+ pacFileUrl = in.readUTF();
} else if (key.equals(EXCLUSION_LIST_KEY)) {
exclusionList = in.readUTF();
} else if (key.equals(EOS)) {
@@ -920,6 +932,12 @@ class WifiConfigStore {
new ProxyProperties(proxyHost, proxyPort, exclusionList);
linkProperties.setHttpProxy(proxyProperties);
break;
+ case PAC:
+ config.proxySettings = proxySettings;
+ ProxyProperties proxyPacProperties =
+ new ProxyProperties(pacFileUrl);
+ linkProperties.setHttpProxy(proxyPacProperties);
+ break;
case NONE:
config.proxySettings = proxySettings;
break;
@@ -1247,6 +1265,7 @@ class WifiConfigStore {
switch (newConfig.proxySettings) {
case STATIC:
+ case PAC:
ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index bd8f0eb..de377ee 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -312,7 +312,10 @@ public class WifiConfiguration implements Parcelable {
STATIC,
/* no proxy details are assigned, this is used to indicate
* that any existing proxy settings should be retained */
- UNASSIGNED
+ UNASSIGNED,
+ /* Use a Pac based proxy.
+ */
+ PAC
}
/**
* @hide