Add functionality for changing WebView provider.

Make it possible to change WebView provider (through a Developer
setting) and kill all apps using the old provider.
This includes checking the signatures of the WebView providers to make
sure they are valid.

Now that we can change WebView provider through a setting it is possible
to change provider while some provider is being updated. Because of this
we now keep track of which provider should be in use in
WebViewUpdateService to make sure we use the correct provider at all
times.

We now also read WebView package meta data (name, package name, and
signature) from a separate xml file.

Main bug: crbug.com/546185

Bug: 25338573

Change-Id: I660fd1a40a5388f6569a06a7f0d029e8ff65945a
diff --git a/api/system-current.txt b/api/system-current.txt
index 0fe65c3..caedaed 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -46474,8 +46474,6 @@
     method public static android.content.pm.PackageInfo getLoadedPackageInfo();
     method public static java.lang.String getWebViewPackageName();
     method public static int loadWebViewNativeLibraryFromPackage(java.lang.String);
-    method public static void onWebViewUpdateInstalled();
-    method public static void prepareWebViewInSystemServer();
     method public static void prepareWebViewInZygote();
     field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize";
     field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
@@ -46484,7 +46482,9 @@
     field public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; // 0x6
     field public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; // 0x5
     field public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; // 0x3
+    field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9; // 0x9
     field public static final int LIBLOAD_SUCCESS = 0; // 0x0
+    field public static final int LIBLOAD_WEBVIEW_BEING_REPLACED = 8; // 0x8
     field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
   }
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f3242a7..744c528 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1558,6 +1558,15 @@
             return true;
         }
 
+        case KILL_PACKAGE_DEPENDENTS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String packageName = data.readString();
+            int userId = data.readInt();
+            killPackageDependents(packageName, userId);
+            reply.writeNoException();
+            return true;
+        }
+
         case FORCE_STOP_PACKAGE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String packageName = data.readString();
@@ -4736,6 +4745,18 @@
         reply.recycle();
     }
 
+    public void killPackageDependents(String packageName, int userId) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeInt(userId);
+        mRemote.transact(KILL_PACKAGE_DEPENDENTS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     public void forceStopPackage(String packageName, int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 22a2d64..c3db809 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -259,6 +259,7 @@
     public void killBackgroundProcesses(final String packageName, int userId)
             throws RemoteException;
     public void killAllBackgroundProcesses() throws RemoteException;
+    public void killPackageDependents(final String packageName, int userId) throws RemoteException;
     public void forceStopPackage(final String packageName, int userId) throws RemoteException;
 
     // Note: probably don't want to allow applications access to these.
@@ -912,4 +913,5 @@
     int UNLOCK_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 351;
     int IN_MULTI_WINDOW_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 352;
     int IN_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 353;
+    int KILL_PACKAGE_DEPENDENTS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 354;
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 74fd8cd..a1e5510 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5749,6 +5749,13 @@
                 "camera_double_tap_power_gesture_disabled";
 
         /**
+         * Name of the package used as WebView provider (if unset the provider is instead determined
+         * by the system).
+         * @hide
+         */
+        public static final String WEBVIEW_PROVIDER = "webview_provider";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index a77459b..89d5d69 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -16,6 +16,10 @@
 
 package android.webkit;
 
+import android.content.pm.PackageInfo;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
 /**
  * Private service to wait for the updatable WebView to be ready for use.
  * @hide
@@ -25,12 +29,28 @@
     /**
      * Used by the relro file creator to notify the service that it's done.
      */
-    void notifyRelroCreationCompleted(boolean is64Bit, boolean success);
+    void notifyRelroCreationCompleted();
 
     /**
      * Used by WebViewFactory to block loading of WebView code until
-     * preparations are complete.
+     * preparations are complete. Returns the package used as WebView provider.
      */
-    void waitForRelroCreationCompleted(boolean is64Bit);
+    WebViewProviderResponse waitForAndGetProvider();
 
+    /**
+     * DevelopmentSettings uses this to notify WebViewUpdateService that a
+     * new provider has been selected by the user.
+     */
+    void changeProviderAndSetting(String newProvider);
+
+    /**
+     * DevelopmentSettings uses this to get the current available WebView
+     * providers (to display as choices to the user).
+     */
+    WebViewProviderInfo[] getValidWebViewPackages();
+
+    /**
+     * Used by DevelopmentSetting to get the name of the WebView provider currently in use.
+     */
+    String getCurrentWebViewPackageName();
 }
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 229011d..01d1566 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -24,6 +24,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
 import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
@@ -31,20 +32,27 @@
 import android.os.StrictMode;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 
+import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
 
 import dalvik.system.VMRuntime;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+import org.xmlpull.v1.XmlPullParserException;
+
 /**
  * Top level factory, used creating all the main WebView implementation classes.
  *
@@ -83,6 +91,8 @@
     public static final int LIBLOAD_SUCCESS = 0;
     public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
     public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2;
+
+    // error codes for waiting for WebView preparation
     public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3;
     public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
 
@@ -91,57 +101,95 @@
     public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6;
     public static final int LIBLOAD_FAILED_JNI_CALL = 7;
 
-    private static class MissingWebViewPackageException extends AndroidRuntimeException {
+    // more error codes for waiting for WebView preparation
+    public static final int LIBLOAD_WEBVIEW_BEING_REPLACED = 8;
+    public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9;
+
+    private static String getWebViewPreparationErrorReason(int error) {
+        switch (error) {
+            case LIBLOAD_FAILED_WAITING_FOR_RELRO:
+                return "Time out waiting for Relro files being created";
+            case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
+                return "No WebView installed";
+            case LIBLOAD_WEBVIEW_BEING_REPLACED:
+                return "Time out waiting for WebView to be replaced";
+            case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN:
+                return "Crashed for unknown reason";
+        }
+        return "Unknown";
+    }
+
+    /**
+     * @hide
+     */
+    public static class MissingWebViewPackageException extends AndroidRuntimeException {
         public MissingWebViewPackageException(String message) { super(message); }
         public MissingWebViewPackageException(Exception e) { super(e); }
     }
 
-    /** @hide */
-    public static String[] getWebViewPackageNames() {
-        return AppGlobals.getInitialApplication().getResources().getStringArray(
-                com.android.internal.R.array.config_webViewPackageNames);
+    private static String TAG_START = "webviewproviders";
+    private static String TAG_WEBVIEW_PROVIDER = "webviewprovider";
+    private static String TAG_PACKAGE_NAME = "packageName";
+    private static String TAG_DESCRIPTION = "description";
+    private static String TAG_SIGNATURE = "signature";
+
+    /**
+     * Returns all packages declared in the framework resources as potential WebView providers.
+     * @hide
+     * */
+    public static WebViewProviderInfo[] getWebViewPackages() {
+        XmlResourceParser parser = null;
+        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
+        try {
+            parser = AppGlobals.getInitialApplication().getResources().getXml(
+                    com.android.internal.R.xml.config_webview_packages);
+            XmlUtils.beginDocument(parser, TAG_START);
+            while(true) {
+                XmlUtils.nextElement(parser);
+                String element = parser.getName();
+                if (element == null) {
+                    break;
+                }
+                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
+                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
+                    if (packageName == null) {
+                        throw new MissingWebViewPackageException(
+                                "WebView provider in framework resources missing package name");
+                    }
+                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
+                    if (description == null) {
+                        throw new MissingWebViewPackageException(
+                                "WebView provider in framework resources missing description");
+                    }
+                    String signature = parser.getAttributeValue(null, TAG_SIGNATURE);
+                    webViewProviders.add(
+                            new WebViewProviderInfo(packageName, description, signature));
+                }
+                else {
+                    Log.e(LOGTAG, "Found an element that is not a webview provider");
+                }
+            }
+        } catch(XmlPullParserException e) {
+            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+        } catch(IOException e) {
+            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
     }
 
+
     // TODO (gsennton) remove when committing webview xts test change
     public static String getWebViewPackageName() {
-        String[] webViewPackageNames = getWebViewPackageNames();
-        return webViewPackageNames[webViewPackageNames.length-1];
+        WebViewProviderInfo[] providers = getWebViewPackages();
+        return providers[0].packageName;
     }
 
     /**
-     * Return the package info of the first package in the webview priority list that contains
-     * webview.
-     *
      * @hide
      */
-    public static PackageInfo findPreferredWebViewPackage() {
-        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-
-        for (String packageName : getWebViewPackageNames()) {
-            try {
-                PackageInfo packageInfo = pm.getPackageInfo(packageName,
-                    PackageManager.GET_META_DATA);
-                ApplicationInfo applicationInfo = packageInfo.applicationInfo;
-
-                // If the correct flag is set the package contains webview.
-                if (getWebViewLibrary(applicationInfo) != null) {
-                    return packageInfo;
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-            }
-        }
-        throw new MissingWebViewPackageException("Could not find a loadable WebView package");
-    }
-
-    // throws MissingWebViewPackageException
-    private static ApplicationInfo getWebViewApplicationInfo() {
-        if (sPackageInfo == null)
-            return findPreferredWebViewPackage().applicationInfo;
-        else
-            return sPackageInfo.applicationInfo;
-    }
-
-    private static String getWebViewLibrary(ApplicationInfo ai) {
+    public static String getWebViewLibrary(ApplicationInfo ai) {
         if (ai.metaData != null)
             return ai.metaData.getString("com.android.webview.WebViewLibrary");
         return null;
@@ -156,16 +204,14 @@
      * name is the same as the one providing the webview.
      */
     public static int loadWebViewNativeLibraryFromPackage(String packageName) {
-        try {
-            sPackageInfo = findPreferredWebViewPackage();
-        } catch (MissingWebViewPackageException e) {
-            return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+        int ret = waitForProviderAndSetPackageInfo();
+        if (ret != LIBLOAD_SUCCESS) {
+            return ret;
         }
+        if (!sPackageInfo.packageName.equals(packageName))
+            return LIBLOAD_WRONG_PACKAGE_NAME;
 
-        if (packageName != null && packageName.equals(sPackageInfo.packageName)) {
-            return loadNativeLibrary();
-        }
-        return LIBLOAD_WRONG_PACKAGE_NAME;
+        return loadNativeLibrary();
     }
 
     static WebViewFactoryProvider getProvider() {
@@ -207,17 +253,45 @@
     private static Class<WebViewFactoryProvider> getProviderClass() {
         try {
             // First fetch the package info so we can log the webview package version.
-            sPackageInfo = findPreferredWebViewPackage();
+            int res = waitForProviderAndSetPackageInfo();
+            if (res != LIBLOAD_SUCCESS) {
+                throw new MissingWebViewPackageException(
+                        "Failed to load WebView provider, error: "
+                        + getWebViewPreparationErrorReason(res));
+            }
             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
                 sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
 
+            Application initialApplication = AppGlobals.getInitialApplication();
+            Context webViewContext = null;
+            try {
+                // Construct a package context to load the Java code into the current app.
+                // This is done as early as possible since by constructing a package context we
+                // register the WebView package as a dependency for the current application so that
+                // when the WebView package is updated this application will be killed.
+                webViewContext = initialApplication.createPackageContext(
+                        sPackageInfo.packageName,
+                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new MissingWebViewPackageException(e);
+            }
+
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
             loadNativeLibrary();
             Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
 
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
             try {
-                return getChromiumProviderClass();
+                initialApplication.getAssets().addAssetPathAsSharedLibrary(
+                        webViewContext.getApplicationInfo().sourceDir);
+                ClassLoader clazzLoader = webViewContext.getClassLoader();
+                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
+                try {
+                    return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
+                            true, clazzLoader);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
+                }
             } catch (ClassNotFoundException e) {
                 Log.e(LOGTAG, "error loading provider", e);
                 throw new AndroidRuntimeException(e);
@@ -239,30 +313,6 @@
         }
     }
 
-    // throws MissingWebViewPackageException
-    private static Class<WebViewFactoryProvider> getChromiumProviderClass()
-            throws ClassNotFoundException {
-        Application initialApplication = AppGlobals.getInitialApplication();
-        try {
-            // Construct a package context to load the Java code into the current app.
-            Context webViewContext = initialApplication.createPackageContext(
-                    sPackageInfo.packageName,
-                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
-            initialApplication.getAssets().addAssetPathAsSharedLibrary(
-                    webViewContext.getApplicationInfo().sourceDir);
-            ClassLoader clazzLoader = webViewContext.getClassLoader();
-            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
-            try {
-                return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,
-                                                                     clazzLoader);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new MissingWebViewPackageException(e);
-        }
-    }
-
     /**
      * Perform any WebView loading preparations that must happen in the zygote.
      * Currently, this means allocating address space to load the real JNI library later.
@@ -289,44 +339,34 @@
         }
     }
 
-    /**
-     * Perform any WebView loading preparations that must happen at boot from the system server,
-     * after the package manager has started or after an update to the webview is installed.
-     * This must be called in the system server.
-     * Currently, this means spawning the child processes which will create the relro files.
-     */
-    public static void prepareWebViewInSystemServer() {
-        String[] nativePaths = null;
-        try {
-            nativePaths = getWebViewNativeLibraryPaths();
-        } catch (Throwable t) {
-            // Log and discard errors at this stage as we must not crash the system server.
-            Log.e(LOGTAG, "error preparing webview native library", t);
-        }
-        prepareWebViewInSystemServer(nativePaths);
-    }
-
-    private static void prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
+    private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
         if (DEBUG) Log.v(LOGTAG, "creating relro files");
+        int numRelros = 0;
 
         // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any
         // unexpected values will be handled there to ensure that we trigger notifying any process
-        // waiting on relreo creation.
+        // waiting on relro creation.
         if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
             if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
             createRelroFile(false /* is64Bit */, nativeLibraryPaths);
+            numRelros++;
         }
 
         if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
             if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
             createRelroFile(true /* is64Bit */, nativeLibraryPaths);
+            numRelros++;
         }
+        return numRelros;
     }
 
-    public static void onWebViewUpdateInstalled() {
+    /**
+     * @hide
+     */
+    public static int onWebViewProviderChanged(PackageInfo packageInfo) {
         String[] nativeLibs = null;
         try {
-            nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths();
+            nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo);
             if (nativeLibs != null) {
                 long newVmSize = 0L;
 
@@ -373,7 +413,7 @@
             // Log and discard errors at this stage as we must not crash the system server.
             Log.e(LOGTAG, "error preparing webview native library", t);
         }
-        prepareWebViewInSystemServer(nativeLibs);
+        return prepareWebViewInSystemServer(nativeLibs);
     }
 
     // throws MissingWebViewPackageException
@@ -397,8 +437,8 @@
     }
 
     // throws MissingWebViewPackageException
-    private static String[] getWebViewNativeLibraryPaths() {
-        ApplicationInfo ai = getWebViewApplicationInfo();
+    private static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo) {
+        ApplicationInfo ai = packageInfo.applicationInfo;
         final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai);
 
         String path32;
@@ -460,7 +500,7 @@
             public void run() {
                 try {
                     Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
-                    getUpdateService().notifyRelroCreationCompleted(is64Bit, false);
+                    getUpdateService().notifyRelroCreationCompleted();
                 } catch (RemoteException e) {
                     Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
                 }
@@ -508,7 +548,7 @@
             } finally {
                 // We must do our best to always notify the update service, even if something fails.
                 try {
-                    getUpdateService().notifyRelroCreationCompleted(is64Bit, result);
+                    getUpdateService().notifyRelroCreationCompleted();
                 } catch (RemoteException e) {
                     Log.e(LOGTAG, "error notifying update service", e);
                 }
@@ -521,35 +561,38 @@
         }
     }
 
+    private static int waitForProviderAndSetPackageInfo() {
+        WebViewProviderResponse response = null;
+        try {
+            response =
+                getUpdateService().waitForAndGetProvider();
+            if (response.status == WebViewFactory.LIBLOAD_SUCCESS)
+                sPackageInfo = response.packageInfo;
+        } catch (RemoteException e) {
+            Log.e(LOGTAG, "error waiting for relro creation", e);
+            return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
+        }
+        return response.status;
+    }
+
+    // Assumes that we have waited for relro creation and set sPackageInfo
     private static int loadNativeLibrary() {
         if (!sAddressSpaceReserved) {
             Log.e(LOGTAG, "can't load with relro file; address space not reserved");
             return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
         }
 
-        try {
-            getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit());
-        } catch (RemoteException e) {
-            Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e);
-            return LIBLOAD_FAILED_WAITING_FOR_RELRO;
+        String[] args = getWebViewNativeLibraryPaths(sPackageInfo);
+        int result = nativeLoadWithRelroFile(args[0] /* path32 */,
+                                                 args[1] /* path64 */,
+                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+        if (result != LIBLOAD_SUCCESS) {
+            Log.w(LOGTAG, "failed to load with relro file, proceeding without");
+        } else if (DEBUG) {
+            Log.v(LOGTAG, "loaded with relro file");
         }
-
-        try {
-            String[] args = getWebViewNativeLibraryPaths();
-            int result = nativeLoadWithRelroFile(args[0] /* path32 */,
-                                                     args[1] /* path64 */,
-                                                     CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
-                                                     CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
-            if (result != LIBLOAD_SUCCESS) {
-                Log.w(LOGTAG, "failed to load with relro file, proceeding without");
-            } else if (DEBUG) {
-                Log.v(LOGTAG, "loaded with relro file");
-            }
-            return result;
-        } catch (MissingWebViewPackageException e) {
-            Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e);
-            return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
-        }
+        return result;
     }
 
     private static IWebViewUpdateService getUpdateService() {
diff --git a/core/java/android/webkit/WebViewProviderInfo.aidl b/core/java/android/webkit/WebViewProviderInfo.aidl
new file mode 100644
index 0000000..82e5a79
--- /dev/null
+++ b/core/java/android/webkit/WebViewProviderInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 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.webkit;
+
+parcelable WebViewProviderInfo;
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
new file mode 100644
index 0000000..d5e3a230
--- /dev/null
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 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.webkit;
+
+import android.app.AppGlobals;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AndroidRuntimeException;
+import android.util.Base64;
+
+import java.util.Arrays;
+
+/** @hide */
+public class WebViewProviderInfo implements Parcelable {
+
+    /**
+     * @hide
+     */
+    public static class WebViewPackageNotFoundException extends AndroidRuntimeException {
+        public WebViewPackageNotFoundException(String message) { super(message); }
+        public WebViewPackageNotFoundException(Exception e) { super(e); }
+    }
+
+    public WebViewProviderInfo(String packageName, String description, String signature) {
+        this.packageName = packageName;
+        this.description = description;
+        this.signature = signature;
+    }
+
+    private boolean hasValidSignature() {
+        if (Build.IS_DEBUGGABLE)
+            return true;
+        Signature[] packageSignatures;
+        try {
+            // If no signature is declared, instead check whether the package is included in the
+            // system.
+            if (signature == null)
+                return getPackageInfo().applicationInfo.isSystemApp();
+
+            packageSignatures = getPackageInfo().signatures;
+        } catch (WebViewPackageNotFoundException e) {
+            return false;
+        }
+        if (packageSignatures.length != 1)
+            return false;
+        final byte[] releaseSignature = Base64.decode(signature, Base64.DEFAULT);
+        return Arrays.equals(releaseSignature, packageSignatures[0].toByteArray());
+    }
+
+    /**
+     * Returns whether this provider is valid for use as a WebView provider.
+     */
+    public boolean isValidProvider() {
+        ApplicationInfo applicationInfo;
+        try {
+            applicationInfo = getPackageInfo().applicationInfo;
+        } catch (WebViewPackageNotFoundException e) {
+            return false;
+        }
+        if (hasValidSignature() && WebViewFactory.getWebViewLibrary(applicationInfo) != null) {
+            return true;
+        }
+        return false;
+    }
+
+    public PackageInfo getPackageInfo() {
+        if (packageInfo == null) {
+            try {
+                PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+                packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new WebViewPackageNotFoundException(e);
+            }
+        }
+        return packageInfo;
+    }
+
+    // aidl stuff
+    public static final Parcelable.Creator<WebViewProviderInfo> CREATOR =
+        new Parcelable.Creator<WebViewProviderInfo>() {
+            public WebViewProviderInfo createFromParcel(Parcel in) {
+                return new WebViewProviderInfo(in);
+            }
+
+            public WebViewProviderInfo[] newArray(int size) {
+                return new WebViewProviderInfo[size];
+            }
+        };
+
+    private WebViewProviderInfo(Parcel in) {
+        packageName = in.readString();
+        description = in.readString();
+        signature = in.readString();
+        packageInfo = null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(packageName);
+        out.writeString(description);
+        out.writeString(signature);
+    }
+
+    // fields read from framework resource
+    public String packageName;
+    public String description;
+
+    private String signature;
+
+    private PackageInfo packageInfo;
+    // flags declaring we want extra info from the package manager
+    private final static int PACKAGE_FLAGS =
+        PackageManager.GET_META_DATA
+        | PackageManager.GET_SIGNATURES;
+}
+
diff --git a/core/java/android/webkit/WebViewProviderResponse.aidl b/core/java/android/webkit/WebViewProviderResponse.aidl
new file mode 100644
index 0000000..9c884cc
--- /dev/null
+++ b/core/java/android/webkit/WebViewProviderResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 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.webkit;
+
+parcelable WebViewProviderResponse;
diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java
new file mode 100644
index 0000000..f5e09e2
--- /dev/null
+++ b/core/java/android/webkit/WebViewProviderResponse.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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.webkit;
+
+import android.content.pm.PackageInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class WebViewProviderResponse implements Parcelable {
+
+    public WebViewProviderResponse(PackageInfo packageInfo, int status) {
+        this.packageInfo = packageInfo;
+        this.status = status;
+    }
+
+    // aidl stuff
+    public static final Parcelable.Creator<WebViewProviderResponse> CREATOR =
+        new Parcelable.Creator<WebViewProviderResponse>() {
+            public WebViewProviderResponse createFromParcel(Parcel in) {
+                return new WebViewProviderResponse(in);
+            }
+
+            public WebViewProviderResponse[] newArray(int size) {
+                return new WebViewProviderResponse[size];
+            }
+        };
+
+    private WebViewProviderResponse(Parcel in) {
+        packageInfo = in.readTypedObject(PackageInfo.CREATOR);
+        status = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedObject(packageInfo, flags);
+        out.writeInt(status);
+    }
+
+    PackageInfo packageInfo;
+    int status;
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 97c0e07..e2b1dbb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2154,11 +2154,6 @@
         string that's stored in 8-bit unpacked format) characters.-->
     <bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
 
-    <!-- List of package names (ordered by preference) providing WebView implementations. -->
-    <string-array name="config_webViewPackageNames" translatable="false">
-      <item>com.android.webview</item>
-    </string-array>
-
     <!-- If EMS is not supported, framework breaks down EMS into single segment SMS
          and adds page info " x/y". This config is used to set which carrier doesn't
          support EMS and whether page info should be added at the beginning or the end.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dd81f89..be99b61 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2018,7 +2018,7 @@
   <java-symbol type="attr" name="actionModeWebSearchDrawable" />
   <java-symbol type="string" name="websearch" />
   <java-symbol type="drawable" name="ic_media_video_poster" />
-  <java-symbol type="array" name="config_webViewPackageNames" />
+  <java-symbol type="xml" name="config_webview_packages" />
 
   <!-- From SubtitleView -->
   <java-symbol type="dimen" name="subtitle_corner_radius" />
diff --git a/core/res/res/xml/config_webview_packages.xml b/core/res/res/xml/config_webview_packages.xml
new file mode 100644
index 0000000..6f9c58d
--- /dev/null
+++ b/core/res/res/xml/config_webview_packages.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2015 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.
+-->
+
+<webviewproviders>
+    <!-- The default WebView implementation -->
+    <webviewprovider description="Android WebView" packageName="com.android.webview" />
+</webviewproviders>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 55bd08a..7e22881 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -675,6 +675,11 @@
     <!-- Sound & display settings screen, theme setting value to automatically switch between a light- or dark-colored user interface [CHAR LIMIT=30] -->
     <string name="night_mode_auto">Automatic</string>
 
+    <!-- Developer settings: select WebView provider title -->
+    <string name="select_webview_provider_title">WebView implementation</string>
+    <!-- Developer settings: select WebView provider dialog title -->
+    <string name="select_webview_provider_dialog_title">Set WebView implementation</string>
+
     <!-- Developer settings screen, convert userdata to file encryption option name -->
     <string name="convert_to_file_encryption">Convert to file encryption</string>
     <!-- Developer settings screen, convert userdata to file encryption summary when option is available -->
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 26ece54..94b27ab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5388,7 +5388,7 @@
                     return;
                 }
                 killPackageProcessesLocked(packageName, appId, userId,
-                        ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
+                        ProcessList.SERVICE_ADJ, false, true, true, false, true, "kill background");
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -5689,7 +5689,7 @@
 
     private final boolean killPackageProcessesLocked(String packageName, int appId,
             int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
-            boolean doit, boolean evenPersistent, String reason) {
+            boolean doit, boolean evenPersistent, boolean killPackageApp, String reason) {
         ArrayList<ProcessRecord> procs = new ArrayList<>();
 
         // Remove all processes this package may have touched: all with the
@@ -5738,7 +5738,7 @@
                     if (userId != UserHandle.USER_ALL && app.userId != userId) {
                         continue;
                     }
-                    if (!app.pkgList.containsKey(packageName) && !isDep) {
+                    if ((!killPackageApp || !app.pkgList.containsKey(packageName)) && !isDep) {
                         continue;
                     }
                 }
@@ -5904,7 +5904,7 @@
         }
 
         boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
-                ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
+                ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent, true,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
@@ -11819,7 +11819,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 killPackageProcessesLocked(null, appId, userId,
-                        ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true,
+                        ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true, true,
                         reason != null ? reason : "kill uid");
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -20777,4 +20777,35 @@
             }
         }
     }
+
+    /**
+     * Kill processes for the user with id userId and that depend on the package named packageName
+     */
+    @Override
+    public void killPackageDependents(String packageName, int userId) {
+        enforceCallingPermission(android.Manifest.permission.KILL_UID, "killPackageDependents()");
+        if (packageName == null) {
+            throw new NullPointerException("Cannot kill the dependents of a package without its name.");
+        }
+
+        long callingId = Binder.clearCallingIdentity();
+        IPackageManager pm = AppGlobals.getPackageManager();
+        int pkgUid = -1;
+        try {
+            pkgUid = pm.getPackageUid(packageName, userId);
+        } catch (RemoteException e) {
+        }
+        if (pkgUid == -1) {
+            throw new IllegalArgumentException("Cannot kill dependents of non-existing package " + packageName);
+        }
+        try {
+            synchronized(this) {
+                killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid), userId,
+                        ProcessList.FOREGROUND_APP_ADJ, false, true, true, false, false,
+                        "dep: " + packageName);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 97713fc..7be0ead 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -16,18 +16,35 @@
 
 package com.android.server.webkit;
 
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.os.Binder;
 import android.os.Process;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
 import android.webkit.WebViewFactory;
 
 import com.android.server.SystemService;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * Private service to wait for the updatable WebView to be ready for use.
  * @hide
@@ -35,12 +52,23 @@
 public class WebViewUpdateService extends SystemService {
 
     private static final String TAG = "WebViewUpdateService";
-    private static final int WAIT_TIMEOUT_MS = 5000; // Same as KEY_DISPATCHING_TIMEOUT.
+    private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
 
-    private boolean mRelroReady32Bit = false;
-    private boolean mRelroReady64Bit = false;
+    // Keeps track of the number of running relro creations
+    private int mNumRelroCreationsStarted = 0;
+    private int mNumRelroCreationsFinished = 0;
+    // Implies that we need to rerun relro creation because we are using an out-of-date package
+    private boolean mWebViewPackageDirty = false;
+    // Set to true when the current provider is being replaced
+    private boolean mCurrentProviderBeingReplaced = false;
+    private boolean mAnyWebViewInstalled = false;
 
-    private String oldWebViewPackageName = null;
+    private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+    // The WebView package currently in use (or the one we are preparing).
+    private PackageInfo mCurrentWebViewPackage = null;
+    // The WebView providers that are currently available.
+    private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
 
@@ -53,28 +81,69 @@
         mWebViewUpdatedReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-
-                    // When a package is replaced we will receive two intents, one representing the
-                    // removal of the old package and one representing the addition of the new
-                    // package. We here ignore the intent representing the removed package to make
-                    // sure we don't change WebView provider twice.
+                    // When a package is replaced we will receive two intents, one representing
+                    // the removal of the old package and one representing the addition of the
+                    // new package.
+                    // In the case where we receive an intent to remove the old version of the
+                    // package that is being replaced we set a flag here and early-out so that we
+                    // don't change provider while replacing the current package (we will instead
+                    // change provider when the new version of the package is being installed).
                     if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
-                            && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
+                        && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
+                        synchronized(this) {
+                            if (mCurrentWebViewPackage == null) return;
+
+                            String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
+                            if (webViewPackage.equals(intent.getDataString()))
+                                mCurrentProviderBeingReplaced = true;
+                        }
+
                         return;
                     }
 
-                    for (String packageName : WebViewFactory.getWebViewPackageNames()) {
-                        String webviewPackage = "package:" + packageName;
+                    for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
+                        String webviewPackage = "package:" + provider.packageName;
 
                         if (webviewPackage.equals(intent.getDataString())) {
-                            String usedPackageName =
-                                WebViewFactory.findPreferredWebViewPackage().packageName;
-                            // Only trigger update actions if the updated package is the one that
-                            // will be used, or the one that was in use before the update.
-                            if (packageName.equals(usedPackageName) ||
-                                    packageName.equals(oldWebViewPackageName)) {
-                                onWebViewUpdateInstalled();
-                                oldWebViewPackageName = usedPackageName;
+                            boolean updateWebView = false;
+                            boolean removedOldPackage = false;
+                            String oldProviderName = null;
+                            PackageInfo newPackage = null;
+                            synchronized(WebViewUpdateService.this) {
+                                try {
+                                    updateValidWebViewPackages();
+                                    newPackage = findPreferredWebViewPackage();
+                                    if (mCurrentWebViewPackage != null)
+                                        oldProviderName = mCurrentWebViewPackage.packageName;
+                                    // Only trigger update actions if the updated package is the one
+                                    // that will be used, or the one that was in use before the
+                                    // update, or if we haven't seen a valid WebView package before.
+                                    updateWebView =
+                                        provider.packageName.equals(newPackage.packageName)
+                                        || provider.packageName.equals(oldProviderName)
+                                        || mCurrentWebViewPackage == null;
+                                    // We removed the old package if we received an intent to remove
+                                    // or replace the old package.
+                                    removedOldPackage =
+                                        provider.packageName.equals(oldProviderName);
+                                    if (updateWebView) {
+                                        onWebViewProviderChanged(newPackage);
+                                    }
+                                } catch (WebViewFactory.MissingWebViewPackageException e) {
+                                    Slog.e(TAG, "Could not find valid WebView package to create " +
+                                            "relro with " + e);
+                                }
+                            }
+                            if(updateWebView && !removedOldPackage && oldProviderName != null) {
+                                // If the provider change is the result of adding or replacing a
+                                // package that was not the previous provider then we must kill
+                                // packages dependent on the old package ourselves. The framework
+                                // only kills dependents of packages that are being removed.
+                                try {
+                                    ActivityManagerNative.getDefault().killPackageDependents(
+                                        oldProviderName, getContext().getUserId());
+                                } catch (RemoteException e) {
+                                }
                             }
                             return;
                         }
@@ -90,14 +159,182 @@
         publishBinderService("webviewupdate", new BinderService());
     }
 
-    private void onWebViewUpdateInstalled() {
-        Slog.d(TAG, "WebView Package updated!");
-
-        synchronized (this) {
-            mRelroReady32Bit = false;
-            mRelroReady64Bit = false;
+    /**
+     * Perform any WebView loading preparations that must happen at boot from the system server,
+     * after the package manager has started or after an update to the webview is installed.
+     * This must be called in the system server.
+     * Currently, this means spawning the child processes which will create the relro files.
+     */
+    public void prepareWebViewInSystemServer() {
+        try {
+            synchronized(this) {
+                updateValidWebViewPackages();
+                mCurrentWebViewPackage = findPreferredWebViewPackage();
+                onWebViewProviderChanged(mCurrentWebViewPackage);
+            }
+        } catch (Throwable t) {
+            // Log and discard errors at this stage as we must not crash the system server.
+            Slog.e(TAG, "error preparing webview provider from system server", t);
         }
-        WebViewFactory.onWebViewUpdateInstalled();
+    }
+
+
+    /**
+     * Change WebView provider and provider setting and kill packages using the old provider.
+     */
+    private void changeProviderAndSetting(String newProviderName) {
+        PackageInfo oldPackage = null;
+        PackageInfo newPackage = null;
+        synchronized(this) {
+            oldPackage = mCurrentWebViewPackage;
+            updateUserSetting(newProviderName);
+
+            try {
+                newPackage = findPreferredWebViewPackage();
+                if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
+                    // If we don't perform the user change, revert the settings change.
+                    updateUserSetting(newPackage.packageName);
+                    return;
+                }
+            } catch (WebViewFactory.MissingWebViewPackageException e) {
+                Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView package "
+                        + e);
+                // If we don't perform the user change but don't have an installed WebView package,
+                // we will have changed the setting and it will be used when a package is available.
+                return;
+            }
+            onWebViewProviderChanged(newPackage);
+        }
+        // Kill apps using the old provider
+        try {
+            if (oldPackage != null) {
+                ActivityManagerNative.getDefault().killPackageDependents(
+                        oldPackage.packageName, getContext().getUserId());
+            }
+        } catch (RemoteException e) {
+        }
+        return;
+    }
+
+    /**
+     * This is called when we change WebView provider, either when the current provider is updated
+     * or a new provider is chosen / takes precedence.
+     */
+    private void onWebViewProviderChanged(PackageInfo newPackage) {
+        synchronized(this) {
+            mAnyWebViewInstalled = true;
+            // If we have changed provider then the replacement of the old provider is
+            // irrelevant - we can only have chosen a new provider if its package is available.
+            mCurrentProviderBeingReplaced = false;
+            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                mCurrentWebViewPackage = newPackage;
+                updateUserSetting(newPackage.packageName);
+
+                // The relro creations might 'finish' (not start at all) before
+                // WebViewFactory.onWebViewProviderChanged which means we might not know the number
+                // of started creations before they finish.
+                mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+                mNumRelroCreationsFinished = 0;
+                mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage);
+                // If the relro creations finish before we know the number of started creations we
+                // will have to do any cleanup/notifying here.
+                checkIfRelrosDoneLocked();
+            } else {
+                mWebViewPackageDirty = true;
+            }
+        }
+    }
+
+    /**
+     * Updates the currently valid WebView provider packages.
+     * Should be used when a provider has been installed or removed.
+     * @hide
+     * */
+    private void updateValidWebViewPackages() {
+        List<WebViewProviderInfo> webViewProviders  =
+            new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages()));
+        Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
+        // remove non-valid packages
+        while(it.hasNext()) {
+            WebViewProviderInfo current = it.next();
+            if (!current.isValidProvider())
+                it.remove();
+        }
+        synchronized(this) {
+            mCurrentValidWebViewPackages =
+                webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
+        }
+    }
+
+    private static String getUserChosenWebViewProvider() {
+        return Settings.Secure.getString(AppGlobals.getInitialApplication().getContentResolver(),
+                Settings.Secure.WEBVIEW_PROVIDER);
+    }
+
+    private void updateUserSetting(String newProviderName) {
+        Settings.Secure.putString(getContext().getContentResolver(),
+                Settings.Secure.WEBVIEW_PROVIDER,
+                newProviderName == null ? "" : newProviderName);
+    }
+
+    /**
+     * Returns either the package info of the WebView provider determined in the following way:
+     * If the user has chosen a provider then use that if it is valid,
+     * otherwise use the first package in the webview priority list that is valid.
+     *
+     * @hide
+     */
+    private PackageInfo findPreferredWebViewPackage() {
+        WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
+
+        String userChosenProvider = getUserChosenWebViewProvider();
+
+        // If the user has chosen provider, use that
+        for (WebViewProviderInfo provider : providers) {
+            if (provider.packageName.equals(userChosenProvider)) {
+                return provider.getPackageInfo();
+            }
+        }
+
+        // User did not choose, or the choice failed, use the most stable provider available
+        for (WebViewProviderInfo provider : providers) {
+            return provider.getPackageInfo();
+        }
+        mAnyWebViewInstalled = false;
+        throw new WebViewFactory.MissingWebViewPackageException(
+                "Could not find a loadable WebView package");
+    }
+
+    /**
+     * Returns whether WebView is ready and is not going to go through its preparation phase again
+     * directly.
+     */
+    private boolean webViewIsReadyLocked() {
+        return !mWebViewPackageDirty
+            && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+            && !mCurrentProviderBeingReplaced
+            // The current package might be replaced though we haven't received an intent declaring
+            // this yet, the following flag makes anyone loading WebView to wait in this case.
+            && mAnyWebViewInstalled;
+    }
+
+    private void checkIfRelrosDoneLocked() {
+        if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+            if (mWebViewPackageDirty) {
+                mWebViewPackageDirty = false;
+                // If we have changed provider since we started the relro creation we need to
+                // redo the whole process using the new package instead.
+                // Though, if the current provider package is being replaced we don't want to change
+                // provider here since we will perform the change either when the package is added
+                // again or when we switch to another provider (whichever comes first).
+                if (!mCurrentProviderBeingReplaced) {
+                    PackageInfo newPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(newPackage);
+                }
+            } else {
+                this.notifyAll();
+            }
+        }
     }
 
     private class BinderService extends IWebViewUpdateService.Stub {
@@ -108,7 +345,7 @@
          * crashed.
          */
         @Override // Binder call
-        public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
+        public void notifyRelroCreationCompleted() {
             // Verify that the caller is either the shared relro process (nominal case) or the
             // system server (only in the case the relro process crashes and we get here via the
             // crashHandler).
@@ -118,20 +355,17 @@
             }
 
             synchronized (WebViewUpdateService.this) {
-                if (is64Bit) {
-                    mRelroReady64Bit = true;
-                } else {
-                    mRelroReady32Bit = true;
-                }
-                WebViewUpdateService.this.notifyAll();
+                mNumRelroCreationsFinished++;
+                checkIfRelrosDoneLocked();
             }
         }
 
         /**
          * WebViewFactory calls this to block WebView loading until the relro file is created.
+         * Returns the WebView provider for which we create relro files.
          */
         @Override // Binder call
-        public void waitForRelroCreationCompleted(boolean is64Bit) {
+        public WebViewProviderResponse waitForAndGetProvider() {
             // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which
             // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If
             // another service there tries to bring up a WebView in the between, the wait below
@@ -140,21 +374,74 @@
                 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
             }
 
+            PackageInfo webViewPackage = null;
             final long NS_PER_MS = 1000000;
             final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
-            boolean relroReady = (is64Bit ? mRelroReady64Bit : mRelroReady32Bit);
+            boolean webViewReady = false;
+            int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
             synchronized (WebViewUpdateService.this) {
-                while (!relroReady) {
+                webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
+                while (!webViewReady) {
                     final long timeNowMs = System.nanoTime() / NS_PER_MS;
                     if (timeNowMs >= timeoutTimeMs) break;
                     try {
                         WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
                     } catch (InterruptedException e) {}
-                    relroReady = (is64Bit ? mRelroReady64Bit : mRelroReady32Bit);
+                    webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
+                }
+                // Make sure we return the provider that was used to create the relro file
+                webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
+                if (webViewReady) {
+                } else if (mCurrentProviderBeingReplaced) {
+                    // It is important that we check this flag before the one representing WebView
+                    // being installed, otherwise we might think there is no WebView though the
+                    // current one is just being replaced.
+                    webViewStatus = WebViewFactory.LIBLOAD_WEBVIEW_BEING_REPLACED;
+                } else if (!mAnyWebViewInstalled) {
+                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+                } else {
+                    // Either the current relro creation  isn't done yet, or the new relro creatioin
+                    // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
                 }
             }
-            if (!relroReady) Slog.w(TAG, "creating relro file timed out");
+            if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+            return new WebViewProviderResponse(webViewPackage, webViewStatus);
+        }
+
+        /**
+         * This is called from DeveloperSettings when the user changes WebView provider.
+         */
+        @Override // Binder call
+        public void changeProviderAndSetting(String newProvider) {
+            if (getContext().checkCallingPermission(
+                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                String msg = "Permission Denial: changeProviderAndSetting() from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid()
+                        + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+
+            WebViewUpdateService.this.changeProviderAndSetting(newProvider);
+        }
+
+        @Override // Binder call
+        public WebViewProviderInfo[] getValidWebViewPackages() {
+            synchronized(WebViewUpdateService.this) {
+                return mCurrentValidWebViewPackages;
+            }
+        }
+
+        @Override // Binder call
+        public String getCurrentWebViewPackageName() {
+            synchronized(WebViewUpdateService.this) {
+                if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
+                    return null;
+                return WebViewUpdateService.this.mCurrentWebViewPackage.packageName;
+            }
         }
     }
-
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2f33d7c..189ed33 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -150,6 +150,7 @@
     // TODO: remove all of these references by improving dependency resolution and boot phases
     private PowerManagerService mPowerManagerService;
     private ActivityManagerService mActivityManagerService;
+    private WebViewUpdateService mWebViewUpdateService;
     private DisplayManagerService mDisplayManagerService;
     private PackageManagerService mPackageManagerService;
     private PackageManager mPackageManager;
@@ -409,7 +410,7 @@
                 LocalServices.getService(UsageStatsManagerInternal.class));
 
         // Tracks whether the updatable WebView is in a ready state and watches for update installs.
-        mSystemServiceManager.startService(WebViewUpdateService.class);
+        mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
     }
 
     /**
@@ -1180,7 +1181,7 @@
 
                 Slog.i(TAG, "WebViewFactory preparation");
                 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "WebViewFactoryPreparation");
-                WebViewFactory.prepareWebViewInSystemServer();
+                mWebViewUpdateService.prepareWebViewInSystemServer();
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
                 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");