Change PacProcessor to Android Service

This switches the PacProcessor over to an Android Service.  The service
is bound and unbound by the PacManager, which also adds it to the
ServiceManager, allowing for Context-Free access by the PacProxySelector
in all DVMs.

bug:10182711
Change-Id: Id1ff7660be56e8976cdcccd76e041feb47a17a61
diff --git a/Android.mk b/Android.mk
index 656e40c..73f4048 100644
--- a/Android.mk
+++ b/Android.mk
@@ -264,7 +264,7 @@
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
-	packages/services/Proxy/com/android/net/IProxyService.aidl \
+	packages/services/PacProcessor/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/CleanSpec.mk b/CleanSpec.mk
index 8fd5186..a94252d 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -170,6 +170,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrinterDiscoveryObserver.*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/printservice/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/packages/services/Proxy/)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index d3ce2e5..b674324 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2013 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.
+ */
 
 package android.net;
 
@@ -25,19 +40,25 @@
     private static final String TAG = "PacProxySelector";
     public static final String PROXY_SERVICE = "com.android.net.IProxyService";
     private IProxyService mProxyService;
+    private final List<Proxy> mDefaultList;
 
     public PacProxySelector() {
         mProxyService = IProxyService.Stub.asInterface(
                 ServiceManager.getService(PROXY_SERVICE));
         if (mProxyService == null) {
             // Added because of b10267814 where mako is restarting.
-            Log.e(TAG, "PackManager: no proxy service");
+            Log.e(TAG, "PacManager: no proxy service");
         }
+        mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
     }
 
     @Override
     public List<Proxy> select(URI uri) {
         if (mProxyService == null) {
+            mProxyService = IProxyService.Stub.asInterface(
+                    ServiceManager.getService(PROXY_SERVICE));
+        }
+        if (mProxyService == null) {
             Log.e(TAG, "select: no proxy service return NO_PROXY");
             return Lists.newArrayList(java.net.Proxy.NO_PROXY);
         }
@@ -53,6 +74,9 @@
         } catch (RemoteException e) {
             e.printStackTrace();
         }
+        if (response == null) {
+            return mDefaultList;
+        }
 
         return parseResponse(response);
     }
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 5b38f57..c3e1438 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -48,7 +48,6 @@
     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
@@ -352,11 +351,8 @@
             System.clearProperty("http.nonProxyHosts");
             System.clearProperty("https.nonProxyHosts");
         }
-        if ((pacFileUrl != null) && !TextUtils.isEmpty(pacFileUrl)) {
-            if (sPacProxySelector == null) {
-                sPacProxySelector = new PacProxySelector();
-            }
-            ProxySelector.setDefault(sPacProxySelector);
+        if (!TextUtils.isEmpty(pacFileUrl)) {
+            ProxySelector.setDefault(new PacProxySelector());
         } else {
             ProxySelector.setDefault(sDefaultProxySelector);
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1363e3c..813b55f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -218,6 +218,7 @@
     <protected-broadcast android:name="android.location.GPS_ENABLED_CHANGE" />
     <protected-broadcast android:name="android.location.PROVIDERS_CHANGED" />
     <protected-broadcast android:name="android.location.GPS_FIX_CHANGE" />
+    <protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
index e4afde6..d9566d5 100644
--- a/packages/services/PacProcessor/Android.mk
+++ b/packages/services/PacProcessor/Android.mk
@@ -18,24 +18,15 @@
 
 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
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-include $(BUILD_EXECUTABLE)
+LOCAL_PACKAGE_NAME := PacProcessor
+LOCAL_CERTIFICATE := platform
+
+LOCAL_REQUIRED_MODULES := libjni_pacprocessor
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/services/PacProcessor/AndroidManifest.xml b/packages/services/PacProcessor/AndroidManifest.xml
new file mode 100644
index 0000000..6740c16
--- /dev/null
+++ b/packages/services/PacProcessor/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.pacprocessor">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application
+        android:label="@string/app_name">
+
+        <service android:name=".PacService"
+            android:exported="true">
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/packages/services/PacProcessor/IProxyService.cpp b/packages/services/PacProcessor/IProxyService.cpp
deleted file mode 100644
index 3707d85..0000000
--- a/packages/services/PacProcessor/IProxyService.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#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
deleted file mode 100644
index 57c527b..0000000
--- a/packages/services/PacProcessor/IProxyService.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#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
deleted file mode 100644
index 7084a47..0000000
--- a/packages/services/PacProcessor/ProxyService.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#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
deleted file mode 100644
index a0861b2..0000000
--- a/packages/services/PacProcessor/ProxyService.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#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/com/android/net/IProxyService.aidl b/packages/services/PacProcessor/com/android/net/IProxyService.aidl
new file mode 100644
index 0000000..4e54aba
--- /dev/null
+++ b/packages/services/PacProcessor/com/android/net/IProxyService.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2013, 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.
+ */
+package com.android.net;
+
+/** @hide */
+interface IProxyService
+{
+    String resolvePacFile(String host, String url);
+
+    oneway void setPacFile(String scriptContents);
+
+    oneway void startPacSystem();
+    oneway void stopPacSystem();
+}
diff --git a/packages/services/PacProcessor/jni/Android.mk b/packages/services/PacProcessor/jni/Android.mk
new file mode 100644
index 0000000..f16c90b
--- /dev/null
+++ b/packages/services/PacProcessor/jni/Android.mk
@@ -0,0 +1,41 @@
+#
+# Copyright (C) 2013 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 := \
+    jni_init.cpp \
+    com_android_pacprocessor_PacNative.cpp
+
+LOCAL_C_INCLUDES += \
+    external/chromium-libpac/src
+
+LOCAL_SHARED_LIBRARIES := \
+    libandroidfw \
+    libandroid_runtime \
+    liblog \
+    libutils \
+    libnativehelper \
+    libpac
+
+LOCAL_MODULE := libjni_pacprocessor
+LOCAL_MODULE_TAGS := optional
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
new file mode 100644
index 0000000..c5aa13b
--- /dev/null
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 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 "PacProcessor"
+
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include "android_runtime/AndroidRuntime.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include "proxy_resolver_v8.h"
+
+namespace android {
+
+class ProxyErrorLogger : public net::ProxyErrorListener {
+public:
+    ~ProxyErrorLogger() {
+
+    }
+    void AlertMessage(String16 message) {
+        String8 str(message);
+        ALOGD("Alert: %s", str.string());
+    }
+    void ErrorMessage(String16 message) {
+        String8 str(message);
+        ALOGE("Error: %s", str.string());
+    }
+};
+
+net::ProxyResolverV8* proxyResolver = NULL;
+ProxyErrorLogger* logger = NULL;
+bool pacSet = false;
+
+String16 jstringToString16(JNIEnv* env, jstring jstr) {
+    const jchar* str = env->GetStringCritical(jstr, 0);
+    String16 str16(str, env->GetStringLength(jstr));
+    env->ReleaseStringCritical(jstr, str);
+    return str16;
+}
+
+jstring string16ToJstring(JNIEnv* env, String16 string) {
+    const char16_t* str = string.string();
+    size_t len = string.size();
+
+    return env->NewString(str, len);
+}
+
+static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JNIEnv* env, 
+        jobject) {
+    if (proxyResolver == NULL) {
+        logger = new ProxyErrorLogger();
+        proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault(),
+                logger);
+        pacSet = false;
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static jboolean com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked(JNIEnv* env, 
+        jobject) {
+    if (proxyResolver != NULL) {
+        delete logger;
+        delete proxyResolver;
+        logger = NULL;
+        proxyResolver = NULL;
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static jboolean com_android_pacprocessor_PacNative_setProxyScriptNativeLocked(JNIEnv* env, jobject,
+        jstring script) {
+    String16 script16 = jstringToString16(env, script);
+
+    if (proxyResolver == NULL) {
+        ALOGE("V8 Parser not started when setting PAC script");
+        return JNI_TRUE;
+    }
+
+    if (proxyResolver->SetPacScript(script16) != OK) {
+        ALOGE("Unable to set PAC script");
+        return JNI_TRUE;
+    }
+    pacSet = true;
+
+    return JNI_FALSE;
+}
+
+static jstring com_android_pacprocessor_PacNative_makeProxyRequestNativeLocked(JNIEnv* env, jobject,
+        jstring url, jstring host) {
+    String16 url16 = jstringToString16(env, url);
+    String16 host16 = jstringToString16(env, host);
+    String16 ret;
+
+    if (proxyResolver == NULL) {
+        ALOGE("V8 Parser not initialized when running PAC script");
+        return NULL;
+    }
+
+    if (!pacSet) {
+        ALOGW("Attempting to run PAC with no script set");
+        return NULL;
+    }
+
+    if (proxyResolver->GetProxyForURL(url16, host16, &ret) != OK) {
+        String8 ret8(ret);
+        ALOGE("Error Running PAC: %s", ret8.string());
+        return NULL;
+    }
+
+    jstring jret = string16ToJstring(env, ret);
+
+    return jret;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "createV8ParserNativeLocked", "()Z",
+        (void*)com_android_pacprocessor_PacNative_createV8ParserNativeLocked},
+    { "destroyV8ParserNativeLocked", "()Z",
+        (void*)com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked},
+    { "setProxyScriptNativeLocked", "(Ljava/lang/String;)Z",
+        (void*)com_android_pacprocessor_PacNative_setProxyScriptNativeLocked},
+    { "makeProxyRequestNativeLocked", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+        (void*)com_android_pacprocessor_PacNative_makeProxyRequestNativeLocked},
+};
+
+int register_com_android_pacprocessor_PacNative(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/pacprocessor/PacNative",
+            gMethods, NELEM(gMethods));
+}
+
+} /* namespace android */
diff --git a/packages/services/PacProcessor/jni/jni_init.cpp b/packages/services/PacProcessor/jni/jni_init.cpp
new file mode 100644
index 0000000..bda33fb
--- /dev/null
+++ b/packages/services/PacProcessor/jni/jni_init.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 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 "PacProcessor"
+
+#include <utils/Log.h>
+#include "jni.h"
+
+namespace android {
+    extern int register_com_android_pacprocessor_PacNative(JNIEnv *env);
+}
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+    JNIEnv *env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        ALOGE("ERROR: GetEnv failed");
+        return -1;
+    }
+
+    register_com_android_pacprocessor_PacNative(env);
+
+    return JNI_VERSION_1_6;
+}
diff --git a/packages/services/PacProcessor/main_pacserver.cpp b/packages/services/PacProcessor/main_pacserver.cpp
deleted file mode 100644
index 19588b5..0000000
--- a/packages/services/PacProcessor/main_pacserver.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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/PacProcessor/res/values/strings.xml b/packages/services/PacProcessor/res/values/strings.xml
new file mode 100644
index 0000000..301a2b6
--- /dev/null
+++ b/packages/services/PacProcessor/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">PacProcessor</string>
+
+</resources>
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
new file mode 100644
index 0000000..c67fe9f
--- /dev/null
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2013, 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.
+ */
+package com.android.pacprocessor;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class PacNative {
+    private static final String TAG = "PacProxy";
+
+    private String mCurrentPac;
+
+    private boolean mIsActive;
+
+    // Only make native calls from inside synchronized blocks.
+    private native boolean createV8ParserNativeLocked();
+    private native boolean destroyV8ParserNativeLocked();
+
+    private native boolean setProxyScriptNativeLocked(String script);
+
+    private native String makeProxyRequestNativeLocked(String url, String host);
+
+    static {
+        System.loadLibrary("jni_pacprocessor");
+    }
+
+    PacNative() {
+
+    }
+
+    public synchronized boolean startPacSupport() {
+        if (createV8ParserNativeLocked()) {
+            Log.e(TAG, "Unable to Create v8 Proxy Parser.");
+            return true;
+        }
+        mIsActive = true;
+        return false;
+    }
+
+    public synchronized boolean stopPacSupport() {
+        if (mIsActive) {
+            if (destroyV8ParserNativeLocked()) {
+                Log.e(TAG, "Unable to Destroy v8 Proxy Parser.");
+                return true;
+            }
+            mIsActive = false;
+        }
+        return false;
+    }
+
+    public synchronized boolean setCurrentProxyScript(String script) {
+        if (setProxyScriptNativeLocked(script)) {
+            Log.e(TAG, "Unable to parse proxy script.");
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized String makeProxyRequest(String url, String host) {
+        String ret = makeProxyRequestNativeLocked(url, host);
+        if ((ret == null) || (ret.length() == 0)) {
+            Log.e(TAG, "v8 Proxy request failed.");
+            ret = null;
+        }
+        return ret;
+    }
+
+    public synchronized boolean isActive() {
+        return mIsActive;
+    }
+}
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
new file mode 100644
index 0000000..7e76025
--- /dev/null
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2013, 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.
+ */
+package com.android.pacprocessor;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.net.IProxyService;
+
+public class PacService extends Service {
+    private static final String TAG = "PacService";
+
+    private PacNative mPacNative;
+    private ProxyServiceStub mStub;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (mPacNative == null) {
+            mPacNative = new PacNative();
+            mStub = new ProxyServiceStub(mPacNative);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mPacNative != null) {
+            mPacNative.stopPacSupport();
+            mPacNative = null;
+            mStub = null;
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (mPacNative == null) {
+            mPacNative = new PacNative();
+            mStub = new ProxyServiceStub(mPacNative);
+        }
+        return mStub;
+    }
+
+    private static class ProxyServiceStub extends IProxyService.Stub {
+        private final PacNative mPacNative;
+
+        public ProxyServiceStub(PacNative pacNative) {
+            mPacNative = pacNative;
+        }
+
+        @Override
+        public String resolvePacFile(String host, String url) throws RemoteException {
+            return mPacNative.makeProxyRequest(url, host);
+        }
+
+        @Override
+        public void setPacFile(String script) throws RemoteException {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                Log.e(TAG, "Only system user is allowed to call setPacFile");
+                throw new SecurityException();
+            }
+            mPacNative.setCurrentProxyScript(script);
+        }
+
+        @Override
+        public void startPacSystem() throws RemoteException {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                Log.e(TAG, "Only system user is allowed to call startPacSystem");
+                throw new SecurityException();
+            }
+            mPacNative.startPacSupport();
+        }
+
+        @Override
+        public void stopPacSystem() throws RemoteException {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                Log.e(TAG, "Only system user is allowed to call stopPacSystem");
+                throw new SecurityException();
+            }
+            mPacNative.stopPacSupport();
+        }
+    }
+}
diff --git a/packages/services/Proxy/com/android/net/IProxyService.aidl b/packages/services/Proxy/com/android/net/IProxyService.aidl
deleted file mode 100644
index 7e9ed79..0000000
--- a/packages/services/Proxy/com/android/net/IProxyService.aidl
+++ /dev/null
@@ -1,16 +0,0 @@
-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/services/java/com/android/server/connectivity/PacManager.java b/services/java/com/android/server/connectivity/PacManager.java
index defe9f0..0b68ff5 100644
--- a/services/java/com/android/server/connectivity/PacManager.java
+++ b/services/java/com/android/server/connectivity/PacManager.java
@@ -1,14 +1,31 @@
+/**
+ * Copyright (c) 2013, 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.
+ */
 package com.android.server.connectivity;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.net.ConnectivityManager;
+import android.content.ServiceConnection;
 import android.net.ProxyProperties;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -17,42 +34,26 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.net.IProxyService;
+import com.android.server.IoThread;
 
+import libcore.io.Streams;
 
-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";
+public class PacManager {
+    public static final String PROXY_PACKAGE = "com.android.pacprocessor";
+    public static final String PROXY_SERVICE = "com.android.pacprocessor.PacService";
+    public static final String PROXY_SERVICE_NAME = "com.android.net.IProxyService";
 
 
-    private static final String TAG = "PACManager";
+    private static final String TAG = "PacManager";
 
     private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
 
@@ -64,31 +65,57 @@
     /** Keep these values up-to-date with ProxyService.java */
     public static final String KEY_PROXY = "keyProxy";
     private String mCurrentPac;
-    private volatile String mPacUrl;
+    @GuardedBy("mProxyLock")
+    private String mPacUrl;
 
     private AlarmManager mAlarmManager;
+    @GuardedBy("mProxyLock")
     private IProxyService mProxyService;
     private PendingIntent mPacRefreshIntent;
+    private ServiceConnection mConnection;
     private Context mContext;
 
     private int mCurrentDelay;
 
+    /**
+     * Used for locking when setting mProxyService and all references to mPacUrl or mCurrentPac.
+     */
+    private final Object mProxyLock = new Object();
+
+    private Runnable mPacDownloader = new Runnable() {
+        @Override
+        public void run() {
+            String file;
+            synchronized (mProxyLock) {
+                if (mPacUrl == null) return;
+                try {
+                    file = get(mPacUrl);
+                } catch (IOException ioe) {
+                    file = null;
+                    Log.w(TAG, "Failed to load PAC file: " + ioe);
+                }
+            }
+            if (file != null) {
+                synchronized (mProxyLock) {
+                    if (!file.equals(mCurrentPac)) {
+                        setCurrentProxyScript(file);
+                    }
+                }
+                longSchedule();
+            } else {
+                reschedule();
+            }
+        }
+    };
+
     class PacRefreshIntentReceiver extends BroadcastReceiver {
         public void onReceive(Context context, Intent intent) {
-            new Thread(PacManager.this).start();
+            IoThread.getHandler().post(mPacDownloader);
         }
     }
 
     public PacManager(Context context) {
         mContext = context;
-        mProxyService = IProxyService.Stub.asInterface(
-                ServiceManager.getService(PROXY_SERVICE));
-        if (mProxyService == null) {
-            // Added because of b10267814 where mako is restarting.
-            Log.e(TAG, "PacManager: no proxy service");
-        } else {
-            Log.d(TAG, "PacManager: mProxyService available");
-        }
 
         mPacRefreshIntent = PendingIntent.getBroadcast(
                 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
@@ -103,26 +130,28 @@
         return mAlarmManager;
     }
 
-    public void setCurrentProxyScriptUrl(ProxyProperties proxy) {
-        if (mProxyService == null) {
-            Log.e(TAG, "setCurrentProxyScriptUrl: no proxy service");
-            return;
-        }
+    public synchronized void setCurrentProxyScriptUrl(ProxyProperties proxy) {
         if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
-            try {
-                mProxyService.startPacSystem();
+            synchronized (mProxyLock) {
                 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);
             }
+            mCurrentDelay = DELAY_1;
+            getAlarmManager().cancel(mPacRefreshIntent);
+            bind();
         } else {
-            try {
-                mProxyService.stopPacSystem();
-            } catch (RemoteException e) {
-                e.printStackTrace();
+            getAlarmManager().cancel(mPacRefreshIntent);
+            synchronized (mProxyLock) {
+                mPacUrl = null;
+                mCurrentPac = null;
+                if (mProxyService != null) {
+                    try {
+                        mProxyService.stopPacSystem();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to stop PAC service", e);
+                    } finally {
+                        unbind();
+                    }
+                }
             }
         }
     }
@@ -132,51 +161,10 @@
      *
      * @throws IOException
      */
-    public static String get(String urlString) throws IOException {
+    private 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();
-        }
+        return new String(Streams.readFully(urlConnection.getInputStream()));
     }
 
     private int getNextDelay(int currentDelay) {
@@ -227,14 +215,60 @@
             return false;
         }
         try {
-            if (mProxyService.setPacFile(script) != NO_ERROR) {
-                Log.e(TAG, "Unable to parse proxy script.");
-                return false;
-            }
+            mProxyService.setPacFile(script);
             mCurrentPac = script;
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to set PAC file", e);
         }
         return true;
     }
+
+    private void bind() {
+        if (mContext == null) {
+            Log.e(TAG, "No context for binding");
+            return;
+        }
+        Intent intent = new Intent();
+        intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
+        mConnection = new ServiceConnection() {
+            @Override
+            public void onServiceDisconnected(ComponentName component) {
+                synchronized (mProxyLock) {
+                    mProxyService = null;
+                }
+            }
+
+            @Override
+            public void onServiceConnected(ComponentName component, IBinder binder) {
+                synchronized (mProxyLock) {
+                    try {
+                        Log.d(TAG, "Adding service " + PROXY_SERVICE_NAME + " "
+                                + binder.getInterfaceDescriptor());
+                    } catch (RemoteException e1) {
+                        Log.e(TAG, "Remote Exception", e1);
+                    }
+                    ServiceManager.addService(PROXY_SERVICE_NAME, binder);
+                    mProxyService = IProxyService.Stub.asInterface(binder);
+                    if (mProxyService == null) {
+                        Log.e(TAG, "No proxy service");
+                    } else {
+                        try {
+                            mProxyService.startPacSystem();
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
+                        }
+                        IoThread.getHandler().post(mPacDownloader);
+                    }
+                }
+            }
+        };
+        Log.e(TAG, "Attempting to bind");
+        mContext.bindService(intent, mConnection,
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
+    }
+
+    private void unbind() {
+        mContext.unbindService(mConnection);
+        mConnection = null;
+    }
 }