Make WebView data directory configurable.

Add WebView.setDataDirectory(String suffix) and WebView.disableWebView()
interfaces. These will enable applications to better control the use of
WebView when running multiple processes, since it's not safe for more
than one process in an app to use the same data directory.

disableWebView is completely implemented in this change;
setDataDirectory will require WebView-side implementation changes to be
functional.

Bug: 63748219
Test: cts android.webkit.cts.WebViewDataDirTest
Change-Id: Ieff225dc99b6b3ca5f223c35f7a854f0c53692df
diff --git a/api/current.txt b/api/current.txt
index 2702af6..c46d046 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -49791,6 +49791,7 @@
     method public android.print.PrintDocumentAdapter createPrintDocumentAdapter(java.lang.String);
     method public android.webkit.WebMessagePort[] createWebMessageChannel();
     method public void destroy();
+    method public static void disableWebView();
     method public void documentHasImages(android.os.Message);
     method public static void enableSlowWholeDocumentDraw();
     method public void evaluateJavascript(java.lang.String, android.webkit.ValueCallback<java.lang.String>);
@@ -49851,6 +49852,7 @@
     method public void saveWebArchive(java.lang.String);
     method public void saveWebArchive(java.lang.String, boolean, android.webkit.ValueCallback<java.lang.String>);
     method public deprecated void setCertificate(android.net.http.SslCertificate);
+    method public static void setDataDirectorySuffix(java.lang.String);
     method public void setDownloadListener(android.webkit.DownloadListener);
     method public void setFindListener(android.webkit.WebView.FindListener);
     method public deprecated void setHorizontalScrollbarOverlay(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 50a8ea4..1f9c85e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4538,6 +4538,7 @@
     method public boolean canInvokeDrawGlFunctor(android.view.View);
     method public void detachDrawGlFunctor(android.view.View, long);
     method public android.app.Application getApplication();
+    method public java.lang.String getDataDirectorySuffix();
     method public java.lang.String getErrorString(android.content.Context, int);
     method public int getPackageId(android.content.res.Resources, java.lang.String);
     method public void invokeDrawGlFunctor(android.view.View, long, boolean);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6f99254..0b1fb92 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2075,6 +2075,48 @@
     }
 
     /**
+     * Define the directory used to store WebView data for the current process.
+     * The provided suffix will be used when constructing data and cache
+     * directory paths. If this API is not called, no suffix will be used.
+     * Each directory can be used by only one process in the application. If more
+     * than one process in an app wishes to use WebView, only one process can use
+     * the default directory, and other processes must call this API to define
+     * a unique suffix.
+     * <p>
+     * This API must be called before any instances of WebView are created in
+     * this process and before any other methods in the android.webkit package
+     * are called by this process.
+     *
+     * @param suffix The directory name suffix to be used for the current
+     *               process. Must not contain a path separator.
+     * @throws IllegalStateException if WebView has already been initialized
+     *                               in the current process.
+     * @throws IllegalArgumentException if the suffix contains a path separator.
+     */
+    public static void setDataDirectorySuffix(String suffix) {
+        WebViewFactory.setDataDirectorySuffix(suffix);
+    }
+
+    /**
+     * Indicate that the current process does not intend to use WebView, and
+     * that an exception should be thrown if a WebView is created or any other
+     * methods in the android.webkit package are used.
+     * <p>
+     * Applications with multiple processes may wish to call this in processes
+     * which are not intended to use WebView to prevent potential data directory
+     * conflicts (see {@link #setDataDirectorySuffix}) and to avoid accidentally
+     * incurring the memory usage of initializing WebView in long-lived
+     * processes which have no need for it.
+     *
+     * @throws IllegalStateException if WebView has already been initialized
+     *                               in the current process.
+     */
+    public static void disableWebView() {
+        WebViewFactory.disableWebView();
+    }
+
+
+    /**
      * @deprecated This was used for Gears, which has been deprecated.
      * @hide
      */
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 7339931..f067091 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -218,4 +218,11 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Returns the data directory suffix to use, or null for none.
+     */
+    public String getDataDirectorySuffix() {
+        return WebViewFactory.getDataDirectorySuffix();
+    }
 }
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 797bdfb..ab447f8 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -33,6 +33,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import java.io.File;
 import java.lang.reflect.Method;
 
 /**
@@ -63,6 +64,8 @@
     private static final Object sProviderLock = new Object();
     private static PackageInfo sPackageInfo;
     private static Boolean sWebViewSupported;
+    private static boolean sWebViewDisabled;
+    private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
 
     // Error codes for loadWebViewNativeLibraryFromPackage
     public static final int LIBLOAD_SUCCESS = 0;
@@ -115,6 +118,45 @@
     /**
      * @hide
      */
+    static void disableWebView() {
+        synchronized (sProviderLock) {
+            if (sProviderInstance != null) {
+                throw new IllegalStateException(
+                        "Can't disable WebView: WebView already initialized");
+            }
+            sWebViewDisabled = true;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    static void setDataDirectorySuffix(String suffix) {
+        synchronized (sProviderLock) {
+            if (sProviderInstance != null) {
+                throw new IllegalStateException(
+                        "Can't set data directory suffix: WebView already initialized");
+            }
+            if (suffix.indexOf(File.separatorChar) >= 0) {
+                throw new IllegalArgumentException("Suffix " + suffix
+                                                   + " contains a path separator");
+            }
+            sDataDirectorySuffix = suffix;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    static String getDataDirectorySuffix() {
+        synchronized (sProviderLock) {
+            return sDataDirectorySuffix;
+        }
+    }
+
+    /**
+     * @hide
+     */
     public static String getWebViewLibrary(ApplicationInfo ai) {
         if (ai.metaData != null)
             return ai.metaData.getString("com.android.webview.WebViewLibrary");
@@ -204,6 +246,11 @@
                 throw new UnsupportedOperationException();
             }
 
+            if (sWebViewDisabled) {
+                throw new IllegalStateException(
+                        "WebView.disableWebView() was called: WebView is disabled");
+            }
+
             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
             try {