Merge "QS: Back button closes detail (if showing)" into lmp-dev
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5262a5f..2a17fa6 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -25,4 +25,6 @@
     // Called by the power manager.
     public abstract void goingToSleep();
     public abstract void wakingUp();
+    public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
+            String processName, String abiOverride, int uid, Runnable crashHandler);
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5347f03..772e132 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -454,7 +454,7 @@
      * Private non-Binder interfaces
      */
     /* package */ boolean testIsSystemReady();
-    
+
     /** Information you can retrieve about a particular application. */
     public static class ContentProviderHolder implements Parcelable {
         public final ProviderInfo info;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 6278e69..5248131 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -50,14 +50,17 @@
     /**
      * Indicates that recognition for the given keyphrase is not available on the system
      * because of the hardware configuration.
+     * No further interaction should be performed with the detector that returns this availability.
      */
     public static final int STATE_HARDWARE_UNAVAILABLE = -2;
     /**
      * Indicates that recognition for the given keyphrase is not supported.
+     * No further interaction should be performed with the detector that returns this availability.
      */
     public static final int STATE_KEYPHRASE_UNSUPPORTED = -1;
     /**
      * Indicates that the given keyphrase is not enrolled.
+     * The caller may choose to begin an enrollment flow for the keyphrase.
      */
     public static final int STATE_KEYPHRASE_UNENROLLED = 1;
     /**
@@ -92,12 +95,14 @@
     // Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags.
 
     /**
-     * Simple recognition of the key phrase. Returned by {@link #getSupportedRecognitionModes()}
+     * Simple recognition of the key phrase.
+     * Returned by {@link #getSupportedRecognitionModes()}
      */
     public static final int RECOGNITION_MODE_VOICE_TRIGGER
             = SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
     /**
-     * Trigger only if one user is identified. Returned by {@link #getSupportedRecognitionModes()}
+     * User identification performed with the keyphrase recognition.
+     * Returned by {@link #getSupportedRecognitionModes()}
      */
     public static final int RECOGNITION_MODE_USER_IDENTIFICATION
             = SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
@@ -150,13 +155,11 @@
          *
          * Availability implies whether the hardware on this system is capable of listening for
          * the given keyphrase or not. <p/>
-         * If the return code is one of {@link #STATE_HARDWARE_UNAVAILABLE} or
-         * {@link #STATE_KEYPHRASE_UNSUPPORTED},
-         * detection is not possible and no further interaction should be
-         * performed with this detector. <br/>
-         * If it is {@link #STATE_KEYPHRASE_UNENROLLED} the caller may choose to begin
-         * an enrollment flow for the keyphrase. <br/>
-         * and for {@link #STATE_KEYPHRASE_ENROLLED} a recognition can be started as desired. <p/>
+         *
+         * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE
+         * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED
+         * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
+         * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
          */
         void onAvailabilityChanged(int status);
         /**
@@ -215,6 +218,9 @@
     /**
      * Gets the recognition modes supported by the associated keyphrase.
      *
+     * @see #RECOGNITION_MODE_USER_IDENTIFICATION
+     * @see #RECOGNITION_MODE_VOICE_TRIGGER
+     *
      * @throws UnsupportedOperationException if the keyphrase itself isn't supported.
      *         Callers should only call this method after a supported state callback on
      *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 2c7b3eb..2e836fb 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,16 +16,26 @@
 
 package android.webkit;
 
+import android.app.ActivityManagerInternal;
+import android.app.Application;
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
+import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
+import com.android.server.LocalServices;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
+import java.util.Arrays;
 
 import com.android.internal.os.Zygote;
 
@@ -42,17 +52,15 @@
     private static final String NULL_WEBVIEW_FACTORY =
             "com.android.webview.nullwebview.NullWebViewFactoryProvider";
 
-    // TODO(torne): we need to use a system property instead of hardcoding the library paths to
-    // enable it to be changed when a webview update apk is installed.
-    private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_32 =
-            "/system/lib/libwebviewchromium.so";
-    private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_64 =
-            "/system/lib64/libwebviewchromium.so";
     private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
             "/data/misc/shared_relro/libwebviewchromium32.relro";
     private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
             "/data/misc/shared_relro/libwebviewchromium64.relro";
 
+    public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
+            "persist.sys.webview.vmsize";
+    private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024;
+
     private static final String LOGTAG = "WebViewFactory";
 
     private static final boolean DEBUG = false;
@@ -64,8 +72,8 @@
     private static boolean sAddressSpaceReserved = false;
 
     public static String getWebViewPackageName() {
-        // TODO: Make this dynamic based on resource configuration.
-        return "com.android.webview";
+        return AppGlobals.getInitialApplication().getString(
+                com.android.internal.R.string.config_webViewPackageName);
     }
 
     static WebViewFactoryProvider getProvider() {
@@ -99,10 +107,18 @@
     }
 
     private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
+        Application initialApplication = AppGlobals.getInitialApplication();
         try {
-            return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
-        } catch (ClassNotFoundException e) {
-            Log.e(LOGTAG, "Chromium WebView does not exist");
+            Context webViewContext = initialApplication.createPackageContext(
+                    getWebViewPackageName(),
+                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+            initialApplication.getAssets().addAssetPath(
+                    webViewContext.getApplicationInfo().sourceDir);
+            ClassLoader clazzLoader = webViewContext.getClassLoader();
+            return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,
+                                                                 clazzLoader);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOGTAG, "Chromium WebView package does not exist");
             return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
         }
     }
@@ -114,79 +130,197 @@
     public static void prepareWebViewInZygote() {
         try {
             System.loadLibrary("webviewchromium_loader");
-            sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
-                                                              CHROMIUM_WEBVIEW_NATIVE_LIB_64);
+            long addressSpaceToReserve =
+                    SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
+                    CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
+            sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
+
             if (sAddressSpaceReserved) {
-                if (DEBUG) Log.v(LOGTAG, "address space reserved");
+                if (DEBUG) {
+                    Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
+                }
             } else {
-                Log.e(LOGTAG, "reserving address space failed");
+                Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
+                        " bytes of address space failed");
             }
-        } catch (Throwable e) {
+        } catch (Throwable t) {
             // Log and discard errors at this stage as we must not crash the zygote.
-            Log.e(LOGTAG, "error preparing native loader", e);
+            Log.e(LOGTAG, "error preparing native loader", t);
         }
     }
 
     /**
      * Perform any WebView loading preparations that must happen at boot from the system server,
-     * after the package manager has started.
+     * 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() {
-        if (DEBUG) Log.v(LOGTAG, "creating relro files");
-        if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) {
-            createRelroFile(Build.SUPPORTED_64_BIT_ABIS[0]);
+        String[] nativePaths = null;
+        try {
+            nativePaths = getWebViewNativeLibraryPaths();
+        } catch (PackageManager.NameNotFoundException e) {
         }
-        if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) {
-            createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]);
+        prepareWebViewInSystemServer(nativePaths);
+    }
+
+    private static void prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
+        if (DEBUG) Log.v(LOGTAG, "creating relro files");
+
+        // 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.
+        if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+            if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
+            createRelroFile(false /* is64Bit */, nativeLibraryPaths);
+        }
+
+        if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+            if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
+            createRelroFile(true /* is64Bit */, nativeLibraryPaths);
         }
     }
 
-    private static void createRelroFile(String abi) {
+    public static void onWebViewUpdateInstalled() {
+        String[] nativeLibs = null;
         try {
-            Process.start("android.webkit.WebViewFactory$RelroFileCreator",
-                          "WebViewLoader-" + abi,
-                          Process.SHARED_RELRO_UID,
-                          Process.SHARED_RELRO_UID,
-                          null,
-                          0,                 // TODO(torne): do we need to set debug flags?
-                          Zygote.MOUNT_EXTERNAL_NONE,
-                          Build.VERSION.SDK_INT,
-                          null,
-                          abi,
-                          null);
-        } catch (Throwable e) {
+            nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths();
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+
+        if (nativeLibs != null) {
+            long newVmSize = 0L;
+
+            for (String path : nativeLibs) {
+                if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
+                if (path == null) continue;
+                File f = new File(path);
+                if (f.exists()) {
+                    long length = f.length();
+                    if (length > newVmSize) {
+                        newVmSize = length;
+                    }
+                }
+            }
+
+            if (DEBUG) {
+                Log.v(LOGTAG, "Based on library size, need " + newVmSize +
+                        " bytes of address space.");
+            }
+            // The required memory can be larger than the file on disk (due to .bss), and an
+            // upgraded version of the library will likely be larger, so always attempt to reserve
+            // twice as much as we think to allow for the library to grow during this boot cycle.
+            newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
+            Log.d(LOGTAG, "Setting new address space to " + newVmSize);
+            SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
+                    Long.toString(newVmSize));
+        }
+        prepareWebViewInSystemServer(nativeLibs);
+    }
+
+    private static String[] getWebViewNativeLibraryPaths()
+            throws PackageManager.NameNotFoundException {
+        final String NATIVE_LIB_FILE_NAME = "libwebviewchromium.so";
+
+        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+        ApplicationInfo ai = pm.getApplicationInfo(getWebViewPackageName(), 0);
+
+        String path32;
+        String path64;
+        boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
+        if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
+            // Multi-arch case.
+            if (primaryArchIs64bit) {
+                // Primary arch: 64-bit, secondary: 32-bit.
+                path64 = ai.nativeLibraryDir;
+                path32 = ai.secondaryNativeLibraryDir;
+            } else {
+                // Primary arch: 32-bit, secondary: 64-bit.
+                path64 = ai.secondaryNativeLibraryDir;
+                path32 = ai.nativeLibraryDir;
+            }
+        } else if (primaryArchIs64bit) {
+            // Single-arch 64-bit.
+            path64 = ai.nativeLibraryDir;
+            path32 = "";
+        } else {
+            // Single-arch 32-bit.
+            path32 = ai.nativeLibraryDir;
+            path64 = "";
+        }
+        if (!TextUtils.isEmpty(path32)) path32 += "/" + NATIVE_LIB_FILE_NAME;
+        if (!TextUtils.isEmpty(path64)) path64 += "/" + NATIVE_LIB_FILE_NAME;
+        return new String[] { path32, path64 };
+    }
+
+    private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
+        final String abi =
+                is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
+
+        // crashHandler is invoked by the ActivityManagerService when the isolated process crashes.
+        Runnable crashHandler = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
+                    getUpdateService().notifyRelroCreationCompleted(is64Bit, false);
+                } catch (RemoteException e) {
+                    Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
+                }
+            }
+        };
+
+        try {
+            if (nativeLibraryPaths == null
+                    || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
+                throw new IllegalArgumentException(
+                        "Native library paths to the WebView RelRo process must not be null!");
+            }
+            int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
+                    RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,
+                    Process.SHARED_RELRO_UID, crashHandler);
+            if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
+        } catch (Throwable t) {
             // Log and discard errors as we must not crash the system server.
-            Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e);
+            Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
+            crashHandler.run();
         }
     }
 
     private static class RelroFileCreator {
         // Called in an unprivileged child process to create the relro file.
         public static void main(String[] args) {
-            if (!sAddressSpaceReserved) {
-                Log.e(LOGTAG, "can't create relro file; address space not reserved");
-                return;
-            }
-            boolean result = nativeCreateRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
-                                                   CHROMIUM_WEBVIEW_NATIVE_LIB_64,
-                                                   CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
-                                                   CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
-            if (!result) {
-                Log.e(LOGTAG, "failed to create relro file");
-            } else if (DEBUG) {
-                Log.v(LOGTAG, "created relro file");
-            }
-            try {
-                getUpdateService().notifyRelroCreationCompleted(VMRuntime.getRuntime().is64Bit(),
-                                                                result);
-            } catch (RemoteException e) {
-                Log.e(LOGTAG, "error notifying update service", e);
-            }
+            boolean result = false;
+            boolean is64Bit = VMRuntime.getRuntime().is64Bit();
+            try{
+                if (args.length != 2 || args[0] == null || args[1] == null) {
+                    Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
+                    return;
+                }
+                Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " +
+                        " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
+                if (!sAddressSpaceReserved) {
+                    Log.e(LOGTAG, "can't create relro file; address space not reserved");
+                    return;
+                }
+                result = nativeCreateRelroFile(args[0] /* path32 */,
+                                               args[1] /* path64 */,
+                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+                if (result && DEBUG) Log.v(LOGTAG, "created relro file");
+            } finally {
+                // We must do our best to always notify the update service, even if something fails.
+                try {
+                    getUpdateService().notifyRelroCreationCompleted(is64Bit, result);
+                } catch (RemoteException e) {
+                    Log.e(LOGTAG, "error notifying update service", e);
+                }
 
-            // Must explicitly exit or else this process will just sit around after we return.
-            System.exit(0);
+                if (!result) Log.e(LOGTAG, "failed to create relro file");
+
+                // Must explicitly exit or else this process will just sit around after we return.
+                System.exit(0);
+            }
         }
     }
 
@@ -203,14 +337,19 @@
             return;
         }
 
-        boolean result = nativeLoadWithRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
-                                                 CHROMIUM_WEBVIEW_NATIVE_LIB_64,
-                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
-                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
-        if (!result) {
-            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();
+            boolean result = nativeLoadWithRelroFile(args[0] /* path32 */,
+                                                     args[1] /* path64 */,
+                                                     CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+                                                     CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+            if (!result) {
+                Log.w(LOGTAG, "failed to load with relro file, proceeding without");
+            } else if (DEBUG) {
+                Log.v(LOGTAG, "loaded with relro file");
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e);
         }
     }
 
@@ -218,7 +357,7 @@
         return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
     }
 
-    private static native boolean nativeReserveAddressSpace(String lib32, String lib64);
+    private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
     private static native boolean nativeCreateRelroFile(String lib32, String lib64,
                                                         String relro32, String relro64);
     private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 217658c..b9e5c66 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1661,4 +1661,8 @@
     <!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet
         string that's stored in 8-bit unpacked format) characters.-->
     <bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
+
+    <!-- Package name providing WebView implementation. -->
+    <string name="config_webViewPackageName" translatable="false">com.android.webview</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d37e468..ccd7005 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1839,6 +1839,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="string" name="config_webViewPackageName" />
 
   <!-- From SubtitleView -->
   <java-symbol type="dimen" name="subtitle_corner_radius" />
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 2fad73e..898b6f1 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -49,6 +49,11 @@
 
     private static final int MSG_TRY_CONNECTING = 1;
 
+    private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
+    private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
+    private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
+    private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
+
     private Context mContext;
     // The actual MMS service instance to invoke
     private volatile IMms mService;
@@ -275,7 +280,9 @@
             mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
-                return null;
+                // Silently fail AppOps failure due to not being the default SMS app
+                // while writing the TelephonyProvider
+                return FAKE_SMS_SENT_URI;
             }
             return getServiceGuarded().importTextMessage(
                     callingPkg, address, type, text, timestampMillis, seen, read);
@@ -287,7 +294,9 @@
             mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
-                return null;
+                // Silently fail AppOps failure due to not being the default SMS app
+                // while writing the TelephonyProvider
+                return FAKE_MMS_SENT_URI;
             }
             return getServiceGuarded().importMultimediaMessage(
                     callingPkg, pdu, messageId, timestampSecs, seen, read);
@@ -340,7 +349,9 @@
             mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
-                return null;
+                // Silently fail AppOps failure due to not being the default SMS app
+                // while writing the TelephonyProvider
+                return FAKE_SMS_DRAFT_URI;
             }
             return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
         }
@@ -350,7 +361,9 @@
             mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
-                return null;
+                // Silently fail AppOps failure due to not being the default SMS app
+                // while writing the TelephonyProvider
+                return FAKE_MMS_DRAFT_URI;
             }
             return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f3ac0f5..0ad36fc 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2034,7 +2034,7 @@
             mSystemThread.installSystemApplicationInfo(info);
 
             synchronized (this) {
-                ProcessRecord app = newProcessRecordLocked(info, info.processName, false);
+                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
                 app.persistent = true;
                 app.pid = MY_PID;
                 app.maxAdj = ProcessList.SYSTEM_ADJ;
@@ -2834,10 +2834,45 @@
                 || transit == AppTransition.TRANSIT_TASK_TO_FRONT;
     }
 
+    int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+            String processName, String abiOverride, int uid, Runnable crashHandler) {
+        synchronized(this) {
+            ApplicationInfo info = new ApplicationInfo();
+            // In general the ApplicationInfo.uid isn't neccesarily equal to ProcessRecord.uid.
+            // For isolated processes, the former contains the parent's uid and the latter the
+            // actual uid of the isolated process.
+            // In the special case introduced by this method (which is, starting an isolated
+            // process directly from the SystemServer without an actual parent app process) the
+            // closest thing to a parent's uid is SYSTEM_UID.
+            // The only important thing here is to keep AI.uid != PR.uid, in order to trigger
+            // the |isolated| logic in the ProcessRecord constructor.
+            info.uid = Process.SYSTEM_UID;
+            info.processName = processName;
+            info.className = entryPoint;
+            info.packageName = "android";
+            ProcessRecord proc = startProcessLocked(processName, info /* info */,
+                    false /* knownToBeDead */, 0 /* intentFlags */, ""  /* hostingType */,
+                    null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
+                    uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
+                    crashHandler);
+            return proc != null ? proc.pid : 0;
+        }
+    }
+
     final ProcessRecord startProcessLocked(String processName,
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
             String hostingType, ComponentName hostingName, boolean allowWhileBooting,
             boolean isolated, boolean keepIfLarge) {
+        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
+                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
+                null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
+                null /* crashHandler */);
+    }
+
+    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
+            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
+            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
@@ -2905,7 +2940,8 @@
         }
 
         if (app == null) {
-            app = newProcessRecordLocked(info, processName, isolated);
+            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+            app.crashHandler = crashHandler;
             if (app == null) {
                 Slog.w(TAG, "Failed making new process record for "
                         + processName + "/" + info.uid + " isolated=" + isolated);
@@ -2932,7 +2968,8 @@
             return app;
         }
 
-        startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */);
+        startProcessLocked(
+                app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
         return (app.pid != 0) ? app : null;
     }
 
@@ -2941,7 +2978,13 @@
     }
 
     private final void startProcessLocked(ProcessRecord app,
-            String hostingType, String hostingNameStr, String abiOverride) {
+            String hostingType, String hostingNameStr) {
+        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
+                null /* entryPoint */, null /* entryPointArgs */);
+    }
+
+    private final void startProcessLocked(ProcessRecord app, String hostingType,
+            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
         if (app.pid > 0 && app.pid != MY_PID) {
             synchronized (mPidsSelfLocked) {
                 mPidsSelfLocked.remove(app.pid);
@@ -3034,9 +3077,11 @@
 
             // Start the process.  It will either succeed and return a result containing
             // the PID of the new process, or else throw a RuntimeException.
-            Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
+            boolean isActivityProcess = (entryPoint == null);
+            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
+            Process.ProcessStartResult startResult = Process.start(entryPoint,
                     app.processName, uid, uid, gids, debugFlags, mountExternal,
-                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, null);
+                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, entryPointArgs);
 
             if (app.isolated) {
                 mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
@@ -3056,6 +3101,11 @@
             buf.setLength(0);
             buf.append("Start proc ");
             buf.append(app.processName);
+            if (!isActivityProcess) {
+                buf.append(" [");
+                buf.append(entryPoint);
+                buf.append("]");
+            }
             buf.append(" for ");
             buf.append(hostingType);
             if (hostingNameStr != null) {
@@ -3086,10 +3136,12 @@
             app.killedByAm = false;
             synchronized (mPidsSelfLocked) {
                 this.mPidsSelfLocked.put(startResult.pid, app);
-                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
-                msg.obj = app;
-                mHandler.sendMessageDelayed(msg, startResult.usingWrapper
-                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+                if (isActivityProcess) {
+                    Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+                    msg.obj = app;
+                    mHandler.sendMessageDelayed(msg, startResult.usingWrapper
+                            ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+                }
             }
         } catch (RuntimeException e) {
             // XXX do better error recovery.
@@ -5359,7 +5411,7 @@
             app.deathRecipient = adr;
         } catch (RemoteException e) {
             app.resetPackageList(mProcessStats);
-            startProcessLocked(app, "link fail", processName, null /* ABI override */);
+            startProcessLocked(app, "link fail", processName);
             return false;
         }
 
@@ -5452,7 +5504,7 @@
 
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            startProcessLocked(app, "bind fail", processName, null /* ABI override */);
+            startProcessLocked(app, "bind fail", processName);
             return false;
         }
 
@@ -5607,7 +5659,7 @@
                 for (int ip=0; ip<NP; ip++) {
                     if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
                             + procs.get(ip));
-                    startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */);
+                    startProcessLocked(procs.get(ip), "on-hold", null);
                 }
             }
             
@@ -8955,29 +9007,35 @@
     // =========================================================
 
     final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
-            boolean isolated) {
+            boolean isolated, int isolatedUid) {
         String proc = customProcess != null ? customProcess : info.processName;
         BatteryStatsImpl.Uid.Proc ps = null;
         BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         int uid = info.uid;
         if (isolated) {
-            int userId = UserHandle.getUserId(uid);
-            int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
-            while (true) {
-                if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
-                        || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
-                    mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
+            if (isolatedUid == 0) {
+                int userId = UserHandle.getUserId(uid);
+                int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
+                while (true) {
+                    if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
+                            || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
+                        mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
+                    }
+                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
+                    mNextIsolatedProcessUid++;
+                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+                        // No process for this uid, use it.
+                        break;
+                    }
+                    stepsLeft--;
+                    if (stepsLeft <= 0) {
+                        return null;
+                    }
                 }
-                uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
-                mNextIsolatedProcessUid++;
-                if (mIsolatedProcesses.indexOfKey(uid) < 0) {
-                    // No process for this uid, use it.
-                    break;
-                }
-                stepsLeft--;
-                if (stepsLeft <= 0) {
-                    return null;
-                }
+            } else {
+                // Special case for startIsolatedProcess (internal only), where
+                // the uid of the isolated process is specified by the caller.
+                uid = isolatedUid;
             }
         }
         return new ProcessRecord(stats, info, proc, uid);
@@ -8993,7 +9051,7 @@
         }
 
         if (app == null) {
-            app = newProcessRecordLocked(info, null, isolated);
+            app = newProcessRecordLocked(info, null, isolated, 0);
             mProcessNames.put(info.processName, app.uid, app);
             if (isolated) {
                 mIsolatedProcesses.put(app.uid, app);
@@ -9019,8 +9077,8 @@
         }
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
-            startProcessLocked(app, "added application", app.processName,
-                    abiOverride);
+            startProcessLocked(app, "added application", app.processName, abiOverride,
+                    null /* entryPoint */, null /* entryPointArgs */);
         }
 
         return app;
@@ -10519,6 +10577,7 @@
             mProcessCrashTimes.put(app.info.processName, app.uid, now);
         }
 
+        if (app.crashHandler != null) mHandler.post(app.crashHandler);
         return true;
     }
 
@@ -13517,7 +13576,7 @@
             // We have components that still need to be running in the
             // process, so re-launch it.
             mProcessNames.put(app.processName, app.uid, app);
-            startProcessLocked(app, "restart", app.processName, null /* ABI override */);
+            startProcessLocked(app, "restart", app.processName);
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
             boolean removed;
@@ -17849,6 +17908,13 @@
         public void wakingUp() {
             ActivityManagerService.this.wakingUp();
         }
+
+        @Override
+        public int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+                String processName, String abiOverride, int uid, Runnable crashHandler) {
+            return ActivityManagerService.this.startIsolatedProcess(entryPoint, entryPointArgs,
+                    processName, abiOverride, uid, crashHandler);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b33f7b7..f1bcb60 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -127,7 +127,8 @@
     Object adjSource;           // Debugging: option dependent object.
     int adjSourceProcState;     // Debugging: proc state of adjSource's process.
     Object adjTarget;           // Debugging: target component impacting oom_adj.
-    
+    Runnable crashHandler;      // Optional local handler to be invoked in the process crash.
+
     // contains HistoryRecord objects
     final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
     // all ServiceRecord running in this process
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 60724e7..ff68cf7 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -22,17 +22,20 @@
 import android.content.IntentFilter;
 import android.os.Binder;
 import android.os.Process;
-import android.util.Log;
+import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
 import android.webkit.WebViewFactory;
 
+import com.android.server.SystemService;
+
 /**
  * Private service to wait for the updatable WebView to be ready for use.
  * @hide
  */
-public class WebViewUpdateService extends IWebViewUpdateService.Stub {
+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 boolean mRelroReady32Bit = false;
     private boolean mRelroReady64Bit = false;
@@ -40,6 +43,11 @@
     private BroadcastReceiver mWebViewUpdatedReceiver;
 
     public WebViewUpdateService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
         mWebViewUpdatedReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
@@ -52,57 +60,76 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addDataScheme("package");
-        context.registerReceiver(mWebViewUpdatedReceiver, filter);
-    }
+        getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
 
-    /**
-     * The shared relro process calls this to notify us that it's done trying to create a relro
-     * file.
-     */
-    public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
-        // Verify that the caller is the shared relro process.
-        if (Binder.getCallingUid() != Process.SHARED_RELRO_UID) {
-            return;
-        }
-
-        synchronized (this) {
-            if (is64Bit) {
-                mRelroReady64Bit = true;
-            } else {
-                mRelroReady32Bit = true;
-            }
-            this.notifyAll();
-        }
-    }
-
-    /**
-     * WebViewFactory calls this to block WebView loading until the relro file is created.
-     */
-    public void waitForRelroCreationCompleted(boolean is64Bit) {
-        synchronized (this) {
-            if (is64Bit) {
-                while (!mRelroReady64Bit) {
-                    try {
-                        this.wait();
-                    } catch (InterruptedException e) {}
-                }
-            } else {
-                while (!mRelroReady32Bit) {
-                    try {
-                        this.wait();
-                    } catch (InterruptedException e) {}
-                }
-            }
-        }
+        publishBinderService("webviewupdate", new BinderService());
     }
 
     private void onWebViewUpdateInstalled() {
-        Log.d(TAG, "WebView Package updated!");
+        Slog.d(TAG, "WebView Package updated!");
 
         synchronized (this) {
             mRelroReady32Bit = false;
             mRelroReady64Bit = false;
         }
-        WebViewFactory.prepareWebViewInSystemServer();
+        WebViewFactory.onWebViewUpdateInstalled();
     }
+
+    private class BinderService extends IWebViewUpdateService.Stub {
+
+        /**
+         * The shared relro process calls this to notify us that it's done trying to create a relro
+         * file. This method gets called even if the relro creation has failed or the process
+         * crashed.
+         */
+        @Override // Binder call
+        public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
+            // 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).
+            if (Binder.getCallingUid() != Process.SHARED_RELRO_UID &&
+                    Binder.getCallingUid() != Process.SYSTEM_UID) {
+                return;
+            }
+
+            synchronized (WebViewUpdateService.this) {
+                if (is64Bit) {
+                    mRelroReady64Bit = true;
+                } else {
+                    mRelroReady32Bit = true;
+                }
+                WebViewUpdateService.this.notifyAll();
+            }
+        }
+
+        /**
+         * WebViewFactory calls this to block WebView loading until the relro file is created.
+         */
+        @Override // Binder call
+        public void waitForRelroCreationCompleted(boolean is64Bit) {
+            // 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
+            // would deadlock without the check below.
+            if (Binder.getCallingPid() == Process.myPid()) {
+                throw new IllegalStateException("Cannot create a WebView from the SystemServer");
+            }
+
+            final long NS_PER_MS = 1000000;
+            final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+            boolean relroReady = false;
+            synchronized (WebViewUpdateService.this) {
+                while (!relroReady) {
+                    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);
+                }
+            }
+            if (!relroReady) Slog.w(TAG, "creating relro file timed out");
+        }
+    }
+
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d955354..e8d6773 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -376,6 +376,9 @@
         mSystemServiceManager.startService(UsageStatsService.class);
         mActivityManagerService.setUsageStatsManager(
                 LocalServices.getService(UsageStatsManagerInternal.class));
+
+        // Tracks whether the updatable WebView is in a ready state and watches for update installs.
+        mSystemServiceManager.startService(WebViewUpdateService.class);
     }
 
     /**
@@ -422,12 +425,6 @@
             Slog.i(TAG, "Reading configuration...");
             SystemConfig.getInstance();
 
-            Slog.i(TAG, "WebView Update Service");
-            ServiceManager.addService("webviewupdate", new WebViewUpdateService(context));
-
-            Slog.i(TAG, "WebViewFactory preparation");
-            WebViewFactory.prepareWebViewInSystemServer();
-
             Slog.i(TAG, "Scheduling Policy");
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
 
@@ -1080,6 +1077,10 @@
                 } catch (Throwable e) {
                     reportWtf("observing native crashes", e);
                 }
+
+                Slog.i(TAG, "WebViewFactory preparation");
+                WebViewFactory.prepareWebViewInSystemServer();
+
                 try {
                     startSystemUi(context);
                 } catch (Throwable e) {