Merge "Remove unused context parameter"
diff --git a/Cronet/tests/common/Android.bp b/Cronet/tests/common/Android.bp
index 939a81c..5d2f6e5 100644
--- a/Cronet/tests/common/Android.bp
+++ b/Cronet/tests/common/Android.bp
@@ -28,7 +28,6 @@
     name: "NetHttpCoverageTests",
     defaults: ["CronetTestJavaDefaults"],
     enforce_default_target_sdk_version: true,
-    sdk_version: "test_current",
     min_sdk_version: "30",
     test_suites: ["general-tests", "mts-tethering"],
     static_libs: [
@@ -36,6 +35,9 @@
         "CtsNetHttpTestsLib",
         "NetHttpTestsLibPreJarJar",
     ],
-    jarjar_rules: ":framework-tethering-jarjar-rules",
+    jarjar_rules: ":net-http-test-jarjar-rules",
     compile_multilib: "both", // Include both the 32 and 64 bit versions
+    jni_libs: [
+       "cronet_aml_components_cronet_android_cronet_tests__testing"
+    ],
 }
diff --git a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
index c561ee7..ed86854 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
@@ -26,6 +26,7 @@
 import static org.hamcrest.Matchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -33,6 +34,7 @@
 import android.net.http.ConnectionMigrationOptions;
 import android.net.http.DnsOptions;
 import android.net.http.HttpEngine;
+import android.net.http.QuicOptions;
 import android.net.http.UrlRequest;
 import android.net.http.UrlResponseInfo;
 import android.net.http.cts.util.HttpCtsTestServer;
@@ -48,6 +50,11 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 public class HttpEngineTest {
     private static final String HOST = "source.android.com";
@@ -111,8 +118,8 @@
         mEngine =
                 mEngineBuilder
                         .setStoragePath(mContext.getApplicationInfo().dataDir)
-                        .setEnableHttpCache(HttpEngine.Builder.HTTP_CACHE_DISK,
-                                            /* maxSize */ 100 * 1024)
+                        .setEnableHttpCache(
+                                HttpEngine.Builder.HTTP_CACHE_DISK, /* maxSize */ 100 * 1024)
                         .build();
 
         UrlRequest.Builder builder =
@@ -181,6 +188,38 @@
         // server.
     }
 
+    private byte[] generateSha256() {
+        byte[] sha256 = new byte[32];
+        Arrays.fill(sha256, (byte) 58);
+        return sha256;
+    }
+
+    private Instant instantInFuture(int secondsIntoFuture) {
+        Calendar cal = Calendar.getInstance();
+        cal.add(Calendar.SECOND, secondsIntoFuture);
+        return cal.getTime().toInstant();
+    }
+
+    @Test
+    public void testHttpEngine_AddPublicKeyPins() {
+        // CtsTestServer, when set in SslMode.NO_CLIENT_AUTH (required to trigger
+        // certificate verification, needed by this test), uses a certificate that
+        // doesn't match the hostname. For this reason, CtsTestServer cannot be used
+        // by this test.
+        Instant expirationInstant = instantInFuture(/* secondsIntoFuture */ 100);
+        boolean includeSubdomains = true;
+        Set<byte[]> pinsSha256 = Set.of(generateSha256());
+        mEngine = mEngineBuilder.addPublicKeyPins(
+                HOST, pinsSha256, includeSubdomains, expirationInstant).build();
+
+        UrlRequest.Builder builder =
+                mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
+        mRequest = builder.build();
+        mRequest.start();
+        mCallback.expectCallback(ResponseStep.ON_FAILED);
+        assertNotNull("Expected an error", mCallback.mError);
+    }
+
     @Test
     public void testHttpEngine_EnableQuic() throws Exception {
         mEngine = mEngineBuilder.setEnableQuic(true).addQuicHint(HOST, 443, 443).build();
@@ -259,9 +298,7 @@
     private static String extractUserAgent(String userAgentResponseBody) {
         // If someone wants to be evil and have the title HTML tag a part of the user agent,
         // they'll have to fix this method :)
-        return userAgentResponseBody
-                .replaceFirst(".*<title>", "")
-                .replaceFirst("</title>.*", "");
+        return userAgentResponseBody.replaceFirst(".*<title>", "").replaceFirst("</title>.*", "");
     }
 
     @Test
@@ -339,4 +376,52 @@
     public void getVersionString_notEmpty() {
         assertThat(HttpEngine.getVersionString()).isNotEmpty();
     }
+
+    @Test
+    public void testHttpEngine_SetQuicOptions_RequestSucceedsWithQuic() throws Exception {
+        QuicOptions options = new QuicOptions.Builder().build();
+        mEngine = mEngineBuilder
+                .setEnableQuic(true)
+                .addQuicHint(HOST, 443, 443)
+                .setQuicOptions(options)
+                .build();
+
+        // The hint doesn't guarantee that QUIC will win the race, just that it will race TCP.
+        // We send multiple requests to reduce the flakiness of the test.
+        boolean quicWasUsed = false;
+        for (int i = 0; i < 5; i++) {
+            mCallback = new TestUrlRequestCallback();
+            UrlRequest.Builder builder =
+                    mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
+            mRequest = builder.build();
+            mRequest.start();
+            mCallback.blockForDone();
+
+            quicWasUsed = isQuic(mCallback.mResponseInfo.getNegotiatedProtocol());
+            if (quicWasUsed) {
+                break;
+            }
+        }
+
+        assertTrue(quicWasUsed);
+        // This tests uses a non-hermetic server. Instead of asserting, assume the next callback.
+        // This way, if the request were to fail, the test would just be skipped instead of failing.
+        assumeOKStatusCode(mCallback.mResponseInfo);
+    }
+
+    @Test
+    public void testHttpEngine_enableBrotli_brotliAdvertised() {
+        mEngine = mEngineBuilder.setEnableBrotli(true).build();
+        mRequest =
+                mEngine.newUrlRequestBuilder(
+                        mTestServer.getEchoHeadersUrl(), mCallback.getExecutor(), mCallback)
+                        .build();
+        mRequest.start();
+
+        mCallback.assumeCallback(ResponseStep.ON_SUCCEEDED);
+        UrlResponseInfo info = mCallback.mResponseInfo;
+        assertThat(info.getHeaders().getAsMap().get("x-request-header-Accept-Encoding").toString())
+                .contains("br");
+        assertOKStatusCode(info);
+    }
 }
diff --git a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
index 5f1979f..0b02aa7 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
@@ -71,4 +71,14 @@
         assertThat(quicOptions.inMemoryServerConfigsCacheSize)
                 .isEqualTo(42)
     }
+
+    @Test
+    fun testQuicOptions_handshakeUserAgent_returnsSetValue() {
+        val userAgent = "test"
+        val quicOptions = QuicOptions.Builder()
+            .setHandshakeUserAgent(userAgent)
+            .build()
+        assertThat(quicOptions.handshakeUserAgent)
+            .isEqualTo(userAgent)
+    }
 }
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index 03d163c..adbc384 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -17,19 +17,39 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+java_genrule {
+    name: "net-http-test-jarjar-rules",
+    tool_files: [
+        ":NetHttpTestsLibPreJarJar{.jar}",
+        "jarjar_excludes.txt",
+    ],
+    tools: [
+        "jarjar-rules-generator",
+    ],
+    out: ["net_http_test_jarjar_rules.txt"],
+    cmd: "$(location jarjar-rules-generator) " +
+        "$(location :NetHttpTestsLibPreJarJar{.jar}) " +
+        "--prefix android.net.http.internal " +
+        "--excludes $(location jarjar_excludes.txt) " +
+        "--output $(out)",
+}
+
 android_library {
     name: "NetHttpTestsLibPreJarJar",
     srcs: [":cronet_aml_javatests_sources"],
-    sdk_version: "test_current",
+    sdk_version: "module_current",
     min_sdk_version: "30",
     static_libs: [
+        "cronet_testserver_utils",
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "junit",
     ],
     libs: [
         "android.test.base",
-        "framework-tethering-pre-jarjar",
+        // Needed for direct access to tethering's hidden apis and to avoid `symbol not found`
+        //  errors on some builds.
+        "framework-tethering.impl",
     ],
     lint: { test: true }
 }
@@ -40,9 +60,11 @@
         "CronetTestJavaDefaults",
         "mts-target-sdk-version-current",
      ],
-     sdk_version: "test_current",
      static_libs: ["NetHttpTestsLibPreJarJar"],
-     jarjar_rules: ":framework-tethering-jarjar-rules",
+     jarjar_rules: ":net-http-test-jarjar-rules",
+     jni_libs: [
+        "cronet_aml_components_cronet_android_cronet_tests__testing"
+     ],
      test_suites: [
          "general-tests",
          "mts-tethering",
diff --git a/Cronet/tests/mts/AndroidManifest.xml b/Cronet/tests/mts/AndroidManifest.xml
index 62c2060..f597134 100644
--- a/Cronet/tests/mts/AndroidManifest.xml
+++ b/Cronet/tests/mts/AndroidManifest.xml
@@ -21,7 +21,7 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.INTERNET"/>
 
-    <application>
+    <application android:networkSecurityConfig="@xml/network_security_config">
         <uses-library android:name="android.test.runner" />
     </application>
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/Cronet/tests/mts/jarjar_excludes.txt b/Cronet/tests/mts/jarjar_excludes.txt
new file mode 100644
index 0000000..01f4d6a
--- /dev/null
+++ b/Cronet/tests/mts/jarjar_excludes.txt
@@ -0,0 +1,10 @@
+# It's prohibited to jarjar androidx packages
+androidx\..+
+# Do not jarjar the api classes
+android\.net\..+
+# cronet_tests.so is not jarjared and uses base classes. We can remove this when there's a
+# separate java base target to depend on.
+org\.chromium\.base\..+
+# Do not jarjar the tests and its utils as they also do JNI with cronet_tests.so
+org\.chromium\.net\..*Test.*(\$.+)?
+org\.chromium\.net\.NativeTestServer(\$.+)?
\ No newline at end of file
diff --git a/Cronet/tests/mts/res/xml/network_security_config.xml b/Cronet/tests/mts/res/xml/network_security_config.xml
new file mode 100644
index 0000000..d44c36f
--- /dev/null
+++ b/Cronet/tests/mts/res/xml/network_security_config.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<network-security-config>
+    <domain-config cleartextTrafficPermitted="true">
+        <!-- Used as the base URL by native test server (net::EmbeddedTestServer) -->
+        <domain includeSubdomains="true">127.0.0.1</domain>
+        <!-- Used by CronetHttpURLConnectionTest#testIOExceptionInterruptRethrown -->
+        <domain includeSubdomains="true">localhost</domain>
+        <!-- Used by CronetHttpURLConnectionTest#testBadIP -->
+        <domain includeSubdomains="true">0.0.0.0</domain>
+        <!-- Used by CronetHttpURLConnectionTest#testSetUseCachesFalse -->
+        <domain includeSubdomains="true">host-cache-test-host</domain>
+        <!-- Used by CronetHttpURLConnectionTest#testBadHostname -->
+        <domain includeSubdomains="true">this-weird-host-name-does-not-exist</domain>
+        <!-- Used by CronetUrlRequestContextTest#testHostResolverRules -->
+        <domain includeSubdomains="true">some-weird-hostname</domain>
+    </domain-config>
+</network-security-config>
\ No newline at end of file
diff --git a/Cronet/tools/import/copy.bara.sky b/Cronet/tools/import/copy.bara.sky
index 8353fd3..5372a4d 100644
--- a/Cronet/tools/import/copy.bara.sky
+++ b/Cronet/tools/import/copy.bara.sky
@@ -20,6 +20,7 @@
     # Exclude existing *OWNERS files
     "**/*OWNERS",
     "**/.git/**",
+    "**/.gitignore",
 ]
 
 cronet_origin_files = glob(
@@ -29,7 +30,6 @@
         "build/buildflag.h",
         "chrome/VERSION",
         "components/cronet/**",
-        "components/grpc_suport/**",
         "components/metrics/**",
         "components/nacl/**",
         "components/prefs/**",
@@ -97,7 +97,9 @@
         "third_party/protobuf/**",
         # Note: Only used for tests.
         "third_party/quic_trace/**",
-        "third_party/zlib/**",
+        # Note: Cronet currently uses Android's zlib
+        # "third_party/zlib/**",
+        "url/third_party/mozilla/**",
     ],
     exclude = common_excludes,
 )
diff --git a/Cronet/tools/import/import_cronet.sh b/Cronet/tools/import/import_cronet.sh
index d0c8deb..0f04af7 100755
--- a/Cronet/tools/import/import_cronet.sh
+++ b/Cronet/tools/import/import_cronet.sh
@@ -24,6 +24,8 @@
 #   -n rev: The new revision to import.
 #   -f: Force copybara to ignore a failure to find the last imported revision.
 
+set -e -x
+
 OPTSTRING=fl:n:
 
 usage() {
@@ -36,7 +38,7 @@
 COPYBARA_FOLDER_ORIGIN="/tmp/copybara-origin"
 
 #######################################
-# Create upstream-import branch in external/cronet.
+# Create local upstream-import branch in external/cronet.
 # Globals:
 #   ANDROID_BUILD_TOP
 # Arguments:
@@ -44,10 +46,8 @@
 #######################################
 setup_upstream_import_branch() {
     local git_dir="${ANDROID_BUILD_TOP}/external/cronet"
-    local initial_empty_repo_sha="d1add53d6e90815f363c91d433735556ce79b0d2"
 
-    # Suppress error message if branch already exists.
-    (cd "${git_dir}" && git branch upstream-import "${initial_empty_repo_sha}") 2>/dev/null
+    (cd "${git_dir}" && git fetch aosp upstream-import:upstream-import)
 }
 
 #######################################
@@ -57,13 +57,18 @@
 # Arguments:
 #   new_rev, string
 #######################################
-setup_folder_origin() {
+setup_folder_origin() (
     local _new_rev=$1
     mkdir -p "${COPYBARA_FOLDER_ORIGIN}"
     cd "${COPYBARA_FOLDER_ORIGIN}"
 
-    # For this to work _new_rev must be a branch or a tag.
-    git clone --depth=1 --branch "${_new_rev}" https://chromium.googlesource.com/chromium/src.git
+    if [ -d src ]; then
+        (cd src && git fetch --tags && git checkout "${_new_rev}")
+    else
+        # For this to work _new_rev must be a branch or a tag.
+        git clone --depth=1 --branch "${_new_rev}" https://chromium.googlesource.com/chromium/src.git
+    fi
+
 
     cat <<EOF >.gclient
 solutions = [
@@ -80,9 +85,10 @@
     cd src
     # Set appropriate gclient flags to speed up syncing.
     gclient sync \
-        --no-history
-        --shallow
-}
+        --no-history \
+        --shallow \
+        --delete_unversioned_trees
+)
 
 #######################################
 # Runs the copybara import of Chromium
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 67206cd..15b3938 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -66,11 +66,19 @@
 
 apex_defaults {
     name: "CronetInTetheringApexDefaultsEnabled",
-    jni_libs: ["cronet_aml_components_cronet_android_cronet"],
+    jni_libs: [
+        "cronet_aml_components_cronet_android_cronet",
+        "//external/cronet/third_party/boringssl:libcrypto",
+        "//external/cronet/third_party/boringssl:libssl",
+    ],
     arch: {
         riscv64: {
             // TODO: remove this when there is a riscv64 libcronet
-            exclude_jni_libs: ["cronet_aml_components_cronet_android_cronet"],
+            exclude_jni_libs: [
+                "cronet_aml_components_cronet_android_cronet",
+                "//external/cronet/third_party/boringssl:libcrypto",
+                "//external/cronet/third_party/boringssl:libssl",
+            ],
         },
     },
 }
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 4f95bdd..74170cb 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -54,6 +54,7 @@
         "//packages/modules/CaptivePortalLogin/tests",
         "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
         "//packages/modules/Connectivity/tests:__subpackages__",
+        "//packages/modules/Connectivity/Cronet/tests:__subpackages__",
         "//packages/modules/IPsec/tests/iketests",
         "//packages/modules/NetworkStack/tests:__subpackages__",
         "//packages/modules/Wifi/service/tests/wifitests",
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 2e71fda..4c5bf4e 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1861,6 +1861,7 @@
                 mNotificationUpdater.onUpstreamCapabilitiesChanged(
                         (ns != null) ? ns.networkCapabilities : null);
             }
+            mTetheringMetrics.maybeUpdateUpstreamType(ns);
         }
 
         protected void setUpstreamNetwork(UpstreamNetworkState ns) {
@@ -2090,6 +2091,7 @@
                     mNotificationUpdater.onUpstreamCapabilitiesChanged(null);
                 }
                 mBpfCoordinator.stopPolling();
+                mTetheringMetrics.cleanup();
             }
 
             private boolean updateUpstreamWanted() {
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index ffcea4e..c181994 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -16,6 +16,13 @@
 
 package com.android.networkstack.tethering.metrics;
 
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_NCM;
@@ -39,6 +46,8 @@
 import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
 import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
 
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
 import android.stats.connectivity.DownstreamType;
 import android.stats.connectivity.ErrorCode;
 import android.stats.connectivity.UpstreamType;
@@ -49,6 +58,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.networkstack.tethering.UpstreamNetworkState;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
 /**
  * Collection of utilities for tethering metrics.
  *
@@ -66,21 +80,58 @@
     private static final String SYSTEMUI_PKG_NAME = "com.android.systemui";
     private static final String GMS_PKG_NAME = "com.google.android.gms";
     private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
+    private final SparseArray<Long> mDownstreamStartTime = new SparseArray<Long>();
+    private final ArrayList<RecordUpstreamEvent> mUpstreamEventList = new ArrayList<>();
+    private UpstreamType mCurrentUpstream = null;
+    private Long mCurrentUpStreamStartTime = 0L;
 
-    /** Update Tethering stats about caller's package name and downstream type. */
-    public void createBuilder(final int downstreamType, final String callerPkg) {
-        NetworkTetheringReported.Builder statsBuilder =
-                    NetworkTetheringReported.newBuilder();
-        statsBuilder.setDownstreamType(downstreamTypeToEnum(downstreamType))
-                    .setUserType(userTypeToEnum(callerPkg))
-                    .setUpstreamType(UpstreamType.UT_UNKNOWN)
-                    .setErrorCode(ErrorCode.EC_NO_ERROR)
-                    .setUpstreamEvents(UpstreamEvents.newBuilder())
-                    .setDurationMillis(0);
-        mBuilderMap.put(downstreamType, statsBuilder);
+
+    /**
+     * Return the current system time in milliseconds.
+     * @return the current system time in milliseconds.
+     */
+    public long timeNow() {
+        return System.currentTimeMillis();
     }
 
-    /** Update error code of given downstreamType. */
+    private static class RecordUpstreamEvent {
+        public final long mStartTime;
+        public final long mStopTime;
+        public final UpstreamType mUpstreamType;
+
+        RecordUpstreamEvent(final long startTime, final long stopTime,
+                final UpstreamType upstream) {
+            mStartTime = startTime;
+            mStopTime = stopTime;
+            mUpstreamType = upstream;
+        }
+    }
+
+    /**
+     * Creates a |NetworkTetheringReported.Builder| object to update the tethering stats for the
+     * specified downstream type and caller's package name. Initializes the upstream events, error
+     * code, and duration to default values. Sets the start time for the downstream type in the
+     * |mDownstreamStartTime| map.
+     * @param downstreamType The type of downstream connection (e.g. Wifi, USB, Bluetooth).
+     * @param callerPkg The package name of the caller.
+     */
+    public void createBuilder(final int downstreamType, final String callerPkg) {
+        NetworkTetheringReported.Builder statsBuilder = NetworkTetheringReported.newBuilder()
+                .setDownstreamType(downstreamTypeToEnum(downstreamType))
+                .setUserType(userTypeToEnum(callerPkg))
+                .setUpstreamType(UpstreamType.UT_UNKNOWN)
+                .setErrorCode(ErrorCode.EC_NO_ERROR)
+                .setUpstreamEvents(UpstreamEvents.newBuilder())
+                .setDurationMillis(0);
+        mBuilderMap.put(downstreamType, statsBuilder);
+        mDownstreamStartTime.put(downstreamType, timeNow());
+    }
+
+    /**
+     * Update the error code of the given downstream type in the Tethering stats.
+     * @param downstreamType The downstream type whose error code to update.
+     * @param errCode The error code to set.
+     */
     public void updateErrorCode(final int downstreamType, final int errCode) {
         NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
         if (statsBuilder == null) {
@@ -90,38 +141,155 @@
         statsBuilder.setErrorCode(errorCodeToEnum(errCode));
     }
 
-    /** Remove Tethering stats.
-     *  If Tethering stats is ready to write then write it before removing.
+    /**
+     * Update the list of upstream types and their duration whenever the current upstream type
+     * changes.
+     * @param ns The UpstreamNetworkState object representing the current upstream network state.
+     */
+    public void maybeUpdateUpstreamType(@Nullable final UpstreamNetworkState ns) {
+        UpstreamType upstream = transportTypeToUpstreamTypeEnum(ns);
+        if (upstream.equals(mCurrentUpstream)) return;
+
+        final long newTime = timeNow();
+        if (mCurrentUpstream != null) {
+            mUpstreamEventList.add(new RecordUpstreamEvent(mCurrentUpStreamStartTime, newTime,
+                    mCurrentUpstream));
+        }
+        mCurrentUpstream = upstream;
+        mCurrentUpStreamStartTime = newTime;
+    }
+
+    /**
+     * Returns the greater of two start times.
+     * @param first the first start time
+     * @param second the second start time
+     * @return the greater start time
+     */
+    private long getGreaterStartTime(long first, long second) {
+        return first > second ? first : second;
+    }
+
+    /**
+     * Updates the upstream events builder with a new upstream event.
+     * @param upstreamEventsBuilder the builder for the upstream events list
+     * @param start the start time of the upstream event
+     * @param stop the stop time of the upstream event
+     * @param upstream the type of upstream type (e.g. Wifi, Cellular, Bluetooth, ...)
+     */
+    private void updateUpstreamEvents(final UpstreamEvents.Builder upstreamEventsBuilder,
+            final long start, final long stop, @Nullable final UpstreamType upstream) {
+        final UpstreamEvent.Builder upstreamEventBuilder = UpstreamEvent.newBuilder()
+                .setUpstreamType(upstream == null ? UpstreamType.UT_NO_NETWORK : upstream)
+                .setDurationMillis(stop - start);
+        upstreamEventsBuilder.addUpstreamEvent(upstreamEventBuilder);
+    }
+
+    /**
+     * Updates the |NetworkTetheringReported.Builder| with relevant upstream events associated with
+     * the downstream event identified by the given downstream start time.
+     *
+     * This method iterates through the list of upstream events and adds any relevant events to a
+     * |UpstreamEvents.Builder|. Upstream events are considered relevant if their stop time is
+     * greater than or equal to the given downstream start time. The method also adds the last
+     * upstream event that occurred up until the current time.
+     *
+     * The resulting |UpstreamEvents.Builder| is then added to the
+     * |NetworkTetheringReported.Builder|, along with the duration of the downstream event
+     * (i.e., stop time minus downstream start time).
+     *
+     * @param statsBuilder the builder for the NetworkTetheringReported message
+     * @param downstreamStartTime the start time of the downstream event to find relevant upstream
+     * events for
+     */
+    private void updateStatsBuilderToWrite(final NetworkTetheringReported.Builder statsBuilder,
+                    final long downstreamStartTime) {
+        UpstreamEvents.Builder upstreamEventsBuilder = UpstreamEvents.newBuilder();
+        for (RecordUpstreamEvent event : mUpstreamEventList) {
+            if (downstreamStartTime > event.mStopTime) continue;
+
+            final long startTime = getGreaterStartTime(downstreamStartTime, event.mStartTime);
+            // Handle completed upstream events.
+            updateUpstreamEvents(upstreamEventsBuilder, startTime, event.mStopTime,
+                    event.mUpstreamType);
+        }
+        final long startTime = getGreaterStartTime(downstreamStartTime, mCurrentUpStreamStartTime);
+        final long stopTime = timeNow();
+        // Handle the last upstream event.
+        updateUpstreamEvents(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream);
+        statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
+        statsBuilder.setDurationMillis(stopTime - downstreamStartTime);
+    }
+
+    /**
+     * Removes tethering statistics for the given downstream type. If there are any stats to write
+     * for the downstream event associated with the type, they are written before removing the
+     * statistics.
+     *
+     * If the given downstream type does not exist in the map, an error message is logged and the
+     * method returns without doing anything.
+     *
+     * @param downstreamType the type of downstream event to remove statistics for
      */
     public void sendReport(final int downstreamType) {
-        final NetworkTetheringReported.Builder statsBuilder =
-                mBuilderMap.get(downstreamType);
+        final NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
         if (statsBuilder == null) {
             Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
             return;
         }
+
+        updateStatsBuilderToWrite(statsBuilder, mDownstreamStartTime.get(downstreamType));
         write(statsBuilder.build());
+
         mBuilderMap.remove(downstreamType);
+        mDownstreamStartTime.remove(downstreamType);
     }
 
-    /** Collect Tethering stats and write metrics data to statsd pipeline. */
+    /**
+     * Collects tethering statistics and writes them to the statsd pipeline. This method takes in a
+     * NetworkTetheringReported object, extracts its fields and uses them to write statistics data
+     * to the statsd pipeline.
+     *
+     * @param reported a NetworkTetheringReported object containing statistics to write
+     */
     @VisibleForTesting
     public void write(@NonNull final NetworkTetheringReported reported) {
-        TetheringStatsLog.write(TetheringStatsLog.NETWORK_TETHERING_REPORTED,
+        final byte[] upstreamEvents = reported.getUpstreamEvents().toByteArray();
+
+        TetheringStatsLog.write(
+                TetheringStatsLog.NETWORK_TETHERING_REPORTED,
                 reported.getErrorCode().getNumber(),
                 reported.getDownstreamType().getNumber(),
                 reported.getUpstreamType().getNumber(),
                 reported.getUserType().getNumber(),
-                null, 0);
+                upstreamEvents,
+                reported.getDurationMillis());
         if (DBG) {
-            Log.d(TAG, "Write errorCode: " + reported.getErrorCode().getNumber()
-                    + ", downstreamType: " + reported.getDownstreamType().getNumber()
-                    + ", upstreamType: " + reported.getUpstreamType().getNumber()
-                    + ", userType: " + reported.getUserType().getNumber());
+            Log.d(
+                    TAG,
+                    "Write errorCode: "
+                    + reported.getErrorCode().getNumber()
+                    + ", downstreamType: "
+                    + reported.getDownstreamType().getNumber()
+                    + ", upstreamType: "
+                    + reported.getUpstreamType().getNumber()
+                    + ", userType: "
+                    + reported.getUserType().getNumber()
+                    + ", upstreamTypes: "
+                    + Arrays.toString(upstreamEvents)
+                    + ", durationMillis: "
+                    + reported.getDurationMillis());
         }
     }
 
-    /** Map {@link TetheringType} to {@link DownstreamType} */
+    /**
+     * Cleans up the variables related to upstream events when tethering is turned off.
+     */
+    public void cleanup() {
+        mUpstreamEventList.clear();
+        mCurrentUpstream = null;
+        mCurrentUpStreamStartTime = 0L;
+    }
+
     private DownstreamType downstreamTypeToEnum(final int ifaceType) {
         switch(ifaceType) {
             case TETHERING_WIFI:
@@ -141,7 +309,6 @@
         }
     }
 
-    /** Map {@link StartTetheringError} to {@link ErrorCode} */
     private ErrorCode errorCodeToEnum(final int lastError) {
         switch(lastError) {
             case TETHER_ERROR_NO_ERROR:
@@ -181,7 +348,6 @@
         }
     }
 
-    /** Map callerPkg to {@link UserType} */
     private UserType userTypeToEnum(final String callerPkg) {
         if (callerPkg.equals(SETTINGS_PKG_NAME)) {
             return UserType.USER_SETTINGS;
@@ -193,4 +359,40 @@
             return UserType.USER_UNKNOWN;
         }
     }
+
+    private UpstreamType transportTypeToUpstreamTypeEnum(final UpstreamNetworkState ns) {
+        final NetworkCapabilities nc = (ns != null) ? ns.networkCapabilities : null;
+        if (nc == null) return UpstreamType.UT_NO_NETWORK;
+
+        final int typeCount = nc.getTransportTypes().length;
+
+        boolean hasCellular = nc.hasTransport(TRANSPORT_CELLULAR);
+        boolean hasWifi = nc.hasTransport(TRANSPORT_WIFI);
+        boolean hasBT = nc.hasTransport(TRANSPORT_BLUETOOTH);
+        boolean hasEthernet = nc.hasTransport(TRANSPORT_ETHERNET);
+        boolean hasVpn = nc.hasTransport(TRANSPORT_VPN);
+        boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE);
+        boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN);
+
+        if (typeCount == 3 && hasCellular && hasWifi && hasVpn) {
+            return UpstreamType.UT_WIFI_CELLULAR_VPN;
+        }
+
+        if (typeCount == 2 && hasVpn) {
+            if (hasCellular) return UpstreamType.UT_CELLULAR_VPN;
+            if (hasWifi) return UpstreamType.UT_WIFI_VPN;
+            if (hasBT) return UpstreamType.UT_BLUETOOTH_VPN;
+            if (hasEthernet) return UpstreamType.UT_ETHERNET_VPN;
+        }
+
+        if (typeCount == 1) {
+            if (hasCellular) return UpstreamType.UT_CELLULAR;
+            if (hasWifi) return UpstreamType.UT_WIFI;
+            if (hasBT) return UpstreamType.UT_BLUETOOTH;
+            if (hasEthernet) return UpstreamType.UT_ETHERNET;
+            if (hasWifiAware) return UpstreamType.UT_WIFI_AWARE;
+            if (hasLopan) return UpstreamType.UT_LOWPAN;
+        }
+        return UpstreamType.UT_UNKNOWN;
+    }
 }
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index adb1590..c15b85e 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2000,6 +2000,7 @@
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
 
+        verify(mTetheringMetrics, times(0)).maybeUpdateUpstreamType(any());
         verify(mTetheringMetrics, times(2)).updateErrorCode(eq(TETHERING_WIFI),
                 eq(TETHER_ERROR_INTERNAL_ERROR));
         verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_WIFI));
@@ -3365,6 +3366,7 @@
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
                 any(), any());
         verify(mTetheringMetrics).createBuilder(eq(TETHERING_NCM), anyString());
+        verify(mTetheringMetrics, times(1)).maybeUpdateUpstreamType(any());
 
         // Change the USB tethering function to NCM. Because the USB tethering function was set to
         // RNDIS (the default), tethering is stopped.
@@ -3381,6 +3383,7 @@
         mLooper.dispatchAll();
         ncmResult.assertHasResult();
         verify(mTetheringMetrics, times(2)).createBuilder(eq(TETHERING_NCM), anyString());
+        verify(mTetheringMetrics, times(1)).maybeUpdateUpstreamType(any());
         verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_NCM),
                 eq(TETHER_ERROR_SERVICE_UNAVAIL));
         verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_NCM));
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index 98c873d..aa2d16c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -16,6 +16,13 @@
 
 package com.android.networkstack.tethering.metrics;
 
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_NCM;
@@ -44,6 +51,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.net.NetworkCapabilities;
 import android.stats.connectivity.DownstreamType;
 import android.stats.connectivity.ErrorCode;
 import android.stats.connectivity.UpstreamType;
@@ -52,6 +60,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.networkstack.tethering.UpstreamNetworkState;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -64,46 +74,105 @@
     private static final String SETTINGS_PKG = "com.android.settings";
     private static final String SYSTEMUI_PKG = "com.android.systemui";
     private static final String GMS_PKG = "com.google.android.gms";
-    private TetheringMetrics mTetheringMetrics;
+    private static final long TEST_START_TIME = 1670395936033L;
+    private static final long SECOND_IN_MILLIS = 1_000L;
 
+    private TetheringMetrics mTetheringMetrics;
     private final NetworkTetheringReported.Builder mStatsBuilder =
             NetworkTetheringReported.newBuilder();
 
+    private long mElapsedRealtime;
+
     private class MockTetheringMetrics extends TetheringMetrics {
         @Override
-        public void write(final NetworkTetheringReported reported) { }
+        public void write(final NetworkTetheringReported reported) {}
+        @Override
+        public long timeNow() {
+            return currentTimeMillis();
+        }
+    }
+
+    private long currentTimeMillis() {
+        return TEST_START_TIME + mElapsedRealtime;
+    }
+
+    private void incrementCurrentTime(final long duration) {
+        mElapsedRealtime += duration;
+        mTetheringMetrics.timeNow();
+    }
+
+    private long getElapsedRealtime() {
+        return mElapsedRealtime;
+    }
+
+    private void clearElapsedRealtime() {
+        mElapsedRealtime = 0;
     }
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mTetheringMetrics = spy(new MockTetheringMetrics());
+        mElapsedRealtime = 0L;
     }
 
-    private void verifyReport(DownstreamType downstream, ErrorCode error, UserType user)
+    private void verifyReport(final DownstreamType downstream, final ErrorCode error,
+            final UserType user, final UpstreamEvents.Builder upstreamEvents, final long duration)
             throws Exception {
         final NetworkTetheringReported expectedReport =
                 mStatsBuilder.setDownstreamType(downstream)
                 .setUserType(user)
                 .setUpstreamType(UpstreamType.UT_UNKNOWN)
                 .setErrorCode(error)
-                .setUpstreamEvents(UpstreamEvents.newBuilder())
-                .setDurationMillis(0)
+                .setUpstreamEvents(upstreamEvents)
+                .setDurationMillis(duration)
                 .build();
         verify(mTetheringMetrics).write(expectedReport);
     }
 
-    private void updateErrorAndSendReport(int downstream, int error) {
+    private void updateErrorAndSendReport(final int downstream, final int error) {
         mTetheringMetrics.updateErrorCode(downstream, error);
         mTetheringMetrics.sendReport(downstream);
     }
 
+    private static NetworkCapabilities buildUpstreamCapabilities(final int[] transports) {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        for (int type: transports) {
+            nc.addTransportType(type);
+        }
+        return nc;
+    }
+
+    private static UpstreamNetworkState buildUpstreamState(final int... transports) {
+        return new UpstreamNetworkState(
+                null,
+                buildUpstreamCapabilities(transports),
+                null);
+    }
+
+    private void addUpstreamEvent(UpstreamEvents.Builder upstreamEvents,
+            final UpstreamType expectedResult, final long duration) {
+        UpstreamEvent.Builder upstreamEvent = UpstreamEvent.newBuilder()
+                .setUpstreamType(expectedResult)
+                .setDurationMillis(duration);
+        upstreamEvents.addUpstreamEvent(upstreamEvent);
+    }
+
     private void runDownstreamTypesTest(final int type, final DownstreamType expectedResult)
             throws Exception {
         mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG);
+        final long duration = 2 * SECOND_IN_MILLIS;
+        incrementCurrentTime(duration);
+        UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+        // Set UpstreamType as NO_NETWORK because the upstream type has not been changed.
+        addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration);
         updateErrorAndSendReport(type, TETHER_ERROR_NO_ERROR);
-        verifyReport(expectedResult, ErrorCode.EC_NO_ERROR, UserType.USER_UNKNOWN);
+
+        verifyReport(expectedResult, ErrorCode.EC_NO_ERROR, UserType.USER_UNKNOWN,
+                upstreamEvents, getElapsedRealtime());
         reset(mTetheringMetrics);
+        clearElapsedRealtime();
+        mTetheringMetrics.cleanup();
     }
 
     @Test
@@ -119,8 +188,18 @@
     private void runErrorCodesTest(final int errorCode, final ErrorCode expectedResult)
             throws Exception {
         mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
+        mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+        final long duration = 2 * SECOND_IN_MILLIS;
+        incrementCurrentTime(duration);
         updateErrorAndSendReport(TETHERING_WIFI, errorCode);
-        verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN);
+
+        UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, duration);
+        verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN,
+                    upstreamEvents, getElapsedRealtime());
+        reset(mTetheringMetrics);
+        clearElapsedRealtime();
+        mTetheringMetrics.cleanup();
     }
 
     @Test
@@ -151,9 +230,18 @@
     private void runUserTypesTest(final String callerPkg, final UserType expectedResult)
             throws Exception {
         mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg);
+        final long duration = 1 * SECOND_IN_MILLIS;
+        incrementCurrentTime(duration);
         updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
-        verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR, expectedResult);
+
+        UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+        // Set UpstreamType as NO_NETWORK because the upstream type has not been changed.
+        addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration);
+        verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR, expectedResult,
+                    upstreamEvents, getElapsedRealtime());
         reset(mTetheringMetrics);
+        clearElapsedRealtime();
+        mTetheringMetrics.cleanup();
     }
 
     @Test
@@ -164,22 +252,139 @@
         runUserTypesTest(GMS_PKG, UserType.USER_GMS);
     }
 
+    private void runUpstreamTypesTest(final UpstreamNetworkState ns,
+            final UpstreamType expectedResult) throws Exception {
+        mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
+        mTetheringMetrics.maybeUpdateUpstreamType(ns);
+        final long duration = 2 * SECOND_IN_MILLIS;
+        incrementCurrentTime(duration);
+        updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
+
+        UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(upstreamEvents, expectedResult, duration);
+        verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
+                UserType.USER_UNKNOWN, upstreamEvents, getElapsedRealtime());
+        reset(mTetheringMetrics);
+        clearElapsedRealtime();
+        mTetheringMetrics.cleanup();
+    }
+
+    @Test
+    public void testUpstreamTypes() throws Exception {
+        runUpstreamTypesTest(null , UpstreamType.UT_NO_NETWORK);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR), UpstreamType.UT_CELLULAR);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI), UpstreamType.UT_WIFI);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH), UpstreamType.UT_BLUETOOTH);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET), UpstreamType.UT_ETHERNET);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI_AWARE), UpstreamType.UT_WIFI_AWARE);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_LOWPAN), UpstreamType.UT_LOWPAN);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_VPN),
+                UpstreamType.UT_CELLULAR_VPN);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI, TRANSPORT_VPN),
+                UpstreamType.UT_WIFI_VPN);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH, TRANSPORT_VPN),
+                UpstreamType.UT_BLUETOOTH_VPN);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET, TRANSPORT_VPN),
+                UpstreamType.UT_ETHERNET_VPN);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI, TRANSPORT_VPN),
+                UpstreamType.UT_WIFI_CELLULAR_VPN);
+        runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI, TRANSPORT_VPN,
+                TRANSPORT_BLUETOOTH), UpstreamType.UT_UNKNOWN);
+    }
+
     @Test
     public void testMultiBuildersCreatedBeforeSendReport() throws Exception {
         mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+        final long wifiTetheringStartTime = currentTimeMillis();
+        incrementCurrentTime(1 * SECOND_IN_MILLIS);
         mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG);
+        final long usbTetheringStartTime = currentTimeMillis();
+        incrementCurrentTime(2 * SECOND_IN_MILLIS);
         mTetheringMetrics.createBuilder(TETHERING_BLUETOOTH, GMS_PKG);
-
+        final long bluetoothTetheringStartTime = currentTimeMillis();
+        incrementCurrentTime(3 * SECOND_IN_MILLIS);
         updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_DHCPSERVER_ERROR);
+
+        UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
+                currentTimeMillis() - wifiTetheringStartTime);
         verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_DHCPSERVER_ERROR,
-                UserType.USER_SETTINGS);
-
+                UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
+                currentTimeMillis() - wifiTetheringStartTime);
+        incrementCurrentTime(1 * SECOND_IN_MILLIS);
         updateErrorAndSendReport(TETHERING_USB, TETHER_ERROR_ENABLE_FORWARDING_ERROR);
-        verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_ENABLE_FORWARDING_ERROR,
-                UserType.USER_SYSTEMUI);
 
+        UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
+                currentTimeMillis() - usbTetheringStartTime);
+
+        verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_ENABLE_FORWARDING_ERROR,
+                UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
+                currentTimeMillis() - usbTetheringStartTime);
+        incrementCurrentTime(1 * SECOND_IN_MILLIS);
         updateErrorAndSendReport(TETHERING_BLUETOOTH, TETHER_ERROR_TETHER_IFACE_ERROR);
+
+        UpstreamEvents.Builder bluetoothTetheringUpstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(bluetoothTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
+                currentTimeMillis() - bluetoothTetheringStartTime);
         verifyReport(DownstreamType.DS_TETHERING_BLUETOOTH, ErrorCode.EC_TETHER_IFACE_ERROR,
-                UserType.USER_GMS);
+                UserType.USER_GMS, bluetoothTetheringUpstreamEvents,
+                currentTimeMillis() - bluetoothTetheringStartTime);
+    }
+
+    @Test
+    public void testUpstreamsWithMultipleDownstreams() throws Exception {
+        mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+        final long wifiTetheringStartTime = currentTimeMillis();
+        incrementCurrentTime(1 * SECOND_IN_MILLIS);
+        mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+        final long wifiUpstreamStartTime = currentTimeMillis();
+        incrementCurrentTime(5 * SECOND_IN_MILLIS);
+        mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG);
+        final long usbTetheringStartTime = currentTimeMillis();
+        incrementCurrentTime(5 * SECOND_IN_MILLIS);
+        updateErrorAndSendReport(TETHERING_USB, TETHER_ERROR_NO_ERROR);
+
+        UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_WIFI,
+                currentTimeMillis() - usbTetheringStartTime);
+        verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_NO_ERROR,
+                UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
+                currentTimeMillis() - usbTetheringStartTime);
+        incrementCurrentTime(7 * SECOND_IN_MILLIS);
+        updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
+
+        UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_WIFI,
+                currentTimeMillis() - wifiUpstreamStartTime);
+        verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
+                UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
+                currentTimeMillis() - wifiTetheringStartTime);
+    }
+
+    @Test
+    public void testSwitchingMultiUpstreams() throws Exception {
+        mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+        final long wifiTetheringStartTime = currentTimeMillis();
+        incrementCurrentTime(1 * SECOND_IN_MILLIS);
+        mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+        final long wifiDuration = 5 * SECOND_IN_MILLIS;
+        incrementCurrentTime(wifiDuration);
+        mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_BLUETOOTH));
+        final long bluetoothDuration = 15 * SECOND_IN_MILLIS;
+        incrementCurrentTime(bluetoothDuration);
+        mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_CELLULAR));
+        final long celltoothDuration = 20 * SECOND_IN_MILLIS;
+        incrementCurrentTime(celltoothDuration);
+        updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
+
+        UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+        addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, wifiDuration);
+        addUpstreamEvent(upstreamEvents, UpstreamType.UT_BLUETOOTH, bluetoothDuration);
+        addUpstreamEvent(upstreamEvents, UpstreamType.UT_CELLULAR, celltoothDuration);
+
+        verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
+                UserType.USER_SETTINGS, upstreamEvents,
+                currentTimeMillis() - wifiTetheringStartTime);
     }
 }
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 7350209..7c6811a 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -91,6 +91,12 @@
     if (ip6->version != 6) return TC_ACT_PIPE;
 
     // Maximum IPv6 payload length that can be translated to IPv4
+    // Note: technically this check is too strict for an IPv6 fragment,
+    // which by virtue of stripping the extra 8 byte fragment extension header,
+    // could thus be 8 bytes larger and still fit in an ipv4 packet post
+    // translation.  However... who ever heard of receiving ~64KB frags...
+    // fragments are kind of by definition smaller than ingress device mtu,
+    // and thus, on the internet, very very unlikely to exceed 1500 bytes.
     if (ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr)) return TC_ACT_PIPE;
 
     ClatIngress6Key k = {
diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java
index d90bd8d..b3c70cf 100644
--- a/framework-t/src/android/net/NetworkTemplate.java
+++ b/framework-t/src/android/net/NetworkTemplate.java
@@ -47,6 +47,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -58,7 +59,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
@@ -279,6 +282,102 @@
         return new NetworkTemplate(MATCH_PROXY, null, null);
     }
 
+    /**
+     * Template to match all metered carrier networks with the given IMSI.
+     *
+     * @hide
+     */
+    // TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
+    //  including in OEM code which can access this by linking against the framework.
+    public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
+        if (SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException(
+                    "buildTemplateCarrierMetered is not supported on Android U devices or above");
+        }
+        return new NetworkTemplate.Builder(MATCH_CARRIER)
+                // Set.of will throw if subscriberId is null
+                .setSubscriberIds(Set.of(subscriberId))
+                .setMeteredness(METERED_YES)
+                .build();
+    }
+
+    /**
+     * Template to match cellular networks with the given IMSI, {@code ratType} and
+     * {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
+     * filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
+     *
+     * @hide
+     */
+    // TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
+    //  including in OEM code which can access this by linking against the framework.
+    public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
+            int ratType, int metered) {
+        if (SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException("buildTemplateMobileWithRatType is not "
+                    + "supported on Android U devices or above");
+        }
+        return new NetworkTemplate.Builder(MATCH_MOBILE)
+                .setSubscriberIds(TextUtils.isEmpty(subscriberId)
+                        ? Collections.emptySet()
+                        : Set.of(subscriberId))
+                .setMeteredness(metered)
+                .setRatType(ratType)
+                .build();
+    }
+
+
+    /**
+     * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
+     * given key of the wifi network.
+     *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
+     *                  to know details about the key.
+     * @hide
+     */
+    // TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
+    //  including in OEM code which can access this by linking against the framework.
+    public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
+        if (SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException("buildTemplateWifi is not "
+                    + "supported on Android U devices or above");
+        }
+        return new NetworkTemplate.Builder(MATCH_WIFI)
+                // Set.of will throw if wifiNetworkKey is null
+                .setWifiNetworkKeys(Set.of(wifiNetworkKey))
+                .build();
+    }
+
+    /**
+     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given
+     * key of the wifi network and IMSI.
+     *
+     * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
+     * of key of the wifi network.
+     *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
+     *                  to know details about the key.
+     * @param subscriberId the IMSI associated to this wifi network.
+     *
+     * @hide
+     */
+    // TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
+    //  including in OEM code which can access this by linking against the framework.
+    public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
+            @Nullable String subscriberId) {
+        if (SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException("buildTemplateWifi is not "
+                    + "supported on Android U devices or above");
+        }
+        return new NetworkTemplate.Builder(MATCH_WIFI)
+                .setSubscriberIds(subscriberId == null
+                        ? Collections.emptySet()
+                        : Set.of(subscriberId))
+                .setWifiNetworkKeys(wifiNetworkKey == null
+                        ? Collections.emptySet()
+                        : Set.of(wifiNetworkKey))
+                .build();
+    }
+
     private final int mMatchRule;
 
     /**
@@ -830,8 +929,7 @@
      * subscribers.
      * <p>
      * For example, given an incoming template matching B, and the currently
-     * active merge set [A,B], we'd return a new template that primarily matches
-     * A, but also matches B.
+     * active merge set [A,B], we'd return a new template that matches both A and B.
      *
      * @hide
      */
@@ -840,6 +938,49 @@
                     + "Callers should have their own logic to merge template for"
                     + " different IMSIs and stop calling this function.")
     public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) {
+        return normalizeImpl(template, Collections.singletonList(merged));
+    }
+
+    /**
+     * Examine the given template and normalize it.
+     * We pick the "lowest" merged subscriber as the primary
+     * for key purposes, and expand the template to match all other merged
+     * subscribers.
+     *
+     * There can be multiple merged subscriberIds for multi-SIM devices.
+     *
+     * <p>
+     * For example, given an incoming template matching B, and the currently
+     * active merge set [A,B], we'd return a new template that matches both A and B.
+     *
+     * @hide
+     */
+    // TODO(b/273963543): Remove this method. This can only be done after there are no more callers,
+    //  including in OEM code which can access this by linking against the framework.
+    public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) {
+        if (SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException(
+                    "normalize is not supported on Android U devices or above");
+        }
+        return normalizeImpl(template, mergedList);
+    }
+
+    /**
+     * Examine the given template and normalize it.
+     * We pick the "lowest" merged subscriber as the primary
+     * for key purposes, and expand the template to match all other merged
+     * subscribers.
+     *
+     * There can be multiple merged subscriberIds for multi-SIM devices.
+     *
+     * <p>
+     * For example, given an incoming template matching B, and the currently
+     * active merge set [A,B], we'd return a new template that matches both A and B.
+     *
+     * @hide
+     */
+    private static NetworkTemplate normalizeImpl(NetworkTemplate template,
+            List<String[]> mergedList) {
         // Now there are several types of network which uses SubscriberId to store network
         // information. For instances:
         // The TYPE_WIFI with subscriberId means that it is a merged carrier wifi network.
@@ -847,18 +988,21 @@
 
         if (CollectionUtils.isEmpty(template.mMatchSubscriberIds)) return template;
 
-        if (CollectionUtils.contains(merged, template.mMatchSubscriberIds[0])) {
-            // Requested template subscriber is part of the merge group; return
-            // a template that matches all merged subscribers.
-            final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
-            // TODO: Use NetworkTemplate.Builder to build a template after NetworkTemplate
-            // could handle incompatible subscriberIds. See b/217805241.
-            return new NetworkTemplate(template.mMatchRule, merged,
-                    CollectionUtils.isEmpty(matchWifiNetworkKeys)
-                            ? new String[0] : new String[] { matchWifiNetworkKeys[0] },
-                    (template.mMatchRule == MATCH_MOBILE || template.mMatchRule == MATCH_CARRIER)
-                            ? METERED_YES : METERED_ALL,
-                    ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL);
+        for (String[] merged : mergedList) {
+            if (CollectionUtils.contains(merged, template.mMatchSubscriberIds[0])) {
+                // Requested template subscriber is part of the merge group; return
+                // a template that matches all merged subscribers.
+                final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
+                // TODO: Use NetworkTemplate.Builder to build a template after NetworkTemplate
+                // could handle incompatible subscriberIds. See b/217805241.
+                return new NetworkTemplate(template.mMatchRule, merged,
+                        CollectionUtils.isEmpty(matchWifiNetworkKeys)
+                                ? new String[0] : new String[] { matchWifiNetworkKeys[0] },
+                        (template.mMatchRule == MATCH_MOBILE
+                                || template.mMatchRule == MATCH_CARRIER)
+                                ? METERED_YES : METERED_ALL,
+                        ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL);
+            }
         }
 
         return template;
diff --git a/service-t/native/libs/libnetworkstats/NetworkTracePollerTest.cpp b/service-t/native/libs/libnetworkstats/NetworkTracePollerTest.cpp
index 725cec1..df07bbe 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTracePollerTest.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTracePollerTest.cpp
@@ -26,6 +26,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <thread>
 #include <vector>
 
 #include "netdbpf/NetworkTracePoller.h"
@@ -130,16 +132,21 @@
 
 TEST_F(NetworkTracePollerTest, TraceTcpSession) {
   __be16 server_port = 0;
-  std::vector<PacketTrace> packets;
+  std::vector<PacketTrace> packets, unmatched;
 
   // Record all packets with the bound address and current uid. This callback is
   // involked only within ConsumeAll, at which point the port should have
   // already been filled in and all packets have been processed.
   NetworkTracePoller handler([&](const std::vector<PacketTrace>& pkts) {
     for (const PacketTrace& pkt : pkts) {
-      if (pkt.sport != server_port && pkt.dport != server_port) return;
-      if (pkt.uid != getuid()) return;
-      packets.push_back(pkt);
+      if ((pkt.sport == server_port || pkt.dport == server_port) &&
+          pkt.uid == getuid()) {
+        packets.push_back(pkt);
+      } else {
+        // There may be spurious packets not caused by the test. These are only
+        // captured so that we can report them to help debug certain errors.
+        unmatched.push_back(pkt);
+      }
     }
   });
 
@@ -179,7 +186,13 @@
     EXPECT_EQ(std::string(data), std::string(buff));
   }
 
-  ASSERT_TRUE(handler.ConsumeAll());
+  // Poll until we get all the packets (typically we get it first try).
+  for (int attempt = 0; attempt < 10; attempt++) {
+    ASSERT_TRUE(handler.ConsumeAll());
+    if (packets.size() >= 12) break;
+    std::this_thread::sleep_for(std::chrono::milliseconds(5));
+  }
+
   ASSERT_TRUE(handler.Stop());
 
   // There are 12 packets in total (6 messages: each seen by client & server):
@@ -189,7 +202,9 @@
   // 4. Client sends data with psh ack
   // 5. Server acks the data packet
   // 6. Client closes connection with fin ack
-  ASSERT_EQ(packets.size(), 12) << PacketPrinter{packets};
+  ASSERT_EQ(packets.size(), 12)
+      << PacketPrinter{packets}
+      << "\nUnmatched packets: " << PacketPrinter{unmatched};
 
   // All packets should be TCP packets.
   EXPECT_THAT(packets, Each(Field(&PacketTrace::ipProto, Eq(IPPROTO_TCP))));
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 743f946..1d9a24c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -211,6 +211,12 @@
             tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
 
             mHandler.post(mNetlinkMonitor::stop);
+            // Clear all saved status.
+            mActiveNetworksLinkProperties.clear();
+            mNetworkSockets.clear();
+            mTetherInterfaceSockets.clear();
+            mLocalOnlyInterfaces.clear();
+            mTetheredInterfaces.clear();
             mMonitoringSockets = false;
         }
     }
diff --git a/service/libconnectivity/src/connectivity_native.cpp b/service/libconnectivity/src/connectivity_native.cpp
index 9545ed1..a476498 100644
--- a/service/libconnectivity/src/connectivity_native.cpp
+++ b/service/libconnectivity/src/connectivity_native.cpp
@@ -23,8 +23,8 @@
 
 
 static std::shared_ptr<IConnectivityNative> getBinder() {
-    static ndk::SpAIBinder sBinder = ndk::SpAIBinder(reinterpret_cast<AIBinder*>(
-        AServiceManager_getService("connectivity_native")));
+    ndk::SpAIBinder sBinder = ndk::SpAIBinder(reinterpret_cast<AIBinder*>(
+        AServiceManager_checkService("connectivity_native")));
     return aidl::android::net::connectivity::aidl::IConnectivityNative::fromBinder(sBinder);
 }
 
@@ -45,21 +45,33 @@
 
 int AConnectivityNative_blockPortForBind(in_port_t port) {
     std::shared_ptr<IConnectivityNative> c = getBinder();
+    if (!c) {
+        return EAGAIN;
+    }
     return getErrno(c->blockPortForBind(port));
 }
 
 int AConnectivityNative_unblockPortForBind(in_port_t port) {
     std::shared_ptr<IConnectivityNative> c = getBinder();
+    if (!c) {
+        return EAGAIN;
+    }
     return getErrno(c->unblockPortForBind(port));
 }
 
 int AConnectivityNative_unblockAllPortsForBind() {
     std::shared_ptr<IConnectivityNative> c = getBinder();
+    if (!c) {
+        return EAGAIN;
+    }
     return getErrno(c->unblockAllPortsForBind());
 }
 
 int AConnectivityNative_getPortsBlockedForBind(in_port_t *ports, size_t *count) {
     std::shared_ptr<IConnectivityNative> c = getBinder();
+    if (!c) {
+        return EAGAIN;
+    }
     std::vector<int32_t> actualBlockedPorts;
     int err = getErrno(c->getPortsBlockedForBind(&actualBlockedPorts));
     if (err) {
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
index 2e79182..37dc7a0 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -99,7 +99,8 @@
             }
             case TYPE_COMPONENT_EXPEDITED_JOB: {
                 final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
-                if ((capabilities & ActivityManager.PROCESS_CAPABILITY_NETWORK) == 0) {
+                if ((capabilities
+                        & ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) == 0) {
                     observer.onNetworkStateChecked(
                             INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES,
                             "Unexpected capabilities: " + capabilities);
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 79987e6..dd9177ee 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -48,6 +48,7 @@
 
 import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
 import static com.android.server.connectivity.Vpn.AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
+import static com.android.server.connectivity.Vpn.DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC;
 import static com.android.server.connectivity.Vpn.DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
 import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_AUTO;
 import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV4_UDP;
@@ -1860,6 +1861,13 @@
 
     private PlatformVpnSnapshot verifySetupPlatformVpn(VpnProfile vpnProfile,
             IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6) throws Exception {
+        return verifySetupPlatformVpn(vpnProfile, ikeConfig, mtuSupportsIpv6,
+                false /* areLongLivedTcpConnectionsExpensive */);
+    }
+
+    private PlatformVpnSnapshot verifySetupPlatformVpn(VpnProfile vpnProfile,
+            IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6,
+            boolean areLongLivedTcpConnectionsExpensive) throws Exception {
         if (!mtuSupportsIpv6) {
             doReturn(IPV6_MIN_MTU - 1).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(),
                     anyBoolean());
@@ -1942,8 +1950,10 @@
 
         // Check if allowBypass is set or not.
         assertTrue(nacCaptor.getValue().isBypassableVpn());
-        assertTrue(((VpnTransportInfo) ncCaptor.getValue().getTransportInfo()).isBypassable());
-
+        final VpnTransportInfo info = (VpnTransportInfo) ncCaptor.getValue().getTransportInfo();
+        assertTrue(info.isBypassable());
+        assertEquals(areLongLivedTcpConnectionsExpensive,
+                info.areLongLivedTcpConnectionsExpensive());
         return new PlatformVpnSnapshot(vpn, nwCb, ikeCb, childCb);
     }
 
@@ -2069,7 +2079,8 @@
         final PlatformVpnSnapshot vpnSnapShot =
                 verifySetupPlatformVpn(profile,
                         createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
-                        false /* mtuSupportsIpv6 */);
+                        false /* mtuSupportsIpv6 */,
+                        expectedKeepalive < DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC);
         // Simulate a new network coming up
         vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
         verify(mIkeSessionWrapper, never()).setNetwork(any(), anyInt(), anyInt(), anyInt());
@@ -2116,7 +2127,9 @@
                 PREFERRED_IKE_PROTOCOL_IPV4_UDP,
                 AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_AUTO /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
+                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+                false /* expectedReadFromCarrierConfig*/,
+                true /* areLongLivedTcpConnectionsExpensive */);
     }
 
     @Test
@@ -2126,7 +2139,9 @@
                 PREFERRED_IKE_PROTOCOL_IPV4_UDP,
                 AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_AUTO /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
+                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+                false /* expectedReadFromCarrierConfig*/,
+                true /* areLongLivedTcpConnectionsExpensive */);
     }
 
     @Test
@@ -2136,7 +2151,9 @@
                 PREFERRED_IKE_PROTOCOL_AUTO,
                 TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_AUTO /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
+                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+                true /* expectedReadFromCarrierConfig*/,
+                false /* areLongLivedTcpConnectionsExpensive */);
     }
 
     @Test
@@ -2150,7 +2167,9 @@
                 PREFERRED_IKE_PROTOCOL_IPV4_UDP,
                 AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_AUTO /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
+                ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+                false /* expectedReadFromCarrierConfig*/,
+                true /* areLongLivedTcpConnectionsExpensive */);
     }
 
     @Test
@@ -2160,7 +2179,9 @@
                 PREFERRED_IKE_PROTOCOL_IPV4_UDP,
                 TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_IPV4 /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_UDP /* expectedEncapType */);
+                ESP_ENCAP_TYPE_UDP /* expectedEncapType */,
+                true /* expectedReadFromCarrierConfig*/,
+                false /* areLongLivedTcpConnectionsExpensive */);
     }
 
     @Test
@@ -2170,7 +2191,9 @@
                 PREFERRED_IKE_PROTOCOL_IPV6_ESP,
                 TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_IPV6 /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_NONE /* expectedEncapType */);
+                ESP_ENCAP_TYPE_NONE /* expectedEncapType */,
+                true /* expectedReadFromCarrierConfig*/,
+                false /* areLongLivedTcpConnectionsExpensive */);
     }
 
     @Test
@@ -2180,7 +2203,9 @@
                 PREFERRED_IKE_PROTOCOL_IPV6_UDP,
                 TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
                 ESP_IP_VERSION_IPV6 /* expectedIpVersion */,
-                ESP_ENCAP_TYPE_UDP /* expectedEncapType */);
+                ESP_ENCAP_TYPE_UDP /* expectedEncapType */,
+                true /* expectedReadFromCarrierConfig*/,
+                false /* areLongLivedTcpConnectionsExpensive */);
     }
 
     private NetworkCapabilities createTestCellNc() {
@@ -2193,7 +2218,9 @@
     }
 
     private void doTestReadCarrierConfig(NetworkCapabilities nc, int simState, int preferredIpProto,
-            int expectedKeepaliveTimer, int expectedIpVersion, int expectedEncapType)
+            int expectedKeepaliveTimer, int expectedIpVersion, int expectedEncapType,
+            boolean expectedReadFromCarrierConfig,
+            boolean areLongLivedTcpConnectionsExpensive)
             throws Exception {
         final Ikev2VpnProfile ikeProfile =
                 new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
@@ -2206,7 +2233,8 @@
         final PlatformVpnSnapshot vpnSnapShot =
                 verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
                         createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
-                        false /* mtuSupportsIpv6 */);
+                        false /* mtuSupportsIpv6 */,
+                        true /* areLongLivedTcpConnectionsExpensive */);
 
         final CarrierConfigManager.CarrierConfigChangeListener listener =
                 getCarrierConfigListener();
@@ -2221,15 +2249,31 @@
         vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2, nc);
         verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2,
                 expectedIpVersion, expectedEncapType, expectedKeepaliveTimer);
+        if (expectedReadFromCarrierConfig) {
+            final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+                    ArgumentCaptor.forClass(NetworkCapabilities.class);
+            verify(mMockNetworkAgent).doSendNetworkCapabilities(ncCaptor.capture());
+
+            final VpnTransportInfo info =
+                    (VpnTransportInfo) ncCaptor.getValue().getTransportInfo();
+            assertEquals(areLongLivedTcpConnectionsExpensive,
+                    info.areLongLivedTcpConnectionsExpensive());
+        } else {
+            verify(mMockNetworkAgent, never()).doSendNetworkCapabilities(any());
+        }
 
         reset(mExecutor);
         reset(mIkeSessionWrapper);
+        reset(mMockNetworkAgent);
 
         // Trigger carrier config change
         listener.onCarrierConfigChanged(1 /* logicalSlotIndex */, TEST_SUB_ID,
                 -1 /* carrierId */, -1 /* specificCarrierId */);
         verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2,
                 expectedIpVersion, expectedEncapType, expectedKeepaliveTimer);
+        // Expect no NetworkCapabilities change.
+        // Call to doSendNetworkCapabilities() will not be triggered.
+        verify(mMockNetworkAgent, never()).doSendNetworkCapabilities(any());
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index b9cb255..6e1debe 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
@@ -101,7 +102,7 @@
             doCallRealMethod().when(mContext).getSystemService(TetheringManager.class);
         }
         doReturn(true).when(mDeps).canScanOnInterface(any());
-        doReturn(mTestNetworkIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TEST_IFACE_NAME);
+        doReturn(mTestNetworkIfaceWrapper).when(mDeps).getNetworkInterfaceByName(anyString());
         doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
                 .getNetworkInterfaceByName(LOCAL_ONLY_IFACE_NAME);
         doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
@@ -354,4 +355,55 @@
         verify(mCm, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
         verify(mTm, times(2)).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
     }
+
+    @Test
+    public void testLinkPropertiesAreClearedAfterStopMonitoringSockets() {
+        startMonitoringSockets();
+
+        // Request a socket with null network.
+        final TestSocketCallback testCallback = new TestSocketCallback();
+        mHandler.post(() -> mSocketProvider.requestSocket(null, testCallback));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        testCallback.expectedNoCallback();
+
+        // Notify a LinkPropertiesChanged with TEST_NETWORK.
+        final LinkProperties testLp = new LinkProperties();
+        testLp.setInterfaceName(TEST_IFACE_NAME);
+        testLp.setLinkAddresses(List.of(LINKADDRV4));
+        mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
+
+        // Try to stop monitoring and unrequest the socket.
+        mHandler.post(mSocketProvider::requestStopWhenInactive);
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        testCallback.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
+        verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
+        verify(mTm, times(1)).unregisterTetheringEventCallback(any());
+
+        // Start sockets monitoring and request a socket again. Expected no socket created callback
+        // because all saved LinkProperties has been cleared.
+        mHandler.post(mSocketProvider::startMonitoringSockets);
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        verify(mCm, times(2)).registerNetworkCallback(any(), any(NetworkCallback.class), any());
+        verify(mTm, times(2)).registerTetheringEventCallback(
+                any(), any(TetheringEventCallback.class));
+        mHandler.post(() -> mSocketProvider.requestSocket(null, testCallback));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        testCallback.expectedNoCallback();
+
+        // Notify a LinkPropertiesChanged with another network.
+        final LinkProperties otherLp = new LinkProperties();
+        final LinkAddress otherAddress = new LinkAddress("192.0.2.1/24");
+        final Network otherNetwork = new Network(456);
+        otherLp.setInterfaceName("test2");
+        otherLp.setLinkAddresses(List.of(otherAddress));
+        mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(otherNetwork, otherLp));
+        HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+        verify(mTestNetworkIfaceWrapper, times(2)).getNetworkInterface();
+        testCallback.expectedSocketCreatedForNetwork(otherNetwork, List.of(otherAddress));
+    }
 }
diff --git a/tools/gen_jarjar.py b/tools/gen_jarjar.py
index eb686ce..5129128 100755
--- a/tools/gen_jarjar.py
+++ b/tools/gen_jarjar.py
@@ -120,9 +120,11 @@
                         _get_toplevel_class(clazz) not in excluded_classes and
                         not any(r.fullmatch(clazz) for r in exclude_regexes)):
                     outfile.write(f'rule {clazz} {args.prefix}.@0\n')
-                    # Also include jarjar rules for unit tests of the class, so the package matches
-                    outfile.write(f'rule {clazz}Test {args.prefix}.@0\n')
-                    outfile.write(f'rule {clazz}Test$* {args.prefix}.@0\n')
+                    # Also include jarjar rules for unit tests of the class if it's not explicitly
+                    # excluded, so the package matches
+                    if not any(r.fullmatch(clazz + 'Test') for r in exclude_regexes):
+                        outfile.write(f'rule {clazz}Test {args.prefix}.@0\n')
+                        outfile.write(f'rule {clazz}Test$* {args.prefix}.@0\n')
 
 
 def _main():
diff --git a/tools/gn2bp/Android.bp.swp b/tools/gn2bp/Android.bp.swp
index 19901fa..21482d9 100644
--- a/tools/gn2bp/Android.bp.swp
+++ b/tools/gn2bp/Android.bp.swp
@@ -21,6 +21,9 @@
     default_applicable_licenses: [
         "external_cronet_license",
     ],
+    default_visibility: [
+        ":__subpackages__",
+    ],
 }
 
 // GN: //components/cronet/android:cronet_api_java
@@ -52,6 +55,9 @@
         "components/cronet/android/api/src/android/net/http/UrlRequest.java",
         "components/cronet/android/api/src/android/net/http/UrlResponseInfo.java",
     ],
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
 }
 
 // GN: //base/allocator:buildflags
@@ -989,7 +995,6 @@
         "base/allocator/dispatcher/dispatcher.cc",
         "base/allocator/dispatcher/internal/dispatch_data.cc",
         "base/allocator/dispatcher/reentry_guard.cc",
-        "base/allocator/partition_allocator/shim/allocator_shim.cc",
         "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
         "base/android/android_hardware_buffer_compat.cc",
         "base/android/android_image_reader_compat.cc",
@@ -1561,6 +1566,11 @@
         "-Wl,-wrap,vasprintf",
     ],
     target: {
+        android: {
+            srcs: [
+                "base/allocator/partition_allocator/shim/allocator_shim.cc",
+            ],
+        },
         android_arm: {
             srcs: [
                 "base/android/reached_code_profiler.cc",
@@ -1600,6 +1610,11 @@
                 "-msse3",
             ],
         },
+        glibc: {
+            srcs: [
+                "base/allocator/partition_allocator/shim/allocator_shim.cc",
+            ],
+        },
     },
 }
 
@@ -1660,7 +1675,6 @@
         "base/allocator/dispatcher/dispatcher.cc",
         "base/allocator/dispatcher/internal/dispatch_data.cc",
         "base/allocator/dispatcher/reentry_guard.cc",
-        "base/allocator/partition_allocator/shim/allocator_shim.cc",
         "base/at_exit.cc",
         "base/barrier_closure.cc",
         "base/base64.cc",
@@ -2129,6 +2143,9 @@
     ],
     target: {
         android: {
+            srcs: [
+                "base/allocator/partition_allocator/shim/allocator_shim.cc",
+            ],
             shared_libs: [
                 "libandroid",
                 "liblog",
@@ -2609,9 +2626,14 @@
                 "-Wl,-wrap,vasprintf",
             ],
         },
+        glibc: {
+            srcs: [
+                "base/allocator/partition_allocator/shim/allocator_shim.cc",
+                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc",
+            ],
+        },
         host: {
             srcs: [
-                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc",
                 "base/base_paths_posix.cc",
                 "base/debug/stack_trace_posix.cc",
                 "base/files/file_util_linux.cc",
@@ -6554,6 +6576,166 @@
         "-Wl,-wrap,vasprintf",
     ],
     stem: "libcronet.108.0.5359.128",
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
+    target: {
+        android_arm: {
+            cflags: [
+                "-fstack-protector",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-fstack-protector",
+                "-mno-outline",
+                "-mno-outline-atomics",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-fstack-protector",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/android:cronet__testing
+cc_library_shared {
+    name: "cronet_aml_components_cronet_android_cronet__testing",
+    srcs: [
+        ":cronet_aml_buildtools_third_party_libc___libc____testing",
+        ":cronet_aml_buildtools_third_party_libc__abi_libc__abi__testing",
+        ":cronet_aml_components_cronet_android_cronet_static__testing",
+        ":cronet_aml_components_cronet_cronet_common__testing",
+        ":cronet_aml_components_cronet_metrics_util__testing",
+        ":cronet_aml_components_metrics_library_support__testing",
+        "components/cronet/android/cronet_jni.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libz",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc__testing",
+        "cronet_aml_base_base__testing",
+        "cronet_aml_base_base_static__testing",
+        "cronet_aml_base_third_party_double_conversion_double_conversion__testing",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations__testing",
+        "cronet_aml_components_prefs_prefs__testing",
+        "cronet_aml_crypto_crypto__testing",
+        "cronet_aml_net_net__testing",
+        "cronet_aml_net_preload_decoder__testing",
+        "cronet_aml_net_third_party_quiche_quiche__testing",
+        "cronet_aml_net_uri_template__testing",
+        "cronet_aml_third_party_boringssl_boringssl__testing",
+        "cronet_aml_third_party_brotli_common__testing",
+        "cronet_aml_third_party_brotli_dec__testing",
+        "cronet_aml_third_party_icu_icui18n__testing",
+        "cronet_aml_third_party_icu_icuuc_private__testing",
+        "cronet_aml_third_party_libevent_libevent__testing",
+        "cronet_aml_third_party_modp_b64_modp_b64__testing",
+        "cronet_aml_third_party_protobuf_protobuf_lite__testing",
+        "cronet_aml_url_url__testing",
+    ],
+    generated_headers: [
+        "cronet_aml_base_debugging_buildflags__testing",
+        "cronet_aml_base_logging_buildflags__testing",
+        "cronet_aml_build_chromeos_buildflags__testing",
+        "cronet_aml_components_cronet_android_buildflags__testing",
+        "cronet_aml_components_cronet_android_cronet_jni_headers__testing",
+        "cronet_aml_components_cronet_android_cronet_jni_registration__testing",
+        "cronet_aml_components_cronet_cronet_buildflags__testing",
+        "cronet_aml_components_cronet_cronet_version_header_action__testing",
+        "cronet_aml_third_party_metrics_proto_metrics_proto__testing_gen_headers",
+        "cronet_aml_url_buildflags__testing",
+    ],
+    export_generated_headers: [
+        "cronet_aml_base_debugging_buildflags__testing",
+        "cronet_aml_base_logging_buildflags__testing",
+        "cronet_aml_build_chromeos_buildflags__testing",
+        "cronet_aml_components_cronet_android_buildflags__testing",
+        "cronet_aml_components_cronet_android_cronet_jni_headers__testing",
+        "cronet_aml_components_cronet_android_cronet_jni_registration__testing",
+        "cronet_aml_components_cronet_cronet_buildflags__testing",
+        "cronet_aml_components_cronet_cronet_version_header_action__testing",
+        "cronet_aml_third_party_metrics_proto_metrics_proto__testing_gen_headers",
+        "cronet_aml_url_buildflags__testing",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
+        "-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNDEBUG",
+        "-DNO_UNWIND_TABLES",
+        "-DNVALGRIND",
+        "-DOFFICIAL_BUILD",
+        "-D_FORTIFY_SOURCE=2",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+        "-Oz",
+        "-fdata-sections",
+        "-ffunction-sections",
+        "-fno-asynchronous-unwind-tables",
+        "-fno-unwind-tables",
+        "-fvisibility-inlines-hidden",
+        "-fvisibility=hidden",
+        "-g1",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++17",
+    ldflags: [
+        "-Wl,--as-needed",
+        "-Wl,--gc-sections",
+        "-Wl,--icf=all",
+        "-Wl,--script,external/cronet/base/android/library_loader/anchor_functions.lds",
+        "-Wl,-wrap,asprintf",
+        "-Wl,-wrap,calloc",
+        "-Wl,-wrap,free",
+        "-Wl,-wrap,getcwd",
+        "-Wl,-wrap,malloc",
+        "-Wl,-wrap,malloc_usable_size",
+        "-Wl,-wrap,memalign",
+        "-Wl,-wrap,posix_memalign",
+        "-Wl,-wrap,pvalloc",
+        "-Wl,-wrap,realloc",
+        "-Wl,-wrap,realpath",
+        "-Wl,-wrap,strdup",
+        "-Wl,-wrap,strndup",
+        "-Wl,-wrap,valloc",
+        "-Wl,-wrap,vasprintf",
+    ],
     target: {
         android_arm: {
             cflags: [
@@ -7948,6 +8130,283 @@
     },
 }
 
+// GN: //components/cronet/android:cronet_tests__testing
+cc_library_shared {
+    name: "cronet_aml_components_cronet_android_cronet_tests__testing",
+    srcs: [
+        ":cronet_aml_buildtools_third_party_libc___libc____testing",
+        ":cronet_aml_buildtools_third_party_libc__abi_libc__abi__testing",
+        ":cronet_aml_components_cronet_cronet_common__testing",
+        ":cronet_aml_components_cronet_testing_test_support__testing",
+        ":cronet_aml_components_metrics_library_support__testing",
+        ":cronet_aml_net_simple_quic_tools__testing",
+        "components/cronet/android/test/cronet_test_jni.cc",
+        "components/cronet/android/test/cronet_test_util.cc",
+        "components/cronet/android/test/cronet_url_request_context_config_test.cc",
+        "components/cronet/android/test/cronet_url_request_test.cc",
+        "components/cronet/android/test/experimental_options_test.cc",
+        "components/cronet/android/test/mock_cert_verifier.cc",
+        "components/cronet/android/test/mock_url_request_job_factory.cc",
+        "components/cronet/android/test/native_test_server.cc",
+        "components/cronet/android/test/quic_test_server.cc",
+        "components/cronet/android/test/test_upload_data_stream_handler.cc",
+        "components/cronet/android/test/url_request_intercepting_job_factory.cc",
+    ],
+    shared_libs: [
+        "cronet_aml_components_cronet_android_cronet__testing",
+        "libandroid",
+        "liblog",
+        "libz",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc__testing",
+        "cronet_aml_base_base__testing",
+        "cronet_aml_base_base_static__testing",
+        "cronet_aml_base_i18n__testing",
+        "cronet_aml_base_test_test_config__testing",
+        "cronet_aml_base_test_test_support__testing",
+        "cronet_aml_base_third_party_double_conversion_double_conversion__testing",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations__testing",
+        "cronet_aml_components_prefs_prefs__testing",
+        "cronet_aml_crypto_crypto__testing",
+        "cronet_aml_net_gtest_util__testing",
+        "cronet_aml_net_net__testing",
+        "cronet_aml_net_preload_decoder__testing",
+        "cronet_aml_net_test_support__testing",
+        "cronet_aml_net_third_party_quiche_quiche__testing",
+        "cronet_aml_net_third_party_quiche_quiche_tool_support__testing",
+        "cronet_aml_net_uri_template__testing",
+        "cronet_aml_testing_gtest_gtest__testing",
+        "cronet_aml_third_party_boringssl_boringssl__testing",
+        "cronet_aml_third_party_brotli_common__testing",
+        "cronet_aml_third_party_brotli_dec__testing",
+        "cronet_aml_third_party_ced_ced__testing",
+        "cronet_aml_third_party_icu_icui18n__testing",
+        "cronet_aml_third_party_icu_icuuc_private__testing",
+        "cronet_aml_third_party_libevent_libevent__testing",
+        "cronet_aml_third_party_libxml_libxml__testing",
+        "cronet_aml_third_party_libxml_libxml_utils__testing",
+        "cronet_aml_third_party_libxml_xml_reader__testing",
+        "cronet_aml_third_party_modp_b64_modp_b64__testing",
+        "cronet_aml_third_party_protobuf_protobuf_lite__testing",
+        "cronet_aml_url_url__testing",
+    ],
+    generated_headers: [
+        "cronet_aml_base_debugging_buildflags__testing",
+        "cronet_aml_base_logging_buildflags__testing",
+        "cronet_aml_build_chromeos_buildflags__testing",
+        "cronet_aml_components_cronet_android_cronet_tests_jni_headers__testing",
+        "cronet_aml_components_cronet_cronet_buildflags__testing",
+        "cronet_aml_components_cronet_cronet_version_header_action__testing",
+        "cronet_aml_third_party_metrics_proto_metrics_proto__testing_gen_headers",
+    ],
+    export_generated_headers: [
+        "cronet_aml_base_debugging_buildflags__testing",
+        "cronet_aml_base_logging_buildflags__testing",
+        "cronet_aml_build_chromeos_buildflags__testing",
+        "cronet_aml_components_cronet_android_cronet_tests_jni_headers__testing",
+        "cronet_aml_components_cronet_cronet_buildflags__testing",
+        "cronet_aml_components_cronet_cronet_version_header_action__testing",
+        "cronet_aml_third_party_metrics_proto_metrics_proto__testing_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
+        "-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DGTEST_API_=",
+        "-DGTEST_HAS_ABSL=1",
+        "-DGTEST_HAS_POSIX_RE=0",
+        "-DGTEST_HAS_TR1_TUPLE=0",
+        "-DGTEST_LANG_CXX11=1",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+        "-DNDEBUG",
+        "-DNO_UNWIND_TABLES",
+        "-DNVALGRIND",
+        "-DOFFICIAL_BUILD",
+        "-DUNIT_TEST",
+        "-DUSE_CHROMIUM_ICU=1",
+        "-DUSE_REMOTE_TEST_SERVER",
+        "-DU_ENABLE_DYLOAD=0",
+        "-DU_ENABLE_RESOURCE_TRACING=0",
+        "-DU_ENABLE_TRACING=1",
+        "-DU_STATIC_IMPLEMENTATION",
+        "-DU_USING_ICU_NAMESPACE=0",
+        "-D_FORTIFY_SOURCE=2",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+        "-Oz",
+        "-fdata-sections",
+        "-ffunction-sections",
+        "-fno-asynchronous-unwind-tables",
+        "-fno-unwind-tables",
+        "-fvisibility-inlines-hidden",
+        "-fvisibility=hidden",
+        "-g1",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/ced/src/",
+        "third_party/googletest/custom/",
+        "third_party/googletest/src/googlemock/include/",
+        "third_party/googletest/src/googletest/include/",
+        "third_party/icu/source/common/",
+        "third_party/icu/source/i18n/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++17",
+    ldflags: [
+        "-Wl,--as-needed",
+        "-Wl,--gc-sections",
+        "-Wl,--icf=all",
+        "-Wl,--script,external/cronet/base/android/library_loader/anchor_functions.lds",
+        "-Wl,-wrap,asprintf",
+        "-Wl,-wrap,calloc",
+        "-Wl,-wrap,free",
+        "-Wl,-wrap,getcwd",
+        "-Wl,-wrap,malloc",
+        "-Wl,-wrap,malloc_usable_size",
+        "-Wl,-wrap,memalign",
+        "-Wl,-wrap,posix_memalign",
+        "-Wl,-wrap,pvalloc",
+        "-Wl,-wrap,realloc",
+        "-Wl,-wrap,realpath",
+        "-Wl,-wrap,strdup",
+        "-Wl,-wrap,strndup",
+        "-Wl,-wrap,valloc",
+        "-Wl,-wrap,vasprintf",
+    ],
+    stem: "libcronet_tests",
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
+    target: {
+        android_arm: {
+            cflags: [
+                "-fstack-protector",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-fstack-protector",
+                "-mno-outline",
+                "-mno-outline-atomics",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-fstack-protector",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/android:cronet_tests_jni_headers__testing
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_tests_jni_headers__testing",
+    srcs: [
+        "components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
+        "components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java",
+        "components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java",
+        "components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java",
+        "components/cronet/android/test/src/org/chromium/net/MockCertVerifier.java",
+        "components/cronet/android/test/src/org/chromium/net/MockUrlRequestJobFactory.java",
+        "components/cronet/android/test/src/org/chromium/net/NativeTestServer.java",
+        "components/cronet/android/test/src/org/chromium/net/QuicTestServer.java",
+        "components/cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/cronet/android/cronet_tests_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "CronetUrlRequestContextTest_jni.h " +
+         "--output_name " +
+         "CronetUrlRequestTest_jni.h " +
+         "--output_name " +
+         "ExperimentalOptionsTest_jni.h " +
+         "--output_name " +
+         "CronetTestUtil_jni.h " +
+         "--output_name " +
+         "MockCertVerifier_jni.h " +
+         "--output_name " +
+         "MockUrlRequestJobFactory_jni.h " +
+         "--output_name " +
+         "NativeTestServer_jni.h " +
+         "--output_name " +
+         "QuicTestServer_jni.h " +
+         "--output_name " +
+         "TestUploadDataStreamHandler_jni.h " +
+         "--input_file " +
+         "$(location components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/src/org/chromium/net/MockCertVerifier.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/src/org/chromium/net/MockUrlRequestJobFactory.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/src/org/chromium/net/NativeTestServer.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/src/org/chromium/net/QuicTestServer.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java)",
+    out: [
+        "components/cronet/android/cronet_tests_jni_headers/CronetTestUtil_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/CronetUrlRequestContextTest_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/CronetUrlRequestTest_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/ExperimentalOptionsTest_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/MockCertVerifier_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/MockUrlRequestJobFactory_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/NativeTestServer_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/QuicTestServer_jni.h",
+        "components/cronet/android/cronet_tests_jni_headers/TestUploadDataStreamHandler_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
 // GN: //components/cronet/android:cronet_unittests_android__library__testing
 cc_library_shared {
     name: "cronet_aml_components_cronet_android_cronet_unittests_android__library__testing",
@@ -8116,6 +8575,9 @@
         "-Wl,-wrap,vasprintf",
     ],
     stem: "libcronet_unittests_android__library",
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
     target: {
         android_arm: {
             cflags: [
@@ -9045,6 +9507,142 @@
     },
 }
 
+// GN: //components/cronet/testing:test_support__testing
+cc_object {
+    name: "cronet_aml_components_cronet_testing_test_support__testing",
+    srcs: [
+        "components/cronet/testing/test_server/test_server.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libz",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc__testing",
+        "cronet_aml_base_base__testing",
+        "cronet_aml_base_base_static__testing",
+        "cronet_aml_base_i18n__testing",
+        "cronet_aml_base_test_test_config__testing",
+        "cronet_aml_base_test_test_support__testing",
+        "cronet_aml_base_third_party_double_conversion_double_conversion__testing",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations__testing",
+        "cronet_aml_crypto_crypto__testing",
+        "cronet_aml_net_gtest_util__testing",
+        "cronet_aml_net_net__testing",
+        "cronet_aml_net_preload_decoder__testing",
+        "cronet_aml_net_test_support__testing",
+        "cronet_aml_net_third_party_quiche_quiche__testing",
+        "cronet_aml_net_third_party_quiche_quiche_tool_support__testing",
+        "cronet_aml_net_uri_template__testing",
+        "cronet_aml_testing_gtest_gtest__testing",
+        "cronet_aml_third_party_boringssl_boringssl__testing",
+        "cronet_aml_third_party_brotli_common__testing",
+        "cronet_aml_third_party_brotli_dec__testing",
+        "cronet_aml_third_party_ced_ced__testing",
+        "cronet_aml_third_party_icu_icui18n__testing",
+        "cronet_aml_third_party_icu_icuuc_private__testing",
+        "cronet_aml_third_party_libevent_libevent__testing",
+        "cronet_aml_third_party_libxml_libxml__testing",
+        "cronet_aml_third_party_libxml_libxml_utils__testing",
+        "cronet_aml_third_party_libxml_xml_reader__testing",
+        "cronet_aml_third_party_modp_b64_modp_b64__testing",
+        "cronet_aml_third_party_protobuf_protobuf_lite__testing",
+        "cronet_aml_url_url__testing",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
+        "-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DGTEST_API_=",
+        "-DGTEST_HAS_ABSL=1",
+        "-DGTEST_HAS_POSIX_RE=0",
+        "-DGTEST_HAS_TR1_TUPLE=0",
+        "-DGTEST_LANG_CXX11=1",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+        "-DNDEBUG",
+        "-DNO_UNWIND_TABLES",
+        "-DNVALGRIND",
+        "-DOFFICIAL_BUILD",
+        "-DUNIT_TEST",
+        "-DUSE_CHROMIUM_ICU=1",
+        "-DUSE_REMOTE_TEST_SERVER",
+        "-DU_ENABLE_DYLOAD=0",
+        "-DU_ENABLE_RESOURCE_TRACING=0",
+        "-DU_ENABLE_TRACING=1",
+        "-DU_STATIC_IMPLEMENTATION",
+        "-DU_USING_ICU_NAMESPACE=0",
+        "-D_FORTIFY_SOURCE=2",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+        "-Oz",
+        "-fdata-sections",
+        "-ffunction-sections",
+        "-fno-asynchronous-unwind-tables",
+        "-fno-unwind-tables",
+        "-fvisibility-inlines-hidden",
+        "-fvisibility=hidden",
+        "-g1",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/ced/src/",
+        "third_party/googletest/custom/",
+        "third_party/googletest/src/googlemock/include/",
+        "third_party/googletest/src/googletest/include/",
+        "third_party/icu/source/common/",
+        "third_party/icu/source/i18n/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++17",
+    target: {
+        android_arm: {
+            cflags: [
+                "-fstack-protector",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-fstack-protector",
+                "-mno-outline",
+                "-mno-outline-atomics",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-fstack-protector",
+                "-msse3",
+            ],
+        },
+    },
+}
+
 // GN: //components/metrics:library_support
 cc_object {
     name: "cronet_aml_components_metrics_library_support",
@@ -10298,6 +10896,7 @@
 cc_defaults {
     name: "cronet_aml_defaults",
     cflags: [
+        "-DBORINGSSL_SHARED_LIBRARY",
         "-DGOOGLE_PROTOBUF_NO_RTTI",
         "-Wno-ambiguous-reversed-operator",
         "-Wno-c++11-narrowing",
@@ -10318,6 +10917,7 @@
         "com.android.tethering",
     ],
     min_sdk_version: "29",
+    c_std: "gnu11",
     target: {
         android: {
             shared_libs: [
@@ -10612,6 +11212,9 @@
         "-Aorg.chromium.chrome.skipGenJni",
         "-Apackage_prefix=android.net.http.internal",
     ],
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
 }
 
 // GN: //gn:java
@@ -10889,6 +11492,9 @@
     javacflags: [
         "-Aorg.chromium.chrome.skipGenJni",
     ],
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
 }
 
 // GN: //base/android/jni_generator:jni_processor
@@ -15576,6 +16182,9 @@
         "-Wl,-wrap,vasprintf",
     ],
     stem: "libnet_unittests__library",
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
     target: {
         android_arm: {
             cflags: [
@@ -16656,10 +17265,6 @@
         "net/third_party/quiche/src/quiche/quic/core/proto/crypto_server_config.proto",
         "net/third_party/quiche/src/quiche/quic/core/proto/source_address_token.proto",
     ],
-    shared_libs: [
-        "//external/cronet/third_party/boringssl:libcrypto",
-        "//external/cronet/third_party/boringssl:libssl",
-    ],
     tools: [
         "cronet_aml_third_party_protobuf_protoc",
     ],
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
index b55b9bf..0f12cf3 100755
--- a/tools/gn2bp/gen_android_bp
+++ b/tools/gn2bp/gen_android_bp
@@ -50,6 +50,7 @@
 DEFAULT_TESTS = [
   '//components/cronet/android:cronet_unittests_android__library',
   '//net:net_unittests__library',
+  '//components/cronet/android:cronet_tests',
 ]
 
 EXTRAS_ANDROID_BP_FILE = "Android.extras.bp"
@@ -235,6 +236,9 @@
     module.arch[arch].shared_libs.add('libz')
 
 def enable_boringssl(module, arch):
+  # Do not add boringssl targets to cc_genrules. This happens, because protobuf targets are
+  # originally static_libraries, but later get converted to a cc_genrule.
+  if module.is_genrule(): return
   if arch is None:
     shared_libs = module.shared_libs
   else:
@@ -290,6 +294,12 @@
 # Name of cronet api target
 java_api_target_name = "//components/cronet/android:cronet_api_java"
 
+# Visibility set for package default
+package_default_visibility = ":__subpackages__"
+
+# Visibility set for modules used from Connectivity
+connectivity_visibility = "//packages/modules/Connectivity:__subpackages__"
+
 # ----------------------------------------------------------------------------
 # End of configuration.
 # ----------------------------------------------------------------------------
@@ -441,6 +451,7 @@
     self.target['android_arm'] = Target('android_arm')
     self.target['android_arm64'] = Target('android_arm64')
     self.target['host'] = Target('host')
+    self.target['glibc'] = Target('glibc')
     self.stl = None
     self.cpp_std = None
     self.dist = dict()
@@ -472,9 +483,10 @@
     self.processor_class = None
     self.sdk_version = None
     self.javacflags = set()
-    self.license_kinds = set()
-    self.license_text = set()
+    self.c_std = None
     self.default_applicable_licenses = set()
+    self.default_visibility = []
+    self.visibility = []
 
   def to_string(self, output):
     if self.comment:
@@ -531,9 +543,10 @@
     self._output_field(output, 'processor_class')
     self._output_field(output, 'sdk_version')
     self._output_field(output, 'javacflags')
-    self._output_field(output, 'license_kinds')
-    self._output_field(output, 'license_text')
+    self._output_field(output, 'c_std')
     self._output_field(output, 'default_applicable_licenses')
+    self._output_field(output, 'default_visibility')
+    self._output_field(output, 'visibility')
     if self.rtti:
       self._output_field(output, 'rtti')
 
@@ -1723,6 +1736,7 @@
       module.srcs.add(':' + create_action_module(blueprint, target, 'java_genrule', is_test_target).name)
   preprocessor_module = create_java_jni_preprocessor(blueprint)
   module.plugins.add(preprocessor_module.name)
+  module.visibility.append(connectivity_visibility)
   blueprint.add_module(module)
   return module
 
@@ -1738,6 +1752,7 @@
     ':' + create_action_module(blueprint, gn.get_target(dep), 'java_genrule', False).name
     for dep in get_api_java_actions(gn)])
   blueprint.add_module(source_module)
+  source_module.visibility.append(connectivity_visibility)
   return source_module
 
 def update_jni_registration_module(module, gn):
@@ -1746,6 +1761,28 @@
                       for source in get_non_api_java_sources(gn)
                       if source.endswith('.java')])
 
+
+def turn_off_allocator_shim_for_musl(module):
+  allocation_shim = "base/allocator/partition_allocator/shim/allocator_shim.cc"
+  allocator_shim_files = {
+    allocation_shim,
+    "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc",
+  }
+  module.srcs -= allocator_shim_files
+  for arch in module.target.values():
+    arch.srcs -= allocator_shim_files
+  module.target['android'].srcs.add(allocation_shim)
+  if gn_utils.TESTING_SUFFIX in module.name:
+    # allocator_shim_default_dispatch_to_glibc is only added to the __testing version of base
+    # since base_base__testing is compiled for host. When compiling for host. Soong compiles
+    # using glibc or musl(experimental). We currently only support compiling for glibc.
+    module.target['glibc'].srcs.update(allocator_shim_files)
+  else:
+    # allocator_shim_default_dispatch_to_glibc does not exist in the prod version of base
+    # `base_base` since this only compiles for android and bionic is used. Bionic is the equivalent
+    # of glibc but for android.
+    module.target['glibc'].srcs.add(allocation_shim)
+
 def create_blueprint_for_targets(gn, targets, test_targets):
   """Generate a blueprint for a list of GN targets."""
   blueprint = Blueprint()
@@ -1754,6 +1791,7 @@
   defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
   defaults.cflags = [
       '-DGOOGLE_PROTOBUF_NO_RTTI',
+      '-DBORINGSSL_SHARED_LIBRARY',
       '-Wno-error=return-type',
       '-Wno-non-virtual-dtor',
       '-Wno-macro-redefined',
@@ -1767,6 +1805,7 @@
       '-fPIC',
       '-Wno-c++11-narrowing',
   ]
+  defaults.c_std = 'gnu11'
   # Chromium builds do not add a dependency for headers found inside the
   # sysroot, so they are added globally via defaults.
   defaults.target['android'].header_libs = [
@@ -1790,10 +1829,14 @@
   blueprint.add_module(defaults)
 
   for target in targets:
-    create_modules_from_target(blueprint, gn, target, is_test_target=False)
+    module = create_modules_from_target(blueprint, gn, target, is_test_target=False)
+    if module:
+      module.visibility.append(connectivity_visibility)
 
   for test_target in test_targets:
-    create_modules_from_target(blueprint, gn, test_target + gn_utils.TESTING_SUFFIX, is_test_target=True)
+    module = create_modules_from_target(blueprint, gn, test_target + gn_utils.TESTING_SUFFIX, is_test_target=True)
+    if module:
+      module.visibility.append(connectivity_visibility)
 
   create_java_api_module(blueprint, gn)
   java_module = create_java_module(blueprint, gn, is_test_target=False)
@@ -1803,6 +1846,8 @@
   for module in blueprint.modules.values():
     if 'cronet_jni_registration' in module.name:
       update_jni_registration_module(module, gn)
+    if module.name in ['cronet_aml_base_base', 'cronet_aml_base_base' + gn_utils.TESTING_SUFFIX]:
+      turn_off_allocator_shim_for_musl(module)
 
   # Merge in additional hardcoded arguments.
   for module in blueprint.modules.values():
@@ -1824,11 +1869,12 @@
 
   return blueprint
 
-def create_default_license_module(blueprint):
-  default_license = Module("package", "", "PACKAGE")
-  default_license.comment = "The actual license can be found in Android.extras.bp"
-  default_license.default_applicable_licenses.add(CRONET_LICENSE_NAME)
-  blueprint.add_module(default_license)
+def create_package_module(blueprint):
+  package = Module("package", "", "PACKAGE")
+  package.comment = "The actual license can be found in Android.extras.bp"
+  package.default_applicable_licenses.add(CRONET_LICENSE_NAME)
+  package.default_visibility.append(package_default_visibility)
+  blueprint.add_module(package)
 
 def main():
   parser = argparse.ArgumentParser(
@@ -1882,7 +1928,7 @@
   # Add any proto groups to the blueprint.
   for l_name, t_names in proto_groups.items():
     create_proto_group_modules(blueprint, gn, l_name, t_names)
-  create_default_license_module(blueprint)
+  create_package_module(blueprint)
   output = [
       """// Copyright (C) 2022 The Android Open Source Project
 //