Unhide override user-agent metadata APIs

Relnote: """Add APIs to override user-agent metadata,
    We introduced a new API `WebSettingsCompat#setUserAgentMetadata`
    to override the user-agent metadata for WebView, which is used to
    populate the user-agent client hints, and we also added another
    new API `WebSettingsCompat#getUserAgentMetadata` to get current
    user-agent overrides. We encourage apps to use the new API to set
    the right override values instead of relying on changing user-agent.
    """

This Cl alsp added a demo app for override user-agent metadata.

Bug: b/294183509
Test: :webkit:integration-tests:instrumentation:connectedAndroidTest

Change-Id: I745009a8c24d287416787802fb29210a78967855
diff --git a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
index d337e48..4b1e168 100644
--- a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -142,6 +142,9 @@
         <activity
             android:name=".ImageDragActivity"
             android:exported="false" />
+        <activity
+            android:name=".UserAgentMetadataActivity"
+            android:exported="true" />
 
         <provider
             android:authorities="com.example.androidx.webkit.DropDataProvider"
diff --git a/webkit/integration-tests/testapp/src/main/assets/www/user_agent_metadata_main.html b/webkit/integration-tests/testapp/src/main/assets/www/user_agent_metadata_main.html
new file mode 100644
index 0000000..aca6b25
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/assets/www/user_agent_metadata_main.html
@@ -0,0 +1,86 @@
+<html>
+<!-- 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.
+-->
+
+<head>
+    <style>
+        pre.input {
+          color: hsl(300, 24%, 40%);
+          font-style: italic;
+          font-weight: 400;
+          padding-bottom: 0;
+        }
+
+        pre.input::before {
+          content: "> ";
+          color: hsl(300, 24%, 70%);
+        }
+    </style>
+
+    <script type="text/javascript">
+        var xhr = new XMLHttpRequest();
+        xhr.onreadystatechange = function () {
+          if (this.readyState === 4 && this.status === 200) {
+            <!-- Low-entropy client hints -->
+            const brandsText = document.querySelector(".brands");
+            brandsText.textContent = JSON.stringify(navigator.userAgentData.brands, null, "  ");
+            const platform = document.querySelector(".platform");
+            platform.textContent = navigator.userAgentData.platform;
+            const mobileText = document.querySelector(".mobile");
+            mobileText.textContent = navigator.userAgentData.mobile ? "true" : "false";
+
+            <!-- High-entropy client hints -->
+            const highEntropyText = document.querySelector(".high-entropy");
+            navigator.userAgentData
+              .getHighEntropyValues([
+                "architecture",
+                "bitness",
+                "brands",
+                "mobile",
+                "model",
+                "platform",
+                "platformVersion",
+                "uaFullVersion",
+                "fullVersionList",
+              ]).then((ua) => {
+                highEntropyText.textContent = JSON.stringify(ua, null, "  ");
+              });
+
+            document.querySelector(".user-agent").textContent = navigator.userAgent;
+          }
+        };
+
+        xhr.open("GET", "https://example.com/androidx_webkit/example/assets/www/some_text.html", true);
+        xhr.send();
+    </script>
+</head>
+
+<body>
+    <pre class="input">console.log(navigator.userAgentData.brands);</pre>
+    <pre class="brands" disabled>[…]</pre>
+    <pre class="input">console.log(navigator.userAgentData.mobile);</pre>
+    <pre class="mobile" disabled>[…]</pre>
+    <pre class="input">console.log(navigator.userAgentData.platform);</pre>
+    <pre class="platform" disabled>[…]</pre>
+    <pre class="input">
+    navigator.userAgentData
+    .getHighEntropyValues(["architecture", "bitness", "model", "platform", "platformVersion", "uaFullVersion", "fullVersionList"])
+    .then(ua =&gt; { console.log(ua) });</pre
+    >
+    <pre class="high-entropy" disabled></pre>
+    <pre class="input">console.log(navigator.userAgent);</pre>
+    <pre class="user-agent" disabled>[…]</pre>
+</body>
+</html>
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
index bf8f85e..7aaf25e 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
@@ -87,6 +87,9 @@
                 new MenuListView.MenuItem(
                         getResources().getString(R.string.image_drag_drop_activity_title),
                         new Intent(activityContext, ImageDragActivity.class)),
+                new MenuListView.MenuItem(
+                        getResources().getString(R.string.user_agent_metadata_activity_title),
+                        new Intent(activityContext, UserAgentMetadataActivity.class)),
         };
         listView.setItems(menuItems);
     }
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/UserAgentMetadataActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/UserAgentMetadataActivity.java
new file mode 100644
index 0000000..e1213a7
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/UserAgentMetadataActivity.java
@@ -0,0 +1,145 @@
+/*
+ * 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 com.example.androidx.webkit;
+
+import static androidx.webkit.WebViewAssetLoader.AssetsPathHandler;
+
+import android.annotation.SuppressLint;
+import android.net.Uri;
+import android.os.Bundle;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.RadioGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.UserAgentMetadata;
+import androidx.webkit.WebSettingsCompat;
+import androidx.webkit.WebViewAssetLoader;
+import androidx.webkit.WebViewFeature;
+
+import java.util.Collections;
+
+/**
+ * Demo activity to demonstrate the behaviour of overriding user-agent metadata APIs.
+ */
+public class UserAgentMetadataActivity extends AppCompatActivity {
+
+    private final Uri mExampleUri = new Uri.Builder()
+            .scheme("https")
+            .authority("example.com")
+            .appendPath("androidx_webkit")
+            .appendPath("example")
+            .appendPath("assets")
+            .build();
+
+    /**
+     * A WebViewClient to intercept the request to mock HTTPS response.
+     */
+    private static class MyWebViewClient extends WebViewClient {
+        private final WebViewAssetLoader mAssetLoader;
+
+        MyWebViewClient(WebViewAssetLoader loader) {
+            mAssetLoader = loader;
+        }
+
+        @Override
+        @RequiresApi(21)
+        public WebResourceResponse shouldInterceptRequest(WebView view,
+                WebResourceRequest request) {
+            return mAssetLoader.shouldInterceptRequest(Api21Impl.getUrl(request));
+        }
+
+        @Override
+        @SuppressWarnings("deprecation") // use the old one for compatibility with all API levels.
+        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+            return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
+        }
+    }
+
+    private WebView mWebView;
+
+
+    @SuppressLint("SetJavascriptEnabled")
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_user_agent_metadata);
+
+        setTitle(R.string.user_agent_metadata_activity_title);
+        WebkitHelpers.appendWebViewVersionToTitle(this);
+
+        // Check if override user-agent metadata feature is enabled
+        if (!WebViewFeature.isFeatureSupported(WebViewFeature.USER_AGENT_METADATA)) {
+            WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available);
+            return;
+        }
+
+        mWebView = findViewById(R.id.user_agent_metadata_webview);
+        mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
+        mWebView.getSettings().setJavaScriptEnabled(true);
+
+        RadioGroup radioGroup = findViewById(R.id.user_agent_metadata_radio_group);
+        radioGroup.check(R.id.user_agent_metadata_without_override_mode);
+        radioGroup.setOnCheckedChangeListener(this::onRadioGroupChanged);
+
+        // Initially send a request without overrides
+        refreshView(false);
+    }
+
+    private void refreshView(boolean setOverrides) {
+        UserAgentMetadata overrideSetting;
+        if (setOverrides) {
+            overrideSetting = new UserAgentMetadata.Builder()
+                    .setBrandVersionList(Collections.singletonList(
+                            new UserAgentMetadata.BrandVersion(
+                                    "myBrand", "1", "1.1.1.1")))
+                    .setFullVersion("1.1.1.1").setPlatform("myPlatform")
+                    .setPlatformVersion("2.2.2.2").setArchitecture("myArch")
+                    .setMobile(true).setModel("myModel").setBitness(32)
+                    .setWow64(false).build();
+
+        } else {
+            overrideSetting = new UserAgentMetadata.Builder().build();
+        }
+        WebSettingsCompat.setUserAgentMetadata(mWebView.getSettings(), overrideSetting);
+
+        // Use WebViewAssetLoader to load html page from app's assets.
+        WebViewAssetLoader assetLoader =
+                new WebViewAssetLoader.Builder()
+                        .setDomain("example.com")
+                        .addPathHandler(mExampleUri.getPath() + "/", new AssetsPathHandler(this))
+                        .build();
+        mWebView.setWebViewClient(new MyWebViewClient(assetLoader));
+        mWebView.loadUrl(Uri.withAppendedPath(mExampleUri,
+                "www/user_agent_metadata_main.html").toString());
+    }
+
+    /**
+     * Handler for selecting w/o user-agent metadata mode through the radio group.
+     * @param unused Triggering radio group
+     * @param checkedId ID of checked radio button
+     */
+    public void onRadioGroupChanged(@NonNull RadioGroup unused, int checkedId) {
+        refreshView(checkedId == R.id.user_agent_metadata_with_override_mode);
+    }
+}
diff --git a/webkit/integration-tests/testapp/src/main/res/layout/activity_user_agent_metadata.xml b/webkit/integration-tests/testapp/src/main/res/layout/activity_user_agent_metadata.xml
new file mode 100644
index 0000000..1223b4e
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/res/layout/activity_user_agent_metadata.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/activity_user_agent_metadata"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/user_agent_metadata_radio_group_heading"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/user_agent_metadata_override_mode"
+        android:textColor="@color/colorPrimary"
+        android:layout_alignParentTop="true"/>
+    <RadioGroup
+        android:id="@+id/user_agent_metadata_radio_group"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/user_agent_metadata_radio_group_heading">
+        <RadioButton
+            android:id="@+id/user_agent_metadata_without_override_mode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="48dp"
+            android:text="@string/user_agent_metadata_without_override"
+            android:textColor="@color/colorAccent"/>
+        <RadioButton
+            android:id="@+id/user_agent_metadata_with_override_mode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="48dp"
+            android:textColor="@color/colorAccent"
+            android:text="@string/user_agent_metadata_with_override"/>
+    </RadioGroup>
+    <WebView
+        android:id="@+id/user_agent_metadata_webview"
+        android:layout_width="match_parent"
+        android:layout_below="@id/user_agent_metadata_radio_group"
+        android:layout_alignParentBottom="true"
+        android:layout_height="0dp"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index 9ac8144..fa1d0e1 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -109,4 +109,10 @@
     <string name="requested_with_no_allow_list">Empty</string>
     <string name="requested_with_use_allow_list">Containing the web server</string>
 
+    <!-- User Agent Metadata -->
+    <string name="user_agent_metadata_activity_title">Override User-Agent Metadata</string>
+    <string name="user_agent_metadata_override_mode">User-Agent Metadata</string>
+    <string name="user_agent_metadata_without_override">Without Overrides</string>
+    <string name="user_agent_metadata_with_override">With Overrides</string>
+
 </resources>
diff --git a/webkit/webkit/api/current.txt b/webkit/webkit/api/current.txt
index b608615..ebb7e57 100644
--- a/webkit/webkit/api/current.txt
+++ b/webkit/webkit/api/current.txt
@@ -127,6 +127,41 @@
     method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
   }
 
+  public final class UserAgentMetadata {
+    method public String? getArchitecture();
+    method public int getBitness();
+    method public java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>? getBrandVersionList();
+    method public String? getFullVersion();
+    method public String? getModel();
+    method public String? getPlatform();
+    method public String? getPlatformVersion();
+    method public boolean isMobile();
+    method public boolean isWow64();
+    field public static final int BITNESS_DEFAULT = 0; // 0x0
+  }
+
+  public static final class UserAgentMetadata.BrandVersion {
+    ctor public UserAgentMetadata.BrandVersion(String, String, String);
+    method public String getBrand();
+    method public String getFullVersion();
+    method public String getMajorVersion();
+  }
+
+  public static final class UserAgentMetadata.Builder {
+    ctor public UserAgentMetadata.Builder();
+    ctor public UserAgentMetadata.Builder(androidx.webkit.UserAgentMetadata);
+    method public androidx.webkit.UserAgentMetadata build();
+    method public androidx.webkit.UserAgentMetadata.Builder setArchitecture(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setBitness(int);
+    method public androidx.webkit.UserAgentMetadata.Builder setBrandVersionList(java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>);
+    method public androidx.webkit.UserAgentMetadata.Builder setFullVersion(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setMobile(boolean);
+    method public androidx.webkit.UserAgentMetadata.Builder setModel(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatform(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatformVersion(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setWow64(boolean);
+  }
+
   public class WebMessageCompat {
     ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
     ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
@@ -169,6 +204,7 @@
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
     method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
@@ -178,6 +214,7 @@
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
     method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
     field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
     field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
     field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
@@ -297,6 +334,7 @@
     field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
     field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
     field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+    field public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
     field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
     field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
     field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
diff --git a/webkit/webkit/api/restricted_current.txt b/webkit/webkit/api/restricted_current.txt
index b608615..ebb7e57 100644
--- a/webkit/webkit/api/restricted_current.txt
+++ b/webkit/webkit/api/restricted_current.txt
@@ -127,6 +127,41 @@
     method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
   }
 
+  public final class UserAgentMetadata {
+    method public String? getArchitecture();
+    method public int getBitness();
+    method public java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>? getBrandVersionList();
+    method public String? getFullVersion();
+    method public String? getModel();
+    method public String? getPlatform();
+    method public String? getPlatformVersion();
+    method public boolean isMobile();
+    method public boolean isWow64();
+    field public static final int BITNESS_DEFAULT = 0; // 0x0
+  }
+
+  public static final class UserAgentMetadata.BrandVersion {
+    ctor public UserAgentMetadata.BrandVersion(String, String, String);
+    method public String getBrand();
+    method public String getFullVersion();
+    method public String getMajorVersion();
+  }
+
+  public static final class UserAgentMetadata.Builder {
+    ctor public UserAgentMetadata.Builder();
+    ctor public UserAgentMetadata.Builder(androidx.webkit.UserAgentMetadata);
+    method public androidx.webkit.UserAgentMetadata build();
+    method public androidx.webkit.UserAgentMetadata.Builder setArchitecture(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setBitness(int);
+    method public androidx.webkit.UserAgentMetadata.Builder setBrandVersionList(java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>);
+    method public androidx.webkit.UserAgentMetadata.Builder setFullVersion(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setMobile(boolean);
+    method public androidx.webkit.UserAgentMetadata.Builder setModel(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatform(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatformVersion(String);
+    method public androidx.webkit.UserAgentMetadata.Builder setWow64(boolean);
+  }
+
   public class WebMessageCompat {
     ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
     ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
@@ -169,6 +204,7 @@
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
     method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
@@ -178,6 +214,7 @@
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
     method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
     field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
     field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
     field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
@@ -297,6 +334,7 @@
     field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
     field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
     field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+    field public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
     field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
     field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
     field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
diff --git a/webkit/webkit/src/main/java/androidx/webkit/UserAgentMetadata.java b/webkit/webkit/src/main/java/androidx/webkit/UserAgentMetadata.java
index ca9f828..f799f505 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/UserAgentMetadata.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/UserAgentMetadata.java
@@ -16,6 +16,8 @@
 
 package androidx.webkit;
 
+import android.annotation.SuppressLint;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
@@ -29,13 +31,8 @@
  * <p>
  * This class is functionally equivalent to
  * <a href="https://wicg.github.io/ua-client-hints/#interface">UADataValues</a>.
- * <p>
- * TODO(b/294183509): unhide
- *
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class UserAgentMetadata {
+public final class UserAgentMetadata {
     /**
      * Use this value for bitness to use the platform's default bitness value, which is an empty
      * string for Android WebView.
@@ -80,6 +77,7 @@
      * @see Builder#setBrandVersionList
      *
      */
+    @SuppressLint("NullableCollection")
     @Nullable
     public List<BrandVersion> getBrandVersionList() {
         return mBrandVersionList;
@@ -170,7 +168,8 @@
      * <p>
      * @see Builder#setWow64
      *
-     * @return A boolean to indicate whether user-agent's binary is running in 64-bit Windows.
+     * @return A boolean to indicate whether user-agent's binary is running in 32-bit mode on
+     * 64-bit Windows.
      */
     public boolean isWow64() {
         return mWow64;
@@ -207,12 +206,11 @@
      * <a href="https://wicg.github.io/ua-client-hints/#interface">NavigatorUABrandVersion</a>.
      *
      */
-    public static class BrandVersion {
+    public static final class BrandVersion {
         private final String mBrand;
         private final String mMajorVersion;
         private final String mFullVersion;
 
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
         public BrandVersion(@NonNull String brand, @NonNull String majorVersion,
                 @NonNull String fullVersion) {
             if (brand.trim().isEmpty() || majorVersion.trim().isEmpty()
@@ -344,7 +342,9 @@
 
         /**
          * Sets user-agent metadata brands and their versions. The brand name, major version and
-         * full version should not be blank.
+         * full version should not be blank. The default value is null which means the system
+         * default user-agent metadata brands and versions will be used to generate the
+         * user-agent client hints.
          *
          * @param brandVersions a list of {@link BrandVersion} used to generated user-agent client
          *                     hints {@code sec-cu-ua} and {@code sec-ch-ua-full-version-list}.
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index 9ce9e66..4a544da 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -722,12 +722,9 @@
      * {@link WebViewFeature#isFeatureSupported(String)}
      * returns true for {@link WebViewFeature#USER_AGENT_METADATA}.
      *
+     * @param settings Settings retrieved from {@link WebView#getSettings()}.
      * @param metadata the WebView's user-agent metadata.
-     *
-     * TODO(b/294183509): unhide
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresFeature(name = WebViewFeature.USER_AGENT_METADATA,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static void setUserAgentMetadata(@NonNull WebSettings settings,
@@ -752,10 +749,8 @@
      * {@link WebViewFeature#isFeatureSupported(String)}
      * returns true for {@link WebViewFeature#USER_AGENT_METADATA}.
      *
-     * TODO(b/294183509): unhide
-     * @hide
+     * @param settings Settings retrieved from {@link WebView#getSettings()}.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresFeature(name = WebViewFeature.USER_AGENT_METADATA,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     @NonNull
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 81ba5ed..11936ee 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -538,13 +538,9 @@
     /**
      * Feature for {@link #isFeatureSupported(String)}.
      * This feature covers
-     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#getUserAgentMetadata(WebSettings)}, and
-     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#setUserAgentMetadata(WebSettings, UserAgentMetadata)}.
-     *
-     * TODO(b/294183509): unhide
-     * @hide
+     * {@link androidx.webkit.WebSettingsCompat#getUserAgentMetadata(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setUserAgentMetadata(WebSettings, UserAgentMetadata)}.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
 
     /**