Merge "Support dnd condition in Settings"
diff --git a/api/system-current.txt b/api/system-current.txt
index 332a516..492d6a9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -46479,8 +46479,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
@@ -46489,7 +46487,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 4cb2619..f11bf742 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 eb8d6bc..64c69af 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/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 8186378..d23f26d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -145,8 +145,9 @@
     native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
-    private static void callPostForkChildHooks(int debugFlags, String instructionSet) {
-        VM_HOOKS.postForkChild(debugFlags, instructionSet);
+    private static void callPostForkChildHooks(int debugFlags, boolean isSystemServer,
+            String instructionSet) {
+        VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet);
     }
 
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 73c7487..96d150b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -581,7 +581,7 @@
     UnsetSigChldHandler();
 
     env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
-                              is_system_server ? NULL : instructionSet);
+                              is_system_server, instructionSet);
     if (env->ExceptionCheck()) {
       RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
     }
@@ -652,7 +652,7 @@
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
   gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
   gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
-                                                   "(ILjava/lang/String;)V");
+                                                   "(IZLjava/lang/String;)V");
 
   return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
 }
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 6367d37..bf758c2 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/FusedLocation/res/values-de/strings.xml b/packages/FusedLocation/res/values-de/strings.xml
index d7e5faa..5c846d8 100644
--- a/packages/FusedLocation/res/values-de/strings.xml
+++ b/packages/FusedLocation/res/values-de/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="5379477904423203699">"Kombinierte Standortbestimmung"</string>
+    <string name="app_label" msgid="5379477904423203699">"Kombinierte Standortbest."</string>
 </resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 51c0281..0e91fdd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
@@ -35,9 +34,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.HashSet;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Database for MTP objects.
@@ -397,6 +397,7 @@
         values.putNull(COLUMN_OBJECT_HANDLE);
         values.putNull(COLUMN_PARENT_DOCUMENT_ID);
         values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
+        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE);
         values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
         values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
         values.putNull(Document.COLUMN_SUMMARY);
@@ -436,6 +437,7 @@
         values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
         values.put(COLUMN_PARENT_DOCUMENT_ID, parentId);
         values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
+        values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_OBJECT);
         values.put(Document.COLUMN_MIME_TYPE, mimeType);
         values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
         values.putNull(Document.COLUMN_SUMMARY);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index 0ead2d5..72bd6ee 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -49,6 +49,7 @@
     static final String COLUMN_STORAGE_ID = "storage_id";
     static final String COLUMN_OBJECT_HANDLE = "object_handle";
     static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
+    static final String COLUMN_DOCUMENT_TYPE = "document_type";
     static final String COLUMN_ROW_STATE = "row_state";
 
     /**
@@ -83,6 +84,23 @@
      */
     static final int MAP_BY_NAME = 1;
 
+    /**
+     * Document that represents a MTP device.
+     * Note we have "device" document only when the device has multiple storage volumes. Otherwise
+     * we regard the single "storage" document as root.
+     */
+    static final int DOCUMENT_TYPE_DEVICE = 0;
+
+    /**
+     * Document that represents a MTP storage.
+     */
+    static final int DOCUMENT_TYPE_STORAGE = 1;
+
+    /**
+     * Document that represents a MTP object.
+     */
+    static final int DOCUMENT_TYPE_OBJECT = 2;
+
     static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
     static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
     static final String SELECTION_ROOT_DOCUMENTS =
@@ -98,6 +116,7 @@
             COLUMN_OBJECT_HANDLE + " INTEGER," +
             COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
             COLUMN_ROW_STATE + " INTEGER NOT NULL," +
+            COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
             Document.COLUMN_MIME_TYPE + " TEXT," +
             Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
             Document.COLUMN_SUMMARY + " TEXT," +
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 67b0672..8166de1 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -42,7 +42,8 @@
         DocumentsContract.Document.COLUMN_LAST_MODIFIED,
         DocumentsContract.Document.COLUMN_ICON,
         DocumentsContract.Document.COLUMN_FLAGS,
-        DocumentsContract.Document.COLUMN_SIZE
+        DocumentsContract.Document.COLUMN_SIZE,
+        MtpDatabaseConstants.COLUMN_DOCUMENT_TYPE
     };
 
     private final TestResources resources = new TestResources();
@@ -83,6 +84,8 @@
             assertEquals("icon", R.drawable.ic_root_mtp, cursor.getInt(8));
             assertEquals("flag", 0, cursor.getInt(9));
             assertEquals("size", 1000, cursor.getInt(10));
+            assertEquals(
+                    "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, cursor.getInt(11));
 
             cursor.moveToNext();
             assertEquals("documentId", 2, cursor.getInt(0));
@@ -178,6 +181,8 @@
                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
                 cursor.getInt(9));
         assertEquals("size", 1024, cursor.getInt(10));
+        assertEquals(
+                "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
 
         cursor.moveToNext();
         assertEquals("documentId", 2, cursor.getInt(0));
@@ -195,6 +200,8 @@
                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
                 cursor.getInt(9));
         assertEquals("size", 2 * 1024 * 1024, cursor.getInt(10));
+        assertEquals(
+                "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
 
         cursor.moveToNext();
         assertEquals("documentId", 3, cursor.getInt(0));
@@ -212,6 +219,8 @@
                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
                 cursor.getInt(9));
         assertEquals("size", 3 * 1024 * 1024, cursor.getInt(10));
+        assertEquals(
+                "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
 
         cursor.close();
     }
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 82e8dc6..1033bb19 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -141,7 +141,7 @@
     <string name="development_settings_not_available" msgid="4308569041701535607">"Bu foydalanuvchiga dasturchi imkoniyatlari taqdim etilmagan"</string>
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Ushbu foydalanuvchi uchun VPN sozlamalari mavjud emas"</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"Ushbu foydalanuvchi uchun Modem rejimi sozlamalari mavjud emas"</string>
-    <string name="apn_settings_not_available" msgid="7873729032165324000">"Ushbu foydalanuvchi uchun Ulanish nuqtasi nomi (APN) sozlamalari mavjud emas"</string>
+    <string name="apn_settings_not_available" msgid="7873729032165324000">"Ushbu foydalanuvchi uchun Internetga kirish nuqtasi (APN) sozlamalari mavjud emas"</string>
     <string name="enable_adb" msgid="7982306934419797485">"USB orqali nosozliklarni tuzatish"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"USB orqali kompyuterga ulanganda tuzatish rejimi yoqilsin"</string>
     <string name="clear_adb_keys" msgid="4038889221503122743">"USB orqali nosozliklarni tuzatishni taqiqlash"</string>
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/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index 7d37137..c871727 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -124,6 +124,16 @@
 
         if (gotIt) {
             Log.v(TAG, "Found activity " + name + ", it's the default action");
+            // Clicks the "Just Once" button.
+            gotIt = mDevice
+                    .wait(Until.hasObject(By.res("android", "button_once")), mTimeout);
+            assertTrue("'Just Once' button not visible yet", gotIt);
+
+            UiObject justOnce = mDevice
+                    .findObject(new UiSelector().resourceId("android:id/button_once"));
+            assertTrue("'Just Once' button not found", justOnce.exists());
+
+            click(justOnce, "Just Once");
         } else {
             // Since it's not, need to find it in the scrollable list...
             Log.v(TAG, "Activity " + name + " is not default action");
@@ -140,16 +150,5 @@
             // ... then select it.
             click(activity, name);
         }
-
-        // Then clicks the "Just Once" button.
-        gotIt = mDevice
-                .wait(Until.hasObject(By.res("android", "button_once")), mTimeout);
-        assertTrue("'Just Once' button not visible yet", gotIt);
-
-        UiObject justOnce = mDevice
-                .findObject(new UiSelector().resourceId("android:id/button_once"));
-        assertTrue("'Just Once' button not found", justOnce.exists());
-
-        click(justOnce, "Just Once");
     }
 }
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5acd0ef..8d3da117 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -217,7 +217,7 @@
     <string name="accessibility_casting_turned_off" msgid="1430668982271976172">"העברת המסך הופסקה."</string>
     <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"מצב עבודה כבוי."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"מצב עבודה מופעל."</string>
-    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"מצב עבודה כובה."</string>
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"מצב עבודה הושבת."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"מצב עבודה הופעל."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"בהירות תצוגה"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"‏השימוש בנתוני 2G-3G מושהה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 1edfd5c..ae57a32 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -161,7 +161,7 @@
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"マナーモード着信。"</string>
     <!-- no translation found for accessibility_casting (6887382141726543668) -->
     <skip />
-    <string name="accessibility_work_mode" msgid="2478631941714607225">"職場モード"</string>
+    <string name="accessibility_work_mode" msgid="2478631941714607225">"Work モード"</string>
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>は削除されました。"</string>
     <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近のアプリケーションをすべて消去しました。"</string>
@@ -213,10 +213,10 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"モバイルアクセスポイントをOFFにしました。"</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2890951609226476206">"モバイルアクセスポイントをONにしました。"</string>
     <string name="accessibility_casting_turned_off" msgid="1430668982271976172">"画面のキャストが停止しました。"</string>
-    <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"職場モードがオフです。"</string>
-    <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"職場モードがオンです。"</string>
-    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"職場モードをオフにしました。"</string>
-    <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"職場モードをオンにしました。"</string>
+    <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"Work モードがオフです。"</string>
+    <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Work モードがオンです。"</string>
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Work モードをオフにしました。"</string>
+    <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work モードをオンにしました。"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ディスプレイの明るさ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G~3Gデータは一時停止中です"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4Gデータは一時停止中です"</string>
@@ -294,7 +294,7 @@
     <string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g>使用中"</string>
     <string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"警告: 上限は<xliff:g id="DATA_LIMIT">%s</xliff:g>です"</string>
-    <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"職場モード"</string>
+    <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work モード"</string>
     <string name="recents_empty_message" msgid="8682129509540827999">"ここに最近の画面が表示されます"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"アプリ情報"</string>
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"画面固定"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 9cccec4..35a6e1b 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -270,7 +270,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ບໍ່ມີເຄືອຂ່າຍ"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi​-Fi ປິດ"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"ບໍ່​ມີ​ເຄືອ​ຂ່າຍ Wi-Fi ຢູ່"</string>
-    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ຄາສທ໌"</string>
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ການສົ່ງສັນຍານ"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"​ກຳ​ລັງ​ສົ່ງ​ສັນ​ຍານ"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"​ອຸ​ປະ​ກອນບໍ່​ມີ​ຊື່"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"​ພ້ອ​ມ​ສົ່ງ​ສັນ​ຍານ​ແລ້ວ"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 9a22b2c..9a0a0c0 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -213,8 +213,8 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"Tempat liputan mudah alih bergerak dimatikan."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2890951609226476206">"Tempat liputan mudah alih bergerak dihidupkan."</string>
     <string name="accessibility_casting_turned_off" msgid="1430668982271976172">"Penghantaran skrin dihentikan."</string>
-    <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"Mod kerja dimatikan."</string>
-    <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mod kerja dihidupkan."</string>
+    <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"Mod kerja mati."</string>
+    <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mod kerja hidup."</string>
     <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Mod kerja dimatikan."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mod kerja dihidupkan."</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan paparan"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 60bedae..512effa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -93,7 +93,8 @@
 
     public TaskKey key;
     public TaskGrouping group;
-    public int taskAffiliation;
+    // The taskAffiliationId is the task id of the parent task or itself if it is not affiliated with any task
+    public int taskAffiliationId;
     public int taskAffiliationColor;
     public boolean isLaunchTarget;
     public Drawable applicationIcon;
@@ -123,7 +124,7 @@
         boolean isInAffiliationGroup = (taskAffiliation != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
         this.key = key;
-        this.taskAffiliation = taskAffiliation;
+        this.taskAffiliationId = taskAffiliation;
         this.taskAffiliationColor = taskAffiliationColor;
         this.activityLabel = activityTitle;
         this.contentDescription = contentDescription;
@@ -142,7 +143,7 @@
     /** Copies the other task. */
     public void copyFrom(Task o) {
         this.key = o.key;
-        this.taskAffiliation = o.taskAffiliation;
+        this.taskAffiliationId = o.taskAffiliationId;
         this.taskAffiliationColor = o.taskAffiliationColor;
         this.activityLabel = o.activityLabel;
         this.contentDescription = o.contentDescription;
@@ -206,6 +207,13 @@
         }
     }
 
+    /**
+     * Returns whether this task is affiliated with another task.
+     */
+    public boolean isAffiliatedTask() {
+        return key.id != taskAffiliationId;
+    }
+
     @Override
     public boolean equals(Object o) {
         // Check that the id matches
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 7a98393..13ab392 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
+import android.util.SparseArray;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsDebugFlags;
@@ -50,7 +51,7 @@
  */
 interface TaskFilter {
     /** Returns whether the filter accepts the specified task */
-    public boolean acceptTask(Task t, int index);
+    public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
 }
 
 /**
@@ -157,10 +158,17 @@
     private void updateFilteredTasks() {
         mFilteredTasks.clear();
         if (mFilter != null) {
+            // Create a sparse array from task id to Task
+            SparseArray<Task> taskIdMap = new SparseArray<>();
             int taskCount = mTasks.size();
             for (int i = 0; i < taskCount; i++) {
                 Task t = mTasks.get(i);
-                if (mFilter.acceptTask(t, i)) {
+                taskIdMap.put(t.key.id, t);
+            }
+
+            for (int i = 0; i < taskCount; i++) {
+                Task t = mTasks.get(i);
+                if (mFilter.acceptTask(taskIdMap, t, i)) {
                     mFilteredTasks.add(t);
                 }
             }
@@ -318,13 +326,29 @@
         // Ensure that we only show non-docked tasks
         mStackTaskList.setFilter(new TaskFilter() {
             @Override
-            public boolean acceptTask(Task t, int index) {
+            public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
+                if (t.isAffiliatedTask()) {
+                    // If this task is affiliated with another parent in the stack, then the historical state of this
+                    // task depends on the state of the parent task
+                    Task parentTask = taskIdMap.get(t.taskAffiliationId);
+                    if (parentTask != null) {
+                        t = parentTask;
+                    }
+                }
                 return !t.isHistorical && !SystemServicesProxy.isDockedStack(t.key.stackId);
             }
         });
         mHistoryTaskList.setFilter(new TaskFilter() {
             @Override
-            public boolean acceptTask(Task t, int index) {
+            public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
+                if (t.isAffiliatedTask()) {
+                    // If this task is affiliated with another parent in the stack, then the historical state of this
+                    // task depends on the state of the parent task
+                    Task parentTask = taskIdMap.get(t.taskAffiliationId);
+                    if (parentTask != null) {
+                        t = parentTask;
+                    }
+                }
                 return t.isHistorical && !SystemServicesProxy.isDockedStack(t.key.stackId);
             }
         });
@@ -585,8 +609,8 @@
                             taskGrouping2.latestActiveTimeInGroup);
                 }
             });
-            // Sort group tasks by increasing firstActiveTime of the task, and also build a new list of
-            // tasks
+            // Sort group tasks by increasing firstActiveTime of the task, and also build a new list
+            // of tasks
             int taskIndex = 0;
             int groupCount = mGroups.size();
             for (int i = 0; i < groupCount; i++) {
@@ -607,13 +631,13 @@
             mStackTaskList.set(tasks);
         } else {
             // Create the task groups
-            HashMap<Task.TaskKey, Task> tasksMap = new HashMap<Task.TaskKey, Task>();
+            HashMap<Task.TaskKey, Task> tasksMap = new HashMap<>();
             ArrayList<Task> tasks = mStackTaskList.getTasks();
             int taskCount = tasks.size();
             for (int i = 0; i < taskCount; i++) {
                 Task t = tasks.get(i);
                 TaskGrouping group;
-                int affiliation = t.taskAffiliation > 0 ? t.taskAffiliation :
+                int affiliation = t.taskAffiliationId > 0 ? t.taskAffiliationId :
                         IndividualTaskIdOffset + t.key.id;
                 if (mAffinitiesGroups.containsKey(affiliation)) {
                     group = getGroupWithAffiliation(affiliation);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 22d30df..be1fd58 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5390,7 +5390,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);
@@ -5691,7 +5691,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
@@ -5740,7 +5740,7 @@
                     if (userId != UserHandle.USER_ALL && app.userId != userId) {
                         continue;
                     }
-                    if (!app.pkgList.containsKey(packageName) && !isDep) {
+                    if ((!killPackageApp || !app.pkgList.containsKey(packageName)) && !isDep) {
                         continue;
                     }
                 }
@@ -5906,7 +5906,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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 11fdb92..459c47f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -523,7 +523,9 @@
                 case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
                     for (int type : mLocalDevices) {
                         HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
-                        localDevice.setAutoDeviceOff(enabled);
+                        if (localDevice != null) {
+                            localDevice.setAutoDeviceOff(enabled);
+                        }
                     }
                     // No need to propagate to HAL.
                     break;
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/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9b64481..5942198 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -110,6 +110,8 @@
 
     final DimLayerController mDimLayerController;
 
+    final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
+
     /**
      * @param display May not be null.
      * @param service You know.
@@ -412,6 +414,11 @@
             inputMethod.getTouchableRegion(mTmpRegion);
             mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
         }
+        for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
+            WindowState win = mTapExcludedWindows.get(i);
+            win.getTouchableRegion(mTmpRegion);
+            mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
+        }
         if (mTapDetector != null) {
             mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0c606fe..816cab7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -42,6 +42,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -1966,6 +1967,10 @@
 
             res = WindowManagerGlobal.ADD_OKAY;
 
+            if (type == TYPE_STATUS_BAR || type == TYPE_NAVIGATION_BAR) {
+                displayContent.mTapExcludedWindows.add(win);
+            }
+
             origId = Binder.clearCallingIdentity();
 
             if (addToken) {
@@ -2309,6 +2314,11 @@
             Slog.w(TAG_WM, "Removing window " + win, e);
         }
 
+        final int type = win.mAttrs.type;
+        if (type == TYPE_STATUS_BAR || type == TYPE_NAVIGATION_BAR) {
+            final DisplayContent displaycontent = win.getDisplayContent();
+            displaycontent.mTapExcludedWindows.remove(win);
+        }
         mPolicy.removeWindowLw(win);
         win.removeLocked();
 
@@ -2364,7 +2374,7 @@
             }
         }
 
-        if (win.mAttrs.type == TYPE_WALLPAPER) {
+        if (type == TYPE_WALLPAPER) {
             mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
             getDefaultDisplayContentLocked().pendingLayoutChanges |=
                     WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 539810d..cd82a5f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -710,11 +710,13 @@
 
         // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values.
         if (mTmpSize.width() < 1) {
-            Slog.w(TAG, "Width of " + w + " is not positive " + mTmpSize.width());
+            if (!mWin.mLayoutNeeded) Slog.w(TAG,
+                    "Width of " + w + " is not positive " + mTmpSize.width());
             mTmpSize.right = mTmpSize.left + 1;
         }
         if (mTmpSize.height() < 1) {
-            Slog.w(TAG, "Height of " + w + " is not positive " + mTmpSize.height());
+            if (!mWin.mLayoutNeeded) Slog.w(TAG,
+                    "Height of " + w + " is not positive " + mTmpSize.height());
             mTmpSize.bottom = mTmpSize.top + 1;
         }
 
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");