Merge "Improve documentation on simulateDataStall"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index a522237..5487e87 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -693,13 +693,6 @@
                     StateControllerProto.ConnectivityController.REQUESTED_STANDBY_EXCEPTION_UIDS,
                     mRequestedWhitelistJobs.keyAt(i));
         }
-        for (int i = 0; i < mAvailableNetworks.size(); i++) {
-            Network network = mAvailableNetworks.keyAt(i);
-            if (network != null) {
-                network.dumpDebug(proto,
-                        StateControllerProto.ConnectivityController.AVAILABLE_NETWORKS);
-            }
-        }
         for (int i = 0; i < mTrackedJobs.size(); i++) {
             final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
             for (int j = 0; j < jobs.size(); j++) {
@@ -713,12 +706,6 @@
                         StateControllerProto.ConnectivityController.TrackedJob.INFO);
                 proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
                         js.getSourceUid());
-                NetworkRequest rn = js.getJob().getRequiredNetwork();
-                if (rn != null) {
-                    rn.dumpDebug(proto,
-                            StateControllerProto.ConnectivityController.TrackedJob
-                                    .REQUIRED_NETWORK);
-                }
                 proto.end(jsToken);
             }
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index ea8e7bc..a346812 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1911,9 +1911,6 @@
             if (uriPerms != null) {
                 uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS);
             }
-            if (job.getRequiredNetwork() != null) {
-                job.getRequiredNetwork().dumpDebug(proto, JobStatusDumpProto.JobInfo.REQUIRED_NETWORK);
-            }
             if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
                 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_DOWNLOAD_BYTES,
                         mTotalNetworkDownloadBytes);
@@ -2002,10 +1999,6 @@
             }
         }
 
-        if (network != null) {
-            network.dumpDebug(proto, JobStatusDumpProto.NETWORK);
-        }
-
         if (pendingWork != null) {
             for (int i = 0; i < pendingWork.size(); i++) {
                 dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i));
diff --git a/api/dump_api_shas.sh b/api/dump_api_shas.sh
new file mode 100755
index 0000000..c023b31
--- /dev/null
+++ b/api/dump_api_shas.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -e
+# This script dumps the git SHAs of all commits inside api tracking directories.
+# It can used by tools wanting to track API changes, and the primary original
+# purpose is to verify verify all API change SHAs have been tracked by the
+# server-side API-council tools.
+#
+# The only argument is used to specify a git commit range to filter by.
+#
+# Example invocation (API changes between O and P):
+# frameworks/base/api/dump_api_shas.sh origin/oreo-dev..origin/pie-dev
+
+set -o pipefail
+
+eecho() { echo $@ >&2 ; }
+
+if [[ $1 == *..* ]]; then
+    exclude=${1/..*}
+    include=${1/*..}
+else
+    eecho No range or invalid range specified, defaulting to all commits from HEAD.
+    exclude=
+    include=HEAD
+fi
+
+eecho -n building queryview...
+{ source build/envsetup.sh && lunch aosp_arm && m queryview; } >/dev/null 2>&1 \
+  || { eecho failed; exit 1; }
+eecho "done"
+
+# This finds the directories where the dependant java_sdk_libs are defined
+bpdirs=$(
+  bazel query --config=queryview --output=package \
+    'kind(java_sdk_library, deps(//frameworks/base/api/..., 1))' 2>/dev/null
+  echo frameworks/base/core/api # Not a java_sdk_library.
+  echo frameworks/base/services/api # Not a java_sdk_library.
+)
+
+# Find relevant api subdirectories
+apidirs=$(
+  find $bpdirs -type f -name '*current.txt' -path '*/api/*' \
+    | xargs realpath --relative-to=$(pwd) | xargs dirname | sort | uniq
+)
+
+# Dump sorted SHAs of commits in these directories
+{ for d in $apidirs; do
+    ( cd $d
+      eecho inspecting $d
+      exclude_arg=$(test -n "$exclude" && {
+        git rev-parse -q --verify $exclude > /dev/null && echo "--not $exclude" \
+          || eecho "$d has no revision $exclude, including all commits"; } || true)
+      for f in $(find . -name '*current.txt'); do
+        git --no-pager log --pretty=format:%H --no-merges --follow $include $exclude_arg -- $f
+        echo # No trailing newline with --no-pager
+      done
+    )
+done; } | sort | uniq
diff --git a/config/hiddenapi-force-blocked.txt b/boot/hiddenapi/hiddenapi-force-blocked.txt
similarity index 100%
rename from config/hiddenapi-force-blocked.txt
rename to boot/hiddenapi/hiddenapi-force-blocked.txt
diff --git a/config/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt
similarity index 100%
rename from config/hiddenapi-max-target-o.txt
rename to boot/hiddenapi/hiddenapi-max-target-o.txt
diff --git a/config/hiddenapi-max-target-p.txt b/boot/hiddenapi/hiddenapi-max-target-p.txt
similarity index 100%
rename from config/hiddenapi-max-target-p.txt
rename to boot/hiddenapi/hiddenapi-max-target-p.txt
diff --git a/config/hiddenapi-max-target-q.txt b/boot/hiddenapi/hiddenapi-max-target-q.txt
similarity index 100%
rename from config/hiddenapi-max-target-q.txt
rename to boot/hiddenapi/hiddenapi-max-target-q.txt
diff --git a/config/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
similarity index 100%
rename from config/hiddenapi-max-target-r-loprio.txt
rename to boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
diff --git a/config/hiddenapi-unsupported-packages.txt b/boot/hiddenapi/hiddenapi-unsupported-packages.txt
similarity index 100%
rename from config/hiddenapi-unsupported-packages.txt
rename to boot/hiddenapi/hiddenapi-unsupported-packages.txt
diff --git a/config/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
similarity index 100%
rename from config/hiddenapi-unsupported.txt
rename to boot/hiddenapi/hiddenapi-unsupported.txt
diff --git a/config/OWNERS b/config/OWNERS
index 001038d..0691dbc 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,14 +1,8 @@
 include /ZYGOTE_OWNERS
 
-# compat-team@ for changes to hiddenapi files
-
-per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com
-
 # art-team@ manages the boot image profiles
 per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 
-# Escalations:
-per-file hiddenapi-* = bdc@google.com, narayan@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index b944ac9..cc425f0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29284,7 +29284,6 @@
     field public static final String HARDWARE;
     field public static final String HOST;
     field public static final String ID;
-    field public static final boolean IS_DEBUGGABLE;
     field public static final String MANUFACTURER;
     field public static final String MODEL;
     field @NonNull public static final String ODM_SKU;
@@ -33998,8 +33997,8 @@
 
   public static final class SimPhonebookContract.SimRecords {
     method @NonNull public static android.net.Uri getContentUri(int, int);
-    method @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
-    method @NonNull public static android.net.Uri getItemUri(int, int, int);
+    method @IntRange(from=0) @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
+    method @NonNull public static android.net.Uri getItemUri(int, int, @IntRange(from=1) int);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
     field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index fe8b9d6..e7a7fd2 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -27,10 +27,6 @@
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
-  public class Intent implements java.lang.Cloneable android.os.Parcelable {
-    field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
-  }
-
 }
 
 package android.net {
@@ -121,6 +117,10 @@
     method public final void markVintfStability();
   }
 
+  public class Build {
+    method public static boolean isDebuggable();
+  }
+
   public static class Build.VERSION {
     field public static final int FIRST_SDK_INT;
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index eebf283..6c34e1a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6728,12 +6728,18 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnStateCallback(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
     field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
   }
 
+  public static interface NfcAdapter.ControllerAlwaysOnStateCallback {
+    method public void onStateChanged(boolean);
+  }
+
   public static interface NfcAdapter.NfcUnlockHandler {
     method public boolean onUnlockAttempted(android.nfc.Tag);
   }
@@ -8117,7 +8123,7 @@
   }
 
   public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
-    method @Nullable public int[] getAttestationIds();
+    method @NonNull public int[] getAttestationIds();
     method public int getNamespace();
   }
 
@@ -12493,7 +12499,7 @@
   public final class DistanceMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
-    method public double getErrorMeters();
+    method @FloatRange(from=0.0) public double getErrorMeters();
     method public double getMeters();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR;
@@ -12502,8 +12508,8 @@
   public static final class DistanceMeasurement.Builder {
     ctor public DistanceMeasurement.Builder();
     method @NonNull public android.uwb.DistanceMeasurement build();
-    method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(double);
-    method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(double);
+    method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(@FloatRange(from=0.0, to=1.0) double);
+    method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(@FloatRange(from=0.0) double);
     method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0c57f4e..f7b101d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1022,6 +1022,7 @@
 
   public class Build {
     method public static boolean is64BitAbi(String);
+    method public static boolean isDebuggable();
     field public static final boolean IS_EMULATOR;
   }
 
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index 7713e25..74018a8 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -24,7 +24,7 @@
     // below block of transactions.
 
     // Since these transactions are also called from native code, these must be kept in sync with
-    // the ones in frameworks/native/include/binder/IActivityManager.h
+    // the ones in frameworks/native/include_activitymanager/binder/IActivityManager.h
     // =============== Beginning of transactions used on native side as well ======================
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4d68e90..0fad63f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2324,14 +2324,6 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
     /**
-     * Clear DNS Cache Action: This is broadcast when networks have changed and old
-     * DNS entries should be tossed.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
-    /**
      * Alarm Changed Action: This is broadcast when the AlarmClock
      * application's alarm is set or unset.  It is used by the
      * AlarmClock application and the StatusBar service.
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index f13326b..d06bc1d 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -84,7 +84,6 @@
      *         of the result
      */
     public void oneTouchPlay(OneTouchPlayCallback callback) {
-        // TODO: Use PendingResult.
         try {
             mService.oneTouchPlay(getCallbackWrapper(callback));
         } catch (RemoteException e) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index da3febd..b074fad 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -772,6 +772,12 @@
         return DebugUtils.flagsToString(NetworkPolicyManager.class, "BLOCKED_", blockedReasons);
     }
 
+    /** @hide */
+    @NonNull
+    public static String allowedReasonsToString(int allowedReasons) {
+        return DebugUtils.flagsToString(NetworkPolicyManager.class, "ALLOWED_", allowedReasons);
+    }
+
     /**
      * Register a {@link NetworkPolicyCallback} to listen for changes to network blocked status
      * of apps.
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 11445e9..d5cc01a 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -24,6 +24,7 @@
 import android.nfc.TechListParcel;
 import android.nfc.IAppCallback;
 import android.nfc.INfcAdapterExtras;
+import android.nfc.INfcControllerAlwaysOnStateCallback;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcFCardEmulation;
@@ -75,4 +76,6 @@
     boolean setControllerAlwaysOn(boolean value);
     boolean isControllerAlwaysOn();
     boolean isControllerAlwaysOnSupported();
+    void registerControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
+    void unregisterControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
 }
diff --git a/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl b/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
new file mode 100644
index 0000000..1e4fdd7
--- /dev/null
+++ b/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+/**
+ * @hide
+ */
+oneway interface INfcControllerAlwaysOnStateCallback {
+  /**
+   * Called whenever the controller always on state changes
+   *
+   * @param isEnabled true if the state is enabled, false otherwise
+   */
+  void onControllerAlwaysOnStateChanged(boolean isEnabled);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index eed2c77..bbf802c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -47,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Represents the local NFC adapter.
@@ -65,6 +67,8 @@
 public final class NfcAdapter {
     static final String TAG = "NFC";
 
+    private final NfcControllerAlwaysOnStateListener mControllerAlwaysOnStateListener;
+
     /**
      * Intent to start an activity when a tag with NDEF payload is discovered.
      *
@@ -350,22 +354,6 @@
             "android.nfc.extra.HANDOVER_TRANSFER_STATUS";
 
     /** @hide */
-    public static final String ACTION_ALWAYS_ON_STATE_CHANGED =
-            "android.nfc.action.ALWAYS_ON_STATE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
-     * intents to request the current power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     * @hide
-     */
-    public static final String EXTRA_ALWAYS_ON_STATE =
-            "android.nfc.extra.ALWAYS_ON_STATE";
-
-    /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
     /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
@@ -430,6 +418,22 @@
     }
 
     /**
+     * A callback to be invoked when NFC controller always on state changes.
+     * <p>Register your {@code ControllerAlwaysOnStateCallback} implementation with {@link
+     * NfcAdapter#registerControllerAlwaysOnStateCallback} and disable it with {@link
+     * NfcAdapter#unregisterControllerAlwaysOnStateCallback}.
+     * @see #registerControllerAlwaysOnStateCallback
+     * @hide
+     */
+    @SystemApi
+    public interface ControllerAlwaysOnStateCallback {
+        /**
+         * Called on NFC controller always on state changes
+         */
+        void onStateChanged(boolean isEnabled);
+    }
+
+    /**
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
@@ -744,6 +748,7 @@
         mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
         mTagRemovedListener = null;
         mLock = new Object();
+        mControllerAlwaysOnStateListener = new NfcControllerAlwaysOnStateListener(getService());
     }
 
     /**
@@ -2239,14 +2244,16 @@
     /**
      * Sets NFC controller always on feature.
      * <p>This API is for the NFCC internal state management. It allows to discriminate
-     * the controller function from the NFC function by keeping the NFC Controller on without
+     * the controller function from the NFC function by keeping the NFC controller on without
      * any NFC RF enabled if necessary.
-     * <p>This call is asynchronous. Listen for {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
-     * broadcasts to find out when the operation is complete.
-     * <p>If this returns true, then either NFCC is already on, or
-     * a {@link #ACTION_ALWAYS_ON_STATE_CHANGED} broadcast will be sent to indicate
-     * a state transition.
-     * If this returns false, then there is some problem that prevents an attempt to turn NFCC on.
+     * <p>This call is asynchronous. Register a callback {@link #ControllerAlwaysOnStateCallback}
+     * by {@link #registerControllerAlwaysOnStateCallback} to find out when the operation is
+     * complete.
+     * <p>If this returns true, then either NFCC always on state has been set based on the value,
+     * or a {@link ControllerAlwaysOnStateCallback#onStateChanged(boolean)} will be invoked to
+     * indicate the state change.
+     * If this returns false, then there is some problem that prevents an attempt to turn NFCC
+     * always on.
      * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
      * disabled), if false the NFCC will follow completely the Nfc adapter state.
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
@@ -2284,7 +2291,6 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      * @hide
      */
-
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOn() {
@@ -2313,7 +2319,6 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      * @hide
      */
-
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOnSupported() {
@@ -2337,4 +2342,39 @@
             return false;
         }
     }
+
+    /**
+     * Register a {@link ControllerAlwaysOnStateCallback} to listen for NFC controller always on
+     * state changes
+     * <p>The provided callback will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void registerControllerAlwaysOnStateCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        mControllerAlwaysOnStateListener.register(executor, callback);
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnStateCallback}
+     * <p>The same {@link ControllerAlwaysOnStateCallback} object used when calling
+     * {@link #registerControllerAlwaysOnStateCallback(Executor, ControllerAlwaysOnStateCallback)}
+     * must be used.
+     *
+     * <p>Callbacks are automatically unregistered when application process goes away
+     *
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void unregisterControllerAlwaysOnStateCallback(
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        mControllerAlwaysOnStateListener.unregister(callback);
+    }
 }
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
new file mode 100644
index 0000000..69a9ec7
--- /dev/null
+++ b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcControllerAlwaysOnStateListener extends INfcControllerAlwaysOnStateCallback.Stub {
+    private static final String TAG = "NfcControllerAlwaysOnStateListener";
+
+    private final INfcAdapter mAdapter;
+
+    private final Map<ControllerAlwaysOnStateCallback, Executor> mCallbackMap = new HashMap<>();
+
+    private boolean mCurrentState = false;
+    private boolean mIsRegistered = false;
+
+    public NfcControllerAlwaysOnStateListener(@NonNull INfcAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Register a {@link ControllerAlwaysOnStateCallback} with this
+     * {@link NfcControllerAlwaysOnStateListener}
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     */
+    public void register(@NonNull Executor executor,
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            if (mCallbackMap.containsKey(callback)) {
+                return;
+            }
+
+            mCallbackMap.put(callback, executor);
+            if (!mIsRegistered) {
+                try {
+                    mAdapter.registerControllerAlwaysOnStateCallback(this);
+                    mIsRegistered = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to register ControllerAlwaysOnStateListener");
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnStateCallback}
+     *
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     */
+    public void unregister(@NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            if (!mCallbackMap.containsKey(callback)) {
+                return;
+            }
+
+            mCallbackMap.remove(callback);
+
+            if (mCallbackMap.isEmpty() && mIsRegistered) {
+                try {
+                    mAdapter.unregisterControllerAlwaysOnStateCallback(this);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to unregister ControllerAlwaysOnStateListener");
+                }
+                mIsRegistered = false;
+            }
+        }
+    }
+
+    private void sendCurrentState(@NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            Executor executor = mCallbackMap.get(callback);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() -> callback.onStateChanged(
+                        mCurrentState));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void onControllerAlwaysOnStateChanged(boolean isEnabled) {
+        synchronized (this) {
+            mCurrentState = isEnabled;
+            for (ControllerAlwaysOnStateCallback cb : mCallbackMap.keySet()) {
+                sendCurrentState(cb);
+            }
+        }
+    }
+}
+
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 03caafd..e47ffcc 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1309,10 +1309,25 @@
      * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
      * application regardless of whether they have the "debuggable" attribute set, or downgrade
      * selinux into "permissive" mode in particular.
+     * @hide
      */
     public static final boolean IS_DEBUGGABLE =
             SystemProperties.getInt("ro.debuggable", 0) == 1;
 
+    /**
+     * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+     *
+     * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+     * application regardless of whether they have the "debuggable" attribute set, or downgrade
+     * selinux into "permissive" mode in particular.
+     * @hide
+     */
+    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static boolean isDebuggable() {
+        return IS_DEBUGGABLE;
+    }
+
     /** {@hide} */
     public static final boolean IS_ENG = "eng".equals(TYPE);
     /** {@hide} */
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
index 030b863..fb89eb0 100644
--- a/core/java/android/provider/SimPhonebookContract.java
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -17,13 +17,14 @@
 package android.provider;
 
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_SDN;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
@@ -78,11 +79,11 @@
     public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
         switch (efType) {
             case EF_ADN:
-                return EF_ADN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_ADN;
             case EF_FDN:
-                return EF_FDN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_FDN;
             case EF_SDN:
-                return EF_SDN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_SDN;
             default:
                 throw new IllegalArgumentException("Unsupported EfType " + efType);
         }
@@ -109,9 +110,9 @@
      * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH}
      * characters. The {@link SimRecords#NAME} column can contain at most
      * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM.
-     * Encoding is done internally and so the name should be provided unencoded but the number of
-     * bytes required to encode it will vary depending on the characters it contains. This length
-     * can be determined by calling
+     * Encoding is done internally and so the name should be provided to these provider APIs as a
+     * Java String but the number of bytes required to encode it for storage will vary depending on
+     * the characters it contains. This length can be determined by calling
      * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}.
      * </p>
      * <h3>Operations </h3>
@@ -308,7 +309,8 @@
          */
         @NonNull
         public static Uri getItemUri(
-                int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+                int subscriptionId, @ElementaryFiles.EfType int efType,
+                @IntRange(from = 1) int recordNumber) {
             // Elementary file record indices are 1-based.
             Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
 
@@ -332,6 +334,7 @@
          * @see ElementaryFiles#NAME_MAX_LENGTH
          */
         @WorkerThread
+        @IntRange(from = 0)
         public static int getEncodedNameLength(
                 @NonNull ContentResolver resolver, @NonNull String name) {
             Objects.requireNonNull(name);
@@ -442,12 +445,27 @@
          * methods operating on this Uri will throw UnsupportedOperationException
          */
         public static final int EF_SDN = 3;
-        /** @hide */
-        public static final String EF_ADN_PATH_SEGMENT = "adn";
-        /** @hide */
-        public static final String EF_FDN_PATH_SEGMENT = "fdn";
-        /** @hide */
-        public static final String EF_SDN_PATH_SEGMENT = "sdn";
+        /**
+         * The Uri path segment used to target the ADN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_ADN = "adn";
+        /**
+         * The Uri path segment used to target the FDN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_FDN = "fdn";
+        /**
+         * The Uri path segment used to target the SDN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_SDN = "sdn";
         /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
         /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index 2a9bbdf..9856553 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -60,6 +60,7 @@
      *
      * @return error of distance measurement in meters
      */
+    @FloatRange(from = 0.0)
     public double getErrorMeters() {
         return mErrorMeters;
     }
@@ -162,7 +163,7 @@
          * @throws IllegalArgumentException if error is negative or NaN
          */
         @NonNull
-        public Builder setErrorMeters(double errorMeters) {
+        public Builder setErrorMeters(@FloatRange(from = 0.0) double errorMeters) {
             if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
                 throw new IllegalArgumentException(
                         "errorMeters must be >= 0.0 and not NaN: " + errorMeters);
@@ -178,7 +179,8 @@
          * @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
          */
         @NonNull
-        public Builder setConfidenceLevel(double confidenceLevel) {
+        public Builder setConfidenceLevel(
+                @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
             if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
                 throw new IllegalArgumentException(
                         "confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 4036892..30da248 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -160,14 +160,4 @@
    * closed.
    */
   const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
-
-  /**
-   * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY
-   */
-  const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416;
-
-  /**
-   * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY
-   */
-  const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1;
 }
diff --git a/core/java/com/android/internal/os/BINDER_OWNERS b/core/java/com/android/internal/os/BINDER_OWNERS
new file mode 100644
index 0000000..9f68a32
--- /dev/null
+++ b/core/java/com/android/internal/os/BINDER_OWNERS
@@ -0,0 +1,2 @@
+dplotnikov@google.com
+gaillard@google.com
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 3f01ebb..ea3b3a7 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,6 +1,7 @@
 per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
 per-file *Zygote* = file:/ZYGOTE_OWNERS
 per-file *Cpu* = file:CPU_OWNERS
+per-file *Binder* = file:BINDER_OWNERS
 
 # BatteryStats
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1f7695e..cc4e2bb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -313,7 +313,6 @@
     <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED" />
 
     <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
-    <protected-broadcast android:name="android.nfc.action.ALWAYS_ON_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
     <protected-broadcast android:name="android.nfc.action.REQUIRE_UNLOCK_FOR_NFC" />
@@ -333,7 +332,7 @@
     <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE" />
     <protected-broadcast android:name="com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER" />
 
-    <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+    <protected-broadcast android:name="android.net.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
     <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 43fdedc..aef68d0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -410,6 +410,7 @@
         <permission name="android.permission.LOG_COMPAT_CHANGE" />
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
         <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" />
+        <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" />
         <!-- Permissions required to test ambient display. -->
         <permission name="android.permission.READ_DREAM_STATE" />
         <permission name="android.permission.WRITE_DREAM_STATE" />
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1f9022b..a6aa4f2 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -353,7 +353,7 @@
             boolean userPresenceRequired,
             byte[] attestationChallenge,
             boolean devicePropertiesAttestationIncluded,
-            int[] attestationIds,
+            @NonNull int[] attestationIds,
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
@@ -779,9 +779,8 @@
      * @return integer array representing the requested device IDs to attest.
      */
     @SystemApi
-    @Nullable
-    public int[] getAttestationIds() {
-        return Utils.cloneIfNotNull(mAttestationIds);
+    public @NonNull int[] getAttestationIds() {
+        return mAttestationIds.clone();
     }
 
     /**
@@ -911,7 +910,7 @@
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mDevicePropertiesAttestationIncluded = false;
-        private int[] mAttestationIds = null;
+        private int[] mAttestationIds = new int[0];
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index c26d9f583..dc7f3dd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -655,7 +655,7 @@
             }
 
             int[] idTypes = mSpec.getAttestationIds();
-            if (idTypes == null) {
+            if (idTypes.length == 0) {
                 return;
             }
             final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
diff --git a/native/android/Android.bp b/native/android/Android.bp
index d1dddbd..34b9fce 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -77,6 +77,7 @@
         "libgui",
         "libharfbuzz_ng",  // Only for including hb.h via minikin
         "libsensor",
+        "libactivitymanager_aidl",
         "libandroid_runtime",
         "libminikin",
         "libnetd_client",
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index ab290f9..7692e30 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -410,6 +410,8 @@
   }
 
   public class ParseException extends java.lang.RuntimeException {
+    ctor public ParseException(@NonNull String);
+    ctor public ParseException(@NonNull String, @NonNull Throwable);
     field public String response;
   }
 
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 513b630..caf7f49 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -26,6 +26,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
     method public void systemReady();
+    field public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
     field public static final String ACTION_PROMPT_LOST_VALIDATION = "android.net.action.PROMPT_LOST_VALIDATION";
     field public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY = "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
     field public static final String ACTION_PROMPT_UNVALIDATED = "android.net.action.PROMPT_UNVALIDATED";
@@ -132,11 +133,6 @@
     method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
   }
 
-  public class ParseException extends java.lang.RuntimeException {
-    ctor public ParseException(@NonNull String);
-    ctor public ParseException(@NonNull String, @NonNull Throwable);
-  }
-
   public final class TcpRepairWindow {
     ctor public TcpRepairWindow(int, int, int, int, int, int);
     field public final int maxWindow;
@@ -157,10 +153,10 @@
   }
 
   public class TestNetworkManager {
-    method @NonNull public android.net.TestNetworkInterface createTapInterface();
-    method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
-    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
-    method public void teardownTestNetwork(@NonNull android.net.Network);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public android.net.TestNetworkInterface createTapInterface();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public void teardownTestNetwork(@NonNull android.net.Network);
     field public static final String TEST_TAP_PREFIX = "testtap";
   }
 
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 0a82cb7..d6d3888 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -455,14 +455,14 @@
   public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
     ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
     method public int describeContents();
+    method public int getIpTos();
+    method public int getIpTtl();
+    method public int getTcpAck();
+    method public int getTcpSeq();
+    method public int getTcpWindow();
+    method public int getTcpWindowScale();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
-    field public final int ipTos;
-    field public final int ipTtl;
-    field public final int tcpAck;
-    field public final int tcpSeq;
-    field public final int tcpWindow;
-    field public final int tcpWindowScale;
   }
 
 }
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index 82dbd0f..53aa1b9 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -42,7 +42,7 @@
     private final long mByteLimit;
     private final long mExpiryTimeMillis;
     private final boolean mCaptive;
-    private final CharSequence mVenueFriendlyName;
+    private final String mVenueFriendlyName;
     private final int mVenueInfoUrlSource;
     private final int mUserPortalUrlSource;
 
@@ -73,14 +73,14 @@
         mByteLimit = byteLimit;
         mExpiryTimeMillis = expiryTimeMillis;
         mCaptive = captive;
-        mVenueFriendlyName = venueFriendlyName;
+        mVenueFriendlyName = venueFriendlyName == null ? null : venueFriendlyName.toString();
         mVenueInfoUrlSource = venueInfoUrlSource;
         mUserPortalUrlSource = userPortalUrlSource;
     }
 
     private CaptivePortalData(Parcel p) {
         this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
-                p.readLong(), p.readLong(), p.readBoolean(), p.readCharSequence(), p.readInt(),
+                p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(),
                 p.readInt());
     }
 
@@ -98,7 +98,7 @@
         dest.writeLong(mByteLimit);
         dest.writeLong(mExpiryTimeMillis);
         dest.writeBoolean(mCaptive);
-        dest.writeCharSequence(mVenueFriendlyName);
+        dest.writeString(mVenueFriendlyName);
         dest.writeInt(mVenueInfoUrlSource);
         dest.writeInt(mUserPortalUrlSource);
     }
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 80bbf0c..0418450 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -451,6 +451,15 @@
             "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
 
     /**
+     * Clear DNS Cache Action: This is broadcast when networks have changed and old
+     * DNS entries should be cleared.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
+
+    /**
      * Invalid tethering type.
      * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
index 0741414..41fad63 100644
--- a/packages/Connectivity/framework/src/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -27,7 +27,6 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -526,11 +525,4 @@
     public String toString() {
         return Integer.toString(netId);
     }
-
-    /** @hide */
-    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        proto.write(NetworkProto.NET_ID, netId);
-        proto.end(token);
-    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 9a75f0b..a43dd15 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -35,7 +35,6 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Range;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.CollectionUtils;
@@ -2060,34 +2059,6 @@
         }
     }
 
-    /** @hide */
-    public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-
-        for (int transport : getTransportTypes()) {
-            proto.write(NetworkCapabilitiesProto.TRANSPORTS, transport);
-        }
-
-        for (int capability : getCapabilities()) {
-            proto.write(NetworkCapabilitiesProto.CAPABILITIES, capability);
-        }
-
-        proto.write(NetworkCapabilitiesProto.LINK_UP_BANDWIDTH_KBPS, mLinkUpBandwidthKbps);
-        proto.write(NetworkCapabilitiesProto.LINK_DOWN_BANDWIDTH_KBPS, mLinkDownBandwidthKbps);
-
-        if (mNetworkSpecifier != null) {
-            proto.write(NetworkCapabilitiesProto.NETWORK_SPECIFIER, mNetworkSpecifier.toString());
-        }
-        if (mTransportInfo != null) {
-            // TODO b/120653863: write transport-specific info to proto?
-        }
-
-        proto.write(NetworkCapabilitiesProto.CAN_REPORT_SIGNAL_STRENGTH, hasSignalStrength());
-        proto.write(NetworkCapabilitiesProto.SIGNAL_STRENGTH, mSignalStrength);
-
-        proto.end(token);
-    }
-
     /**
      * @hide
      */
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 5d40417..3a8a07a 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -47,7 +47,6 @@
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.Range;
-import android.util.proto.ProtoOutputStream;
 
 import java.util.Arrays;
 import java.util.List;
@@ -680,18 +679,6 @@
         }
     }
 
-    /** @hide */
-    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-
-        proto.write(NetworkRequestProto.TYPE, typeToProtoEnum(type));
-        proto.write(NetworkRequestProto.REQUEST_ID, requestId);
-        proto.write(NetworkRequestProto.LEGACY_TYPE, legacyType);
-        networkCapabilities.dumpDebug(proto, NetworkRequestProto.NETWORK_CAPABILITIES);
-
-        proto.end(token);
-    }
-
     public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 5a76cd6..2bb006d 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -40,6 +40,23 @@
  */
 @SystemApi
 public final class OemNetworkPreferences implements Parcelable {
+    // Valid production preferences must be > 0, negative values reserved for testing
+    /**
+     * This preference is only to be used for testing and nothing else.
+     * Use only TRANSPORT_TEST transport networks.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_TEST_ONLY = -2;
+
+    /**
+     * This preference is only to be used for testing and nothing else.
+     * If an unmetered network is available, use it.
+     * Otherwise, if a network with the TRANSPORT_TEST transport is available, use it.
+     * Otherwise, use the general default network.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_TEST = -1;
+
     /**
      * Default in case this value is not set. Using it will result in an error.
      */
@@ -69,6 +86,12 @@
      */
     public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
 
+    /**
+     * The max allowed value for an OEM network preference.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_MAX = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
     @NonNull
     private final Bundle mNetworkMappings;
 
@@ -96,7 +119,7 @@
 
     @Override
     public String toString() {
-        return "OemNetworkPreferences{" + "mNetworkMappings=" + mNetworkMappings + '}';
+        return "OemNetworkPreferences{" + "mNetworkMappings=" + getNetworkPreferences() + '}';
     }
 
     @Override
@@ -185,6 +208,8 @@
 
     /** @hide */
     @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
+            OEM_NETWORK_PREFERENCE_TEST_ONLY,
+            OEM_NETWORK_PREFERENCE_TEST,
             OEM_NETWORK_PREFERENCE_UNINITIALIZED,
             OEM_NETWORK_PREFERENCE_OEM_PAID,
             OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK,
@@ -205,6 +230,10 @@
     @NonNull
     public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) {
         switch (value) {
+            case OEM_NETWORK_PREFERENCE_TEST_ONLY:
+                return "OEM_NETWORK_PREFERENCE_TEST_ONLY";
+            case OEM_NETWORK_PREFERENCE_TEST:
+                return "OEM_NETWORK_PREFERENCE_TEST";
             case OEM_NETWORK_PREFERENCE_UNINITIALIZED:
                 return "OEM_NETWORK_PREFERENCE_UNINITIALIZED";
             case OEM_NETWORK_PREFERENCE_OEM_PAID:
diff --git a/packages/Connectivity/framework/src/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
index ca6d012..9d4727a 100644
--- a/packages/Connectivity/framework/src/android/net/ParseException.java
+++ b/packages/Connectivity/framework/src/android/net/ParseException.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 
 /**
  * Thrown when parsing failed.
@@ -26,15 +25,11 @@
 public class ParseException extends RuntimeException {
     public String response;
 
-    /** @hide */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public ParseException(@NonNull String response) {
         super(response);
         this.response = response;
     }
 
-    /** @hide */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public ParseException(@NonNull String response, @NonNull Throwable cause) {
         super(response, cause);
         this.response = response;
diff --git a/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
index ddb3a6a7..c2c4f32 100644
--- a/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
+++ b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
@@ -32,22 +32,39 @@
 public final class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
     private static final String TAG = "TcpKeepalivePacketData";
 
-    /** TCP sequence number. */
+    /**
+     * TCP sequence number.
+     * @hide
+     */
     public final int tcpSeq;
 
-    /** TCP ACK number. */
+    /**
+     * TCP ACK number.
+     * @hide
+     */
     public final int tcpAck;
 
-    /** TCP RCV window. */
+    /**
+     * TCP RCV window.
+     * @hide
+     */
     public final int tcpWindow;
 
-    /** TCP RCV window scale. */
+    /** TCP RCV window scale.
+     * @hide
+     */
     public final int tcpWindowScale;
 
-    /** IP TOS. */
+    /**
+     * IP TOS.
+     * @hide
+     */
     public final int ipTos;
 
-    /** IP TTL. */
+    /**
+     * IP TTL.
+     * @hide
+     */
     public final int ipTtl;
 
     public TcpKeepalivePacketData(@NonNull final InetAddress srcAddress, int srcPort,
@@ -63,6 +80,56 @@
         this.ipTtl = ipTtl;
     }
 
+    /**
+     * Get the TCP sequence number.
+     *
+     * See https://tools.ietf.org/html/rfc793#page-15.
+     */
+    public int getTcpSeq() {
+        return tcpSeq;
+    }
+
+    /**
+     * Get the TCP ACK number.
+     *
+     * See https://tools.ietf.org/html/rfc793#page-15.
+     */
+    public int getTcpAck() {
+        return tcpAck;
+    }
+
+    /**
+     * Get the TCP RCV window.
+     *
+     * See https://tools.ietf.org/html/rfc793#page-15.
+     */
+    public int getTcpWindow() {
+        return tcpWindow;
+    }
+
+    /**
+     * Get the TCP RCV window scale.
+     *
+     * See https://tools.ietf.org/html/rfc793#page-15.
+     */
+    public int getTcpWindowScale() {
+        return tcpWindowScale;
+    }
+
+    /**
+     * Get the IP type of service.
+     */
+    public int getIpTos() {
+        return ipTos;
+    }
+
+    /**
+     * Get the IP TTL.
+     */
+    public int getIpTtl() {
+        return ipTtl;
+    }
+
     @Override
     public boolean equals(@Nullable final Object o) {
         if (!(o instanceof TcpKeepalivePacketData)) return false;
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
index a7a6235..9ddd2f5 100644
--- a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
+++ b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
@@ -15,8 +15,10 @@
  */
 package android.net;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -58,6 +60,7 @@
      * @param network The test network that should be torn down
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void teardownTestNetwork(@NonNull Network network) {
         try {
@@ -103,6 +106,7 @@
      * @param binder A binder object guarding the lifecycle of this test network.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
         setupTestNetwork(iface, null, true, new int[0], binder);
@@ -145,6 +149,7 @@
      *     TUN interface.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @NonNull
     public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
@@ -163,6 +168,7 @@
      *     TAP interface.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @NonNull
     public TestNetworkInterface createTapInterface() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d0dc987..bd5f8ab 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -251,10 +251,11 @@
     <!-- permissions required for CTS test - PhoneStateListenerTest -->
     <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
 
-    <!-- Permissions required for ganting and logging -->
+    <!-- Permissions required for granting and logging -->
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
     <!-- Permission required for CTS test - BatterySaverTest -->
     <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5aa3e5b..3066717 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -7677,7 +7677,7 @@
         }
 
         void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
-            if (Build.IS_DEBUGGABLE) {
+            if (Build.isDebuggable()) {
                 // The code is never supposed to add two reassignments of the same request. Make
                 // sure this stays true, but without imposing this expensive check on all
                 // reassignments on all user devices.
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 3bcde12..9821cdc 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -20,6 +20,7 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *Battery* = file:/BATTERY_STATS_OWNERS
+per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 351e616..c6fdb9d 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -45,6 +44,7 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.security.MessageDigest;
@@ -131,13 +131,13 @@
     private static final String FLASH_LOCK_UNLOCKED = "0";
 
     private final Context mContext;
+    private final String mDataBlockFile;
     private final boolean mIsRunningDSU;
     private final Object mLock = new Object();
     private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
 
     private int mAllowedUid = -1;
     private long mBlockDeviceSize;
-    private String mDataBlockFile;
 
     @GuardedBy("mLock")
     private boolean mIsWritable = true;
@@ -145,8 +145,12 @@
     public PersistentDataBlockService(Context context) {
         super(context);
         mContext = context;
-        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
         mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
+        if (mIsRunningDSU) {
+            mDataBlockFile = GSI_SANDBOX;
+        } else {
+            mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+        }
         mBlockDeviceSize = -1; // Load lazily
     }
 
@@ -260,7 +264,11 @@
     private long getBlockDeviceSize() {
         synchronized (mLock) {
             if (mBlockDeviceSize == -1) {
-                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
+                if (mIsRunningDSU) {
+                    mBlockDeviceSize = MAX_DATA_BLOCK_SIZE;
+                } else {
+                    mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
+                }
             }
         }
 
@@ -290,40 +298,30 @@
         return true;
     }
 
-    private FileOutputStream getBlockOutputStream() throws IOException {
-        if (!mIsRunningDSU) {
-            return new FileOutputStream(new File(mDataBlockFile));
-        } else {
-            File sandbox = new File(GSI_SANDBOX);
-            File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
-            if (!sandbox.exists()) {
-                FileUtils.copy(realpdb, sandbox);
-                mDataBlockFile = GSI_SANDBOX;
-            }
-            Slog.i(TAG, "PersistentDataBlock copy-on-write");
-            return new FileOutputStream(sandbox);
-        }
+    private FileChannel getBlockOutputChannel() throws IOException {
+        return new RandomAccessFile(mDataBlockFile, "rw").getChannel();
     }
 
     private boolean computeAndWriteDigestLocked() {
         byte[] digest = computeDigestLocked(null);
         if (digest != null) {
-            DataOutputStream outputStream;
+            FileChannel channel;
             try {
-                outputStream = new DataOutputStream(getBlockOutputStream());
+                channel = getBlockOutputChannel();
             } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return false;
             }
 
             try {
-                outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
-                outputStream.flush();
+                ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES);
+                buf.put(digest);
+                buf.flip();
+                channel.write(buf);
+                channel.force(true);
             } catch (IOException e) {
                 Slog.e(TAG, "failed to write block checksum", e);
                 return false;
-            } finally {
-                IoUtils.closeQuietly(outputStream);
             }
             return true;
         } else {
@@ -374,25 +372,18 @@
     }
 
     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
-        DataOutputStream outputStream;
-        try {
-            outputStream = new DataOutputStream(getBlockOutputStream());
-        } catch (IOException e) {
-            Slog.e(TAG, "partition not available?", e);
-            return;
-        }
 
-        byte[] data = new byte[DIGEST_SIZE_BYTES];
         try {
-            outputStream.write(data, 0, DIGEST_SIZE_BYTES);
-            outputStream.writeInt(PARTITION_TYPE_MARKER);
-            outputStream.writeInt(0); // data size
-            outputStream.flush();
+            FileChannel channel = getBlockOutputChannel();
+            ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES + HEADER_SIZE);
+            buf.put(new byte[DIGEST_SIZE_BYTES]);
+            buf.putInt(PARTITION_TYPE_MARKER);
+            buf.putInt(0);
+            channel.write(buf);
+            channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "failed to format block", e);
             return;
-        } finally {
-            IoUtils.closeQuietly(outputStream);
         }
 
         doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
@@ -400,16 +391,9 @@
     }
 
     private void doSetOemUnlockEnabledLocked(boolean enabled) {
-        FileOutputStream outputStream;
-        try {
-            outputStream = getBlockOutputStream();
-        } catch (IOException e) {
-            Slog.e(TAG, "partition not available", e);
-            return;
-        }
 
         try {
-            FileChannel channel = outputStream.getChannel();
+            FileChannel channel = getBlockOutputChannel();
 
             channel.position(getBlockDeviceSize() - 1);
 
@@ -417,13 +401,12 @@
             data.put(enabled ? (byte) 1 : (byte) 0);
             data.flip();
             channel.write(data);
-            outputStream.flush();
+            channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "unable to access persistent partition", e);
             return;
         } finally {
             SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
-            IoUtils.closeQuietly(outputStream);
         }
     }
 
@@ -477,35 +460,32 @@
                 return (int) -maxBlockSize;
             }
 
-            DataOutputStream outputStream;
+            FileChannel channel;
             try {
-                outputStream = new DataOutputStream(getBlockOutputStream());
+                channel = getBlockOutputChannel();
             } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
-                return -1;
+               return -1;
             }
 
-            ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
+            ByteBuffer headerAndData = ByteBuffer.allocate(
+                                           data.length + HEADER_SIZE + DIGEST_SIZE_BYTES);
+            headerAndData.put(new byte[DIGEST_SIZE_BYTES]);
             headerAndData.putInt(PARTITION_TYPE_MARKER);
             headerAndData.putInt(data.length);
             headerAndData.put(data);
-
+            headerAndData.flip();
             synchronized (mLock) {
                 if (!mIsWritable) {
-                    IoUtils.closeQuietly(outputStream);
                     return -1;
                 }
 
                 try {
-                    byte[] checksum = new byte[DIGEST_SIZE_BYTES];
-                    outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
-                    outputStream.write(headerAndData.array());
-                    outputStream.flush();
+                    channel.write(headerAndData);
+                    channel.force(true);
                 } catch (IOException e) {
                     Slog.e(TAG, "failed writing to the persistent data block", e);
                     return -1;
-                } finally {
-                    IoUtils.closeQuietly(outputStream);
                 }
 
                 if (computeAndWriteDigestLocked()) {
@@ -565,17 +545,6 @@
         public void wipe() {
             enforceOemUnlockWritePermission();
 
-            if (mIsRunningDSU) {
-                File sandbox = new File(GSI_SANDBOX);
-                if (sandbox.exists()) {
-                    if (sandbox.delete()) {
-                        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
-                    } else {
-                        Slog.e(TAG, "Failed to wipe sandbox persistent data block");
-                    }
-                }
-                return;
-            }
             synchronized (mLock) {
                 int ret = nativeWipe(mDataBlockFile);
 
@@ -733,28 +702,18 @@
         }
 
         private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
-            FileOutputStream outputStream;
-            try {
-                outputStream = getBlockOutputStream();
-            } catch (IOException e) {
-                Slog.e(TAG, "partition not available", e);
-                return;
-            }
             synchronized (mLock) {
                 if (!mIsWritable) {
-                    IoUtils.closeQuietly(outputStream);
                     return;
                 }
                 try {
-                    FileChannel channel = outputStream.getChannel();
+                    FileChannel channel = getBlockOutputChannel();
                     channel.position(offset);
                     channel.write(dataBuffer);
-                    outputStream.flush();
+                    channel.force(true);
                 } catch (IOException e) {
                     Slog.e(TAG, "unable to access persistent partition", e);
                     return;
-                } finally {
-                    IoUtils.closeQuietly(outputStream);
                 }
 
                 computeAndWriteDigestLocked();
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f7ae58c..fc1162e 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.DUMP;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
@@ -69,6 +70,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
@@ -76,7 +78,9 @@
 import com.android.server.vcn.VcnNetworkProvider;
 import com.android.server.vcn.util.PersistableBundleUtils;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -929,6 +933,33 @@
         }
     }
 
+    /**
+     * Dumps the state of the VcnManagementService for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+
+        pw.println("VcnManagementService dump:");
+        pw.increaseIndent();
+
+        mNetworkProvider.dump(pw);
+
+        synchronized (mLock) {
+            pw.println("mVcns:");
+            for (Vcn vcn : mVcns.values()) {
+                vcn.dump(pw);
+            }
+            pw.println();
+        }
+
+        pw.decreaseIndent();
+    }
+
     // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
     /** Callback for Vcn signals sent up to VcnManagementService. */
     public interface VcnCallback {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cc5a25a..2744f11 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -236,6 +236,7 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.location.LocationManager;
 import android.media.audiofx.AudioEffect;
+import android.net.ConnectivityManager;
 import android.net.Proxy;
 import android.net.Uri;
 import android.os.AppZygote;
@@ -16458,7 +16459,7 @@
                         stats.noteCurrentTimeChangedLocked();
                     }
                     break;
-                case Intent.ACTION_CLEAR_DNS_CACHE:
+                case ConnectivityManager.ACTION_CLEAR_DNS_CACHE:
                     mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
                     break;
                 case Proxy.PROXY_CHANGE_ACTION:
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index ffeb77d..cf4fe1e 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -420,7 +420,7 @@
         /*
          * Tell the VMs to toss their DNS caches
          */
-        final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+        final Intent intent = new Intent(ConnectivityManager.ACTION_CLEAR_DNS_CACHE);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         /*
          * Connectivity events can happen before boot has completed ...
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 470a25b..3d1a49e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -201,7 +201,6 @@
 
     // Only source devices that react to routing control messages should implement
     // this method (e.g. a TV with built in switch).
-    // TODO(): decide which type will handle the routing when multi device type is supported
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
         // do nothing
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 58a7025..8bb5204 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -800,7 +800,9 @@
         assertRunOnServiceThread();
         if (!mService.isPowerStandbyOrTransient()) {
             addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
-            if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+            if (!isDirectConnectAddress(avr.getPhysicalAddress())) {
+                startArcAction(false);
+            } else if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
                     && !hasAction(SetArcTransmissionStateAction.class)) {
                 startArcAction(true);
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 290307b..319800d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -5857,7 +5857,8 @@
         return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
     }
 
-    private class UidBlockedState {
+    @VisibleForTesting
+    static final class UidBlockedState {
         public int blockedReasons;
         public int allowedReasons;
         public int effectiveBlockedReasons;
@@ -5869,16 +5870,21 @@
         }
 
         void updateEffectiveBlockedReasons() {
-            effectiveBlockedReasons = blockedReasons;
+            effectiveBlockedReasons = getEffectiveBlockedReasons(blockedReasons, allowedReasons);
+        }
+
+        @VisibleForTesting
+        static int getEffectiveBlockedReasons(int blockedReasons, int allowedReasons) {
+            int effectiveBlockedReasons = blockedReasons;
             // If the uid is not subject to any blocked reasons, then return early
             if (blockedReasons == BLOCKED_REASON_NONE) {
-                return;
+                return effectiveBlockedReasons;
             }
             if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) {
-                effectiveBlockedReasons = (blockedReasons & ALLOWED_METERED_REASON_MASK);
+                effectiveBlockedReasons &= ALLOWED_METERED_REASON_MASK;
             }
             if ((allowedReasons & ALLOWED_METERED_REASON_SYSTEM) != 0) {
-                effectiveBlockedReasons = (blockedReasons & ~ALLOWED_METERED_REASON_MASK);
+                effectiveBlockedReasons &= ~ALLOWED_METERED_REASON_MASK;
             }
             if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) {
                 effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
@@ -5904,6 +5910,7 @@
             if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
                 effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
             }
+            return effectiveBlockedReasons;
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0e4a2ee..ca9c75f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -267,7 +267,7 @@
                                 mArtStatsLogger,
                                 sessionId,
                                 compilerFilter,
-                                sharedGid,
+                                pkg.getUid(),
                                 packageStats.getCompileTime(path),
                                 dexMetadataPath,
                                 options.getCompilationReason(),
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 13798ba..d67796b 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -128,18 +128,12 @@
     private static final Map<String, Integer> ISA_MAP = new HashMap();
 
     static {
-        COMPILE_FILTER_MAP.put("arm", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
-        COMPILE_FILTER_MAP.put("arm64", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
-        COMPILE_FILTER_MAP.put("x86", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_X86);
-        COMPILE_FILTER_MAP.put("x86_64", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
-        COMPILE_FILTER_MAP.put("mips", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
-        COMPILE_FILTER_MAP.put("mips64", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
+        ISA_MAP.put("arm", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
+        ISA_MAP.put("arm64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
+        ISA_MAP.put("x86", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_X86);
+        ISA_MAP.put("x86_64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
+        ISA_MAP.put("mips", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
+        ISA_MAP.put("mips64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
     }
 
     public static void writeStatsLog(
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index ae806aa..7bc6056 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.VcnManagementService.VcnCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
@@ -328,6 +329,8 @@
 
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
+        Slog.v(getLogTag(), "Received request " + request);
+
         if (score > getNetworkScore()) {
             if (VDBG) {
                 Slog.v(
@@ -409,6 +412,26 @@
         return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
     }
 
+    /**
+     * Dumps the state of this Vcn for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("Vcn (" + mSubscriptionGroup + "):");
+        pw.increaseIndent();
+
+        pw.println("mCurrentStatus: " + mCurrentStatus);
+
+        pw.println("mVcnGatewayConnections:");
+        for (VcnGatewayConnection gw : mVcnGatewayConnections.values()) {
+            gw.dump(pw);
+        }
+        pw.println();
+
+        pw.decreaseIndent();
+    }
+
     /** Retrieves the network score for a VCN Network */
     // Package visibility for use in VcnGatewayConnection
     static int getNetworkScore() {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 20c08eb..83ac36f 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -77,6 +77,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.WakeupMessage;
@@ -84,6 +85,7 @@
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
 import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.util.MtuUtils;
 
 import java.io.IOException;
 import java.net.Inet4Address;
@@ -448,6 +450,44 @@
      */
     private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10;
 
+    /**
+     * Sent when an IKE has completed migration, and created updated transforms for application.
+     *
+     * <p>Only relevant in the Connected state.
+     *
+     * @param arg1 The session token for the IKE Session that completed migration, used to prevent
+     *     out-of-date signals from propagating.
+     * @param obj @NonNull An EventMigrationCompletedInfo instance with relevant data.
+     */
+    private static final int EVENT_MIGRATION_COMPLETED = 11;
+
+    private static class EventMigrationCompletedInfo implements EventInfo {
+        @NonNull public final IpSecTransform inTransform;
+        @NonNull public final IpSecTransform outTransform;
+
+        EventMigrationCompletedInfo(
+                @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
+            this.inTransform = Objects.requireNonNull(inTransform);
+            this.outTransform = Objects.requireNonNull(outTransform);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(inTransform, outTransform);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventMigrationCompletedInfo)) {
+                return false;
+            }
+
+            final EventMigrationCompletedInfo rhs = (EventMigrationCompletedInfo) other;
+            return Objects.equals(inTransform, rhs.inTransform)
+                    && Objects.equals(outTransform, rhs.outTransform);
+        }
+    }
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
     final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -574,7 +614,7 @@
      * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
      * otherwise.
      */
-    private NetworkAgent mNetworkAgent;
+    private VcnNetworkAgent mNetworkAgent;
 
     @Nullable private WakeupMessage mTeardownTimeoutAlarm;
     @Nullable private WakeupMessage mDisconnectRequestAlarm;
@@ -1053,6 +1093,14 @@
         sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
     }
 
+    private void migrationCompleted(
+            int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
+        sendMessageAndAcquireWakeLock(
+                EVENT_MIGRATION_COMPLETED,
+                token,
+                new EventMigrationCompletedInfo(inTransform, outTransform));
+    }
+
     private void childTransformCreated(
             int token, @NonNull IpSecTransform transform, int direction) {
         sendMessageAndAcquireWakeLock(
@@ -1148,7 +1196,9 @@
                 case EVENT_SETUP_COMPLETED: // Fallthrough
                 case EVENT_DISCONNECT_REQUESTED: // Fallthrough
                 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
-                case EVENT_SUBSCRIPTIONS_CHANGED:
+                case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
+                case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
+                case EVENT_MIGRATION_COMPLETED:
                     logUnexpectedEvent(msg.what);
                     break;
                 default:
@@ -1440,30 +1490,32 @@
     private abstract class ConnectedStateBase extends ActiveBaseState {
         protected void updateNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull NetworkAgent agent,
+                @NonNull VcnNetworkAgent agent,
                 @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
-                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+                    buildConnectedLinkProperties(
+                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
 
             agent.sendNetworkCapabilities(caps);
             agent.sendLinkProperties(lp);
         }
 
-        protected NetworkAgent buildNetworkAgent(
+        protected VcnNetworkAgent buildNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
-                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+                    buildConnectedLinkProperties(
+                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
             final NetworkAgentConfig nac =
                     new NetworkAgentConfig.Builder()
                             .setLegacyType(ConnectivityManager.TYPE_MOBILE)
                             .build();
 
-            final NetworkAgent agent =
+            final VcnNetworkAgent agent =
                     mDeps.newNetworkAgent(
                             mVcnContext,
                             TAG,
@@ -1472,15 +1524,21 @@
                             Vcn.getNetworkScore(),
                             nac,
                             mVcnContext.getVcnNetworkProvider(),
-                            () -> {
-                                Slog.d(TAG, "NetworkAgent was unwanted");
-                                // If network agent has already been torn down, skip sending the
-                                // disconnect. Unwanted() is always called, even when networkAgents
-                                // are unregistered in teardownNetwork(), so prevent duplicate
-                                // notifications.
-                                if (mNetworkAgent != null) {
-                                    teardownAsynchronously();
+                            (agentRef) -> {
+                                // Only trigger teardown if the NetworkAgent hasn't been replaced or
+                                // changed. This guards against two cases - the first where
+                                // unwanted() may be called as a result of the
+                                // NetworkAgent.unregister() call, which might trigger a teardown
+                                // instead of just a Network disconnect, as well as the case where a
+                                // new NetworkAgent replaces an old one before the unwanted() call
+                                // is processed.
+                                if (mNetworkAgent != agentRef) {
+                                    Slog.d(TAG, "unwanted() called on stale NetworkAgent");
+                                    return;
                                 }
+
+                                Slog.d(TAG, "NetworkAgent was unwanted");
+                                teardownAsynchronously();
                             } /* networkUnwantedCallback */,
                             (status) -> {
                                 if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
@@ -1620,12 +1678,36 @@
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
                     handleSafeModeTimeoutExceeded();
                     break;
+                case EVENT_MIGRATION_COMPLETED:
+                    final EventMigrationCompletedInfo migrationCompletedInfo =
+                            (EventMigrationCompletedInfo) msg.obj;
+
+                    handleMigrationCompleted(migrationCompletedInfo);
+                    break;
                 default:
                     logUnhandledMessage(msg);
                     break;
             }
         }
 
+        private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
+            applyTransform(
+                    mCurrentToken,
+                    mTunnelIface,
+                    mUnderlying.network,
+                    migrationCompletedInfo.inTransform,
+                    IpSecManager.DIRECTION_IN);
+
+            applyTransform(
+                    mCurrentToken,
+                    mTunnelIface,
+                    mUnderlying.network,
+                    migrationCompletedInfo.outTransform,
+                    IpSecManager.DIRECTION_OUT);
+
+            updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+        }
+
         private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
             final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
             mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
@@ -1815,7 +1897,10 @@
     private static LinkProperties buildConnectedLinkProperties(
             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
             @NonNull IpSecTunnelInterface tunnelIface,
-            @NonNull VcnChildSessionConfiguration childConfig) {
+            @NonNull VcnChildSessionConfiguration childConfig,
+            @Nullable UnderlyingNetworkRecord underlying) {
+        final VcnControlPlaneIkeConfig controlPlaneConfig =
+                (VcnControlPlaneIkeConfig) gatewayConnectionConfig.getControlPlaneConfig();
         final LinkProperties lp = new LinkProperties();
 
         lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1831,7 +1916,12 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
                 null /*iface*/, RouteInfo.RTN_UNICAST));
 
-        lp.setMtu(gatewayConnectionConfig.getMaxMtu());
+        final int underlyingMtu = (underlying == null) ? 0 : underlying.linkProperties.getMtu();
+        lp.setMtu(
+                MtuUtils.getMtu(
+                        controlPlaneConfig.getChildSessionParams().getSaProposals(),
+                        gatewayConnectionConfig.getMaxMtu(),
+                        underlyingMtu));
 
         return lp;
     }
@@ -1912,8 +2002,7 @@
                 @NonNull IpSecTransform inIpSecTransform,
                 @NonNull IpSecTransform outIpSecTransform) {
             Slog.v(TAG, "ChildTransformsMigrated; token " + mToken);
-            onIpSecTransformCreated(inIpSecTransform, IpSecManager.DIRECTION_IN);
-            onIpSecTransformCreated(outIpSecTransform, IpSecManager.DIRECTION_OUT);
+            migrationCompleted(mToken, inIpSecTransform, outIpSecTransform);
         }
 
         @Override
@@ -1924,6 +2013,27 @@
         }
     }
 
+    /**
+     * Dumps the state of this VcnGatewayConnection for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VcnGatewayConnection (" + mConnectionConfig.getGatewayConnectionName() + "):");
+        pw.increaseIndent();
+
+        pw.println("Current state: " + getCurrentState().getClass().getSimpleName());
+        pw.println("mIsQuitting: " + mIsQuitting);
+        pw.println("mIsInSafeMode: " + mIsInSafeMode);
+        pw.println("mCurrentToken: " + mCurrentToken);
+        pw.println("mFailedAttempts: " + mFailedAttempts);
+        pw.println(
+                "mNetworkAgent.getNetwork(): "
+                        + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
+
+        pw.decreaseIndent();
+    }
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     void setTunnelInterface(IpSecTunnelInterface tunnelIface) {
         mTunnelIface = tunnelIface;
@@ -1965,12 +2075,12 @@
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    NetworkAgent getNetworkAgent() {
+    VcnNetworkAgent getNetworkAgent() {
         return mNetworkAgent;
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    void setNetworkAgent(@Nullable NetworkAgent networkAgent) {
+    void setNetworkAgent(@Nullable VcnNetworkAgent networkAgent) {
         mNetworkAgent = networkAgent;
     }
 
@@ -2058,8 +2168,8 @@
             return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
         }
 
-        /** Builds a new NetworkAgent. */
-        public NetworkAgent newNetworkAgent(
+        /** Builds a new VcnNetworkAgent. */
+        public VcnNetworkAgent newNetworkAgent(
                 @NonNull VcnContext vcnContext,
                 @NonNull String tag,
                 @NonNull NetworkCapabilities caps,
@@ -2067,27 +2177,18 @@
                 @NonNull int score,
                 @NonNull NetworkAgentConfig nac,
                 @NonNull NetworkProvider provider,
-                @NonNull Runnable networkUnwantedCallback,
+                @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
                 @NonNull Consumer<Integer> validationStatusCallback) {
-            return new NetworkAgent(
-                    vcnContext.getContext(),
-                    vcnContext.getLooper(),
+            return new VcnNetworkAgent(
+                    vcnContext,
                     tag,
                     caps,
                     lp,
                     score,
                     nac,
-                    provider) {
-                @Override
-                public void onNetworkUnwanted() {
-                    networkUnwantedCallback.run();
-                }
-
-                @Override
-                public void onValidationStatus(int status, @Nullable Uri redirectUri) {
-                    validationStatusCallback.accept(status);
-                }
-            };
+                    provider,
+                    networkUnwantedCallback,
+                    validationStatusCallback);
         }
 
         /** Gets the elapsed real time since boot, in millis. */
@@ -2203,4 +2304,73 @@
             mImpl.release();
         }
     }
+
+    /** Proxy Implementation of NetworkAgent, used for testing. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class VcnNetworkAgent {
+        private final NetworkAgent mImpl;
+
+        public VcnNetworkAgent(
+                @NonNull VcnContext vcnContext,
+                @NonNull String tag,
+                @NonNull NetworkCapabilities caps,
+                @NonNull LinkProperties lp,
+                @NonNull int score,
+                @NonNull NetworkAgentConfig nac,
+                @NonNull NetworkProvider provider,
+                @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
+                @NonNull Consumer<Integer> validationStatusCallback) {
+            mImpl =
+                    new NetworkAgent(
+                            vcnContext.getContext(),
+                            vcnContext.getLooper(),
+                            tag,
+                            caps,
+                            lp,
+                            score,
+                            nac,
+                            provider) {
+                        @Override
+                        public void onNetworkUnwanted() {
+                            networkUnwantedCallback.accept(VcnNetworkAgent.this);
+                        }
+
+                        @Override
+                        public void onValidationStatus(int status, @Nullable Uri redirectUri) {
+                            validationStatusCallback.accept(status);
+                        }
+                    };
+        }
+
+        /** Registers the underlying NetworkAgent */
+        public void register() {
+            mImpl.register();
+        }
+
+        /** Marks the underlying NetworkAgent as connected */
+        public void markConnected() {
+            mImpl.markConnected();
+        }
+
+        /** Unregisters the underlying NetworkAgent */
+        public void unregister() {
+            mImpl.unregister();
+        }
+
+        /** Sends new NetworkCapabilities for the underlying NetworkAgent */
+        public void sendNetworkCapabilities(@NonNull NetworkCapabilities caps) {
+            mImpl.sendNetworkCapabilities(caps);
+        }
+
+        /** Sends new LinkProperties for the underlying NetworkAgent */
+        public void sendLinkProperties(@NonNull LinkProperties lp) {
+            mImpl.sendLinkProperties(lp);
+        }
+
+        /** Retrieves the Network for the underlying NetworkAgent */
+        @Nullable
+        public Network getNetwork() {
+            return mImpl.getNetwork();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index a909695..be0deb5 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Objects;
 import java.util.Set;
@@ -129,10 +130,50 @@
             mScore = score;
             mProviderId = providerId;
         }
+
+        /**
+         * Dumps the state of this NetworkRequestEntry for logging and debugging purposes.
+         *
+         * <p>PII and credentials MUST NEVER be dumped here.
+         */
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("NetworkRequestEntry:");
+            pw.increaseIndent();
+
+            pw.println("mRequest: " + mRequest);
+            pw.println("mScore: " + mScore);
+            pw.println("mProviderId: " + mProviderId);
+
+            pw.decreaseIndent();
+        }
     }
 
     // package-private
     interface NetworkRequestListener {
         void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId);
     }
+
+    /**
+     * Dumps the state of this VcnNetworkProvider for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VcnNetworkProvider:");
+        pw.increaseIndent();
+
+        pw.println("mListeners:");
+        for (NetworkRequestListener listener : mListeners) {
+            pw.println(listener);
+        }
+        pw.println();
+
+        pw.println("mRequests.values:");
+        for (NetworkRequestEntry entry : mRequests.values()) {
+            entry.dump(pw);
+        }
+        pw.println();
+
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java
new file mode 100644
index 0000000..49c1a02
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.util;
+
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_3DES;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CTR;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_NONE;
+
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+
+import static java.lang.Math.max;
+import static java.util.Collections.unmodifiableMap;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.util.List;
+import java.util.Map;
+
+/** @hide */
+public class MtuUtils {
+    private static final String TAG = MtuUtils.class.getSimpleName();
+    /**
+     * Max ESP overhead possible
+     *
+     * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader)
+     */
+    private static final int GENERIC_ESP_OVERHEAD_MAX = 78;
+
+    /** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */
+    private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD;
+
+    static {
+        final Map<Integer, Integer> map = new ArrayMap<>();
+        map.put(INTEGRITY_ALGORITHM_NONE, 0);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12);
+        map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64);
+        map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12);
+
+        AUTH_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+    }
+
+    /** Maximum overheads of encryption algorithms, keyed on IANA-defined constants */
+    private static final Map<Integer, Integer> CRYPT_ALGORITHM_OVERHEAD;
+
+    static {
+        final Map<Integer, Integer> map = new ArrayMap<>();
+        map.put(ENCRYPTION_ALGORITHM_3DES, 15); // 8 (IV) + 7 (Max pad)
+        map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31); // 16 (IV) + 15 (Max pad)
+        map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11); // 8 (IV) + 3 (Max pad)
+
+        CRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+    }
+
+    /** Maximum overheads of combined mode algorithms, keyed on IANA-defined constants */
+    private static final Map<Integer, Integer> AUTHCRYPT_ALGORITHM_OVERHEAD;
+
+    static {
+        final Map<Integer, Integer> map = new ArrayMap<>();
+        map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19); // 8 (IV) + 3 (Max pad) + 8 (ICV)
+        map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23); // 8 (IV) + 3 (Max pad) + 12 (ICV)
+        map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV)
+        map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV)
+
+        AUTHCRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+    }
+
+    /**
+     * Calculates the MTU of the inner interface based on the parameters provided
+     *
+     * <p>The MTU of the inner interface will be the minimum of the following:
+     *
+     * <ul>
+     *   <li>The MTU of the outer interface, minus the greatest ESP overhead (based on proposed
+     *       algorithms).
+     *   <li>The maximum MTU as provided in the arguments.
+     * </ul>
+     */
+    public static int getMtu(
+            @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) {
+        if (underlyingMtu <= 0) {
+            return IPV6_MIN_MTU;
+        }
+
+        boolean hasUnknownAlgorithm = false;
+        int maxAuthOverhead = 0;
+        int maxCryptOverhead = 0;
+        int maxAuthCryptOverhead = 0;
+
+        for (ChildSaProposal proposal : childProposals) {
+            for (Pair<Integer, Integer> encryptionAlgoPair : proposal.getEncryptionAlgorithms()) {
+                final int algo = encryptionAlgoPair.first;
+
+                if (AUTHCRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) {
+                    maxAuthCryptOverhead =
+                            max(maxAuthCryptOverhead, AUTHCRYPT_ALGORITHM_OVERHEAD.get(algo));
+                    continue;
+                } else if (CRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) {
+                    maxCryptOverhead = max(maxCryptOverhead, CRYPT_ALGORITHM_OVERHEAD.get(algo));
+                    continue;
+                }
+
+                Slog.wtf(TAG, "Unknown encryption algorithm requested: " + algo);
+                return IPV6_MIN_MTU;
+            }
+
+            for (int algo : proposal.getIntegrityAlgorithms()) {
+                if (AUTH_ALGORITHM_OVERHEAD.containsKey(algo)) {
+                    maxAuthOverhead = max(maxAuthOverhead, AUTH_ALGORITHM_OVERHEAD.get(algo));
+                    continue;
+                }
+
+                Slog.wtf(TAG, "Unknown integrity algorithm requested: " + algo);
+                return IPV6_MIN_MTU;
+            }
+        }
+
+        // Return minimum of maxMtu, and the adjusted MTUs based on algorithms.
+        final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX;
+        final int normalModeMtu =
+                underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX;
+        return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu);
+    }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 24bb1b1..d0bd8b3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -102,6 +102,7 @@
         "libaudioclient",
         "libbase",
         "libappfuse",
+        "libbinder_ndk",
         "libbinder",
         "libcutils",
         "libcrypto",
@@ -110,6 +111,7 @@
         "libhardware",
         "libhardware_legacy",
         "libhidlbase",
+        "libmemtrackproxy",
         "libmtp",
         "libnativehelper",
         "libprocessgroup",
@@ -154,6 +156,7 @@
         "android.hardware.input.classifier@1.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
+        "android.hardware.memtrack-V1-ndk_platform",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power-V1-cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index d18043f..b93b8ab 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -23,11 +23,14 @@
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
+#include <android/binder_manager.h>
+#include <android/binder_stability.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
 #include <binder/IServiceManager.h>
 #include <hidl/HidlTransportSupport.h>
 #include <incremental_service.h>
 
+#include <memtrackproxy/MemtrackProxy.h>
 #include <schedulerservice/SchedulingPolicyService.h>
 #include <sensorservice/SensorService.h>
 #include <sensorservicehidl/SensorManager.h>
@@ -57,6 +60,21 @@
 
 }
 
+static void android_server_SystemServer_startMemtrackProxyService(JNIEnv* env,
+                                                                  jobject /* clazz */) {
+    using aidl::android::hardware::memtrack::MemtrackProxy;
+
+    const char* memtrackProxyService = "memtrack.proxy";
+
+    std::shared_ptr<MemtrackProxy> memtrack_proxy = ndk::SharedRefBase::make<MemtrackProxy>();
+    auto binder = memtrack_proxy->asBinder();
+
+    AIBinder_forceDowngradeToLocalStability(binder.get());
+
+    const binder_exception_t err = AServiceManager_addService(binder.get(), memtrackProxyService);
+    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", memtrackProxyService, err);
+}
+
 static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /* clazz */) {
     using ::android::frameworks::schedulerservice::V1_0::ISchedulingPolicyService;
     using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
@@ -121,6 +139,8 @@
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
+        {"startMemtrackProxyService", "()V",
+         (void*)android_server_SystemServer_startMemtrackProxyService},
         {"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices},
         {"initZygoteChildHeapProfiling", "()V",
          (void*)android_server_SystemServer_initZygoteChildHeapProfiling},
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 80f43d7..81ec8f4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -393,6 +393,11 @@
     private static native void startSensorService();
 
     /**
+     * Start the memtrack proxy service.
+     */
+    private static native void startMemtrackProxyService();
+
+    /**
      * Start all HIDL services that are run inside the system server. This may take some time.
      */
     private static native void startHidlServices();
@@ -830,6 +835,12 @@
         mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
         t.traceEnd();
 
+        // Start MemtrackProxyService before ActivityManager, so that early calls
+        // to Memtrack::getMemory() don't fail.
+        t.traceBegin("MemtrackProxyService");
+        startMemtrackProxyService();
+        t.traceEnd();
+
         // Activity manager runs the show.
         t.traceBegin("StartActivityManager");
         // TODO: Might need to move after migration to WM.
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
index 6e539bb..5666985 100644
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
@@ -80,12 +80,12 @@
         parcel.srcPort = pkt.getSrcPort();
         parcel.dstAddress = dstAddress.getAddress();
         parcel.dstPort = pkt.getDstPort();
-        parcel.seq = pkt.tcpSeq;
-        parcel.ack = pkt.tcpAck;
-        parcel.rcvWnd = pkt.tcpWindow;
-        parcel.rcvWndScale = pkt.tcpWindowScale;
-        parcel.tos = pkt.ipTos;
-        parcel.ttl = pkt.ipTtl;
+        parcel.seq = pkt.getTcpSeq();
+        parcel.ack = pkt.getTcpAck();
+        parcel.rcvWnd = pkt.getTcpWindow();
+        parcel.rcvWndScale = pkt.getTcpWindowScale();
+        parcel.tos = pkt.getIpTos();
+        parcel.ttl = pkt.getIpTtl();
         return parcel;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index f5876fa..e9e2486 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -18,6 +18,12 @@
 
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -29,10 +35,17 @@
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.allowedReasonsToString;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
@@ -59,6 +72,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -134,8 +148,10 @@
 import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Range;
 import android.util.RecurrenceRule;
+import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
@@ -1896,6 +1912,65 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
     }
 
+    @Test
+    public void testUpdateEffectiveBlockedReasons() {
+        final SparseArray<Pair<Integer, Integer>> effectiveBlockedReasons = new SparseArray<>();
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE));
+
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+                        ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_METERED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_SYSTEM));
+
+        effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_APP_STANDBY,
+                Pair.create(BLOCKED_REASON_APP_STANDBY | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_SYSTEM));
+
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+                        ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER, ALLOWED_METERED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_BATTERY_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_FOREGROUND));
+        // TODO: test more combinations of blocked reasons.
+
+        for (int i = 0; i < effectiveBlockedReasons.size(); ++i) {
+            final int expectedEffectiveBlockedReasons = effectiveBlockedReasons.keyAt(i);
+            final int blockedReasons = effectiveBlockedReasons.valueAt(i).first;
+            final int allowedReasons = effectiveBlockedReasons.valueAt(i).second;
+            final String errorMsg = "Expected="
+                    + blockedReasonsToString(expectedEffectiveBlockedReasons)
+                    + "; blockedReasons=" + blockedReasonsToString(blockedReasons)
+                    + ", allowedReasons=" + allowedReasonsToString(allowedReasons);
+            assertEquals(errorMsg, expectedEffectiveBlockedReasons,
+                    getEffectiveBlockedReasons(blockedReasons, allowedReasons));
+        }
+    }
+
     private String formatBlockedStateError(int uid, int rule, boolean metered,
             boolean backgroundRestricted) {
         return String.format(
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 38fa907..3c3df51 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -525,7 +525,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE)
                 == PERMISSION_GRANTED) {
@@ -545,7 +545,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
                 == PERMISSION_GRANTED) {
@@ -567,7 +567,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
                 == PERMISSION_GRANTED) {
diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
index 6770066..7a18bb0 100644
--- a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
+++ b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
@@ -92,12 +92,12 @@
         assertTrue(str.contains(data.dstAddress.hostAddress))
         assertTrue(str.contains(data.dstPort.toString()))
         // .packet not included in toString()
-        assertTrue(str.contains(data.tcpSeq.toString()))
-        assertTrue(str.contains(data.tcpAck.toString()))
-        assertTrue(str.contains(data.tcpWindow.toString()))
-        assertTrue(str.contains(data.tcpWindowScale.toString()))
-        assertTrue(str.contains(data.ipTos.toString()))
-        assertTrue(str.contains(data.ipTtl.toString()))
+        assertTrue(str.contains(data.getTcpSeq().toString()))
+        assertTrue(str.contains(data.getTcpAck().toString()))
+        assertTrue(str.contains(data.getTcpWindow().toString()))
+        assertTrue(str.contains(data.getTcpWindowScale().toString()))
+        assertTrue(str.contains(data.getIpTos().toString()))
+        assertTrue(str.contains(data.getIpTtl().toString()))
 
         // Update above assertions if field is added
         assertFieldCountEquals(5, KeepalivePacketData::class.java)
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 34c0018..bb67593 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -26,6 +26,7 @@
 
 import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -46,15 +47,19 @@
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.ChildSaProposal;
 import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.net.ipsec.ike.exceptions.TemporaryFailureException;
+import android.net.vcn.VcnControlPlaneIkeConfig;
 import android.net.vcn.VcnManager.VcnErrorCode;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.vcn.util.MtuUtils;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -73,13 +78,13 @@
 @SmallTest
 public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
     private VcnIkeSession mIkeSession;
-    private NetworkAgent mNetworkAgent;
+    private VcnNetworkAgent mNetworkAgent;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        mNetworkAgent = mock(NetworkAgent.class);
+        mNetworkAgent = mock(VcnNetworkAgent.class);
         doReturn(mNetworkAgent)
                 .when(mDeps)
                 .newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any());
@@ -152,7 +157,9 @@
     }
 
     @Test
-    public void testMigratedTransformsAreApplied() throws Exception {
+    public void testMigration() throws Exception {
+        triggerChildOpened();
+
         getChildSessionCallback()
                 .onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
         mTestLooper.dispatchAll();
@@ -170,6 +177,17 @@
         }
 
         assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+        final List<ChildSaProposal> saProposals =
+                ((VcnControlPlaneIkeConfig) mConfig.getControlPlaneConfig())
+                        .getChildSessionParams()
+                        .getSaProposals();
+        final int expectedMtu =
+                MtuUtils.getMtu(
+                        saProposals,
+                        mConfig.getMaxMtu(),
+                        TEST_UNDERLYING_NETWORK_RECORD_1.linkProperties.getMtu());
+        verify(mNetworkAgent).sendLinkProperties(argThat(lp -> expectedMtu == lp.getMtu()));
     }
 
     private void triggerChildOpened() {
@@ -299,8 +317,9 @@
                 .removeAddressFromTunnelInterface(
                         eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
 
-        // TODO(b/184579891): Also verify link properties updated and sent when sendLinkProperties
-        // is mockable
+        verify(mNetworkAgent).sendLinkProperties(argThat(
+                lp -> newInternalAddrs.equals(lp.getLinkAddresses())
+                        && Collections.singletonList(TEST_DNS_ADDR_2).equals(lp.getDnsServers())));
 
         // Verify that IpSecTunnelInterface only created once
         verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
@@ -323,6 +342,66 @@
         assertFalse(mGatewayConnection.isInSafeMode());
     }
 
+    private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
+        triggerChildOpened();
+        mTestLooper.dispatchAll();
+
+        final ArgumentCaptor<Consumer<VcnNetworkAgent>> unwantedCallbackCaptor =
+                ArgumentCaptor.forClass(Consumer.class);
+        verify(mDeps)
+                .newNetworkAgent(
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        anyInt(),
+                        any(),
+                        any(),
+                        unwantedCallbackCaptor.capture(),
+                        any());
+
+        return unwantedCallbackCaptor.getValue();
+    }
+
+    @Test
+    public void testUnwantedNetworkAgentTriggersTeardown() throws Exception {
+        final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+
+        unwantedCallback.accept(mNetworkAgent);
+        mTestLooper.dispatchAll();
+
+        assertTrue(mGatewayConnection.isQuitting());
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testUnwantedNetworkAgentWithDisconnectedNetworkAgent() throws Exception {
+        final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+
+        mGatewayConnection.setNetworkAgent(null);
+        unwantedCallback.accept(mNetworkAgent);
+        mTestLooper.dispatchAll();
+
+        // Verify that the call was ignored; the state machine is still running, and the state has
+        // not changed.
+        assertFalse(mGatewayConnection.isQuitting());
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testUnwantedNetworkAgentWithNewNetworkAgent() throws Exception {
+        final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+        final VcnNetworkAgent testAgent = mock(VcnNetworkAgent.class);
+
+        mGatewayConnection.setNetworkAgent(testAgent);
+        unwantedCallback.accept(mNetworkAgent);
+        mTestLooper.dispatchAll();
+
+        assertFalse(mGatewayConnection.isQuitting());
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        assertEquals(testAgent, mGatewayConnection.getNetworkAgent());
+    }
+
     @Test
     public void testChildSessionClosedTriggersDisconnect() throws Exception {
         // Verify scheduled but not canceled when entering ConnectedState
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index c5ed8f6..dc73be2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
 import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
 
 import static org.junit.Assert.assertEquals;
@@ -44,7 +45,6 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.IkeSessionCallback;
@@ -90,12 +90,18 @@
     protected static final int TEST_SUB_ID = 5;
     protected static final long ELAPSED_REAL_TIME = 123456789L;
     protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
             new UnderlyingNetworkRecord(
                     new Network(0),
                     new NetworkCapabilities(),
                     new LinkProperties(),
                     false /* blocked */);
+
+    static {
+        TEST_UNDERLYING_NETWORK_RECORD_1.linkProperties.setMtu(1500);
+    }
+
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
             new UnderlyingNetworkRecord(
                     new Network(1),
@@ -103,6 +109,10 @@
                     new LinkProperties(),
                     false /* blocked */);
 
+    static {
+        TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.setMtu(1460);
+    }
+
     protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
             new TelephonySubscriptionSnapshot(
                     Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
@@ -278,8 +288,8 @@
 
     protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
             @NonNull State expectedState) {
-        // Set a NetworkAgent, and expect it to be unregistered and cleared
-        final NetworkAgent mockNetworkAgent = mock(NetworkAgent.class);
+        // Set a VcnNetworkAgent, and expect it to be unregistered and cleared
+        final VcnNetworkAgent mockNetworkAgent = mock(VcnNetworkAgent.class);
         mGatewayConnection.setNetworkAgent(mockNetworkAgent);
 
         // SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
new file mode 100644
index 0000000..29511f7
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.util;
+
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+
+import static com.android.net.module.util.NetworkStackConstants.ETHER_MTU;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+import static com.android.server.vcn.util.MtuUtils.getMtu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.Collections.emptyList;
+
+import android.net.ipsec.ike.ChildSaProposal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MtuUtilsTest {
+    @Test
+    public void testUnderlyingMtuZero() {
+        assertEquals(
+                IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+    }
+
+    @Test
+    public void testClampsToMaxMtu() {
+        assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+    }
+
+    @Test
+    public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
+        final List<ChildSaProposal> saProposals =
+                Arrays.asList(
+                        new ChildSaProposal.Builder()
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+                                .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+                                .build());
+
+        final int actualMtu =
+                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+        assertTrue(ETHER_MTU > actualMtu);
+    }
+
+    @Test
+    public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+        final List<ChildSaProposal> saProposals =
+                Arrays.asList(
+                        new ChildSaProposal.Builder()
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256)
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256)
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256)
+                                .build());
+
+        final int actualMtu =
+                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+        assertTrue(ETHER_MTU > actualMtu);
+    }
+}
diff --git a/tools/hiddenapi/OWNERS b/tools/hiddenapi/OWNERS
new file mode 100644
index 0000000..afbeef5
--- /dev/null
+++ b/tools/hiddenapi/OWNERS
@@ -0,0 +1,7 @@
+# compat-team@ for changes to hiddenapi files
+andreionea@google.com
+mathewi@google.com
+satayev@google.com
+
+# soong-team@ as the files these tools protect are tightly coupled with Soong
+file:platform/build/soong:/OWNERS
diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
index ceb705f..451fed6 100755
--- a/tools/hiddenapi/checksorted_sha.sh
+++ b/tools/hiddenapi/checksorted_sha.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 set -e
 LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+git show --name-only --pretty=format: $1 | grep "boot/hiddenapi/hiddenapi-.*txt" | while read file; do
     diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh )  || {
       echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
       echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $2/frameworks/base/$file\e[0m"
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 73eacc0..822aba4 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -7,11 +7,9 @@
 # the team email to use in the event of this detecting an entry in a <team> package. Also
 # add <team> to the TEAMS list. 
 LIBCORE_PACKAGES="\
-  android.icu \
   android.system \
   android.test \
   com.android.bouncycastle \
-  com.android.conscrypt \
   com.android.i18n.phonenumbers \
   com.android.okhttp \
   com.sun \
@@ -24,37 +22,54 @@
   org.json \
   org.w3c.dom \
   org.xml.sax \
+  org.xmlpull.v1 \
   sun \
   "
 LIBCORE_EMAIL=libcore-team@android.com
 
+I18N_PACKAGES="\
+  android.icu \
+  "
+
+I18N_EMAIL=$LIBCORE_EMAIL
+
+CONSCRYPT_PACKAGES="\
+  com.android.org.conscrypt \
+  "
+
+CONSCRYPT_EMAIL=$LIBCORE_EMAIL
+
 # List of teams.
-TEAMS=LIBCORE
+TEAMS="LIBCORE I18N CONSCRYPT"
+
+SHA=$1
 
 # Generate the list of packages and convert to a regular expression.
 PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
 RE=$(echo ${PACKAGES} | sed "s/ /|/g")
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
-    ENTRIES=$(grep -E "^L(${RE})/" || true <(git show $1:$file))
+EXIT_CODE=0
+for file in $(git show --name-only --pretty=format: $SHA | grep "boot/hiddenapi/hiddenapi-.*txt"); do
+    ENTRIES=$(grep -E "^\+L(${RE})/" <(git diff ${SHA}~1 ${SHA} $file) | sed "s|^\+||" || echo)
     if [[ -n "${ENTRIES}" ]]; then
-      echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+      echo -e "\e[1m\e[31m$file $SHA contains the following entries\e[0m"
       echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
       echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
       # Partition the entries by team and provide contact details to aid in fixing the issue.
       for t in ${TEAMS}
       do
         PACKAGES=$(eval echo \${${t}_PACKAGES})
-        RE=$(echo ${PACKAGES} | sed "s/ /|/g")
-        TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+        TEAM_RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+        TEAM_ENTRIES=$(grep -E "^L(${TEAM_RE})/" <(echo "${ENTRIES}") || echo)
         if [[ -n "${TEAM_ENTRIES}" ]]; then
           EMAIL=$(eval echo \${${t}_EMAIL})
-          echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
-          for i in ${ENTRIES}
+          echo -e "\e[33mContact ${EMAIL} for help with the following:\e[0m"
+          for i in ${TEAM_ENTRIES}
           do
             echo -e "\e[33m  ${i}\e[0m"
           done
         fi
       done
-      exit 1
+      EXIT_CODE=1
     fi
 done
+exit $EXIT_CODE