Merge "Add AndroidX API to get/set status for WebView Media Integrity Api" into androidx-main
diff --git a/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java b/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
index 23a422c..71b3d68 100644
--- a/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
+++ b/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
@@ -16,6 +16,10 @@
 
 package androidx.webkit;
 
+import static androidx.webkit.WebViewMediaIntegrityApiStatusConfig.WEBVIEW_MEDIA_INTEGRITY_API_DISABLED;
+import static androidx.webkit.WebViewMediaIntegrityApiStatusConfig.WEBVIEW_MEDIA_INTEGRITY_API_ENABLED;
+import static androidx.webkit.WebViewMediaIntegrityApiStatusConfig.WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -194,4 +198,69 @@
 
     }
 
+    @Test
+    public void testWebViewMediaIntegrityApiDefaultStatus() throws Throwable {
+        WebkitUtils.checkFeature(WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS);
+        WebSettings settings = mWebViewOnUiThread.getSettings();
+        Assert.assertEquals(WEBVIEW_MEDIA_INTEGRITY_API_ENABLED,
+                WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings).getDefaultStatus());
+        Assert.assertTrue(
+                WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings)
+                        .getOverrideRules().isEmpty());
+    }
+
+    @Test
+    public void testSetWebViewMediaIntegrityApiWithNoRules() throws Throwable {
+        WebkitUtils.checkFeature(WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS);
+        WebSettings settings = mWebViewOnUiThread.getSettings();
+
+        WebViewMediaIntegrityApiStatusConfig config =
+                new WebViewMediaIntegrityApiStatusConfig.Builder(
+                        WEBVIEW_MEDIA_INTEGRITY_API_DISABLED)
+                        .build();
+        WebSettingsCompat.setWebViewMediaIntegrityApiStatus(settings, config);
+        Assert.assertEquals(
+                WEBVIEW_MEDIA_INTEGRITY_API_DISABLED,
+                        WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings)
+                                .getDefaultStatus());
+        Assert.assertTrue(
+                WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings)
+                        .getOverrideRules().isEmpty());
+    }
+
+    @Test
+    public void testSetWebViewMediaIntegrityApiWithRules() throws Throwable {
+        WebkitUtils.checkFeature(WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS);
+        WebSettings settings = mWebViewOnUiThread.getSettings();
+
+        WebViewMediaIntegrityApiStatusConfig config =
+                new WebViewMediaIntegrityApiStatusConfig.Builder(
+                        WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY)
+                        .addOverrideRule("http://*.example.com",
+                                WEBVIEW_MEDIA_INTEGRITY_API_ENABLED)
+                        .build();
+        WebSettingsCompat.setWebViewMediaIntegrityApiStatus(settings, config);
+        Assert.assertEquals(
+                WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY,
+                WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings).getDefaultStatus());
+        Assert.assertEquals(1,
+                WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings)
+                        .getOverrideRules().size());
+    }
+
+    @Test
+    public void testSetWebViewMediaIntegrityApiWithInvalidStatus() throws Throwable {
+        WebkitUtils.checkFeature(WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS);
+        WebSettings settings = mWebViewOnUiThread.getSettings();
+        int invalidStatus = 15;
+
+        WebViewMediaIntegrityApiStatusConfig config =
+                new WebViewMediaIntegrityApiStatusConfig.Builder(invalidStatus).build();
+        Assert.assertThrows(
+                IllegalArgumentException.class,
+                () -> WebSettingsCompat.setWebViewMediaIntegrityApiStatus(settings, config));
+        Assert.assertTrue(
+                WebSettingsCompat.getWebViewMediaIntegrityApiStatus(settings)
+                        .getOverrideRules().isEmpty());
+    }
 }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index 08ed011..d44c282 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -874,6 +874,44 @@
         }
     }
 
+    /**
+     * Sets permissions provided through
+     * {@link WebViewMediaIntegrityApiStatusConfig} for using the
+     * WebView Integrity API.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @RequiresFeature(name = WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+    public static void setWebViewMediaIntegrityApiStatus(
+            @NonNull WebSettings settings,
+            @NonNull WebViewMediaIntegrityApiStatusConfig permissionConfig) {
+        final ApiFeature.NoFramework feature =
+                WebViewFeatureInternal.WEBVIEW_MEDIA_INTEGRITY_API_STATUS;
+        if (feature.isSupportedByWebView()) {
+            getAdapter(settings).setWebViewMediaIntegrityApiStatus(permissionConfig);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Returns the {@link WebViewMediaIntegrityApiStatusConfig} currently in use.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @RequiresFeature(name = WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+    @NonNull
+    public static WebViewMediaIntegrityApiStatusConfig getWebViewMediaIntegrityApiStatus(
+            @NonNull WebSettings settings) {
+        final ApiFeature.NoFramework feature =
+                WebViewFeatureInternal.WEBVIEW_MEDIA_INTEGRITY_API_STATUS;
+        if (feature.isSupportedByWebView()) {
+            return getAdapter(settings).getWebViewMediaIntegrityApiStatus();
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
+        }
+    }
+
     private static WebSettingsAdapter getAdapter(WebSettings settings) {
         return WebViewGlueCommunicator.getCompatConverter().convertSettings(settings);
     }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index fa09c22..8fa0356d 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -569,6 +569,16 @@
             "ATTRIBUTION_REGISTRATION_BEHAVIOR";
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#setWebViewMediaIntegrityApiStatus(WebSettings, WebViewMediaIntegrityApiStatusConfig)}
+     * {@link androidx.webkit.WebSettingsCompat#getWebViewMediaIntegrityApiStatus(WebSettings)}
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final String WEBVIEW_MEDIA_INTEGRITY_API_STATUS =
+            "WEBVIEW_MEDIA_INTEGRITY_API_STATUS";
+
+    /**
      * Return whether a feature is supported at run-time. On devices running Android version {@link
      * android.os.Build.VERSION_CODES#LOLLIPOP} and higher, this will check whether a feature is
      * supported, depending on the combination of the desired feature, the Android version of
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewMediaIntegrityApiStatusConfig.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewMediaIntegrityApiStatusConfig.java
new file mode 100644
index 0000000..df7396d
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewMediaIntegrityApiStatusConfig.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2023 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 androidx.webkit;
+
+import android.webkit.WebView;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+
+import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Configuration to set API enablement status for site origins through override rules.
+ *
+ * <p>Websites will follow the default status supplied in the builder constructor,
+ * unless the site origin matches one of the origin patterns supplied in the override rules.
+ *
+ * <p>The override rules are a map from origin patterns to the desired
+ * {@link WebViewMediaIntegrityApiStatus}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresFeature(name = WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS,
+        enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+public class WebViewMediaIntegrityApiStatusConfig {
+    /**
+     * @hide
+     */
+    @Target(ElementType.TYPE_USE)
+    @IntDef({WEBVIEW_MEDIA_INTEGRITY_API_DISABLED,
+            WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY,
+            WEBVIEW_MEDIA_INTEGRITY_API_ENABLED})
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    @interface WebViewMediaIntegrityApiStatus {
+    }
+
+    /**
+     * {@link WebViewMediaIntegrityApiStatus} that enables the API and allows sharing of the app
+     * package name with the JavaScript caller.
+     *
+     * <p>This is the default value.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED =
+            WebSettingsBoundaryInterface.WebViewMediaIntegrityApiStatus.ENABLED;
+
+    /**
+     * {@link WebViewMediaIntegrityApiStatus} that enables the API for JavaScript callers but
+     * disables sharing app package name in generated tokens.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY =
+            WebSettingsBoundaryInterface.WebViewMediaIntegrityApiStatus
+                    .ENABLED_WITHOUT_APP_IDENTITY;
+
+    /**
+     * {@link WebViewMediaIntegrityApiStatus} that disables the API and causes it to return an
+     * error code to the JavaScript callers indicating that the app has disabled it.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int WEBVIEW_MEDIA_INTEGRITY_API_DISABLED =
+            WebSettingsBoundaryInterface.WebViewMediaIntegrityApiStatus.DISABLED;
+
+    private @WebViewMediaIntegrityApiStatus int mDefaultStatus;
+    private Map<String, @WebViewMediaIntegrityApiStatus Integer> mOverrideRules;
+
+    public WebViewMediaIntegrityApiStatusConfig(@NonNull Builder builder) {
+        this.mDefaultStatus = builder.mDefaultStatus;
+        this.mOverrideRules = builder.mOverrideRules;
+    }
+
+    /**
+     * Builds a {@link WebViewMediaIntegrityApiStatusConfig} having a default API status and
+     * a map of origin pattern rules to their {@link WebViewMediaIntegrityApiStatus}.
+     *
+     * <p>
+     * Example:
+     * <pre class="prettyprint">
+     *     // Create a config with default API status being DISABLED and API status is ENABLED for
+     *     // Uris matching origin pattern "http://*.example.com"
+     *     new WebViewMediaIntegrityApiStatusConfig.Builder(WEBVIEW_MEDIA_INTEGRITY_API_DISABLED)
+     *         .addOverrideRule("http://*.example.com", WEBVIEW_MEDIA_INTEGRITY_API_ENABLED)
+     *         .build();
+     * </pre>
+     */
+    public static final class Builder {
+        private @WebViewMediaIntegrityApiStatus int mDefaultStatus;
+        private Map<String, @WebViewMediaIntegrityApiStatus Integer> mOverrideRules;
+
+        /**
+         * @param defaultStatus Default {@link WebViewMediaIntegrityApiStatus} that will be
+         * used for URIs that don't match any origin pattern rule.
+         */
+        public Builder(@WebViewMediaIntegrityApiStatus int defaultStatus) {
+            this.mDefaultStatus = defaultStatus;
+            this.mOverrideRules = new HashMap<>();
+        }
+
+        /**
+         * Add an override rule to set a specific {@link WebViewMediaIntegrityApiStatus} for
+         * origin sites matching the origin pattern stated in the rule. Origin patterns
+         * should be supplied in the same format as those in
+         * {@link androidx.webkit.WebViewCompat.WebMessageListener#addWebMessageListener(WebView, String, Set, WebViewCompat.WebMessageListener)}
+         *
+         * If two or more origin patterns match a given origin site, the least permissive option
+         * will be chosen.
+         */
+
+        @NonNull
+        public Builder addOverrideRule(@NonNull String originPattern,
+                @WebViewMediaIntegrityApiStatus int permission) {
+            mOverrideRules.put(originPattern, permission);
+            return this;
+        }
+
+        /**
+         * Set all required override rules at once using a map of origin patterns to
+         * {@link WebViewMediaIntegrityApiStatus}. This overwrites existing rules.
+         *
+         * If two or more origin patterns match a given origin site, the least permissive option
+         * will be chosen.
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @NonNull
+        public Builder setOverrideRules(@NonNull Map<String,
+                @WebViewMediaIntegrityApiStatus Integer> overrideRules) {
+            mOverrideRules = overrideRules;
+            return this;
+        }
+
+        /**
+         * Build the config.
+         */
+        @NonNull
+        public WebViewMediaIntegrityApiStatusConfig build() {
+            return new WebViewMediaIntegrityApiStatusConfig(this);
+        }
+    }
+
+    /**
+     * Returns the default value for origins that don't match any override rules.
+     */
+    public @WebViewMediaIntegrityApiStatus int getDefaultStatus() {
+        return mDefaultStatus;
+    }
+
+    /**
+     * Get the explicitly set override rules.
+     * <p> This is a map from origin pattern to {@link WebViewMediaIntegrityApiStatus}.
+     *
+     */
+    @NonNull
+    public Map<String, @WebViewMediaIntegrityApiStatus Integer> getOverrideRules() {
+        return mOverrideRules;
+    }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
index f99861b..64e29ec 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
@@ -20,6 +20,8 @@
 
 import androidx.annotation.NonNull;
 import androidx.webkit.UserAgentMetadata;
+import androidx.webkit.WebViewMediaIntegrityApiStatusConfig;
+
 
 import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
 
@@ -191,5 +193,26 @@
         mBoundaryInterface.setAttributionBehavior(behavior);
     }
 
+    /**
+     * Adapter method for
+     * {@link androidx.webkit.WebSettingsCompat#setWebViewMediaIntegrityApiStatus(WebSettings, WebViewMediaIntegrityApiStatusConfig)}
+     */
+    public void setWebViewMediaIntegrityApiStatus(
+            @NonNull WebViewMediaIntegrityApiStatusConfig permissionConfig) {
+        mBoundaryInterface.setWebViewMediaIntegrityApiStatus(permissionConfig.getDefaultStatus(),
+                permissionConfig.getOverrideRules());
+    }
+
+    /**
+     * Adapter method for
+     * {@link androidx.webkit.WebSettingsCompat#getWebViewMediaIntegrityApiStatus(WebSettings)}
+     */
+    @NonNull
+    public WebViewMediaIntegrityApiStatusConfig getWebViewMediaIntegrityApiStatus() {
+        return new WebViewMediaIntegrityApiStatusConfig
+                .Builder(mBoundaryInterface.getWebViewMediaIntegrityApiDefaultStatus())
+                .setOverrideRules(mBoundaryInterface.getWebViewMediaIntegrityApiOverrideRules())
+                .build();
+    }
 
 }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index 4790c176..802a76b 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -42,6 +42,7 @@
 import androidx.webkit.WebMessagePortCompat;
 import androidx.webkit.WebResourceErrorCompat;
 import androidx.webkit.WebResourceRequestCompat;
+import androidx.webkit.WebSettingsCompat;
 import androidx.webkit.WebViewClientCompat;
 import androidx.webkit.WebViewCompat;
 import androidx.webkit.WebViewFeature;
@@ -583,6 +584,17 @@
             new ApiFeature.NoFramework(WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR,
                     Features.ATTRIBUTION_BEHAVIOR);
 
+    /**
+     * Feature for {@link WebViewFeature#isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#setWebViewMediaIntegrityApiStatus(WebSettings, WebSettingsCompat.WebViewMediaIntegrityApiStatusConfig)}
+     * {@link androidx.webkit.WebSettingsCompat#getWebViewMediaIntegrityApiStatus(WebSettings)}
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final ApiFeature.NoFramework WEBVIEW_MEDIA_INTEGRITY_API_STATUS =
+            new ApiFeature.NoFramework(WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS,
+                    Features.WEBVIEW_MEDIA_INTEGRITY_API_STATUS);
+
     // --- Add new feature constants above this line ---
 
     private WebViewFeatureInternal() {