Merge "Add the IP version and encap type to IkeSessionParamsUtils."
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d043464..38413c2 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -546,7 +546,8 @@
         "api-versions-jars-dir",
     ],
     api_levels_sdk_type: "module-lib",
-    extensions_info_file: ":sdk-extensions-info",
+    // extensions_info_file is purposefully omitted, because this module should just be
+    // the non-updatable portions of the sdk, and extension sdks are updatable.
 }
 
 droidstubs {
@@ -563,7 +564,8 @@
         "api-versions-jars-dir",
     ],
     api_levels_sdk_type: "system-server",
-    extensions_info_file: ":sdk-extensions-info",
+    // extensions_info_file is purposefully omitted, because this module should just be
+    // the non-updatable portions of the sdk, and extension sdks are updatable.
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/api/Android.bp b/api/Android.bp
index fe283e1a..a2b8038 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -178,6 +178,18 @@
         "$(location :frameworks-base-api-module-lib-current.txt)",
 }
 
+genrule {
+    name: "frameworks-base-api-current.srcjar",
+    tools: ["merge_zips"],
+    out: ["current.srcjar"],
+    cmd: "$(location merge_zips) $(out) $(in)",
+    srcs: [
+        ":api-stubs-docs-non-updatable",
+        ":all-modules-public-stubs-source",
+    ],
+    visibility: ["//visibility:private"], // Used by make module in //development, mind
+}
+
 // This produces the same annotations.zip as framework-doc-stubs, but by using
 // outputs from individual modules instead of all the source code.
 genrule_defaults {
diff --git a/api/api.go b/api/api.go
index c91ff81..077ab96 100644
--- a/api/api.go
+++ b/api/api.go
@@ -147,17 +147,6 @@
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
 }
 
-func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
-	props := genruleProps{}
-	props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
-	props.Tools = []string{"merge_zips"}
-	props.Out = []string{"current.srcjar"}
-	props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
-	props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...)
-	props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
-	ctx.CreateModule(genrule.GenRuleFactory, &props)
-}
-
 func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
 	for _, i := range []struct{
 		name    string
@@ -382,8 +371,6 @@
 	}
 	createMergedTxts(ctx, bootclasspath, system_server_classpath)
 
-	createMergedStubsSrcjar(ctx, bootclasspath)
-
 	createMergedPublicStubs(ctx, bootclasspath)
 	createMergedSystemStubs(ctx, bootclasspath)
 	createMergedTestStubsForNonUpdatableModules(ctx)
diff --git a/core/api/current.txt b/core/api/current.txt
index 14dbcbe..7f35d4a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -21527,6 +21527,16 @@
     field public static final int AACObjectSSR = 3; // 0x3
     field public static final int AACObjectScalable = 6; // 0x6
     field public static final int AACObjectXHE = 42; // 0x2a
+    field public static final int AC4Level0 = 1; // 0x1
+    field public static final int AC4Level1 = 2; // 0x2
+    field public static final int AC4Level2 = 4; // 0x4
+    field public static final int AC4Level3 = 8; // 0x8
+    field public static final int AC4Level4 = 16; // 0x10
+    field public static final int AC4Profile00 = 257; // 0x101
+    field public static final int AC4Profile10 = 513; // 0x201
+    field public static final int AC4Profile11 = 514; // 0x202
+    field public static final int AC4Profile21 = 1026; // 0x402
+    field public static final int AC4Profile22 = 1028; // 0x404
     field public static final int AV1Level2 = 1; // 0x1
     field public static final int AV1Level21 = 2; // 0x2
     field public static final int AV1Level22 = 4; // 0x4
@@ -27335,6 +27345,7 @@
     method @NonNull public int[] getExposedCapabilities();
     method @NonNull public String getGatewayConnectionName();
     method @IntRange(from=0x500) public int getMaxMtu();
+    method public int getMinUdpPort4500NatTimeoutSeconds();
     method @NonNull public long[] getRetryIntervalsMillis();
     method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
     method public boolean hasGatewayOption(int);
@@ -27349,6 +27360,7 @@
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeGatewayOption(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
+    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
   }
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3a94198..0424ddc 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -157,12 +157,14 @@
     method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp();
     method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio();
+    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeBroadcast();
     method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo);
     method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean);
     method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
     method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean);
     method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpSamplingRate(int);
     method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpVolume(int);
+    method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setLeAudioSuspended(boolean);
     method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
diff --git a/core/java/android/credentials/OWNERS b/core/java/android/credentials/OWNERS
index e8f393e..f5de01d 100644
--- a/core/java/android/credentials/OWNERS
+++ b/core/java/android/credentials/OWNERS
@@ -3,3 +3,5 @@
 sgjerry@google.com
 leecam@google.com
 akaphle@google.com
+omerozer@google.com
+jwillcox@google.com
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 4c9d150..a40fb15 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -81,6 +81,12 @@
  * </ul>
  */
 public final class VcnGatewayConnectionConfig {
+    /** @hide */
+    public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1;
+
+    /** @hide */
+    public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS = 120;
+
     // TODO: Use MIN_MTU_V6 once it is public, @hide
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static final int MIN_MTU_V6 = 1280;
@@ -225,6 +231,10 @@
     private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
     @NonNull private final long[] mRetryIntervalsMs;
 
+    private static final String MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY =
+            "mMinUdpPort4500NatTimeoutSeconds";
+    private final int mMinUdpPort4500NatTimeoutSeconds;
+
     private static final String GATEWAY_OPTIONS_KEY = "mGatewayOptions";
     @NonNull private final Set<Integer> mGatewayOptions;
 
@@ -236,12 +246,14 @@
             @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             @NonNull long[] retryIntervalsMs,
             @IntRange(from = MIN_MTU_V6) int maxMtu,
+            @NonNull int minUdpPort4500NatTimeoutSeconds,
             @NonNull Set<Integer> gatewayOptions) {
         mGatewayConnectionName = gatewayConnectionName;
         mTunnelConnectionParams = tunnelConnectionParams;
         mExposedCapabilities = new TreeSet(exposedCapabilities);
         mRetryIntervalsMs = retryIntervalsMs;
         mMaxMtu = maxMtu;
+        mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
         mGatewayOptions = Collections.unmodifiableSet(new ArraySet(gatewayOptions));
 
         mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
@@ -301,6 +313,10 @@
 
         mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
         mMaxMtu = in.getInt(MAX_MTU_KEY);
+        mMinUdpPort4500NatTimeoutSeconds =
+                in.getInt(
+                        MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY,
+                        MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
 
         validate();
     }
@@ -323,6 +339,12 @@
         Preconditions.checkArgument(
                 mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
 
+        Preconditions.checkArgument(
+                mMinUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET
+                        || mMinUdpPort4500NatTimeoutSeconds
+                                >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+                "minUdpPort4500NatTimeoutSeconds must be at least 120s");
+
         for (int option : mGatewayOptions) {
             validateGatewayOption(option);
         }
@@ -452,6 +474,15 @@
     }
 
     /**
+     * Retrieves the maximum supported IKEv2/IPsec NATT keepalive timeout.
+     *
+     * @see Builder#setMinUdpPort4500NatTimeoutSeconds(int)
+     */
+    public int getMinUdpPort4500NatTimeoutSeconds() {
+        return mMinUdpPort4500NatTimeoutSeconds;
+    }
+
+    /**
      * Checks if the given VCN gateway option is enabled.
      *
      * @param option the option to check.
@@ -496,6 +527,7 @@
         result.putPersistableBundle(GATEWAY_OPTIONS_KEY, gatewayOptionsBundle);
         result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
         result.putInt(MAX_MTU_KEY, mMaxMtu);
+        result.putInt(MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY, mMinUdpPort4500NatTimeoutSeconds);
 
         return result;
     }
@@ -509,6 +541,7 @@
                 mUnderlyingNetworkTemplates,
                 Arrays.hashCode(mRetryIntervalsMs),
                 mMaxMtu,
+                mMinUdpPort4500NatTimeoutSeconds,
                 mGatewayOptions);
     }
 
@@ -525,6 +558,7 @@
                 && mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
                 && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
                 && mMaxMtu == rhs.mMaxMtu
+                && mMinUdpPort4500NatTimeoutSeconds == rhs.mMinUdpPort4500NatTimeoutSeconds
                 && mGatewayOptions.equals(rhs.mGatewayOptions);
     }
 
@@ -542,6 +576,7 @@
 
         @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
         private int mMaxMtu = DEFAULT_MAX_MTU;
+        private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
 
         @NonNull private final Set<Integer> mGatewayOptions = new ArraySet<>();
 
@@ -703,6 +738,29 @@
         }
 
         /**
+         * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout.
+         *
+         * <p>This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs,
+         * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data.
+         *
+         * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN
+         *     Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN
+         *     Gateway.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        public Builder setMinUdpPort4500NatTimeoutSeconds(
+                @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS)
+                        int minUdpPort4500NatTimeoutSeconds) {
+            Preconditions.checkArgument(
+                    minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+                    "Timeout must be at least 120s");
+
+            mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
+            return this;
+        }
+
+        /**
          * Enables the specified VCN gateway option.
          *
          * @param option the option to be enabled
@@ -744,6 +802,7 @@
                     mUnderlyingNetworkTemplates,
                     mRetryIntervalsMs,
                     mMaxMtu,
+                    mMinUdpPort4500NatTimeoutSeconds,
                     mGatewayOptions);
         }
     }
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 5c47b28..f546910 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -17,6 +17,7 @@
 package android.net.vcn;
 
 import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import android.annotation.NonNull;
@@ -49,18 +50,29 @@
 public class VcnTransportInfo implements TransportInfo, Parcelable {
     @Nullable private final WifiInfo mWifiInfo;
     private final int mSubId;
+    private final int mMinUdpPort4500NatTimeoutSeconds;
 
     public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
-        this(wifiInfo, INVALID_SUBSCRIPTION_ID);
+        this(wifiInfo, INVALID_SUBSCRIPTION_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
+    }
+
+    public VcnTransportInfo(@NonNull WifiInfo wifiInfo, int minUdpPort4500NatTimeoutSeconds) {
+        this(wifiInfo, INVALID_SUBSCRIPTION_ID, minUdpPort4500NatTimeoutSeconds);
     }
 
     public VcnTransportInfo(int subId) {
-        this(null /* wifiInfo */, subId);
+        this(null /* wifiInfo */, subId, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
     }
 
-    private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
+    public VcnTransportInfo(int subId, int minUdpPort4500NatTimeoutSeconds) {
+        this(null /* wifiInfo */, subId, minUdpPort4500NatTimeoutSeconds);
+    }
+
+    private VcnTransportInfo(
+            @Nullable WifiInfo wifiInfo, int subId, int minUdpPort4500NatTimeoutSeconds) {
         mWifiInfo = wifiInfo;
         mSubId = subId;
+        mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
     }
 
     /**
@@ -88,16 +100,28 @@
         return mSubId;
     }
 
+    /**
+     * Get the VCN provided UDP port 4500 NAT timeout
+     *
+     * @return the UDP 4500 NAT timeout, or
+     *     VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET if not set.
+     */
+    public int getMinUdpPort4500NatTimeoutSeconds() {
+        return mMinUdpPort4500NatTimeoutSeconds;
+    }
+
     @Override
     public int hashCode() {
-        return Objects.hash(mWifiInfo, mSubId);
+        return Objects.hash(mWifiInfo, mSubId, mMinUdpPort4500NatTimeoutSeconds);
     }
 
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof VcnTransportInfo)) return false;
         final VcnTransportInfo that = (VcnTransportInfo) o;
-        return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
+        return Objects.equals(mWifiInfo, that.mWifiInfo)
+                && mSubId == that.mSubId
+                && mMinUdpPort4500NatTimeoutSeconds == that.mMinUdpPort4500NatTimeoutSeconds;
     }
 
     /** {@inheritDoc} */
@@ -110,11 +134,14 @@
     @NonNull
     public TransportInfo makeCopy(long redactions) {
         if ((redactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) != 0) {
-            return new VcnTransportInfo(null, INVALID_SUBSCRIPTION_ID);
+            return new VcnTransportInfo(
+                    null, INVALID_SUBSCRIPTION_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
         }
 
         return new VcnTransportInfo(
-                (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions), mSubId);
+                (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions),
+                mSubId,
+                mMinUdpPort4500NatTimeoutSeconds);
     }
 
     @Override
@@ -134,6 +161,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mSubId);
         dest.writeParcelable(mWifiInfo, flags);
+        dest.writeInt(mMinUdpPort4500NatTimeoutSeconds);
     }
 
     @Override
@@ -146,16 +174,21 @@
             new Creator<VcnTransportInfo>() {
                 public VcnTransportInfo createFromParcel(Parcel in) {
                     final int subId = in.readInt();
-                    final WifiInfo wifiInfo = in.readParcelable(null, android.net.wifi.WifiInfo.class);
+                    final WifiInfo wifiInfo =
+                            in.readParcelable(null, android.net.wifi.WifiInfo.class);
+                    final int minUdpPort4500NatTimeoutSeconds = in.readInt();
 
                     // If all fields are their null values, return null TransportInfo to avoid
                     // leaking information about this being a VCN Network (instead of macro
                     // cellular, etc)
-                    if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) {
+                    if (wifiInfo == null
+                            && subId == INVALID_SUBSCRIPTION_ID
+                            && minUdpPort4500NatTimeoutSeconds
+                                    == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET) {
                         return null;
                     }
 
-                    return new VcnTransportInfo(wifiInfo, subId);
+                    return new VcnTransportInfo(wifiInfo, subId, minUdpPort4500NatTimeoutSeconds);
                 }
 
                 public VcnTransportInfo[] newArray(int size) {
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 8012e0c..5f8721e 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -18,29 +18,25 @@
 
 #define LOG_TAG "AudioRecord-JNI"
 
-#include <inttypes.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-#include <utils/Log.h>
-#include <media/AudioRecord.h>
-#include <media/MicrophoneInfo.h>
-#include <vector>
-
 #include <android/content/AttributionSourceState.h>
 #include <android_os_Parcel.h>
-
+#include <inttypes.h>
+#include <jni.h>
+#include <media/AudioRecord.h>
+#include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+
+#include <vector>
 
 #include "android_media_AudioAttributes.h"
-#include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
+#include "android_media_AudioFormat.h"
 #include "android_media_DeviceCallback.h"
 #include "android_media_JNIUtils.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_MicrophoneInfo.h"
-
+#include "core_jni_helpers.h"
 
 // ----------------------------------------------------------------------------
 
@@ -721,7 +717,7 @@
     }
 
     jint jStatus = AUDIO_JAVA_SUCCESS;
-    std::vector<media::MicrophoneInfo> activeMicrophones;
+    std::vector<media::MicrophoneInfoFw> activeMicrophones;
     status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
     if (status != NO_ERROR) {
         ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 47057a4..cdb0580 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -27,7 +27,6 @@
 #include <media/AudioContainers.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioSystem.h>
-#include <media/MicrophoneInfo.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <system/audio.h>
@@ -2325,7 +2324,7 @@
     }
 
     jint jStatus;
-    std::vector<media::MicrophoneInfo> microphones;
+    std::vector<media::MicrophoneInfoFw> microphones;
     status_t status = AudioSystem::getMicrophones(&microphones);
     if (status != NO_ERROR) {
         ALOGE("AudioSystem::getMicrophones error %d", status);
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
index b70190f..65e30d8 100644
--- a/core/jni/android_media_MicrophoneInfo.cpp
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -15,6 +15,9 @@
  */
 
 #include "android_media_MicrophoneInfo.h"
+
+#include <media/AidlConversion.h>
+
 #include "android_media_AudioErrors.h"
 #include "core_jni_helpers.h"
 
@@ -46,8 +49,17 @@
 namespace android {
 
 jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
-        const media::MicrophoneInfo *microphoneInfo)
-{
+                                     const media::MicrophoneInfoFw *microphoneInfo) {
+    // The Java object uses the same enum values as the C enum values, which are
+    // generated from HIDL. Thus, we can use the legacy structure as the source for
+    // creating the Java object. Once we start removing legacy types, we can add
+    // direct converters between Java and AIDL, this will eliminate the need
+    // to have JNI code like this one.
+    auto conv = aidl2legacy_MicrophoneInfoFw_audio_microphone_characteristic_t(*microphoneInfo);
+    if (!conv.ok()) {
+        return nativeToJavaStatus(conv.error());
+    }
+
     jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
     jstring jDeviceId = NULL;
     jstring jAddress = NULL;
@@ -56,36 +68,23 @@
     jobject jFrequencyResponses = NULL;
     jobject jChannelMappings = NULL;
 
-    jDeviceId = env->NewStringUTF(microphoneInfo->getDeviceId().c_str());
-    jAddress = env->NewStringUTF(microphoneInfo->getAddress().c_str());
-    if (microphoneInfo->getGeometricLocation().size() != 3 ||
-            microphoneInfo->getOrientation().size() != 3) {
-        jStatus = nativeToJavaStatus(BAD_VALUE);
-        goto exit;
-    }
-    jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
-                                        gMicrophoneInfoCoordinateCstor,
-                                        microphoneInfo->getGeometricLocation()[0],
-                                        microphoneInfo->getGeometricLocation()[1],
-                                        microphoneInfo->getGeometricLocation()[2]);
-    jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
-                                  gMicrophoneInfoCoordinateCstor,
-                                  microphoneInfo->getOrientation()[0],
-                                  microphoneInfo->getOrientation()[1],
-                                  microphoneInfo->getOrientation()[2]);
+    const auto &micInfo = conv.value();
+    jDeviceId = env->NewStringUTF(micInfo.device_id);
+    jAddress = env->NewStringUTF(micInfo.address);
+    jGeometricLocation =
+            env->NewObject(gMicrophoneInfoCoordinateClass, gMicrophoneInfoCoordinateCstor,
+                           micInfo.geometric_location.x, micInfo.geometric_location.y,
+                           micInfo.geometric_location.z);
+    jOrientation =
+            env->NewObject(gMicrophoneInfoCoordinateClass, gMicrophoneInfoCoordinateCstor,
+                           micInfo.orientation.x, micInfo.orientation.y, micInfo.orientation.z);
     // Create a list of Pair for frequency response.
-    if (microphoneInfo->getFrequencyResponses().size() != 2 ||
-            microphoneInfo->getFrequencyResponses()[0].size() !=
-                    microphoneInfo->getFrequencyResponses()[1].size()) {
-        jStatus = nativeToJavaStatus(BAD_VALUE);
-        goto exit;
-    }
     jFrequencyResponses = env->NewObject(gArrayListClass, gArrayListCstor);
-    for (size_t i = 0; i < microphoneInfo->getFrequencyResponses()[0].size(); i++) {
-        jobject jFrequency = env->NewObject(gFloatClass, gFloatCstor,
-                                            microphoneInfo->getFrequencyResponses()[0][i]);
-        jobject jResponse = env->NewObject(gFloatClass, gFloatCstor,
-                                          microphoneInfo->getFrequencyResponses()[1][i]);
+    for (size_t i = 0; i < micInfo.num_frequency_responses; i++) {
+        jobject jFrequency =
+                env->NewObject(gFloatClass, gFloatCstor, micInfo.frequency_responses[0][i]);
+        jobject jResponse =
+                env->NewObject(gFloatClass, gFloatCstor, micInfo.frequency_responses[1][i]);
         jobject jFrequencyResponse = env->NewObject(gPairClass, gPairCstor, jFrequency, jResponse);
         env->CallBooleanMethod(jFrequencyResponses, gArrayListMethods.add, jFrequencyResponse);
         env->DeleteLocalRef(jFrequency);
@@ -93,13 +92,9 @@
         env->DeleteLocalRef(jFrequencyResponse);
     }
     // Create a list of Pair for channel mapping.
-    if (microphoneInfo->getChannelMapping().size() != AUDIO_CHANNEL_COUNT_MAX) {
-        jStatus = nativeToJavaStatus(BAD_VALUE);
-        goto exit;
-    }
-    jChannelMappings = env->NewObject(gArrayListClass, gArrayListCstor);
-    for (size_t i = 0; i < microphoneInfo->getChannelMapping().size(); i++) {
-        int channelMappingType = microphoneInfo->getChannelMapping()[i];
+    const auto &channelMapping = micInfo.channel_mapping;
+    for (size_t i = 0; i < std::size(channelMapping); i++) {
+        int channelMappingType = channelMapping[i];
         if (channelMappingType != AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
             jobject jChannelIndex = env->NewObject(gIntegerClass, gIntegerCstor, i);
             jobject jChannelMappingType = env->NewObject(gIntegerClass, gIntegerCstor,
@@ -113,18 +108,11 @@
         }
     }
     *jMicrophoneInfo = env->NewObject(gMicrophoneInfoClass, gMicrophoneInfoCstor, jDeviceId,
-                                      microphoneInfo->getType(), jAddress,
-                                      microphoneInfo->getDeviceLocation(),
-                                      microphoneInfo->getDeviceGroup(),
-                                      microphoneInfo->getIndexInTheGroup(),
-                                      jGeometricLocation, jOrientation,
-                                      jFrequencyResponses, jChannelMappings,
-                                      microphoneInfo->getSensitivity(),
-                                      microphoneInfo->getMaxSpl(),
-                                      microphoneInfo->getMinSpl(),
-                                      microphoneInfo->getDirectionality());
+                                      micInfo.device, jAddress, micInfo.location, micInfo.group,
+                                      micInfo.index_in_the_group, jGeometricLocation, jOrientation,
+                                      jFrequencyResponses, jChannelMappings, micInfo.sensitivity,
+                                      micInfo.max_spl, micInfo.min_spl, micInfo.directionality);
 
-exit:
     if (jDeviceId != NULL) {
         env->DeleteLocalRef(jDeviceId);
     }
@@ -145,7 +133,6 @@
     }
     return jStatus;
 }
-
 }
 
 int register_android_media_MicrophoneInfo(JNIEnv *env)
diff --git a/core/jni/android_media_MicrophoneInfo.h b/core/jni/android_media_MicrophoneInfo.h
index 241b6d0..dc531c6 100644
--- a/core/jni/android_media_MicrophoneInfo.h
+++ b/core/jni/android_media_MicrophoneInfo.h
@@ -17,8 +17,8 @@
 #ifndef ANDROID_MEDIA_MICROPHONEINFO_H
 #define ANDROID_MEDIA_MICROPHONEINFO_H
 
+#include <android/media/MicrophoneInfoFw.h>
 #include <system/audio.h>
-#include <media/MicrophoneInfo.h>
 
 #include "jni.h"
 
@@ -27,7 +27,7 @@
 // Conversion from C++ MicrophoneInfo object to Java MicrophoneInfo object
 
 extern jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo,
-        const media::MicrophoneInfo *microphoneInfo);
+                                            const media::MicrophoneInfoFw *microphoneInfo);
 } // namespace android
 
-#endif
\ No newline at end of file
+#endif
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 85911a3..33fe077 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3388,6 +3388,19 @@
     }
 
     /**
+     * Suspends the use of LE Audio.
+     *
+     * @param enable {@code true} to suspend le audio, {@code false} to unsuspend
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+    public void setLeAudioSuspended(boolean enable) {
+        AudioSystem.setParameters("LeAudioSuspended=" + enable);
+    }
+
+    /**
      * Gets a variable number of parameter values from audio hardware.
      *
      * @param keys list of parameters
@@ -6963,22 +6976,13 @@
         return codecConfigList;
     }
 
-    /**
-     * Returns a list of audio formats that corresponds to encoding formats
-     * supported on offload path for Le audio playback.
-     *
-     * @return a list of {@link BluetoothLeAudioCodecConfig} objects containing encoding formats
-     * supported for offload Le Audio playback
-     * @hide
-     */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    @NonNull
-    public List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio() {
+    private List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio(
+            @AudioSystem.BtOffloadDeviceType int deviceType) {
         ArrayList<Integer> formatsList = new ArrayList<>();
         ArrayList<BluetoothLeAudioCodecConfig> leAudioCodecConfigList = new ArrayList<>();
 
         int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
-                AudioSystem.DEVICE_OUT_BLE_HEADSET, formatsList);
+                deviceType, formatsList);
         if (status != AudioManager.SUCCESS) {
             Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForLeAudio failed:" + status);
             return leAudioCodecConfigList;
@@ -6995,6 +6999,34 @@
         return leAudioCodecConfigList;
     }
 
+    /**
+     * Returns a list of audio formats that corresponds to encoding formats
+     * supported on offload path for Le audio playback.
+     *
+     * @return a list of {@link BluetoothLeAudioCodecConfig} objects containing encoding formats
+     * supported for offload Le Audio playback
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio() {
+        return getHwOffloadFormatsSupportedForLeAudio(AudioSystem.DEVICE_OUT_BLE_HEADSET);
+    }
+
+    /**
+     * Returns a list of audio formats that corresponds to encoding formats
+     * supported on offload path for Le Broadcast playback.
+     *
+     * @return a list of {@link BluetoothLeAudioCodecConfig} objects containing encoding formats
+     * supported for offload Le Broadcast playback
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeBroadcast() {
+        return getHwOffloadFormatsSupportedForLeAudio(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
+    }
+
     // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
     // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
     // of the ports that exist at the time of the last notification.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 474ccc9..22033c6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -275,10 +275,11 @@
     /** @hide */
     @IntDef(flag = false, prefix = "DEVICE_", value = {
             DEVICE_OUT_BLUETOOTH_A2DP,
-            DEVICE_OUT_BLE_HEADSET}
+            DEVICE_OUT_BLE_HEADSET,
+            DEVICE_OUT_BLE_BROADCAST}
     )
     @Retention(RetentionPolicy.SOURCE)
-    public @interface DeviceType {}
+    public @interface BtOffloadDeviceType {}
 
     /**
      * @hide
@@ -1972,7 +1973,7 @@
      * Returns a list of audio formats (codec) supported on the A2DP and LE audio offload path.
      */
     public static native int getHwOffloadFormatsSupportedForBluetoothMedia(
-            @DeviceType int deviceType, ArrayList<Integer> formatList);
+            @BtOffloadDeviceType int deviceType, ArrayList<Integer> formatList);
 
     /** @hide */
     public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 55de0b3..193544e 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -4182,6 +4182,77 @@
         @SuppressLint("AllUpper")
         public static final int DTS_UHDProfileP2 = 0x2;
 
+        // Profiles and levels for AC-4 Codec, corresponding to the definitions in
+        // "The MIME codecs parameter", Annex E.13
+        // found at https://www.etsi.org/deliver/etsi_ts/103100_103199/10319002/01.02.01_60/ts_10319002v010201p.pdf
+        // profile = ((1 << bitstream_version) << 8) | (1 << presentation_version);
+        // level = 1 << mdcompat;
+
+        @SuppressLint("AllUpper")
+        private static final int AC4BitstreamVersion0 = 0x01;
+        @SuppressLint("AllUpper")
+        private static final int AC4BitstreamVersion1 = 0x02;
+        @SuppressLint("AllUpper")
+        private static final int AC4BitstreamVersion2 = 0x04;
+
+        @SuppressLint("AllUpper")
+        private static final int AC4PresentationVersion0 = 0x01;
+        @SuppressLint("AllUpper")
+        private static final int AC4PresentationVersion1 = 0x02;
+        @SuppressLint("AllUpper")
+        private static final int AC4PresentationVersion2 = 0x04;
+
+        /**
+         * AC-4 codec profile with bitstream_version 0 and presentation_version 0
+         * as per ETSI TS 103 190-2 v1.2.1
+         */
+        @SuppressLint("AllUpper")
+        public static final int AC4Profile00 = AC4BitstreamVersion0 << 8 | AC4PresentationVersion0;
+
+        /**
+         * AC-4 codec profile with bitstream_version 1 and presentation_version 0
+         * as per ETSI TS 103 190-2 v1.2.1
+         */
+        @SuppressLint("AllUpper")
+        public static final int AC4Profile10 = AC4BitstreamVersion1 << 8 | AC4PresentationVersion0;
+
+        /**
+         * AC-4 codec profile with bitstream_version 1 and presentation_version 1
+         * as per ETSI TS 103 190-2 v1.2.1
+         */
+        @SuppressLint("AllUpper")
+        public static final int AC4Profile11 = AC4BitstreamVersion1 << 8 | AC4PresentationVersion1;
+
+        /**
+         * AC-4 codec profile with bitstream_version 2 and presentation_version 1
+         * as per ETSI TS 103 190-2 v1.2.1
+         */
+        @SuppressLint("AllUpper")
+        public static final int AC4Profile21 = AC4BitstreamVersion2 << 8 | AC4PresentationVersion1;
+
+        /**
+         * AC-4 codec profile with bitstream_version 2 and presentation_version 2
+         * as per ETSI TS 103 190-2 v1.2.1
+         */
+        @SuppressLint("AllUpper")
+        public static final int AC4Profile22 = AC4BitstreamVersion2 << 8 | AC4PresentationVersion2;
+
+        /** AC-4 codec level corresponding to mdcompat 0 as per ETSI TS 103 190-2 v1.2.1 */
+        @SuppressLint("AllUpper")
+        public static final int AC4Level0       = 0x01;
+        /** AC-4 codec level corresponding to mdcompat 1 as per ETSI TS 103 190-2 v1.2.1 */
+        @SuppressLint("AllUpper")
+        public static final int AC4Level1       = 0x02;
+        /** AC-4 codec level corresponding to mdcompat 2 as per ETSI TS 103 190-2 v1.2.1 */
+        @SuppressLint("AllUpper")
+        public static final int AC4Level2       = 0x04;
+        /** AC-4 codec level corresponding to mdcompat 3 as per ETSI TS 103 190-2 v1.2.1 */
+        @SuppressLint("AllUpper")
+        public static final int AC4Level3       = 0x08;
+        /** AC-4 codec level corresponding to mdcompat 4 as per ETSI TS 103 190-2 v1.2.1 */
+        @SuppressLint("AllUpper")
+        public static final int AC4Level4       = 0x10;
+
         /**
          * The profile of the media content. Depending on the type of media this can be
          * one of the profile values defined in this class.
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 7ef0f77..9a6d5d7 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -30,7 +30,6 @@
 #include <camera/Camera.h>
 #include <media/mediarecorder.h>
 #include <media/MediaMetricsItem.h>
-#include <media/MicrophoneInfo.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <utils/threads.h>
 
@@ -774,7 +773,7 @@
     }
 
     jint jStatus = AUDIO_JAVA_SUCCESS;
-    std::vector<media::MicrophoneInfo> activeMicrophones;
+    std::vector<media::MicrophoneInfoFw> activeMicrophones;
     status_t status = mr->getActiveMicrophones(&activeMicrophones);
     if (status != NO_ERROR) {
         ALOGE_IF(status != NO_ERROR, "MediaRecorder::getActiveMicrophones error %d", status);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index c7d8ab2..bbf4943 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -158,6 +158,8 @@
     );
 
     public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
+            "android.hardware.audio.core.IModule/",
+            "android.hardware.audio.core.IConfig/",
             "android.hardware.biometrics.face.IFace/",
             "android.hardware.biometrics.fingerprint.IFingerprint/",
             "android.hardware.bluetooth.IBluetoothHci/",
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 739aff7..a1ef537 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -2255,12 +2255,18 @@
             if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
                     && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
                 final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
-                builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
+                builder.setTransportInfo(
+                        new VcnTransportInfo(
+                                wifiInfo,
+                                gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds()));
             } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
                     && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
                 final TelephonyNetworkSpecifier telNetSpecifier =
                         (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
-                builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
+                builder.setTransportInfo(
+                        new VcnTransportInfo(
+                                telNetSpecifier.getSubscriptionId(),
+                                gatewayConnectionConfig.getMinUdpPort4500NatTimeoutSeconds()));
             } else {
                 Slog.wtf(
                         TAG,
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 1883c85..a1a39ff 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -82,6 +82,7 @@
                 TimeUnit.MINUTES.toMillis(30)
             };
     public static final int MAX_MTU = 1360;
+    public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120;
 
     private static final Set<Integer> GATEWAY_OPTIONS =
             Collections.singleton(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY);
@@ -104,7 +105,9 @@
     public static VcnGatewayConnectionConfig buildTestConfig(
             List<VcnUnderlyingNetworkTemplate> nwTemplates) {
         final VcnGatewayConnectionConfig.Builder builder =
-                newBuilder().setVcnUnderlyingNetworkPriorities(nwTemplates);
+                newBuilder()
+                        .setVcnUnderlyingNetworkPriorities(nwTemplates)
+                        .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT);
 
         return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
     }
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 19df3c7..81814b6 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -19,6 +19,7 @@
 import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
 import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
 import static android.net.NetworkCapabilities.REDACT_NONE;
+import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
@@ -37,11 +38,14 @@
 public class VcnTransportInfoTest {
     private static final int SUB_ID = 1;
     private static final int NETWORK_ID = 5;
+    private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120;
     private static final WifiInfo WIFI_INFO =
             new WifiInfo.Builder().setNetworkId(NETWORK_ID).build();
 
-    private static final VcnTransportInfo CELL_UNDERLYING_INFO = new VcnTransportInfo(SUB_ID);
-    private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
+    private static final VcnTransportInfo CELL_UNDERLYING_INFO =
+            new VcnTransportInfo(SUB_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT);
+    private static final VcnTransportInfo WIFI_UNDERLYING_INFO =
+            new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT);
 
     @Test
     public void testGetWifiInfo() {
@@ -58,6 +62,16 @@
     }
 
     @Test
+    public void testGetMinUdpPort4500NatTimeoutSeconds() {
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT,
+                CELL_UNDERLYING_INFO.getMinUdpPort4500NatTimeoutSeconds());
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT,
+                WIFI_UNDERLYING_INFO.getMinUdpPort4500NatTimeoutSeconds());
+    }
+
+    @Test
     public void testMakeCopyRedactForNetworkSettings() {
         for (VcnTransportInfo info : Arrays.asList(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO)) {
             assertEquals(
@@ -67,6 +81,10 @@
             assertNull(
                     ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
                             .getWifiInfo());
+            assertEquals(
+                    MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET,
+                    ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+                            .getMinUdpPort4500NatTimeoutSeconds());
         }
     }
 
@@ -77,9 +95,17 @@
                 ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
                         .getSubId());
         assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT,
+                ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+                        .getMinUdpPort4500NatTimeoutSeconds());
+        assertEquals(
                 WifiConfiguration.INVALID_NETWORK_ID,
                 ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
                         .getWifiInfo().getNetworkId());
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT,
+                ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+                        .getMinUdpPort4500NatTimeoutSeconds());
     }
 
     @Test
@@ -110,8 +136,12 @@
     public void testParcelNotRedactedForSysUi() {
         VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO);
         assertEquals(SUB_ID, cellRedacted.getSubId());
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT, cellRedacted.getMinUdpPort4500NatTimeoutSeconds());
         VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO);
         assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId());
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT, wifiRedacted.getMinUdpPort4500NatTimeoutSeconds());
     }
 
     private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index aad7a5e..89271e1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -25,6 +25,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
+import static android.net.vcn.VcnGatewayConnectionConfigTest.MIN_UDP_PORT_4500_NAT_TIMEOUT;
 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
@@ -66,6 +67,7 @@
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
 import android.net.vcn.VcnManager.VcnErrorCode;
+import android.net.vcn.VcnTransportInfo;
 import android.os.PersistableBundle;
 
 import androidx.test.filters.SmallTest;
@@ -425,6 +427,12 @@
             assertTrue(nc.hasCapability(cap));
         }
 
+        assertTrue(nc.getTransportInfo() instanceof VcnTransportInfo);
+        final VcnTransportInfo vcnTransportInfo = (VcnTransportInfo) nc.getTransportInfo();
+        assertEquals(
+                MIN_UDP_PORT_4500_NAT_TIMEOUT,
+                vcnTransportInfo.getMinUdpPort4500NatTimeoutSeconds());
+
         // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is
         // canceled
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index bb123ff..5efbf59 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vcn;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
 import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
 import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
@@ -46,6 +48,7 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.ipsec.ike.IkeSessionConfiguration;
@@ -127,7 +130,10 @@
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
             getTestNetworkRecord(
                     mock(Network.class, CALLS_REAL_METHODS),
-                    new NetworkCapabilities(),
+                    new NetworkCapabilities.Builder()
+                            .addTransportType(TRANSPORT_CELLULAR)
+                            .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUB_ID))
+                            .build(),
                     new LinkProperties(),
                     false /* blocked */);