Handle multiprocess flag in WebView update service.

Instead of letting DevelopmentSettings manage the setting directly and
observing the changes from WebViewUpdateService, have the update service
manage the setting and just expose IPCs for the settings app to use to
get/set the setting. This means we can set a more flexible policy for
whether multiprocess is enabled by default and change it without
touching the settings code, though for now this CL does not change the
behaviour and is just a refactoring.

Bug: 21643067
Test: Toggle multiprocess WebView in developer settings
Change-Id: I3057c09d99f5f6f472a5195a8e14e9164ea5733a
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 7ba95a4..02b46ec 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -271,21 +271,20 @@
     }
 
     @Override
-    public void setMultiProcessEnabledFromContext(Context context) {
-        boolean enableMultiProcess = false;
-        try {
-            enableMultiProcess = Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
-        } catch (Settings.SettingNotFoundException ex) {
-        }
-        WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
+    public int getMultiProcessSetting(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                                      Settings.Global.WEBVIEW_MULTIPROCESS, 0);
     }
 
     @Override
-    public void registerContentObserver(Context context, ContentObserver contentObserver) {
-        context.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
-                false, contentObserver);
+    public void setMultiProcessSetting(Context context, int value) {
+        Settings.Global.putInt(context.getContentResolver(),
+                               Settings.Global.WEBVIEW_MULTIPROCESS, value);
+    }
+
+    @Override
+    public void notifyZygote(boolean enableMultiProcess) {
+        WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
     }
 
     // flags declaring we want extra info from the package manager for webview providers
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 2d7a998..fd137eb 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -50,6 +50,7 @@
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
             throws NameNotFoundException;
 
-    public void setMultiProcessEnabledFromContext(Context context);
-    public void registerContentObserver(Context context, ContentObserver contentObserver);
+    public int getMultiProcessSetting(Context context);
+    public void setMultiProcessSetting(Context context, int value);
+    public void notifyZygote(boolean enableMultiProcess);
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 0a7454f..311570e 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -261,6 +261,32 @@
             }
         }
 
+        @Override // Binder call
+        public boolean isMultiProcessEnabled() {
+            return WebViewUpdateService.this.mImpl.isMultiProcessEnabled();
+        }
+
+        @Override // Binder call
+        public void enableMultiProcess(boolean enable) {
+            if (getContext().checkCallingPermission(
+                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                String msg = "Permission Denial: enableMultiProcess() from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid()
+                        + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+
+            long callingId = Binder.clearCallingIdentity();
+            try {
+                WebViewUpdateService.this.mImpl.enableMultiProcess(enable);
+            } finally {
+                Binder.restoreCallingIdentity(callingId);
+            }
+        }
+
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 1a77c68..edfb11c 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,8 +20,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.util.Base64;
@@ -77,7 +75,6 @@
 
     private SystemInterface mSystemInterface;
     private WebViewUpdater mWebViewUpdater;
-    private SettingsObserver mSettingsObserver;
     final private Context mContext;
 
     public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
@@ -97,10 +94,7 @@
     void prepareWebViewInSystemServer() {
         updateFallbackStateOnBoot();
         mWebViewUpdater.prepareWebViewInSystemServer();
-
-        // Register for changes in the multiprocess developer option. This has to be done
-        // here, since the update service gets created before the ContentResolver service.
-        mSettingsObserver = new SettingsObserver();
+        mSystemInterface.notifyZygote(isMultiProcessEnabled());
     }
 
     private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
@@ -247,6 +241,19 @@
                 && packageName.equals(fallbackProvider.packageName));
     }
 
+    boolean isMultiProcessEnabled() {
+        return mSystemInterface.getMultiProcessSetting(mContext) != 0;
+    }
+
+    void enableMultiProcess(boolean enable) {
+        PackageInfo current = getCurrentWebViewPackage();
+        mSystemInterface.setMultiProcessSetting(mContext, enable ? 1 : 0);
+        mSystemInterface.notifyZygote(enable);
+        if (current != null) {
+            mSystemInterface.killPackageDependents(current.packageName);
+        }
+    }
+
     /**
      * Class that decides what WebView implementation to use and prepares that implementation for
      * use.
@@ -740,31 +747,6 @@
     }
 
     /**
-     * Watches for changes in the WEBVIEW_MULTIPROCESS setting and lets
-     * the WebViewZygote know, so it can start or stop the zygote process
-     * appropriately.
-     */
-    private class SettingsObserver extends ContentObserver {
-        SettingsObserver() {
-            super(new Handler());
-
-            mSystemInterface.registerContentObserver(mContext, this);
-
-            // Push the current value of the setting immediately.
-            notifyZygote();
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            notifyZygote();
-        }
-
-        private void notifyZygote() {
-            mSystemInterface.setMultiProcessEnabledFromContext(mContext);
-        }
-    }
-
-    /**
      * Dump the state of this Service.
      */
     void dumpState(PrintWriter pw) {
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index d2512ac..83a61ca 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
 import android.webkit.WebViewProviderInfo;
 
 import java.util.HashMap;
@@ -31,6 +30,7 @@
     private boolean mFallbackLogicEnabled;
     private final int mNumRelros;
     private final boolean mIsDebuggable;
+    private int mMultiProcessSetting;
 
     public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
             int numRelros, boolean isDebuggable) {
@@ -121,8 +121,15 @@
     }
 
     @Override
-    public void setMultiProcessEnabledFromContext(Context context) {}
+    public int getMultiProcessSetting(Context context) {
+        return mMultiProcessSetting;
+    }
 
     @Override
-    public void registerContentObserver(Context context, ContentObserver contentObserver) {}
+    public void setMultiProcessSetting(Context context, int value) {
+        mMultiProcessSetting = value;
+    }
+
+    @Override
+    public void notifyZygote(boolean enableMultiProcess) {}
 }