summaryrefslogtreecommitdiffstats
path: root/packages/services/Proxy
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2013-07-03 17:04:33 -0400
committerJason Monk <jmonk@google.com>2013-08-07 21:01:39 -0400
commit602b232a06ede86999aa362a12eb28cbc782dc1d (patch)
tree07a2168ba91425f81bd7ac9c9311adb78114cc13 /packages/services/Proxy
parent55db1e1218971105e68ba9d451b2e0b1e9e5f9fb (diff)
downloadframeworks_base-602b232a06ede86999aa362a12eb28cbc782dc1d.zip
frameworks_base-602b232a06ede86999aa362a12eb28cbc782dc1d.tar.gz
frameworks_base-602b232a06ede86999aa362a12eb28cbc782dc1d.tar.bz2
Add PAC File support for proxy configuration
PAC (Proxy auto-config) files contain a single javascript function, FindProxyForURL(url, host). It gets called to determine what proxy should be used for a specific request. This adds PAC support to the system. The ProxyProperties has been modified to hold the PAC file when one is present. The Proxy method setHttpProxySystemProperty has been modified to insert a PacProxySelector as the default ProxySelector when it is required. This new ProxySelector makes calls to the ConnectivityService to parse the PAC file. The ConnectivityService and the WifiConfigStore have been modified to support saving the extra PAC file data. The ConnectivityService now has a class attached (PacProxyNative) that interfaces to the native calls for PAC files. The parsing of the PAC file is handled by libpac (which is being added to external/) which utilizes libv8 to parse the javascript. As a fallback to applications that don't use the java ProxySelector, the proxy is setup to point to a local proxy server that will handle the pac parsing. bug:10182711 Change-Id: I5eb8df893c632fd3e1b732385cb7720ad646f401
Diffstat (limited to 'packages/services/Proxy')
-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
8 files changed, 445 insertions, 0 deletions
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();
+ }
+ }
+
+
+}