diff options
27 files changed, 1268 insertions, 45 deletions
@@ -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 |