Merge "Add owners for locksettings related files"
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
new file mode 100644
index 0000000..fbc611a
--- /dev/null
+++ b/MULTIUSER_OWNERS
@@ -0,0 +1,4 @@
+# OWNERS of Multiuser related files
+bookatz@google.com
+omakoto@google.com
+yamasani@google.com
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 23f025b..5a04ba3 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -27,7 +27,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.utils.blob.DummyBlobData;
+import com.android.utils.blob.FakeBlobData;
 
 import org.junit.After;
 import org.junit.Before;
@@ -96,7 +96,7 @@
         mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER);
         try {
             final List<Long> durations = new ArrayList<>();
-            final DummyBlobData blobData = prepareDataBlob(fileSizeInMb);
+            final FakeBlobData blobData = prepareDataBlob(fileSizeInMb);
             final TraceMarkParser parser = new TraceMarkParser(
                     line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX));
             while (mState.keepRunning(durations)) {
@@ -120,15 +120,15 @@
         });
     }
 
-    private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
-        final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+    private FakeBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
+        final FakeBlobData blobData = new FakeBlobData.Builder(mContext)
                 .setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */)
                 .build();
         blobData.prepare();
         return blobData;
     }
 
-    private void commitBlob(DummyBlobData blobData) throws Exception {
+    private void commitBlob(FakeBlobData blobData) throws Exception {
         final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
         try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
             blobData.writeToSession(session);
diff --git a/apct-tests/perftests/multiuser/OWNERS b/apct-tests/perftests/multiuser/OWNERS
new file mode 100644
index 0000000..1a206cb
--- /dev/null
+++ b/apct-tests/perftests/multiuser/OWNERS
@@ -0,0 +1 @@
+include /MULTIUSER_OWNERS
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 39f7526..38500af 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -89,8 +89,8 @@
  * <p> Before committing the session, apps can indicate which apps are allowed to access the
  * contributed data using one or more of the following access modes:
  * <ul>
- *     <li> {@link Session#allowPackageAccess(String, byte[])} which will allow whitelisting
- *          specific packages to access the blobs.
+ *     <li> {@link Session#allowPackageAccess(String, byte[])} which will allow specific packages
+ *          to access the blobs.
  *     <li> {@link Session#allowSameSignatureAccess()} which will allow only apps which are signed
  *          with the same certificate as the app which contributed the blob to access it.
  *     <li> {@link Session#allowPublicAccess()} which will allow any app on the device to access
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index 656749d..bfc5826 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -36,7 +36,7 @@
     // For BlobAccessMode
     public static final String TAG_ACCESS_MODE = "am";
     public static final String ATTR_TYPE = "t";
-    public static final String TAG_WHITELISTED_PACKAGE = "wl";
+    public static final String TAG_ALLOWED_PACKAGE = "wl";
     public static final String ATTR_CERTIFICATE = "ct";
 
     // For BlobHandle
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
index ba0fab6..4a527ad 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -18,7 +18,7 @@
 import static android.app.blob.XmlTags.ATTR_CERTIFICATE;
 import static android.app.blob.XmlTags.ATTR_PACKAGE;
 import static android.app.blob.XmlTags.ATTR_TYPE;
-import static android.app.blob.XmlTags.TAG_WHITELISTED_PACKAGE;
+import static android.app.blob.XmlTags.TAG_ALLOWED_PACKAGE;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -52,21 +52,21 @@
             ACCESS_TYPE_PRIVATE,
             ACCESS_TYPE_PUBLIC,
             ACCESS_TYPE_SAME_SIGNATURE,
-            ACCESS_TYPE_WHITELIST,
+            ACCESS_TYPE_ALLOWLIST,
     })
     @interface AccessType {}
     public static final int ACCESS_TYPE_PRIVATE = 1 << 0;
     public static final int ACCESS_TYPE_PUBLIC = 1 << 1;
     public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
-    public static final int ACCESS_TYPE_WHITELIST = 1 << 3;
+    public static final int ACCESS_TYPE_ALLOWLIST = 1 << 3;
 
     private int mAccessType = ACCESS_TYPE_PRIVATE;
 
-    private final ArraySet<PackageIdentifier> mWhitelistedPackages = new ArraySet<>();
+    private final ArraySet<PackageIdentifier> mAllowedPackages = new ArraySet<>();
 
     void allow(BlobAccessMode other) {
-        if ((other.mAccessType & ACCESS_TYPE_WHITELIST) != 0) {
-            mWhitelistedPackages.addAll(other.mWhitelistedPackages);
+        if ((other.mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) {
+            mAllowedPackages.addAll(other.mAllowedPackages);
         }
         mAccessType |= other.mAccessType;
     }
@@ -80,8 +80,8 @@
     }
 
     void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate) {
-        mAccessType |= ACCESS_TYPE_WHITELIST;
-        mWhitelistedPackages.add(PackageIdentifier.create(packageName, certificate));
+        mAccessType |= ACCESS_TYPE_ALLOWLIST;
+        mAllowedPackages.add(PackageIdentifier.create(packageName, certificate));
     }
 
     boolean isPublicAccessAllowed() {
@@ -93,10 +93,10 @@
     }
 
     boolean isPackageAccessAllowed(@NonNull String packageName, @NonNull byte[] certificate) {
-        if ((mAccessType & ACCESS_TYPE_WHITELIST) == 0) {
+        if ((mAccessType & ACCESS_TYPE_ALLOWLIST) == 0) {
             return false;
         }
-        return mWhitelistedPackages.contains(PackageIdentifier.create(packageName, certificate));
+        return mAllowedPackages.contains(PackageIdentifier.create(packageName, certificate));
     }
 
     boolean isAccessAllowedForCaller(Context context,
@@ -113,9 +113,9 @@
             }
         }
 
-        if ((mAccessType & ACCESS_TYPE_WHITELIST) != 0) {
-            for (int i = 0; i < mWhitelistedPackages.size(); ++i) {
-                final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i);
+        if ((mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) {
+            for (int i = 0; i < mAllowedPackages.size(); ++i) {
+                final PackageIdentifier packageIdentifier = mAllowedPackages.valueAt(i);
                 if (packageIdentifier.packageName.equals(callingPackage)
                         && pm.hasSigningCertificate(callingPackage, packageIdentifier.certificate,
                                 PackageManager.CERT_INPUT_SHA256)) {
@@ -131,20 +131,20 @@
         return mAccessType;
     }
 
-    int getNumWhitelistedPackages() {
-        return mWhitelistedPackages.size();
+    int getAllowedPackagesCount() {
+        return mAllowedPackages.size();
     }
 
     void dump(IndentingPrintWriter fout) {
         fout.println("accessType: " + DebugUtils.flagsToString(
                 BlobAccessMode.class, "ACCESS_TYPE_", mAccessType));
-        fout.print("Whitelisted pkgs:");
-        if (mWhitelistedPackages.isEmpty()) {
+        fout.print("Explicitly allowed pkgs:");
+        if (mAllowedPackages.isEmpty()) {
             fout.println(" (Empty)");
         } else {
             fout.increaseIndent();
-            for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) {
-                fout.println(mWhitelistedPackages.valueAt(i).toString());
+            for (int i = 0, count = mAllowedPackages.size(); i < count; ++i) {
+                fout.println(mAllowedPackages.valueAt(i).toString());
             }
             fout.decreaseIndent();
         }
@@ -152,12 +152,12 @@
 
     void writeToXml(@NonNull XmlSerializer out) throws IOException {
         XmlUtils.writeIntAttribute(out, ATTR_TYPE, mAccessType);
-        for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) {
-            out.startTag(null, TAG_WHITELISTED_PACKAGE);
-            final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i);
+        for (int i = 0, count = mAllowedPackages.size(); i < count; ++i) {
+            out.startTag(null, TAG_ALLOWED_PACKAGE);
+            final PackageIdentifier packageIdentifier = mAllowedPackages.valueAt(i);
             XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageIdentifier.packageName);
             XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate);
-            out.endTag(null, TAG_WHITELISTED_PACKAGE);
+            out.endTag(null, TAG_ALLOWED_PACKAGE);
         }
     }
 
@@ -171,7 +171,7 @@
 
         final int depth = in.getDepth();
         while (XmlUtils.nextElementWithin(in, depth)) {
-            if (TAG_WHITELISTED_PACKAGE.equals(in.getName())) {
+            if (TAG_ALLOWED_PACKAGE.equals(in.getName())) {
                 final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
                 final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE);
                 blobAccessMode.allowPackageAccess(packageName, certificate);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 0b760a6..a9c5c4c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -478,7 +478,7 @@
                 proto.write(BlobStatsEventProto.BlobCommitterProto.ACCESS_MODE,
                         committer.blobAccessMode.getAccessType());
                 proto.write(BlobStatsEventProto.BlobCommitterProto.NUM_WHITELISTED_PACKAGE,
-                        committer.blobAccessMode.getNumWhitelistedPackages());
+                        committer.blobAccessMode.getAllowedPackagesCount());
                 proto.end(token);
             }
             final byte[] committersBytes = proto.getBytes();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 2f83be1..fe68882 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -332,10 +332,10 @@
                 throw new IllegalStateException("Not allowed to change access type in state: "
                         + stateToString(mState));
             }
-            if (mBlobAccessMode.getNumWhitelistedPackages() >= getMaxPermittedPackages()) {
+            if (mBlobAccessMode.getAllowedPackagesCount() >= getMaxPermittedPackages()) {
                 throw new ParcelableException(new LimitExceededException(
                         "Too many packages permitted to access the blob: "
-                                + mBlobAccessMode.getNumWhitelistedPackages()));
+                                + mBlobAccessMode.getAllowedPackagesCount()));
             }
             mBlobAccessMode.allowPackageAccess(packageName, certificate);
         }
diff --git a/config/hiddenapi-temp-blocklist.txt b/config/hiddenapi-max-target-r-loprio.txt
similarity index 100%
rename from config/hiddenapi-temp-blocklist.txt
rename to config/hiddenapi-max-target-r-loprio.txt
diff --git a/core/api/current.txt b/core/api/current.txt
index 252c33c..9f4af37 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40497,12 +40497,13 @@
   public class PhoneNumberUtils {
     ctor public PhoneNumberUtils();
     method public static void addTtsSpan(android.text.Spannable, int, int);
+    method public static boolean areSamePhoneNumber(@NonNull String, @NonNull String, @NonNull String);
     method @Deprecated public static String calledPartyBCDFragmentToString(byte[], int, int);
     method public static String calledPartyBCDFragmentToString(byte[], int, int, int);
     method @Deprecated public static String calledPartyBCDToString(byte[], int, int);
     method public static String calledPartyBCDToString(byte[], int, int, int);
-    method public static boolean compare(String, String);
-    method public static boolean compare(android.content.Context, String, String);
+    method @Deprecated public static boolean compare(String, String);
+    method @Deprecated public static boolean compare(android.content.Context, String, String);
     method public static String convertKeypadLettersToDigits(String);
     method public static android.text.style.TtsSpan createTtsSpan(String);
     method public static CharSequence createTtsSpannable(CharSequence);
@@ -41149,6 +41150,7 @@
     field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
     field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0
     field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1
+    field public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; // 0x2
     field public static final int CALL_STATE_IDLE = 0; // 0x0
     field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
     field public static final int CALL_STATE_RINGING = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2214ab4..03e80fc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5965,6 +5965,7 @@
     method public long getExpiryTimeMillis();
     method public long getRefreshTimeMillis();
     method @Nullable public android.net.Uri getUserPortalUrl();
+    method @Nullable public String getVenueFriendlyName();
     method @Nullable public android.net.Uri getVenueInfoUrl();
     method public boolean isCaptive();
     method public boolean isSessionExtendable();
@@ -5982,6 +5983,7 @@
     method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
     method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
     method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
     method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
   }
 
@@ -6233,6 +6235,7 @@
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
+    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
     method @NonNull public int[] getAdministratorUids();
     method @Nullable public String getSsid();
     method @NonNull public int[] getTransportTypes();
@@ -6240,6 +6243,7 @@
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
     field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
     field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
+    field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
   }
 
   public static final class NetworkCapabilities.Builder {
@@ -6454,6 +6458,11 @@
     field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00
   }
 
+  public interface TransportInfo {
+    method public default boolean hasLocationSensitiveFields();
+    method @NonNull public default android.net.TransportInfo makeCopy(boolean);
+  }
+
   public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
     method @NonNull public String toSafeString();
   }
@@ -7507,11 +7516,13 @@
   }
 
   public class UserManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean canHaveRestrictedProfile();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
@@ -7917,6 +7928,7 @@
     field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
     field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
     field public static final String NAMESPACE_APP_COMPAT = "app_compat";
+    field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
     field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
     field public static final String NAMESPACE_AUTOFILL = "autofill";
     field public static final String NAMESPACE_BIOMETRICS = "biometrics";
@@ -11569,7 +11581,32 @@
   }
 
   public class RcsUceAdapter {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+    field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; // 0x2
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; // 0x3
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
+    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8
+    field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0
+    field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
+    field public static final int PUBLISH_STATE_OK = 1; // 0x1
+    field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+    field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
+    field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
+    field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
+  }
+
+  public static interface RcsUceAdapter.OnPublishStateChangedListener {
+    method public void onPublishStateChange(int);
   }
 
   public final class RtpHeaderExtension implements android.os.Parcelable {
@@ -11776,16 +11813,24 @@
   }
 
   public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
-    ctor public RcsFeature();
+    ctor @Deprecated public RcsFeature();
+    ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
+    method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
   }
 
 }
 
 package android.telephony.ims.stub {
 
+  public interface CapabilityExchangeEventListener {
+    method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
+    method public void onUnpublish() throws android.telephony.ims.ImsException;
+  }
+
   public interface DelegateConnectionMessageCallback {
     method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
     method public void onMessageSendFailure(@NonNull String, int);
@@ -11966,6 +12011,27 @@
     method public int updateColr(int);
   }
 
+  public class RcsCapabilityExchangeImplBase {
+    ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
+    method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
+    field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
+    field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
+    field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
+    field public static final int COMMAND_CODE_INVALID_PARAM = 2; // 0x2
+    field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; // 0x6
+    field public static final int COMMAND_CODE_NOT_FOUND = 8; // 0x8
+    field public static final int COMMAND_CODE_NOT_SUPPORTED = 7; // 0x7
+    field public static final int COMMAND_CODE_NO_CHANGE = 10; // 0xa
+    field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; // 0x4
+    field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; // 0x9
+    field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
+  }
+
+  public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
+    method public void onCommandError(int) throws android.telephony.ims.ImsException;
+    method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+  }
+
   public interface SipDelegate {
     method public void closeDialog(@NonNull String);
     method public void notifyMessageReceiveError(@NonNull String, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 23787eb..bfde2d5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2284,7 +2284,7 @@
         return null;
     }
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
             int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 06ad9c9..6d79e2d 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -2,6 +2,33 @@
 # Remain no owner because multiple modules may touch this file.
 per-file ContextImpl.java = *
 
+# ActivityManager
+per-file ActivityManager* = file:/services/core/java/com/android/server/am/OWNERS
+per-file ApplicationErrorReport* = file:/services/core/java/com/android/server/am/OWNERS
+per-file ApplicationExitInfo* = file:/services/core/java/com/android/server/am/OWNERS
+per-file Application.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file ApplicationLoaders.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file ApplicationThreadConstants.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file BroadcastOptions.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file ContentProviderHolder* = file:/services/core/java/com/android/server/am/OWNERS
+per-file IActivityController.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IStopUserCallback.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IUidObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file LoadedApk.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file LocalActivityManager.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file PendingIntent* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Process* = file:/services/core/java/com/android/server/am/OWNERS
+per-file ProfilerInfo* = file:/services/core/java/com/android/server/am/OWNERS
+per-file Service* = file:/services/core/java/com/android/server/am/OWNERS
+per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
+
 # ActivityThread
 per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file ActivityThread.java = file:/services/core/java/com/android/server/wm/OWNERS
@@ -12,6 +39,9 @@
 # AppOps
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 
+# Multiuser
+per-file *User* = file:/MULTIUSER_OWNERS
+
 # Notification
 per-file *Notification* = file:/packages/SystemUI/OWNERS
 
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index c0cb323..15daf1c 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -118,7 +118,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     public static final String ACTION_ACTIVE_DEVICE_CHANGED =
             "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
 
@@ -409,7 +409,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) log("setActiveDevice(" + device + ")");
         try {
@@ -433,7 +433,7 @@
      * is active
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     @Nullable
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public BluetoothDevice getActiveDevice() {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index e4b2d70..b7203e3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1174,7 +1174,7 @@
      * @return true to indicate adapter shutdown has begun, or false on immediate error
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     public boolean disable(boolean persist) {
 
         try {
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 7a6ff79..381318b 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -824,6 +824,25 @@
      * error
      */
     private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
+        return registerApp(callback, handler, false);
+    }
+
+    /**
+     * Register an application callback to start using GATT.
+     *
+     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
+     * is used to notify success or failure if the function returns true.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callback GATT callback handler that will receive asynchronous callbacks.
+     * @param eatt_support indicate to allow for eatt support
+     * @return If true, the callback will be called to notify success or failure, false on immediate
+     * error
+     * @hide
+     */
+    private boolean registerApp(BluetoothGattCallback callback, Handler handler,
+                                boolean eatt_support) {
         if (DBG) Log.d(TAG, "registerApp()");
         if (mService == null) return false;
 
@@ -833,7 +852,7 @@
         if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
 
         try {
-            mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
+            mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
             return false;
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 13b1b4f..088b016 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -443,6 +443,25 @@
      * error
      */
     /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
+        return registerCallback(callback, false);
+    }
+
+    /**
+     * Register an application callback to start using GattServer.
+     *
+     * <p>This is an asynchronous call. The callback is used to notify
+     * success or failure if the function returns true.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callback GATT callback handler that will receive asynchronous callbacks.
+     * @param eatt_support indicates if server can use eatt
+     * @return true, the callback will be called to notify success or failure, false on immediate
+     * error
+     * @hide
+     */
+    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback,
+                                         boolean eatt_support) {
         if (DBG) Log.d(TAG, "registerCallback()");
         if (mService == null) {
             Log.e(TAG, "GATT service not available");
@@ -459,7 +478,7 @@
 
             mCallback = callback;
             try {
-                mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
+                mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support);
             } catch (RemoteException e) {
                 Log.e(TAG, "", e);
                 mCallback = null;
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index f59ae33..36076da 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -113,7 +113,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     public static final String ACTION_ACTIVE_DEVICE_CHANGED =
             "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
 
@@ -1172,7 +1172,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) {
             Log.d(TAG, "setActiveDevice: " + device);
@@ -1198,7 +1198,7 @@
      * is active.
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     @Nullable
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public BluetoothDevice getActiveDevice() {
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 3b4fe0a..d5c1c3e 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -225,6 +225,24 @@
      *
      * @param context App context
      * @param callback GATT server callback handler that will receive asynchronous callbacks.
+     * @param eatt_support idicates if server should use eatt channel for notifications.
+     * @return BluetoothGattServer instance
+     * @hide
+     */
+    public BluetoothGattServer openGattServer(Context context,
+            BluetoothGattServerCallback callback, boolean eatt_support) {
+        return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support));
+    }
+
+    /**
+     * Open a GATT Server
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as the results of any other GATT server operations.
+     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+     * to conduct GATT server operations.
+     *
+     * @param context App context
+     * @param callback GATT server callback handler that will receive asynchronous callbacks.
      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
      * BluetoothDevice#TRANSPORT_LE}
@@ -233,6 +251,27 @@
      */
     public BluetoothGattServer openGattServer(Context context,
             BluetoothGattServerCallback callback, int transport) {
+        return (openGattServer(context, callback, transport, false));
+    }
+
+    /**
+     * Open a GATT Server
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as the results of any other GATT server operations.
+     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+     * to conduct GATT server operations.
+     *
+     * @param context App context
+     * @param callback GATT server callback handler that will receive asynchronous callbacks.
+     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
+     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
+     * BluetoothDevice#TRANSPORT_LE}
+     * @param eatt_support idicates if server should use eatt channel for notifications.
+     * @return BluetoothGattServer instance
+     * @hide
+     */
+    public BluetoothGattServer openGattServer(Context context,
+            BluetoothGattServerCallback callback, int transport, boolean eatt_support) {
         if (context == null || callback == null) {
             throw new IllegalArgumentException("null parameter: " + context + " " + callback);
         }
@@ -248,7 +287,7 @@
                 return null;
             }
             BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport);
-            Boolean regStatus = mGattServer.registerCallback(callback);
+            Boolean regStatus = mGattServer.registerCallback(callback, eatt_support);
             return regStatus ? mGattServer : null;
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index c1e7e41..144856b 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -1,3 +1,7 @@
 # Remain no owner because multiple modules may touch this file.
 per-file Context.java = *
 per-file ContextWrapper.java = *
+per-file IntentFilter.java = toddke@google.com
+per-file IntentFilter.java = patb@google.com
+per-file Intent.java = toddke@google.com
+per-file Intent.java = patb@google.com
\ No newline at end of file
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 0b950b4..44b5c44 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -17,7 +17,6 @@
 package android.content.om;
 
 import android.content.om.OverlayInfo;
-import android.content.om.OverlayManagerTransaction;
 
 /**
  * Api for getting information about overlay packages.
@@ -164,18 +163,4 @@
      * @param packageName The name of the overlay package whose idmap should be deleted.
      */
     void invalidateCachesForOverlay(in String packageName, in int userIs);
-
-    /**
-     * Perform a series of requests related to overlay packages. This is an
-     * atomic operation: either all requests were performed successfully and
-     * the changes were propagated to the rest of the system, or at least one
-     * request could not be performed successfully and nothing is changed and
-     * nothing is propagated to the rest of the system.
-     *
-     * @see OverlayManagerTransaction
-     *
-     * @param transaction the series of overlay related requests to perform
-     * @throws SecurityException if the transaction failed
-     */
-    void commit(in OverlayManagerTransaction transaction);
 }
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 7c14c28..217f637c 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -254,29 +254,6 @@
     }
 
     /**
-     * Perform a series of requests related to overlay packages. This is an
-     * atomic operation: either all requests were performed successfully and
-     * the changes were propagated to the rest of the system, or at least one
-     * request could not be performed successfully and nothing is changed and
-     * nothing is propagated to the rest of the system.
-     *
-     * @see OverlayManagerTransaction
-     *
-     * @param transaction the series of overlay related requests to perform
-     * @throws Exception if not all the requests could be successfully and
-     *         atomically executed
-     *
-     * @hide
-     */
-    public void commit(@NonNull final OverlayManagerTransaction transaction) {
-        try {
-            mService.commit(transaction);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Starting on R, actor enforcement and app visibility changes introduce additional failure
      * cases, but the SecurityException thrown with these checks is unexpected for existing
      * consumers of the API.
diff --git a/core/java/android/content/om/OverlayManagerTransaction.aidl b/core/java/android/content/om/OverlayManagerTransaction.aidl
deleted file mode 100644
index 6715c82..0000000
--- a/core/java/android/content/om/OverlayManagerTransaction.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2019 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.content.om;
-
-parcelable OverlayManagerTransaction;
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
deleted file mode 100644
index 1fa8973..0000000
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2019 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.content.om;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.UserHandle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Container for a batch of requests to the OverlayManagerService.
- *
- * Transactions are created using a builder interface. Example usage:
- *
- * final OverlayManager om = ctx.getSystemService(OverlayManager.class);
- * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
- *     .setEnabled(...)
- *     .setEnabled(...)
- *     .build();
- * om.commit(t);
- *
- * @hide
- */
-public class OverlayManagerTransaction
-        implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
-    // TODO: remove @hide from this class when OverlayManager is added to the
-    // SDK, but keep OverlayManagerTransaction.Request @hidden
-    private final List<Request> mRequests;
-
-    OverlayManagerTransaction(@NonNull final List<Request> requests) {
-        checkNotNull(requests);
-        if (requests.contains(null)) {
-            throw new IllegalArgumentException("null request");
-        }
-        mRequests = requests;
-    }
-
-    private OverlayManagerTransaction(@NonNull final Parcel source) {
-        final int size = source.readInt();
-        mRequests = new ArrayList<Request>(size);
-        for (int i = 0; i < size; i++) {
-            final int request = source.readInt();
-            final String packageName = source.readString();
-            final int userId = source.readInt();
-            mRequests.add(new Request(request, packageName, userId));
-        }
-    }
-
-    @Override
-    public Iterator<Request> iterator() {
-        return mRequests.iterator();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
-    }
-
-    /**
-     * A single unit of the transaction, such as a request to enable an
-     * overlay, or to disable an overlay.
-     *
-     * @hide
-     */
-    public static class Request {
-        @IntDef(prefix = "TYPE_", value = {
-                TYPE_SET_ENABLED,
-                TYPE_SET_DISABLED,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        @interface RequestType {}
-
-        public static final int TYPE_SET_ENABLED = 0;
-        public static final int TYPE_SET_DISABLED = 1;
-
-        @RequestType public final int type;
-        public final String packageName;
-        public final int userId;
-
-        public Request(@RequestType final int type, @NonNull final String packageName,
-                final int userId) {
-            this.type = type;
-            this.packageName = packageName;
-            this.userId = userId;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
-                    type, typeToString(), packageName, userId);
-        }
-
-        /**
-         * Translate the request type into a human readable string. Only
-         * intended for debugging.
-         *
-         * @hide
-         */
-        public String typeToString() {
-            switch (type) {
-                case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
-                case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
-                default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
-            }
-        }
-    }
-
-    /**
-     * Builder class for OverlayManagerTransaction objects.
-     *
-     * @hide
-     */
-    public static class Builder {
-        private final List<Request> mRequests = new ArrayList<>();
-
-        /**
-         * Request that an overlay package be enabled and change its loading
-         * order to the last package to be loaded, or disabled
-         *
-         * If the caller has the correct permissions, it is always possible to
-         * disable an overlay. Due to technical and security reasons it may not
-         * always be possible to enable an overlay, for instance if the overlay
-         * does not successfully overlay any target resources due to
-         * overlayable policy restrictions.
-         *
-         * An enabled overlay is a part of target package's resources, i.e. it will
-         * be part of any lookups performed via {@link android.content.res.Resources}
-         * and {@link android.content.res.AssetManager}. A disabled overlay will no
-         * longer affect the resources of the target package. If the target is
-         * currently running, its outdated resources will be replaced by new ones.
-         *
-         * @param packageName The name of the overlay package.
-         * @param enable true to enable the overlay, false to disable it.
-         * @return this Builder object, so you can chain additional requests
-         */
-        public Builder setEnabled(@NonNull String packageName, boolean enable) {
-            return setEnabled(packageName, enable, UserHandle.myUserId());
-        }
-
-        /**
-         * @hide
-         */
-        public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
-            checkNotNull(packageName);
-            @Request.RequestType final int type =
-                enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
-            mRequests.add(new Request(type, packageName, userId));
-            return this;
-        }
-
-        /**
-         * Create a new transaction out of the requests added so far. Execute
-         * the transaction by calling OverlayManager#commit.
-         *
-         * @see OverlayManager#commit
-         * @return a new transaction
-         */
-        public OverlayManagerTransaction build() {
-            return new OverlayManagerTransaction(mRequests);
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        final int size = mRequests.size();
-        dest.writeInt(size);
-        for (int i = 0; i < size; i++) {
-            final Request req = mRequests.get(i);
-            dest.writeInt(req.type);
-            dest.writeString(req.packageName);
-            dest.writeInt(req.userId);
-        }
-    }
-
-    public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
-            new Parcelable.Creator<OverlayManagerTransaction>() {
-
-        @Override
-        public OverlayManagerTransaction createFromParcel(Parcel source) {
-            return new OverlayManagerTransaction(source);
-        }
-
-        @Override
-        public OverlayManagerTransaction[] newArray(int size) {
-            return new OverlayManagerTransaction[size];
-        }
-    };
-}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bd6edb4..5f8754e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -64,7 +64,7 @@
  */
 interface IPackageManager {
     void checkPackageStartable(String packageName, int userId);
-    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     boolean isPackageAvailable(String packageName, int userId);
     @UnsupportedAppUsage
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index f88df95..f0def805 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -6,4 +6,6 @@
 
 per-file PackageParser.java = chiuwinson@google.com
 per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
 per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
+per-file UserInfo* = file:/MULTIUSER_OWNERS
diff --git a/core/java/android/hardware/face/OWNERS b/core/java/android/hardware/face/OWNERS
index 33527f8..be10df1 100644
--- a/core/java/android/hardware/face/OWNERS
+++ b/core/java/android/hardware/face/OWNERS
@@ -1,3 +1,7 @@
 # Bug component: 879035
 
+curtislb@google.com
+ilyamaty@google.com
 jaggies@google.com
+joshmccloskey@google.com
+kchyn@google.com
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
index c443c75..18467fa 100644
--- a/core/java/android/net/CaptivePortalData.java
+++ b/core/java/android/net/CaptivePortalData.java
@@ -39,9 +39,11 @@
     private final long mByteLimit;
     private final long mExpiryTimeMillis;
     private final boolean mCaptive;
+    private final String mVenueFriendlyName;
 
     private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
-            boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) {
+            boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
+            String venueFriendlyName) {
         mRefreshTimeMillis = refreshTimeMillis;
         mUserPortalUrl = userPortalUrl;
         mVenueInfoUrl = venueInfoUrl;
@@ -49,11 +51,12 @@
         mByteLimit = byteLimit;
         mExpiryTimeMillis = expiryTimeMillis;
         mCaptive = captive;
+        mVenueFriendlyName = venueFriendlyName;
     }
 
     private CaptivePortalData(Parcel p) {
         this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
-                p.readLong(), p.readLong(), p.readBoolean());
+                p.readLong(), p.readLong(), p.readBoolean(), p.readString());
     }
 
     @Override
@@ -70,6 +73,7 @@
         dest.writeLong(mByteLimit);
         dest.writeLong(mExpiryTimeMillis);
         dest.writeBoolean(mCaptive);
+        dest.writeString(mVenueFriendlyName);
     }
 
     /**
@@ -83,6 +87,7 @@
         private long mBytesRemaining = -1;
         private long mExpiryTime = -1;
         private boolean mCaptive;
+        private String mVenueFriendlyName;
 
         /**
          * Create an empty builder.
@@ -100,7 +105,8 @@
                     .setSessionExtendable(data.mIsSessionExtendable)
                     .setBytesRemaining(data.mByteLimit)
                     .setExpiryTime(data.mExpiryTimeMillis)
-                    .setCaptive(data.mCaptive);
+                    .setCaptive(data.mCaptive)
+                    .setVenueFriendlyName(data.mVenueFriendlyName);
         }
 
         /**
@@ -167,12 +173,22 @@
         }
 
         /**
+         * Set the venue friendly name.
+         */
+        @NonNull
+        public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) {
+            mVenueFriendlyName = venueFriendlyName;
+            return this;
+        }
+
+        /**
          * Create a new {@link CaptivePortalData}.
          */
         @NonNull
         public CaptivePortalData build() {
             return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
-                    mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive);
+                    mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
+                    mVenueFriendlyName);
         }
     }
 
@@ -232,6 +248,14 @@
         return mCaptive;
     }
 
+    /**
+     * Get the venue friendly name
+     */
+    @Nullable
+    public String getVenueFriendlyName() {
+        return mVenueFriendlyName;
+    }
+
     @NonNull
     public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
         @Override
@@ -248,7 +272,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
-                mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive);
+                mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName);
     }
 
     @Override
@@ -261,7 +285,8 @@
                 && mIsSessionExtendable == other.mIsSessionExtendable
                 && mByteLimit == other.mByteLimit
                 && mExpiryTimeMillis == other.mExpiryTimeMillis
-                && mCaptive == other.mCaptive;
+                && mCaptive == other.mCaptive
+                && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
     }
 
     @Override
@@ -274,6 +299,7 @@
                 + ", byteLimit: " + mByteLimit
                 + ", expiryTime: " + mExpiryTimeMillis
                 + ", captive: " + mCaptive
+                + ", venueFriendlyName: " + mVenueFriendlyName
                 + "}";
     }
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 06c15980..8742ecb 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,9 @@
 package android.net;
 
 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static android.net.NetworkRequest.Type.LISTEN;
+import static android.net.NetworkRequest.Type.REQUEST;
+import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -3730,14 +3733,12 @@
     private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
     private static CallbackHandler sCallbackHandler;
 
-    private static final int LISTEN  = 1;
-    private static final int REQUEST = 2;
-
     private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
-            int timeoutMs, int action, int legacyType, CallbackHandler handler) {
+            int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) {
         printStackTrace();
         checkCallbackNotNull(callback);
-        Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities");
+        Preconditions.checkArgument(
+                reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities");
         final NetworkRequest request;
         final String callingPackageName = mContext.getOpPackageName();
         try {
@@ -3750,13 +3751,13 @@
                 }
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
-                if (action == LISTEN) {
+                if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
                             need, messenger, binder, callingPackageName);
                 } else {
                     request = mService.requestNetwork(
-                            need, messenger, timeoutMs, binder, legacyType, callingPackageName,
-                            getAttributionTag());
+                            need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
+                            callingPackageName, getAttributionTag());
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -4260,7 +4261,7 @@
         // request, i.e., the system default network.
         CallbackHandler cbHandler = new CallbackHandler(handler);
         sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
-                REQUEST, TYPE_NONE, cbHandler);
+                TRACK_DEFAULT, TYPE_NONE, cbHandler);
     }
 
     /**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index b32c98b..5e925b6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -167,7 +167,7 @@
             in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
             in int factorySerialNumber);
 
-    NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
+    NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
             String callingPackageName, String callingAttributionTag);
 
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index aeaf09d..aa3682d 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -19,6 +19,9 @@
 import android.os.Parcelable;
 import android.net.ConnectivityMetricsEvent;
 import android.net.INetdEventCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 
 /** {@hide} */
 interface IIpConnectivityMetrics {
@@ -29,6 +32,11 @@
      */
     int logEvent(in ConnectivityMetricsEvent event);
 
+    void logDefaultNetworkValidity(boolean valid);
+    void logDefaultNetworkEvent(in Network defaultNetwork, int score, boolean validated,
+            in LinkProperties lp, in NetworkCapabilities nc, in Network previousDefaultNetwork,
+            int previousScore, in LinkProperties previousLp, in NetworkCapabilities previousNc);
+
     /**
      * Callback can be registered by DevicePolicyManager or NetworkWatchlistService only.
      * @return status {@code true} if registering/unregistering of the callback was successful,
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 37813ce..0a6be20 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,14 +85,14 @@
     /**
      * Interface data activity status is changed.
      *
-     * @param networkType The legacy network type of the data activity change.
+     * @param transportType The transport type of the data activity change.
      * @param active  True if the interface is actively transmitting data, false if it is idle.
      * @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
      * @param uid Uid of this event. It represents the uid that was responsible for waking the
      *            radio. For those events that are reported by system itself, not from specific uid,
      *            use -1 for the events which means no uid.
      */
-    void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, int uid);
+    void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid);
 
     /**
      * Information about available DNS servers has been received.
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 4166c2c..4f46736 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -408,7 +408,8 @@
             throw new IllegalArgumentException();
         }
 
-        mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc),
+        mInitialConfiguration = new InitialConfiguration(context,
+                new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true),
                 new LinkProperties(lp), score, config, ni);
     }
 
@@ -818,7 +819,9 @@
         Objects.requireNonNull(networkCapabilities);
         mBandwidthUpdatePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
-        final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+        final NetworkCapabilities nc =
+                new NetworkCapabilities(networkCapabilities,
+                        /* parcelLocationSensitiveFields */ true);
         queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc));
     }
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1a37fb9..2d9f6d8 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -76,12 +76,33 @@
      */
     private String mRequestorPackageName;
 
+    /**
+     * Indicates whether parceling should preserve fields that are set based on permissions of
+     * the process receiving the {@link NetworkCapabilities}.
+     */
+    private final boolean mParcelLocationSensitiveFields;
+
     public NetworkCapabilities() {
+        mParcelLocationSensitiveFields = false;
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
     }
 
     public NetworkCapabilities(NetworkCapabilities nc) {
+        this(nc, false /* parcelLocationSensitiveFields */);
+    }
+
+    /**
+     * Make a copy of NetworkCapabilities.
+     *
+     * @param nc Original NetworkCapabilities
+     * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not.
+     * @hide
+     */
+    @SystemApi
+    public NetworkCapabilities(
+            @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) {
+        mParcelLocationSensitiveFields = parcelLocationSensitiveFields;
         if (nc != null) {
             set(nc);
         }
@@ -93,6 +114,12 @@
      * @hide
      */
     public void clearAll() {
+        // Ensures that the internal copies maintained by the connectivity stack does not set
+        // this bit.
+        if (mParcelLocationSensitiveFields) {
+            throw new UnsupportedOperationException(
+                    "Cannot clear NetworkCapabilities when parcelLocationSensitiveFields is set");
+        }
         mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
         mNetworkSpecifier = null;
@@ -109,6 +136,8 @@
 
     /**
      * Set all contents of this object to the contents of a NetworkCapabilities.
+     *
+     * @param nc Original NetworkCapabilities
      * @hide
      */
     public void set(@NonNull NetworkCapabilities nc) {
@@ -117,7 +146,11 @@
         mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
         mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
         mNetworkSpecifier = nc.mNetworkSpecifier;
-        mTransportInfo = nc.mTransportInfo;
+        if (nc.getTransportInfo() != null) {
+            setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields));
+        } else {
+            setTransportInfo(null);
+        }
         mSignalStrength = nc.mSignalStrength;
         setUids(nc.mUids); // Will make the defensive copy
         setAdministratorUids(nc.getAdministratorUids());
@@ -171,6 +204,7 @@
             NET_CAPABILITY_PARTIAL_CONNECTIVITY,
             NET_CAPABILITY_TEMPORARILY_NOT_METERED,
             NET_CAPABILITY_OEM_PRIVATE,
+            NET_CAPABILITY_VEHICLE_INTERNAL,
     })
     public @interface NetCapability { }
 
@@ -357,8 +391,17 @@
     @SystemApi
     public static final int NET_CAPABILITY_OEM_PRIVATE = 26;
 
+    /**
+     * Indicates this is an internal vehicle network, meant to communicate with other
+     * automotive systems.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PRIVATE;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -401,15 +444,16 @@
      */
     @VisibleForTesting
     /* package */ static final long RESTRICTED_CAPABILITIES =
-            (1 << NET_CAPABILITY_CBS) |
-            (1 << NET_CAPABILITY_DUN) |
-            (1 << NET_CAPABILITY_EIMS) |
-            (1 << NET_CAPABILITY_FOTA) |
-            (1 << NET_CAPABILITY_IA) |
-            (1 << NET_CAPABILITY_IMS) |
-            (1 << NET_CAPABILITY_RCS) |
-            (1 << NET_CAPABILITY_XCAP) |
-            (1 << NET_CAPABILITY_MCX);
+            (1 << NET_CAPABILITY_CBS)
+            | (1 << NET_CAPABILITY_DUN)
+            | (1 << NET_CAPABILITY_EIMS)
+            | (1 << NET_CAPABILITY_FOTA)
+            | (1 << NET_CAPABILITY_IA)
+            | (1 << NET_CAPABILITY_IMS)
+            | (1 << NET_CAPABILITY_MCX)
+            | (1 << NET_CAPABILITY_RCS)
+            | (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
+            | (1 << NET_CAPABILITY_XCAP);
 
     /**
      * Capabilities that force network to be restricted.
@@ -425,10 +469,10 @@
      */
     @VisibleForTesting
     /* package */ static final long UNRESTRICTED_CAPABILITIES =
-            (1 << NET_CAPABILITY_INTERNET) |
-            (1 << NET_CAPABILITY_MMS) |
-            (1 << NET_CAPABILITY_SUPL) |
-            (1 << NET_CAPABILITY_WIFI_P2P);
+            (1 << NET_CAPABILITY_INTERNET)
+            | (1 << NET_CAPABILITY_MMS)
+            | (1 << NET_CAPABILITY_SUPL)
+            | (1 << NET_CAPABILITY_WIFI_P2P);
 
     /**
      * Capabilities that are managed by ConnectivityService.
@@ -896,8 +940,8 @@
     }
 
     private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
-        return ((this.mTransportTypes == 0) ||
-                ((this.mTransportTypes & nc.mTransportTypes) != 0));
+        return ((this.mTransportTypes == 0)
+                || ((this.mTransportTypes & nc.mTransportTypes) != 0));
     }
 
     /** @hide */
@@ -1151,12 +1195,12 @@
                 Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
     }
     private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
-        return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps ||
-                this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
+        return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps
+                || this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
     }
     private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
-        return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
-                this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
+        return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps
+                && this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
     }
     /** @hide */
     public static int minBandwidth(int a, int b) {
@@ -1671,9 +1715,9 @@
      */
     public boolean equalRequestableCapabilities(@Nullable NetworkCapabilities nc) {
         if (nc == null) return false;
-        return (equalsNetCapabilitiesRequestable(nc) &&
-                equalsTransportTypes(nc) &&
-                equalsSpecifier(nc));
+        return (equalsNetCapabilitiesRequestable(nc)
+                && equalsTransportTypes(nc)
+                && equalsSpecifier(nc));
     }
 
     @Override
@@ -1939,6 +1983,7 @@
             case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
             case NET_CAPABILITY_TEMPORARILY_NOT_METERED:    return "TEMPORARILY_NOT_METERED";
             case NET_CAPABILITY_OEM_PRIVATE:          return "OEM_PRIVATE";
+            case NET_CAPABILITY_VEHICLE_INTERNAL:     return "NET_CAPABILITY_VEHICLE_INTERNAL";
             default:                                  return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ce16a78..c029dea 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -32,8 +32,8 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.os.Build;
+import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
 import android.util.Pair;
@@ -122,17 +122,26 @@
      * @hide
      */
     public static final int RULE_REJECT_ALL = 1 << 6;
+    /**
+     * Reject traffic on all networks for restricted networking mode.
+     */
+    public static final int RULE_REJECT_RESTRICTED_MODE = 1 << 10;
 
     /**
      * Mask used to get the {@code RULE_xxx_METERED} rules
      * @hide
      */
-    public static final int MASK_METERED_NETWORKS = 0b00001111;
+    public static final int MASK_METERED_NETWORKS = 0b000000001111;
     /**
      * Mask used to get the {@code RULE_xxx_ALL} rules
      * @hide
      */
-    public static final int MASK_ALL_NETWORKS     = 0b11110000;
+    public static final int MASK_ALL_NETWORKS     = 0b000011110000;
+    /**
+     * Mask used to get the {@code RULE_xxx_RESTRICTED_MODE} rules
+     * @hide
+     */
+    public static final int MASK_RESTRICTED_MODE_NETWORKS     = 0b111100000000;
 
     /** @hide */
     public static final int FIREWALL_RULE_DEFAULT = 0;
@@ -433,6 +442,24 @@
     }
 
     /**
+     * Check that networking is blocked for the given uid.
+     *
+     * @param uid The target uid.
+     * @param meteredNetwork True if the network is metered.
+     * @return true if networking is blocked for the given uid according to current networking
+     *         policies.
+     *
+     * @hide
+     */
+    public boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) {
+        try {
+            return mService.isUidNetworkingBlocked(uid, meteredNetwork);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get multipath preference for the given network.
      */
     public int getMultipathPreference(Network network) {
@@ -473,7 +500,7 @@
     @Deprecated
     public static boolean isUidValidForPolicy(Context context, int uid) {
         // first, quick-reject non-applications
-        if (!UserHandle.isApp(uid)) {
+        if (!Process.isApplicationUid(uid)) {
             return false;
         }
 
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index dc16d74..f0c637c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -40,6 +40,18 @@
  */
 public class NetworkRequest implements Parcelable {
     /**
+     * The first requestId value that will be allocated.
+     * @hide only used by ConnectivityService.
+     */
+    public static final int FIRST_REQUEST_ID = 1;
+
+    /**
+     * The requestId value that represents the absence of a request.
+     * @hide only used by ConnectivityService.
+     */
+    public static final int REQUEST_ID_NONE = -1;
+
+    /**
      * The {@link NetworkCapabilities} that define this request.
      * @hide
      */
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 85bf79a..326943a 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 
 import com.android.net.IProxyService;
+
 import com.google.android.collect.Lists;
 
 import java.io.IOException;
@@ -50,7 +51,7 @@
                 ServiceManager.getService(PROXY_SERVICE));
         if (mProxyService == null) {
             // Added because of b10267814 where mako is restarting.
-            Log.e(TAG, "PacManager: no proxy service");
+            Log.e(TAG, "PacProxyInstaller: no proxy service");
         }
         mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
     }
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index a32b41f..a202d77 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -127,7 +127,7 @@
     }
 
     /**
-     * Only used in PacManager after Local Proxy is bound.
+     * Only used in PacProxyInstaller after Local Proxy is bound.
      * @hide
      */
     public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
diff --git a/core/java/android/net/TransportInfo.java b/core/java/android/net/TransportInfo.java
index b78d3fe..aa4bbb0 100644
--- a/core/java/android/net/TransportInfo.java
+++ b/core/java/android/net/TransportInfo.java
@@ -16,10 +16,48 @@
 
 package android.net;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
 /**
  * A container for transport-specific capabilities which is returned by
  * {@link NetworkCapabilities#getTransportInfo()}. Specific networks
  * may provide concrete implementations of this interface.
+ * @see android.net.wifi.aware.WifiAwareNetworkInfo
+ * @see android.net.wifi.WifiInfo
  */
 public interface TransportInfo {
+
+    /**
+     * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that
+     * were set based on the permissions of the process that originally received it.
+     *
+     * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as
+     * they should not be shared outside of the process that receives them without appropriate
+     * checks.
+     *
+     * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept
+     *                                      when parceling
+     * @return Copy of this instance.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+        return this;
+    }
+
+    /**
+     * Returns whether this TransportInfo type has location sensitive fields or not (helps
+     * to determine whether to perform a location permission check or not before sending to
+     * apps).
+     *
+     * @return {@code true} if this instance contains location sensitive info, {@code false}
+     * otherwise.
+     * @hide
+     */
+    @SystemApi
+    default boolean hasLocationSensitiveFields() {
+        return false;
+    }
 }
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index a008d85..58ea915 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -17,10 +17,13 @@
 package android.net.metrics;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
+import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -66,6 +69,9 @@
         final IIpConnectivityMetrics service =
                 IIpConnectivityMetrics.Stub.asInterface(ServiceManager.getService(SERVICE_NAME));
         if (service == null) {
+            if (DBG) {
+                Log.d(TAG, SERVICE_NAME + " service was not ready");
+            }
             return false;
         }
         // Two threads racing here will write the same pointer because getService
@@ -83,9 +89,6 @@
      */
     public boolean log(@NonNull ConnectivityMetricsEvent ev) {
         if (!checkLoggerService()) {
-            if (DBG) {
-                Log.d(TAG, SERVICE_NAME + " service was not ready");
-            }
             return false;
         }
         if (ev.timestamp == 0) {
@@ -161,6 +164,56 @@
         return log(makeEv(data));
     }
 
+    /**
+     * Logs the validation status of the default network.
+     * @param valid whether the current default network was validated (i.e., whether it had
+     *              {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}
+     * @return true if the event was successfully logged.
+     * @hide
+     */
+    public boolean logDefaultNetworkValidity(boolean valid) {
+        if (!checkLoggerService()) {
+            return false;
+        }
+        try {
+            mService.logDefaultNetworkValidity(valid);
+        } catch (RemoteException ignored) {
+            // Only called within the system server.
+        }
+        return true;
+    }
+
+    /**
+     * Logs a change in the default network.
+     *
+     * @param defaultNetwork the current default network
+     * @param score the current score of {@code defaultNetwork}
+     * @param lp the {@link LinkProperties} of {@code defaultNetwork}
+     * @param nc the {@link NetworkCapabilities} of the {@code defaultNetwork}
+     * @param validated whether {@code defaultNetwork} network is validated
+     * @param previousDefaultNetwork the previous default network
+     * @param previousScore the score of {@code previousDefaultNetwork}
+     * @param previousLp the {@link LinkProperties} of {@code previousDefaultNetwork}
+     * @param previousNc the {@link NetworkCapabilities} of {@code previousDefaultNetwork}
+     * @return true if the event was successfully logged.
+     * @hide
+     */
+    public boolean logDefaultNetworkEvent(@Nullable Network defaultNetwork, int score,
+            boolean validated, @Nullable LinkProperties lp, @Nullable NetworkCapabilities nc,
+            @Nullable Network previousDefaultNetwork, int previousScore,
+            @Nullable LinkProperties previousLp, @Nullable NetworkCapabilities previousNc) {
+        if (!checkLoggerService()) {
+            return false;
+        }
+        try {
+            mService.logDefaultNetworkEvent(defaultNetwork, score, validated, lp, nc,
+                    previousDefaultNetwork, previousScore, previousLp, previousNc);
+        } catch (RemoteException ignored) {
+            // Only called within the system server.
+        }
+        return true;
+    }
+
     private static ConnectivityMetricsEvent makeEv(Event data) {
         ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
         ev.data = data;
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index aa0f622..8dfd4e1 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -34,7 +34,7 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.util.Slog;
+import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -204,13 +204,13 @@
 
         @Override
         public void onChange(boolean selfChange) {
-            Slog.wtf(TAG, "Should never be reached.");
+            Log.wtf(TAG, "Should never be reached.");
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (!mSettingsUris.contains(uri)) {
-                Slog.wtf(TAG, "Unexpected settings observation: " + uri);
+                Log.wtf(TAG, "Unexpected settings observation: " + uri);
             }
             reevaluate();
         }
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
new file mode 100644
index 0000000..4d8cf91
--- /dev/null
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -0,0 +1,125 @@
+/*
+ * 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 android.net.vcn;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.TransportInfo;
+import android.net.wifi.WifiInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionManager;
+
+import java.util.Objects;
+
+/**
+ * VcnTransportInfo contains information about the VCN's underlying transports for SysUi.
+ *
+ * <p>Presence of this class in the NetworkCapabilities.TransportInfo implies that the network is a
+ * VCN.
+ *
+ * <p>VcnTransportInfo must exist on top of either an underlying Wifi or Cellular Network. If the
+ * underlying Network is WiFi, the subId will be {@link
+ * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. If the underlying Network is Cellular, the WifiInfo
+ * will be {@code null}.
+ *
+ * @hide
+ */
+public class VcnTransportInfo implements TransportInfo, Parcelable {
+    @Nullable private final WifiInfo mWifiInfo;
+    private final int mSubId;
+
+    public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
+        this(wifiInfo, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+    }
+
+    public VcnTransportInfo(int subId) {
+        this(null /* wifiInfo */, subId);
+    }
+
+    private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
+        if (wifiInfo == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            throw new IllegalArgumentException(
+                    "VcnTransportInfo requires either non-null WifiInfo or valid subId");
+        }
+
+        mWifiInfo = wifiInfo;
+        mSubId = subId;
+    }
+
+    /**
+     * Get the {@link WifiInfo} for this VcnTransportInfo.
+     *
+     * <p>If the underlying Network for the associated VCN is Cellular, returns null.
+     *
+     * @return the WifiInfo if there is an underlying WiFi connection, else null.
+     */
+    @Nullable
+    public WifiInfo getWifiInfo() {
+        return mWifiInfo;
+    }
+
+    /**
+     * Get the subId for the VCN Network associated with this VcnTransportInfo.
+     *
+     * <p>If the underlying Network for the associated VCN is WiFi, returns {@link
+     * SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * @return the Subscription ID if a cellular underlying Network is present, else {@link
+     *     android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}.
+     */
+    public int getSubId() {
+        return mSubId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mWifiInfo, mSubId);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof VcnTransportInfo)) return false;
+        final VcnTransportInfo that = (VcnTransportInfo) o;
+
+        return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+    /** Implement the Parcelable interface */
+    public static final @NonNull Creator<VcnTransportInfo> CREATOR =
+            new Creator<VcnTransportInfo>() {
+                public VcnTransportInfo createFromParcel(Parcel in) {
+                    // return null instead of a default VcnTransportInfo to avoid leaking
+                    // information about this being a VCN Network (instead of macro cellular, etc)
+                    return null;
+                }
+
+                public VcnTransportInfo[] newArray(int size) {
+                    return new VcnTransportInfo[size];
+                }
+            };
+}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 8c77a20..6c49b36 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -22,6 +22,10 @@
 per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
 per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS
 
+# Multiuser
+per-file IUser* = file:/MULTIUSER_OWNERS
+per-file User* = file:/MULTIUSER_OWNERS
+
 # Binder
 per-file BadParcelableException.java = file:platform/frameworks/native:/libs/binder/OWNERS
 per-file Binder.java = file:platform/frameworks/native:/libs/binder/OWNERS
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 67d5f5f..59302af 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1992,13 +1992,16 @@
     }
 
     /**
-     * Checks if specified user can have restricted profile.
+     * Checks if the calling context user can have a restricted profile.
+     * @return whether the context user can have a restricted profile.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    public boolean canHaveRestrictedProfile(@UserIdInt int userId) {
+    @UserHandleAware
+    public boolean canHaveRestrictedProfile() {
         try {
-            return mService.canHaveRestrictedProfile(userId);
+            return mService.canHaveRestrictedProfile(mUserId);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2020,6 +2023,25 @@
     }
 
     /**
+     * Get the parent of a restricted profile.
+     *
+     * @return the parent of the user or {@code null} if the user is not restricted profile
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @UserHandleAware
+    public @Nullable UserHandle getRestrictedProfileParent() {
+        final UserInfo info = getUserInfo(mUserId);
+        if (info == null) return null;
+        if (!info.isRestricted()) return null;
+        final int parent = info.restrictedProfileParentId;
+        if (parent == UserHandle.USER_NULL) return null;
+        return UserHandle.of(parent);
+    }
+
+    /**
      * Checks if a user is a guest user.
      * @return whether user is a guest user.
      * @hide
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 0fe8894..5aa4e27 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -35,8 +35,6 @@
 import android.os.Messenger;
 import android.os.ParcelableException;
 import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.FeatureFlagUtils;
 import android.util.Slog;
 
 import java.lang.annotation.Retention;
@@ -251,13 +249,7 @@
                 mService.send(msg);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to get status from installation service");
-                if (mExecutor != null) {
-                    mExecutor.execute(() -> {
-                        mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
-                    });
-                } else {
-                    mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
-                }
+                notifyOnStatusChangedListener(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
             }
         }
 
@@ -311,6 +303,20 @@
         mExecutor = null;
     }
 
+    private void notifyOnStatusChangedListener(
+            int status, int cause, long progress, Throwable detail) {
+        if (mListener != null) {
+            if (mExecutor != null) {
+                mExecutor.execute(
+                        () -> {
+                            mListener.onStatusChanged(status, cause, progress, detail);
+                        });
+            } else {
+                mListener.onStatusChanged(status, cause, progress, detail);
+            }
+        }
+    }
+
     /**
      * Bind to {@code DynamicSystem} installation service. Binding to the installation service
      * allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded
@@ -320,11 +326,6 @@
     @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
     @SystemApi
     public void bind() {
-        if (!featureFlagEnabled()) {
-            Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted.");
-            return;
-        }
-
         Intent intent = new Intent();
         intent.setClassName("com.android.dynsystem",
                 "com.android.dynsystem.DynamicSystemInstallationService");
@@ -395,11 +396,6 @@
     @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
     public void start(@NonNull Uri systemUrl, @BytesLong long systemSize,
             @BytesLong long userdataSize) {
-        if (!featureFlagEnabled()) {
-            Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; start() aborted.");
-            return;
-        }
-
         Intent intent = new Intent();
 
         intent.setClassName("com.android.dynsystem",
@@ -407,6 +403,7 @@
 
         intent.setData(systemUrl);
         intent.setAction(ACTION_START_INSTALL);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
         intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
@@ -414,11 +411,6 @@
         mContext.startActivity(intent);
     }
 
-    private boolean featureFlagEnabled() {
-        return SystemProperties.getBoolean(
-                FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
-    }
-
     private void handleMessage(Message msg) {
         switch (msg.what) {
             case MSG_POST_STATUS:
@@ -432,13 +424,7 @@
 
                 Throwable detail = t == null ? null : t.getCause();
 
-                if (mExecutor != null) {
-                    mExecutor.execute(() -> {
-                        mListener.onStatusChanged(status, cause, progress, detail);
-                    });
-                } else {
-                    mListener.onStatusChanged(status, cause, progress, detail);
-                }
+                notifyOnStatusChangedListener(status, cause, progress, detail);
                 break;
             default:
                 // do nothing
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index e7e2c61..bd84c84 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -92,6 +92,13 @@
     public static final String NAMESPACE_APP_COMPAT = "app_compat";
 
     /**
+     * Namespace for all app hibernation related features.
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
+
+    /**
      * Namespace for AttentionManagerService related features.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1e14e3a..4086161 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14406,6 +14406,17 @@
          */
         public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
                 "nr_nsa_tracking_screen_off_mode";
+
+        /**
+         * Used to enable / disable the Restricted Networking Mode in which network access is
+         * restricted to apps holding the CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+         *
+         * Values are:
+         * 0: disabled
+         * 1: enabled
+         * @hide
+         */
+        public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0aaedf3..266c1b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10379,7 +10379,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     protected boolean isVisibleToUser(Rect boundInView) {
         if (mAttachInfo != null) {
             // Attached to invisible window means this view is not visible.
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 670ca9f..03fe455 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -32,6 +32,7 @@
     private final boolean mDisabled;
     private final boolean mLoggingOnly;
     private final @Nullable String mDescription;
+    private final boolean mOverridable;
 
     public long getId() {
         return mChangeId;
@@ -58,9 +59,13 @@
         return mDescription;
     }
 
+    public boolean getOverridable() {
+        return mOverridable;
+    }
+
     public CompatibilityChangeInfo(
             Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk,
-            boolean disabled, boolean loggingOnly, String description) {
+            boolean disabled, boolean loggingOnly, String description, boolean overridable) {
         this.mChangeId = changeId;
         this.mName = name;
         if (enableAfterTargetSdk > 0) {
@@ -75,6 +80,7 @@
         this.mDisabled = disabled;
         this.mLoggingOnly = loggingOnly;
         this.mDescription = description;
+        this.mOverridable = overridable;
     }
 
     public CompatibilityChangeInfo(CompatibilityChangeInfo other) {
@@ -84,6 +90,7 @@
         this.mDisabled = other.mDisabled;
         this.mLoggingOnly = other.mLoggingOnly;
         this.mDescription = other.mDescription;
+        this.mOverridable = other.mOverridable;
     }
 
     private CompatibilityChangeInfo(Parcel in) {
@@ -93,6 +100,7 @@
         mDisabled = in.readBoolean();
         mLoggingOnly = in.readBoolean();
         mDescription = in.readString();
+        mOverridable = in.readBoolean();
     }
 
     @Override
@@ -108,6 +116,7 @@
         dest.writeBoolean(mDisabled);
         dest.writeBoolean(mLoggingOnly);
         dest.writeString(mDescription);
+        dest.writeBoolean(mOverridable);
     }
 
     @Override
@@ -126,6 +135,9 @@
         if (getLoggingOnly()) {
             sb.append("; loggingOnly");
         }
+        if (getOverridable()) {
+            sb.append("; overridable");
+        }
         return sb.append(")").toString();
     }
 
@@ -143,8 +155,8 @@
                 && this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk
                 && this.mDisabled == that.mDisabled
                 && this.mLoggingOnly == that.mLoggingOnly
-                && this.mDescription.equals(that.mDescription);
-
+                && this.mDescription.equals(that.mDescription)
+                && this.mOverridable == that.mOverridable;
     }
 
     public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 8f78b2a..1b07aa0 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -6,3 +6,5 @@
 per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
 per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
 per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
+per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
+
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 93f89b5..139b88b 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos,
+    public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos,
             int uid) {
         // default no-op
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b4572fd..79a0dfd 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -311,12 +311,6 @@
         },
     },
 
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
-        },
-    },
-
     // Workaround Clang LTO crash.
     lto: {
         never: true,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6381cf5..22dd765 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -14,15 +14,6 @@
  * limitations under the License.
  */
 
-/*
- * Disable optimization of this file if we are compiling with the address
- * sanitizer.  This is a mitigation for b/122921367 and can be removed once the
- * bug is fixed.
- */
-#if __has_feature(address_sanitizer)
-#pragma clang optimize off
-#endif
-
 #define LOG_TAG "Zygote"
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 02cf0b7..a30111b 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -1,7 +1,9 @@
 adamp@google.com
 alanv@google.com
+asc@google.com
 dsandler@android.com
 dsandler@google.com
+dupin@google.com
 hackbod@android.com
 hackbod@google.com
 jsharkey@android.com
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 42a658e..d30efa9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -455,6 +455,10 @@
         -->
     </string-array>
 
+    <!-- Whether the internal vehicle network should remain active even when no
+         apps requested it. -->
+    <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
+
     <!-- Configuration of network interfaces that support WakeOnLAN -->
     <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
         <!--
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1143467..8ac00dc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -110,7 +110,7 @@
     <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
     <string name="ClipMmi">Incoming Caller ID</string>
     <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
-    <string name="ClirMmi">Outgoing Caller ID</string>
+    <string name="ClirMmi">Hide Outgoing Caller ID</string>
     <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. -->
     <string name="ColpMmi">Connected Line ID</string>
     <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c505afe..937716d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -684,6 +684,7 @@
   <java-symbol type="string" name="config_ethernet_iface_regex" />
   <java-symbol type="string" name="not_checked" />
   <java-symbol type="array" name="config_ethernet_interfaces" />
+  <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" />
   <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
   <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
   <java-symbol type="string" name="config_mms_user_agent" />
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 912db1e..696aa11 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1,3 +1,4 @@
 per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
 per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/tests/coretests/src/android/graphics/OWNERS b/core/tests/coretests/src/android/graphics/OWNERS
new file mode 100644
index 0000000..1e8478e
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/OWNERS
+
+per-file Font* = file:/graphics/java/android/graphics/fonts/OWNERS
+per-file Typeface* = file:/graphics/java/android/graphics/fonts/OWNERS
diff --git a/core/tests/coretests/src/android/view/autofill/OWNERS b/core/tests/coretests/src/android/view/autofill/OWNERS
new file mode 100644
index 0000000..9a30e82
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 351486
+
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/tests/coretests/src/android/view/contentcapture/OWNERS b/core/tests/coretests/src/android/view/contentcapture/OWNERS
new file mode 100644
index 0000000..24561c5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 544200
+
+include /core/java/android/view/contentcapture/OWNERS
diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
index aec6096..5371a0f 100644
--- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
+++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
@@ -60,35 +60,35 @@
     @Test
     public void testExclusionForThumb_limitedTo48dp() {
         mBar.setPadding(10, 10, 10, 10);
-        mBar.setThumb(newThumb(dpToPx(20)));
+        mBar.setThumb(newThumb(dpToPxSize(20)));
         mBar.setMin(0);
         mBar.setMax(100);
         mBar.setProgress(50);
-        measureAndLayout(dpToPx(200), dpToPx(100));
+        measureAndLayout(dpToPxSize(200), dpToPxSize(100));
         List<Rect> exclusions = mBar.getSystemGestureExclusionRects();
 
         assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size());
         assertEquals("exclusion should be centered on thumb",
                 center(mBar), center(exclusions.get(0)));
-        assertEquals("exclusion should be 48dp high", dpToPx(48), exclusions.get(0).height());
-        assertEquals("exclusion should be 48dp wide", dpToPx(48), exclusions.get(0).width());
+        assertEquals("exclusion should be 48dp high", dpToPxSize(48), exclusions.get(0).height());
+        assertEquals("exclusion should be 48dp wide", dpToPxSize(48), exclusions.get(0).width());
     }
 
     @Test
     public void testExclusionForThumb_limitedToHeight() {
         mBar.setPadding(10, 10, 10, 10);
-        mBar.setThumb(newThumb(dpToPx(20)));
+        mBar.setThumb(newThumb(dpToPxSize(20)));
         mBar.setMin(0);
         mBar.setMax(100);
         mBar.setProgress(50);
-        measureAndLayout(dpToPx(200), dpToPx(32));
+        measureAndLayout(dpToPxSize(200), dpToPxSize(32));
         List<Rect> exclusions = mBar.getSystemGestureExclusionRects();
 
         assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size());
         assertEquals("exclusion should be centered on thumb",
                 center(mBar), center(exclusions.get(0)));
-        assertEquals("exclusion should be 32dp high", dpToPx(32), exclusions.get(0).height());
-        assertEquals("exclusion should be 32dp wide", dpToPx(32), exclusions.get(0).width());
+        assertEquals("exclusion should be 32dp high", dpToPxSize(32), exclusions.get(0).height());
+        assertEquals("exclusion should be 32dp wide", dpToPxSize(32), exclusions.get(0).width());
     }
 
     @Test
@@ -96,11 +96,11 @@
         mBar.setSystemGestureExclusionRects(Arrays.asList(new Rect(1, 2, 3, 4)));
 
         mBar.setPadding(10, 10, 10, 10);
-        mBar.setThumb(newThumb(dpToPx(20)));
+        mBar.setThumb(newThumb(dpToPxSize(20)));
         mBar.setMin(0);
         mBar.setMax(100);
         mBar.setProgress(50);
-        measureAndLayout(dpToPx(200), dpToPx(32));
+        measureAndLayout(dpToPxSize(200), dpToPxSize(32));
 
         assertThat(mBar.getSystemGestureExclusionRects(), hasItem(new Rect(1, 2, 3, 4)));
         assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2));
@@ -130,7 +130,7 @@
         mBar.layout(0, 0, wPx, hPx);
     }
 
-    private int dpToPx(int dp) {
-        return (int) (mContext.getResources().getDisplayMetrics().density * dp);
+    private int dpToPxSize(int dp) {
+        return (int) (mContext.getResources().getDisplayMetrics().density * dp + 0.5f);
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/OWNERS b/core/tests/coretests/src/com/android/internal/app/OWNERS
new file mode 100644
index 0000000..6888be3
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/OWNERS
@@ -0,0 +1 @@
+include /core/java/com/android/internal/app/OWNERS
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index f86ac9c..12a2b08 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -16,11 +16,7 @@
     name: "OverlayDeviceTests",
     srcs: ["src/**/*.java"],
     platform_apis: true,
-    certificate: "platform",
-    static_libs: [
-        "androidx.test.rules",
-        "testng",
-    ],
+    static_libs: ["androidx.test.rules"],
     test_suites: ["device-tests"],
     data: [
         ":OverlayDeviceTests_AppOverlayOne",
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index a69911f..4881636 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -19,8 +19,6 @@
 
     <uses-sdk android:minSdkVersion="21" />
 
-    <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
-
     <application>
         <uses-library android:name="android.test.runner"/>
     </application>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index db45750..6507839 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,20 +19,9 @@
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-instrumentation" />
 
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-        <option name="cleanup" value="true" />
-        <option name="remount-system" value="true" />
-        <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
-    </target_preparer>
-  
-    <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
-    <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
-      <option name="pre-reboot" value="true" />
-      <option name="post-reboot" value="true" />
-    </target_preparer>
-
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
         <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="OverlayDeviceTests.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
diff --git a/core/tests/overlaytests/device/TEST_MAPPING b/core/tests/overlaytests/device/TEST_MAPPING
deleted file mode 100644
index 43ee00f..0000000
--- a/core/tests/overlaytests/device/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name" : "OverlayDeviceTests"
-    }
-  ]
-}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 76c01a7..390bb76 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -18,76 +18,60 @@
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.om.OverlayManager;
-import android.content.om.OverlayManagerTransaction;
-import android.os.UserHandle;
+import android.app.UiAutomation;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
 
 import androidx.test.InstrumentationRegistry;
 
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 
 class LocalOverlayManager {
     private static final long TIMEOUT = 30;
 
-    public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
-            @NonNull final String[] overlaysToDisable) throws Exception {
-        final int userId = UserHandle.myUserId();
-        OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
-        for (String pkg : overlaysToEnable) {
-            builder.setEnabled(pkg, true, userId);
+    public static void setEnabledAndWait(Executor executor, final String packageName,
+            boolean enable) throws Exception {
+        final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
+        if (executeShellCommand("cmd overlay list").contains(pattern)) {
+            // nothing to do, overlay already in the requested state
+            return;
         }
-        for (String pkg : overlaysToDisable) {
-            builder.setEnabled(pkg, false, userId);
-        }
-        OverlayManagerTransaction transaction = builder.build();
 
-        final Context ctx = InstrumentationRegistry.getTargetContext();
+        final Resources res = InstrumentationRegistry.getContext().getResources();
+        final String[] oldApkPaths = res.getAssets().getApkPaths();
         FutureTask<Boolean> task = new FutureTask<>(() -> {
             while (true) {
-                final String[] paths = ctx.getResources().getAssets().getApkPaths();
-                if (arrayTailContains(paths, overlaysToEnable)
-                        && arrayDoesNotContain(paths, overlaysToDisable)) {
+                if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
                     return true;
                 }
                 Thread.sleep(10);
             }
         });
-
-        OverlayManager om = ctx.getSystemService(OverlayManager.class);
-        om.commit(transaction);
-
-        Executor executor = (cmd) -> new Thread(cmd).start();
         executor.execute(task);
+        executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
         task.get(TIMEOUT, SECONDS);
     }
 
-    private static boolean arrayTailContains(@NonNull final String[] array,
-            @NonNull final String[] substrings) {
-        if (array.length < substrings.length) {
-            return false;
-        }
-        for (int i = 0; i < substrings.length; i++) {
-            String a = array[array.length - substrings.length + i];
-            String s = substrings[i];
-            if (!a.contains(s)) {
-                return false;
+    private static String executeShellCommand(final String command)
+            throws Exception {
+        final UiAutomation uiAutomation =
+                InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
+        try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            final BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(in, StandardCharsets.UTF_8));
+            StringBuilder str = new StringBuilder();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                str.append(line);
             }
+            return str.toString();
         }
-        return true;
-    }
-
-    private static boolean arrayDoesNotContain(@NonNull final String[] array,
-            @NonNull final String[] substrings) {
-        for (String s : substrings) {
-            for (String a : array) {
-                if (a.contains(s)) {
-                    return false;
-                }
-            }
-        }
-        return true;
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
deleted file mode 100644
index 0b4f5e2..0000000
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2019 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.overlaytest;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.testng.Assert.assertThrows;
-
-import android.content.Context;
-import android.content.om.OverlayInfo;
-import android.content.om.OverlayManager;
-import android.content.om.OverlayManagerTransaction;
-import android.content.res.Resources;
-import android.os.UserHandle;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.List;
-
-@RunWith(JUnit4.class)
-@MediumTest
-public class TransactionTest {
-    static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
-    static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
-
-    private Context mContext;
-    private Resources mResources;
-    private OverlayManager mOverlayManager;
-    private int mUserId;
-    private UserHandle mUserHandle;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getContext();
-        mResources = mContext.getResources();
-        mOverlayManager = mContext.getSystemService(OverlayManager.class);
-        mUserId = UserHandle.myUserId();
-        mUserHandle = UserHandle.of(mUserId);
-
-        LocalOverlayManager.toggleOverlaysAndWait(
-                new String[]{},
-                new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
-    }
-
-    @Test
-    public void testValidTransaction() throws Exception {
-        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
-        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
-
-        OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
-                .setEnabled(APP_OVERLAY_ONE_PKG, true)
-                .setEnabled(APP_OVERLAY_TWO_PKG, true)
-                .build();
-        mOverlayManager.commit(t);
-
-        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
-        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
-        List<OverlayInfo> ois =
-                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
-        assertEquals(ois.size(), 2);
-        assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
-        assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
-
-        OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
-                .setEnabled(APP_OVERLAY_TWO_PKG, true)
-                .setEnabled(APP_OVERLAY_ONE_PKG, true)
-                .build();
-        mOverlayManager.commit(t2);
-
-        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
-        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
-        List<OverlayInfo> ois2 =
-                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
-        assertEquals(ois2.size(), 2);
-        assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
-        assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
-
-        OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
-                .setEnabled(APP_OVERLAY_TWO_PKG, false)
-                .build();
-        mOverlayManager.commit(t3);
-
-        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
-        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
-        List<OverlayInfo> ois3 =
-                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
-        assertEquals(ois3.size(), 2);
-        assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
-        assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
-    }
-
-    @Test
-    public void testInvalidRequestHasNoEffect() {
-        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
-        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
-
-        OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
-                .setEnabled(APP_OVERLAY_ONE_PKG, true)
-                .setEnabled("does-not-exist", true)
-                .setEnabled(APP_OVERLAY_TWO_PKG, true)
-                .build();
-        assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
-
-        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
-        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
-    }
-
-    private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
-        final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
-        assertNotNull(oi);
-        assertEquals(oi.isEnabled(), enabled);
-    }
-}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index 420f755..d28c47d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.Executor;
+
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -31,8 +33,9 @@
 
     @BeforeClass
     public static void enableOverlay() throws Exception {
-        LocalOverlayManager.toggleOverlaysAndWait(
-                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
-                new String[]{});
+        Executor executor = (cmd) -> new Thread(cmd).start();
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
+        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index a86255e..6566ad3 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.Executor;
+
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithOverlayTest extends OverlayBaseTest {
@@ -30,9 +32,10 @@
     }
 
     @BeforeClass
-    public static void enableOverlays() throws Exception {
-        LocalOverlayManager.toggleOverlaysAndWait(
-                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
-                new String[]{APP_OVERLAY_TWO_PKG});
+    public static void enableOverlay() throws Exception {
+        Executor executor = (cmd) -> new Thread(cmd).start();
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 51c4118..48cfeab 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.Executor;
+
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithoutOverlayTest extends OverlayBaseTest {
@@ -31,8 +33,9 @@
 
     @BeforeClass
     public static void disableOverlays() throws Exception {
-        LocalOverlayManager.toggleOverlaysAndWait(
-                new String[]{},
-                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+        Executor executor = (cmd) -> new Thread(cmd).start();
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
+        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
     }
 }
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index 847b491..da3aa00 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -15,6 +15,6 @@
 android_test {
     name: "OverlayDeviceTests_AppOverlayOne",
     sdk_version: "current",
-    certificate: "platform",
+
     aaptflags: ["--no-resource-removal"],
 }
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 7d5f82a..215b66da3 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -15,6 +15,6 @@
 android_test {
     name: "OverlayDeviceTests_AppOverlayTwo",
     sdk_version: "current",
-    certificate: "platform",
+
     aaptflags: ["--no-resource-removal"],
 }
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 5efd0bd..9867d81 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -11,3 +11,5 @@
 toddke@android.com
 toddke@google.com
 yamasani@google.com
+
+per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
\ No newline at end of file
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index cf643dd..1a367d9 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -44,6 +44,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseIntArray;
 import android.view.Display;
 
 import java.lang.annotation.Retention;
@@ -94,6 +95,7 @@
 
         RouteInfo mDefaultAudioVideo;
         RouteInfo mBluetoothA2dpRoute;
+        boolean mIsBluetoothA2dpOn;
 
         RouteInfo mSelectedRoute;
 
@@ -108,9 +110,16 @@
         IMediaRouterClient mClient;
         MediaRouterClientState mClientState;
 
+        SparseIntArray mStreamVolume = new SparseIntArray();
+
         final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
             @Override
             public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
+                try {
+                    mIsBluetoothA2dpOn = mAudioService.isBluetoothA2dpOn();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error querying Bluetooth A2DP state", e);
+                }
                 mHandler.post(new Runnable() {
                     @Override public void run() {
                         updateAudioRoutes(newRoutes);
@@ -259,13 +268,24 @@
             mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
         }
 
-        boolean isBluetoothA2dpOn() {
-            try {
-                return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
-                return false;
+        int getStreamVolume(int streamType) {
+            int idx = mStreamVolume.indexOfKey(streamType);
+            if (idx < 0) {
+                int volume = 0;
+                try {
+                    volume = mAudioService.getStreamVolume(streamType);
+                    mStreamVolume.put(streamType, volume);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error getting local stream volume", e);
+                } finally {
+                    return volume;
+                }
             }
+            return mStreamVolume.valueAt(idx);
+        }
+
+        boolean isBluetoothA2dpOn() {
+            return mBluetoothA2dpRoute != null && mIsBluetoothA2dpOn;
         }
 
         void updateDiscoveryRequest() {
@@ -1426,12 +1446,8 @@
                 selectedRoute == sStatic.mDefaultAudioVideo) {
             dispatchRouteVolumeChanged(selectedRoute);
         } else if (sStatic.mBluetoothA2dpRoute != null) {
-            try {
-                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
-                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
-            }
+            dispatchRouteVolumeChanged(sStatic.mIsBluetoothA2dpOn
+                    ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
         } else {
             dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
         }
@@ -1956,13 +1972,7 @@
          */
         public int getVolume() {
             if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
-                int vol = 0;
-                try {
-                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error getting local stream volume", e);
-                }
-                return vol;
+                return sStatic.getStreamVolume(mPlaybackStream);
             } else {
                 return mVolume;
             }
@@ -3077,11 +3087,12 @@
             if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
                 final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
                         -1);
+                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+                sStatic.mStreamVolume.put(streamType, newVolume);
                 if (streamType != AudioManager.STREAM_MUSIC) {
                     return;
                 }
 
-                final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
                 final int oldVolume = intent.getIntExtra(
                         AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
                 if (newVolume != oldVolume) {
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index a26f715..c8f3bd3 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-cc_defaults {
-    name: "libservice-connectivity-defaults",
+cc_library_shared {
+    name: "libservice-connectivity",
     // TODO: build against the NDK (sdk_version: "30" for example)
     cflags: [
         "-Wall",
@@ -26,6 +26,7 @@
     srcs: [
         "jni/com_android_server_TestNetworkService.cpp",
         "jni/com_android_server_connectivity_Vpn.cpp",
+        "jni/onload.cpp",
     ],
     shared_libs: [
         "libbase",
@@ -35,27 +36,11 @@
         // addresses, and remove dependency on libnetutils.
         "libnetutils",
     ],
-}
-
-cc_library_shared {
-    name: "libservice-connectivity",
-    defaults: ["libservice-connectivity-defaults"],
-    srcs: [
-        "jni/onload.cpp",
-    ],
     apex_available: [
-        // TODO: move this library to the tethering APEX and remove libservice-connectivity-static
-        // "com.android.tethering",
+        "com.android.tethering",
     ],
 }
 
-// Static library linked into libservices.core until libservice-connectivity can be loaded from
-// the tethering APEX instead.
-cc_library_static {
-    name: "libservice-connectivity-static",
-    defaults: ["libservice-connectivity-defaults"],
-}
-
 java_library {
     name: "service-connectivity",
     srcs: [
@@ -75,5 +60,6 @@
     ],
     apex_available: [
         "//apex_available:platform",
+        "com.android.tethering",
     ],
 }
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index c63cf06..2b5e9cd 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -291,7 +291,7 @@
         <item>256K</item>
         <item>1M</item>
         <item>4M</item>
-        <item>16M</item>
+        <item>8M</item>
     </string-array>
 
     <!-- Titles for logd limit size lowram selection preference. [CHAR LIMIT=14] -->
@@ -309,7 +309,7 @@
         <item>262144</item>
         <item>1048576</item>
         <item>4194304</item>
-        <item>16777216</item>
+        <item>8388608</item>
     </string-array>
 
     <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=50]-->
@@ -319,7 +319,7 @@
         <item>256K per log buffer</item>
         <item>1M per log buffer</item>
         <item>4M per log buffer</item>
-        <item>16M per log buffer</item>
+        <item>8M per log buffer</item>
     </string-array>
 
     <!-- Values for logpersist state selection preference. -->
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index dd94d2e..c559678 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -149,5 +149,6 @@
         VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 5ecb171..1345c3f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -420,6 +420,7 @@
                     Settings.Global.RADIO_WIMAX,
                     Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
                     Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT,
+                    Settings.Global.RESTRICTED_NETWORKING_MODE,
                     Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT,
                     Settings.Global.SAFE_BOOT_DISALLOWED,
                     Settings.Global.SELINUX_STATUS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 741a680..8c2dfd4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -156,6 +156,7 @@
     <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
     <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
     <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
+    <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
index 8dcddc2..7880cbd 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
@@ -16,65 +16,72 @@
  * limitations under the License.
  */
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/screen_pinning_text_area"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="?android:attr/colorAccent"
-    android:gravity="center_vertical">
+    android:fillViewport="true">
 
-    <TextView
-        android:id="@+id/screen_pinning_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingEnd="48dp"
-        android:paddingStart="48dp"
-        android:paddingTop="43dp"
-        android:text="@string/screen_pinning_title"
-        android:textColor="@android:color/white"
-        android:textSize="24sp" />
-
-    <TextView
-        android:id="@+id/screen_pinning_description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/screen_pinning_title"
-        android:paddingEnd="48dp"
-        android:paddingStart="48dp"
-        android:paddingTop="12.6dp"
-        android:text="@string/screen_pinning_description"
-        android:textColor="@android:color/white"
-        android:textSize="16sp" />
-
-    <Button
-        android:id="@+id/screen_pinning_ok_button"
-        style="@android:style/Widget.Material.Button"
+    <RelativeLayout
+        android:id="@+id/screen_pinning_text_area"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
-        android:layout_alignParentEnd="true"
-        android:layout_below="@+id/screen_pinning_description"
-        android:layout_marginEnd="40dp"
-        android:layout_marginTop="18dp"
-        android:background="@null"
-        android:paddingEnd="8dp"
-        android:paddingStart="8dp"
-        android:text="@string/screen_pinning_positive"
-        android:textColor="@android:color/white"
-        android:textSize="14sp" />
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorAccent"
+        android:gravity="center_vertical">
 
-    <Button
-        android:id="@+id/screen_pinning_cancel_button"
-        style="@android:style/Widget.Material.Button"
-        android:layout_width="wrap_content"
-        android:layout_height="36dp"
-        android:layout_alignTop="@id/screen_pinning_ok_button"
-        android:layout_marginEnd="4dp"
-        android:layout_toStartOf="@id/screen_pinning_ok_button"
-        android:background="@null"
-        android:paddingEnd="8dp"
-        android:paddingStart="8dp"
-        android:text="@string/screen_pinning_negative"
-        android:textColor="@android:color/white"
-        android:textSize="14sp" />
+        <TextView
+            android:id="@+id/screen_pinning_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingEnd="48dp"
+            android:paddingStart="48dp"
+            android:paddingTop="43dp"
+            android:text="@string/screen_pinning_title"
+            android:textColor="@android:color/white"
+            android:textSize="24sp" />
 
-</RelativeLayout>
+        <TextView
+            android:id="@+id/screen_pinning_description"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/screen_pinning_title"
+            android:paddingEnd="48dp"
+            android:paddingStart="48dp"
+            android:paddingTop="12.6dp"
+            android:text="@string/screen_pinning_description"
+            android:textColor="@android:color/white"
+            android:textSize="16sp" />
+
+        <Button
+            android:id="@+id/screen_pinning_ok_button"
+            style="@android:style/Widget.Material.Button"
+            android:layout_width="wrap_content"
+            android:layout_height="36dp"
+            android:layout_alignParentEnd="true"
+            android:layout_below="@+id/screen_pinning_description"
+            android:layout_marginEnd="40dp"
+            android:layout_marginTop="18dp"
+            android:background="@null"
+            android:paddingEnd="8dp"
+            android:paddingStart="8dp"
+            android:text="@string/screen_pinning_positive"
+            android:textColor="@android:color/white"
+            android:textSize="14sp" />
+
+        <Button
+            android:id="@+id/screen_pinning_cancel_button"
+            style="@android:style/Widget.Material.Button"
+            android:layout_width="wrap_content"
+            android:layout_height="36dp"
+            android:layout_alignTop="@id/screen_pinning_ok_button"
+            android:layout_marginEnd="4dp"
+            android:layout_toStartOf="@id/screen_pinning_ok_button"
+            android:background="@null"
+            android:paddingEnd="8dp"
+            android:paddingStart="8dp"
+            android:text="@string/screen_pinning_negative"
+            android:textColor="@android:color/white"
+            android:textSize="14sp" />
+
+    </RelativeLayout>
+
+</ScrollView>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 309d4b0..c5a35ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -29,7 +29,6 @@
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IConnectivityManager;
 import android.net.Network;
-import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -66,12 +65,8 @@
     private static final String TAG = "SecurityController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
-            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-            .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
-            .setUids(null)
-            .build();
+    private static final NetworkRequest REQUEST =
+            new NetworkRequest.Builder().clearCapabilities().build();
     private static final int NO_NETWORK = -1;
 
     private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index ac40222..f8b9309 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -19,8 +19,8 @@
 import android.util.Log;
 
 import com.android.net.IProxyPortListener;
+
 import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -34,7 +34,6 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -361,7 +360,7 @@
             try {
                 mCallback.setProxyPort(port);
             } catch (RemoteException e) {
-                Log.w(TAG, "Proxy failed to report port to PacManager", e);
+                Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
             }
         }
         mPort = port;
@@ -372,7 +371,7 @@
             try {
                 callback.setProxyPort(mPort);
             } catch (RemoteException e) {
-                Log.w(TAG, "Proxy failed to report port to PacManager", e);
+                Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
             }
         }
         mCallback = callback;
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
index 970fdc7..bdf478d 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
@@ -30,7 +30,7 @@
 
     private static ProxyServer server = null;
 
-    /** Keep these values up-to-date with PacManager.java */
+    /** Keep these values up-to-date with PacProxyInstaller.java */
     public static final String KEY_PROXY = "keyProxy";
     public static final String HOST = "localhost";
     public static final String EXCL_LIST = "";
diff --git a/services/Android.bp b/services/Android.bp
index f40f7cf..ef52c2a 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -83,7 +83,6 @@
         "services.voiceinteraction",
         "services.wifi",
         "service-blobstore",
-        "service-connectivity",
         "service-jobscheduler",
         "android.hidl.base-V1.0-java",
     ],
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6adf66c..307d344 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -130,7 +130,7 @@
         "capture_state_listener-aidl-java",
         "dnsresolver_aidl_interface-java",
         "icu4j_calendar_astronomer",
-        "netd_aidl_interfaces-platform-java",
+        "netd-client",
         "overlayable_policy_aidl-java",
         "SurfaceFlingerProperties",
         "com.android.sysprop.watchdog",
@@ -189,15 +189,11 @@
         "java/com/android/server/connectivity/AutodestructReference.java",
         "java/com/android/server/connectivity/ConnectivityConstants.java",
         "java/com/android/server/connectivity/DataConnectionStats.java",
-        "java/com/android/server/connectivity/DefaultNetworkMetrics.java",
         "java/com/android/server/connectivity/DnsManager.java",
-        "java/com/android/server/connectivity/IpConnectivityEventBuilder.java",
-        "java/com/android/server/connectivity/IpConnectivityMetrics.java",
         "java/com/android/server/connectivity/KeepaliveTracker.java",
         "java/com/android/server/connectivity/LingerMonitor.java",
         "java/com/android/server/connectivity/MockableSystemProperties.java",
         "java/com/android/server/connectivity/Nat464Xlat.java",
-        "java/com/android/server/connectivity/NetdEventListenerService.java",
         "java/com/android/server/connectivity/NetworkAgentInfo.java",
         "java/com/android/server/connectivity/NetworkDiagnostics.java",
         "java/com/android/server/connectivity/NetworkNotificationManager.java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 397eeb2..6b45abd 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -28,7 +28,6 @@
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_NONE;
@@ -56,12 +55,14 @@
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
 import static android.os.Process.INVALID_UID;
+import static android.os.Process.VPN_UID;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 
 import static java.util.Map.Entry;
 
 import android.Manifest;
+import android.annotation.BoolRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -87,7 +88,6 @@
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IConnectivityManager;
 import android.net.IDnsResolver;
-import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkMonitor;
@@ -154,7 +154,6 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -620,7 +619,7 @@
     private LingerMonitor mLingerMonitor;
 
     // sequence number of NetworkRequests
-    private int mNextNetworkRequestId = 1;
+    private int mNextNetworkRequestId = NetworkRequest.FIRST_REQUEST_ID;
 
     // Sequence number for NetworkProvider IDs.
     private final AtomicInteger mNextNetworkProviderId = new AtomicInteger(
@@ -927,14 +926,6 @@
                     "no IpConnectivityMetrics service");
         }
 
-        /**
-         * @see IpConnectivityMetrics
-         */
-        public IIpConnectivityMetrics getIpConnectivityMetrics() {
-            return IIpConnectivityMetrics.Stub.asInterface(
-                    ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
-        }
-
         public IBatteryStats getBatteryStatsService() {
             return BatteryStatsService.getService();
         }
@@ -973,6 +964,10 @@
         mDefaultWifiRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
 
+        mDefaultVehicleRequest = createAlwaysOnRequestForCapability(
+                NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
+                NetworkRequest.Type.BACKGROUND_REQUEST);
+
         mHandlerThread = mDeps.makeHandlerThread();
         mHandlerThread.start();
         mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -1172,6 +1167,15 @@
         return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
     }
 
+    private NetworkRequest createAlwaysOnRequestForCapability(int capability,
+            NetworkRequest.Type type) {
+        final NetworkCapabilities netCap = new NetworkCapabilities();
+        netCap.clearAll();
+        netCap.addCapability(capability);
+        netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
+        return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
+    }
+
     // Used only for testing.
     // TODO: Delete this and either:
     // 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
@@ -1189,10 +1193,19 @@
         mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
     }
 
+    private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) {
+        final boolean enable = mContext.getResources().getBoolean(id);
+        handleAlwaysOnNetworkRequest(networkRequest, enable);
+    }
+
     private void handleAlwaysOnNetworkRequest(
             NetworkRequest networkRequest, String settingName, boolean defaultValue) {
         final boolean enable = toBool(Settings.Global.getInt(
                 mContext.getContentResolver(), settingName, encodeBool(defaultValue)));
+        handleAlwaysOnNetworkRequest(networkRequest, enable);
+    }
+
+    private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, boolean enable) {
         final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null);
         if (enable == isEnabled) {
             return;  // Nothing to do.
@@ -1209,9 +1222,11 @@
 
     private void handleConfigureAlwaysOnNetworks() {
         handleAlwaysOnNetworkRequest(
-                mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
+                mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
         handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
                 false);
+        handleAlwaysOnNetworkRequest(mDefaultVehicleRequest,
+                com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested);
     }
 
     private void registerSettingsCallbacks() {
@@ -1238,6 +1253,8 @@
     }
 
     private synchronized int nextNetworkRequestId() {
+        // TODO: Consider handle wrapping and exclude {@link NetworkRequest#REQUEST_ID_NONE} if
+        //  doing that.
         return mNextNetworkRequestId++;
     }
 
@@ -1329,15 +1346,20 @@
     /**
      * Check if UID should be blocked from using the specified network.
      */
-    private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid,
-            boolean ignoreBlocked) {
+    private boolean isNetworkWithCapabilitiesBlocked(@Nullable final NetworkCapabilities nc,
+            final int uid, final boolean ignoreBlocked) {
         // Networks aren't blocked when ignoring blocked status
         if (ignoreBlocked) {
             return false;
         }
         if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true;
-        final String iface = (lp == null ? "" : lp.getInterfaceName());
-        return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final boolean metered = nc == null ? true : nc.isMetered();
+            return mPolicyManager.isUidNetworkingBlocked(uid, metered);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) {
@@ -1375,12 +1397,13 @@
     /**
      * Apply any relevant filters to {@link NetworkState} for the given UID. For
      * example, this may mark the network as {@link DetailedState#BLOCKED} based
-     * on {@link #isNetworkWithLinkPropertiesBlocked}.
+     * on {@link #isNetworkWithCapabilitiesBlocked}.
      */
     private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
         if (state == null || state.networkInfo == null || state.linkProperties == null) return;
 
-        if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
+        if (isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid,
+                ignoreBlocked)) {
             state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
         }
         synchronized (mVpns) {
@@ -1420,31 +1443,20 @@
     }
 
     private Network getActiveNetworkForUidInternal(final int uid, boolean ignoreBlocked) {
-        final int user = UserHandle.getUserId(uid);
-        int vpnNetId = NETID_UNSET;
-        synchronized (mVpns) {
-            final Vpn vpn = mVpns.get(user);
-            // TODO : now that capabilities contain the UID, the appliesToUid test should
-            // be removed as the satisfying test below should be enough.
-            if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
-        }
-        NetworkAgentInfo nai;
-        if (vpnNetId != NETID_UNSET) {
-            nai = getNetworkAgentInfoForNetId(vpnNetId);
-            if (nai != null) {
-                final NetworkCapabilities requiredCaps =
-                    createDefaultNetworkCapabilitiesForUid(uid);
-                if (requiredCaps.satisfiedByNetworkCapabilities(nai.networkCapabilities)) {
-                    return nai.network;
-                }
+        final NetworkAgentInfo vpnNai = getVpnForUid(uid);
+        if (vpnNai != null) {
+            final NetworkCapabilities requiredCaps = createDefaultNetworkCapabilitiesForUid(uid);
+            if (requiredCaps.satisfiedByNetworkCapabilities(vpnNai.networkCapabilities)) {
+                return vpnNai.network;
             }
         }
-        nai = getDefaultNetwork();
-        if (nai != null
-                && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, ignoreBlocked)) {
-            nai = null;
+
+        NetworkAgentInfo nai = getDefaultNetwork();
+        if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
+                ignoreBlocked)) {
+            return null;
         }
-        return nai != null ? nai.network : null;
+        return nai.network;
     }
 
     // Public because it's used by mLockdownTracker.
@@ -1513,7 +1525,7 @@
         enforceAccessPermission();
         final int uid = mDeps.getCallingUid();
         NetworkState state = getFilteredNetworkState(networkType, uid);
-        if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
+        if (!isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid, false)) {
             return state.network;
         }
         return null;
@@ -1557,7 +1569,7 @@
         if (nc != null) {
             result.put(
                     nai.network,
-                    maybeSanitizeLocationInfoForCaller(
+                    createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                             nc, mDeps.getCallingUid(), callingPackageName));
         }
 
@@ -1567,7 +1579,9 @@
             for (Network network : networks) {
                 nc = getNetworkCapabilitiesInternal(network);
                 if (nc != null) {
-                    result.put(network, maybeSanitizeLocationInfoForCaller(
+                    result.put(
+                            network,
+                            createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                                     nc, mDeps.getCallingUid(), callingPackageName));
                 }
             }
@@ -1649,7 +1663,7 @@
     public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
         mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
         enforceAccessPermission();
-        return maybeSanitizeLocationInfoForCaller(
+        return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
                 mDeps.getCallingUid(), callingPackageName);
     }
@@ -1670,37 +1684,51 @@
         return newNc;
     }
 
+    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mLocationPermissionChecker.checkLocationPermission(
+                    callerPkgName, null /* featureId */, callerUid, null /* message */);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @VisibleForTesting
     @Nullable
-    NetworkCapabilities maybeSanitizeLocationInfoForCaller(
+    NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
             @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
         if (nc == null) {
             return null;
         }
-        final NetworkCapabilities newNc = new NetworkCapabilities(nc);
-        if (callerUid != newNc.getOwnerUid()) {
+        Boolean hasLocationPermission = null;
+        final NetworkCapabilities newNc;
+        // Avoid doing location permission check if the transport info has no location sensitive
+        // data.
+        if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
+            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            newNc = new NetworkCapabilities(nc, hasLocationPermission);
+        } else {
+            newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
+        }
+        // Reset owner uid if not destined for the owner app.
+        if (callerUid != nc.getOwnerUid()) {
             newNc.setOwnerUid(INVALID_UID);
             return newNc;
         }
-
         // Allow VPNs to see ownership of their own VPN networks - not location sensitive.
         if (nc.hasTransport(TRANSPORT_VPN)) {
             // Owner UIDs already checked above. No need to re-check.
             return newNc;
         }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            if (!mLocationPermissionChecker.checkLocationPermission(
-                    callerPkgName, null /* featureId */, callerUid, null /* message */)) {
-                // Caller does not have the requisite location permissions. Reset the
-                // owner's UID in the NetworkCapabilities.
-                newNc.setOwnerUid(INVALID_UID);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        if (hasLocationPermission == null) {
+            // Location permission not checked yet, check now for masking owner UID.
+            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
         }
-
+        // Reset owner uid if the app has no location permission.
+        if (!hasLocationPermission) {
+            newNc.setOwnerUid(INVALID_UID);
+        }
         return newNc;
     }
 
@@ -1779,12 +1807,28 @@
 
     private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
         @Override
-        public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos,
-                int uid) {
-            sendDataActivityBroadcast(networkType, active, tsNanos);
+        public void interfaceClassDataActivityChanged(int transportType, boolean active,
+                long tsNanos, int uid) {
+            sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos);
         }
     };
 
+    // This is deprecated and only to support legacy use cases.
+    private int transportTypeToLegacyType(int type) {
+        switch (type) {
+            case NetworkCapabilities.TRANSPORT_CELLULAR:
+                return ConnectivityManager.TYPE_MOBILE;
+            case NetworkCapabilities.TRANSPORT_WIFI:
+                return ConnectivityManager.TYPE_WIFI;
+            case NetworkCapabilities.TRANSPORT_BLUETOOTH:
+                return ConnectivityManager.TYPE_BLUETOOTH;
+            case NetworkCapabilities.TRANSPORT_ETHERNET:
+                return ConnectivityManager.TYPE_ETHERNET;
+            default:
+                loge("Unexpected transport in transportTypeToLegacyType: " + type);
+        }
+        return ConnectivityManager.TYPE_NONE;
+    }
     /**
      * Ensures that the system cannot call a particular method.
      */
@@ -2368,13 +2412,13 @@
             timeout = Settings.Global.getInt(mContext.getContentResolver(),
                                              Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
                                              10);
-            type = ConnectivityManager.TYPE_MOBILE;
+            type = NetworkCapabilities.TRANSPORT_CELLULAR;
         } else if (networkAgent.networkCapabilities.hasTransport(
                 NetworkCapabilities.TRANSPORT_WIFI)) {
             timeout = Settings.Global.getInt(mContext.getContentResolver(),
                                              Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
                                              15);
-            type = ConnectivityManager.TYPE_WIFI;
+            type = NetworkCapabilities.TRANSPORT_WIFI;
         } else {
             return; // do not track any other networks
         }
@@ -2949,7 +2993,7 @@
                 case EVENT_CAPPORT_DATA_CHANGED: {
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
-                    handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj);
+                    handleCapportApiDataUpdate(nai, (CaptivePortalData) msg.obj);
                     break;
                 }
             }
@@ -2975,9 +3019,7 @@
             }
             if (valid != nai.lastValidated) {
                 if (wasDefault) {
-                    mDeps.getMetricsLogger()
-                            .defaultNetworkMetrics().logDefaultNetworkValidity(
-                            SystemClock.elapsedRealtime(), valid);
+                    mMetricsLog.logDefaultNetworkValidity(valid);
                 }
                 final int oldScore = nai.getCurrentScore();
                 nai.lastValidated = valid;
@@ -3289,9 +3331,9 @@
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
 
-    private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai,
+    private void handleCapportApiDataUpdate(@NonNull final NetworkAgentInfo nai,
             @Nullable final CaptivePortalData data) {
-        nai.captivePortalData = data;
+        nai.capportApiData = data;
         // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
@@ -3405,7 +3447,9 @@
             // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
             // whose timestamps tell how long it takes to recover a default network.
             long now = SystemClock.elapsedRealtime();
-            mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
+            mMetricsLog.logDefaultNetworkEvent(null, 0, false,
+                    null /* lp */, null /* nc */, nai.network, nai.getCurrentScore(),
+                    nai.linkProperties, nai.networkCapabilities);
         }
         notifyIfacesChangedForNetworkStats();
         // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -4471,7 +4515,8 @@
         if (!nai.everConnected) {
             return;
         }
-        if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) {
+        final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+        if (isNetworkWithCapabilitiesBlocked(nc, uid, false)) {
             return;
         }
         nai.networkMonitor().forceReevaluation(uid);
@@ -4789,15 +4834,15 @@
             if (mLockdownEnabled) {
                 return new VpnInfo[0];
             }
-            List<VpnInfo> infoList = new ArrayList<>();
-            for (NetworkAgentInfo nai : mNetworkAgentInfos) {
-                VpnInfo info = createVpnInfo(nai);
-                if (info != null) {
-                    infoList.add(info);
-                }
-            }
-            return infoList.toArray(new VpnInfo[infoList.size()]);
         }
+        List<VpnInfo> infoList = new ArrayList<>();
+        for (NetworkAgentInfo nai : mNetworkAgentInfos) {
+            VpnInfo info = createVpnInfo(nai);
+            if (info != null) {
+                infoList.add(info);
+            }
+        }
+        return infoList.toArray(new VpnInfo[infoList.size()]);
     }
 
     /**
@@ -5613,31 +5658,40 @@
 
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
-            @NonNull String callingPackageName, @Nullable String callingAttributionTag) {
+            int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
+            int legacyType, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
             if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
         final int callingUid = mDeps.getCallingUid();
-        final NetworkRequest.Type type = (networkCapabilities == null)
-                ? NetworkRequest.Type.TRACK_DEFAULT
-                : NetworkRequest.Type.REQUEST;
-        // If the requested networkCapabilities is null, take them instead from
-        // the default network request. This allows callers to keep track of
-        // the system default network.
-        if (type == NetworkRequest.Type.TRACK_DEFAULT) {
-            networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
-            enforceAccessPermission();
-        } else {
-            networkCapabilities = new NetworkCapabilities(networkCapabilities);
-            enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
-                    callingAttributionTag);
-            // TODO: this is incorrect. We mark the request as metered or not depending on the state
-            // of the app when the request is filed, but we never change the request if the app
-            // changes network state. http://b/29964605
-            enforceMeteredApnPolicy(networkCapabilities);
+        final NetworkRequest.Type reqType;
+        try {
+            reqType = NetworkRequest.Type.values()[reqTypeInt];
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new IllegalArgumentException("Unsupported request type " + reqTypeInt);
+        }
+        switch (reqType) {
+            case TRACK_DEFAULT:
+                // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities}
+                // is unused and will be replaced by the one from the default network request.
+                // This allows callers to keep track of the system default network.
+                networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
+                enforceAccessPermission();
+                break;
+            case REQUEST:
+                networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
+                        callingAttributionTag);
+                // TODO: this is incorrect. We mark the request as metered or not depending on
+                //  the state of the app when the request is filed, but we never change the
+                //  request if the app changes network state. http://b/29964605
+                enforceMeteredApnPolicy(networkCapabilities);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported request type " + reqType);
         }
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
@@ -5656,7 +5710,7 @@
         ensureValid(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
-                nextNetworkRequestId(), type);
+                nextNetworkRequestId(), reqType);
         NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
         if (DBG) log("requestNetwork for " + nri);
 
@@ -5956,6 +6010,9 @@
     // priority networks like ethernet are active.
     private final NetworkRequest mDefaultWifiRequest;
 
+    // Request used to optionally keep vehicle internal network always active
+    private final NetworkRequest mDefaultVehicleRequest;
+
     private NetworkAgentInfo getDefaultNetwork() {
         return mDefaultNetworkNai;
     }
@@ -6095,6 +6152,7 @@
     private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
         lp.ensureDirectlyConnectedRoutes();
         nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
+        nai.networkAgentPortalData = lp.getCaptivePortalData();
     }
 
     private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
@@ -6138,9 +6196,11 @@
 
         updateWakeOnLan(newLp);
 
-        // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo,
-        // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here.
-        newLp.setCaptivePortalData(networkAgent.captivePortalData);
+        // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo.
+        // It is not always contained in the LinkProperties sent from NetworkAgents, and if it
+        // does, it needs to be merged here.
+        newLp.setCaptivePortalData(mergeCaptivePortalData(networkAgent.networkAgentPortalData,
+                networkAgent.capportApiData));
 
         // TODO - move this check to cover the whole function
         if (!Objects.equals(newLp, oldLp)) {
@@ -6160,6 +6220,57 @@
         mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
     }
 
+    /**
+     * @param naData captive portal data from NetworkAgent
+     * @param apiData captive portal data from capport API
+     */
+    @Nullable
+    private CaptivePortalData mergeCaptivePortalData(CaptivePortalData naData,
+            CaptivePortalData apiData) {
+        if (naData == null || apiData == null) {
+            return naData == null ? apiData : naData;
+        }
+        final CaptivePortalData.Builder captivePortalBuilder =
+                new CaptivePortalData.Builder(naData);
+
+        if (apiData.isCaptive()) {
+            captivePortalBuilder.setCaptive(true);
+        }
+        if (apiData.isSessionExtendable()) {
+            captivePortalBuilder.setSessionExtendable(true);
+        }
+        if (apiData.getExpiryTimeMillis() >= 0 || apiData.getByteLimit() >= 0) {
+            // Expiry time, bytes remaining, refresh time all need to come from the same source,
+            // otherwise data would be inconsistent. Prefer the capport API info if present,
+            // as it can generally be refreshed more often.
+            captivePortalBuilder.setExpiryTime(apiData.getExpiryTimeMillis());
+            captivePortalBuilder.setBytesRemaining(apiData.getByteLimit());
+            captivePortalBuilder.setRefreshTime(apiData.getRefreshTimeMillis());
+        } else if (naData.getExpiryTimeMillis() < 0 && naData.getByteLimit() < 0) {
+            // No source has time / bytes remaining information: surface the newest refresh time
+            // for other fields
+            captivePortalBuilder.setRefreshTime(
+                    Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis()));
+        }
+
+        // Prioritize the user portal URL from the network agent.
+        if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null
+                || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) {
+            captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl());
+        }
+        // Prioritize the venue information URL from the network agent.
+        if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null
+                || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) {
+            captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl());
+
+            // Note that venue friendly name can only come from the network agent because it is not
+            // in use in RFC8908. However, if using the Capport venue URL, make sure that the
+            // friendly name is not set from the network agent.
+            captivePortalBuilder.setVenueFriendlyName(null);
+        }
+        return captivePortalBuilder.build();
+    }
+
     private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
         // Marks are only available on WiFi interfaces. Checking for
         // marks on unsupported interfaces is harmless.
@@ -6666,6 +6777,39 @@
         return stableRanges;
     }
 
+    private void maybeCloseSockets(NetworkAgentInfo nai, UidRangeParcel[] ranges,
+            int[] exemptUids) {
+        if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) {
+            try {
+                mNetd.socketDestroy(ranges, exemptUids);
+            } catch (Exception e) {
+                loge("Exception in socket destroy: ", e);
+            }
+        }
+    }
+
+    private void updateUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) {
+        int[] exemptUids = new int[2];
+        // TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used
+        // by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when
+        // starting a legacy VPN, and remove VPN_UID here. (b/176542831)
+        exemptUids[0] = VPN_UID;
+        exemptUids[1] = nai.networkCapabilities.getOwnerUid();
+        UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges);
+
+        maybeCloseSockets(nai, ranges, exemptUids);
+        try {
+            if (add) {
+                mNetd.networkAddUidRanges(nai.network.netId, ranges);
+            } else {
+                mNetd.networkRemoveUidRanges(nai.network.netId, ranges);
+            }
+        } catch (Exception e) {
+            loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges +
+                    " on netId " + nai.network.netId + ". " + e);
+        }
+        maybeCloseSockets(nai, ranges, exemptUids);
+    }
 
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
             NetworkCapabilities newNc) {
@@ -6685,12 +6829,21 @@
             // in both ranges are not subject to any VPN routing rules. Adding new range before
             // removing old range works because, unlike the filtering rules below, it's possible to
             // add duplicate UID routing rules.
+            // TODO: calculate the intersection of add & remove. Imagining that we are trying to
+            // remove uid 3 from a set containing 1-5. Intersection of the prev and new sets is:
+            //   [1-5] & [1-2],[4-5] == [3]
+            // Then we can do:
+            //   maybeCloseSockets([3])
+            //   mNetd.networkAddUidRanges([1-2],[4-5])
+            //   mNetd.networkRemoveUidRanges([1-5])
+            //   maybeCloseSockets([3])
+            // This can prevent the sockets of uid 1-2, 4-5 from being closed. It also reduce the
+            // number of binder calls from 6 to 4.
             if (!newRanges.isEmpty()) {
-                mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
+                updateUidRanges(true, nai, newRanges);
             }
             if (!prevRanges.isEmpty()) {
-                mNetd.networkRemoveUidRanges(
-                        nai.network.netId, toUidRangeStableParcels(prevRanges));
+                updateUidRanges(false, nai, prevRanges);
             }
             final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
             final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
@@ -6839,7 +6992,7 @@
                                 networkAgent.networkCapabilities, nri.mPid, nri.mUid);
                 putParcelable(
                         bundle,
-                        maybeSanitizeLocationInfoForCaller(
+                        createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                                 nc, nri.mUid, nri.request.getRequestorPackageName()));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -6858,7 +7011,7 @@
                                 networkAgent.networkCapabilities, nri.mPid, nri.mUid);
                 putParcelable(
                         bundle,
-                        maybeSanitizeLocationInfoForCaller(
+                        createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                                 netCap, nri.mUid, nri.request.getRequestorPackageName()));
                 break;
             }
@@ -7065,11 +7218,11 @@
                     log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
                 previousSatisfier.removeRequest(nri.request.requestId);
-                previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
+                previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
             } else {
                 if (VDBG || DDBG) log("   accepting network in place of null");
             }
-            newSatisfier.unlingerRequest(nri.request);
+            newSatisfier.unlingerRequest(nri.request.requestId);
             if (!newSatisfier.addRequest(nri.request)) {
                 Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
                         + nri.request);
@@ -7158,9 +7311,28 @@
             updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
             // Notify system services of the new default.
             makeDefault(newDefaultNetwork);
+
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
-            mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
-                    now, newDefaultNetwork, oldDefaultNetwork);
+            final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+            final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+            final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+            final LinkProperties lp = (newDefaultNetwork != null)
+                    ? newDefaultNetwork.linkProperties : null;
+            final NetworkCapabilities nc = (newDefaultNetwork != null)
+                    ? newDefaultNetwork.networkCapabilities : null;
+
+            final Network prevNetwork = (oldDefaultNetwork != null)
+                    ? oldDefaultNetwork.network : null;
+            final int prevScore = (oldDefaultNetwork != null)
+                    ? oldDefaultNetwork.getCurrentScore() : 0;
+            final LinkProperties prevLp = (oldDefaultNetwork != null)
+                    ? oldDefaultNetwork.linkProperties : null;
+            final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+                    ? oldDefaultNetwork.networkCapabilities : null;
+
+            mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+                    prevNetwork, prevScore, prevLp, prevNc);
+
             // Have a new default network, release the transition wakelock in
             scheduleReleaseNetworkTransitionWakelock();
         }
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index f701688..0779f71 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -35,6 +35,8 @@
 
     public ConnectivityServiceInitializer(Context context) {
         super(context);
+        // Load JNI libraries used by ConnectivityService and its dependencies
+        System.loadLibrary("service-connectivity");
         // TODO: Define formal APIs to get the needed services.
         mConnectivity = new ConnectivityService(context, getNetworkManagementService(),
                 getNetworkStatsService());
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java
new file mode 100644
index 0000000..9d43a39
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java
@@ -0,0 +1,28 @@
+/*
+ * 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.apphibernation;
+
+/**
+ * Flags and constants that modify app hibernation behavior.
+ */
+final class AppHibernationConstants {
+
+    private AppHibernationConstants() {}
+
+    // Device config feature flag for app hibernation
+    static final String KEY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled";
+}
diff --git a/services/core/java/com/android/server/apphibernation/OWNERS b/services/core/java/com/android/server/apphibernation/OWNERS
index 4804fa3..c2e27e0 100644
--- a/services/core/java/com/android/server/apphibernation/OWNERS
+++ b/services/core/java/com/android/server/apphibernation/OWNERS
@@ -1,3 +1 @@
-# TODO: Include /core/java/android/apphibernation/OWNERS. See b/177005153
-kevhan@google.com
-rajekumar@google.com
+include /core/java/android/apphibernation/OWNERS
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 18907a1..9ba957e 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -64,7 +64,7 @@
     private Map<String, Boolean> mDeferredOverrides;
 
     public CompatChange(long changeId) {
-        this(changeId, null, -1, -1, false, false, null);
+        this(changeId, null, -1, -1, false, false, null, false);
     }
 
     /**
@@ -77,9 +77,10 @@
      * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set.
      */
     public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
-            int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) {
+            int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description,
+            boolean overridable) {
         super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
-              description);
+              description, overridable);
     }
 
     /**
@@ -88,7 +89,7 @@
     public CompatChange(Change change) {
         super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
                 change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
-                change.getDescription());
+                change.getDescription(), change.getOverridable());
     }
 
     void registerListener(ChangeListener listener) {
@@ -274,6 +275,9 @@
         if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) {
             sb.append("; deferredOverrides=").append(mDeferredOverrides);
         }
+        if (getOverridable()) {
+            sb.append("; overridable");
+        }
         return sb.append(")").toString();
     }
 
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index 995bb24..8cd1fd6 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -17,6 +17,8 @@
 package com.android.server.connectivity;
 
 import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.metrics.DefaultNetworkEvent;
 import android.os.SystemClock;
 
@@ -61,7 +63,7 @@
     private int mLastTransports;
 
     public DefaultNetworkMetrics() {
-        newDefaultNetwork(creationTimeMs, null);
+        newDefaultNetwork(creationTimeMs, null, 0, false, null, null);
     }
 
     public synchronized void listEvents(PrintWriter pw) {
@@ -117,13 +119,21 @@
         mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs;
     }
 
-    public synchronized void logDefaultNetworkEvent(
-            long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) {
-        logCurrentDefaultNetwork(timeMs, oldNai);
-        newDefaultNetwork(timeMs, newNai);
+    /**
+     * Logs a default network event.
+     * @see {IpConnectivityLog#logDefaultNetworkEvent}.
+     */
+    public synchronized void logDefaultNetworkEvent(long timeMs, Network defaultNetwork, int score,
+            boolean validated, LinkProperties lp, NetworkCapabilities nc,
+            Network previousDefaultNetwork, int previousScore, LinkProperties previousLp,
+            NetworkCapabilities previousNc) {
+        logCurrentDefaultNetwork(timeMs, previousDefaultNetwork, previousScore, previousLp,
+                previousNc);
+        newDefaultNetwork(timeMs, defaultNetwork, score, validated, lp, nc);
     }
 
-    private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) {
+    private void logCurrentDefaultNetwork(long timeMs, Network network, int score,
+            LinkProperties lp, NetworkCapabilities nc) {
         if (mIsCurrentlyValid) {
             updateValidationTime(timeMs);
         }
@@ -131,10 +141,10 @@
         ev.updateDuration(timeMs);
         ev.previousTransports = mLastTransports;
         // oldNai is null if the system had no default network before the transition.
-        if (oldNai != null) {
+        if (network != null) {
             // The system acquired a new default network.
-            fillLinkInfo(ev, oldNai);
-            ev.finalScore = oldNai.getCurrentScore();
+            fillLinkInfo(ev, network, lp, nc);
+            ev.finalScore = score;
         }
         // Only change transport of the previous default network if the event currently logged
         // corresponds to an existing default network, and not to the absence of a default network.
@@ -147,14 +157,15 @@
         mEventsLog.append(ev);
     }
 
-    private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) {
+    private void newDefaultNetwork(long timeMs, Network network, int score, boolean validated,
+            LinkProperties lp, NetworkCapabilities nc) {
         DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs);
         ev.durationMs = timeMs;
         // newNai is null if the system has no default network after the transition.
-        if (newNai != null) {
-            fillLinkInfo(ev, newNai);
-            ev.initialScore = newNai.getCurrentScore();
-            if (newNai.lastValidated) {
+        if (network != null) {
+            fillLinkInfo(ev, network, lp, nc);
+            ev.initialScore = score;
+            if (validated) {
                 mIsCurrentlyValid = true;
                 mLastValidationTimeMs = timeMs;
             }
@@ -164,10 +175,10 @@
         mCurrentDefaultNetwork = ev;
     }
 
-    private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) {
-        LinkProperties lp = nai.linkProperties;
-        ev.netId = nai.network().getNetId();
-        ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes());
+    private static void fillLinkInfo(DefaultNetworkEvent ev, Network network, LinkProperties lp,
+            NetworkCapabilities nc) {
+        ev.netId = network.getNetId();
+        ev.transports |= BitUtils.packBits(nc.getTransportTypes());
         ev.ipv4 |= lp.hasIpv4Address() && lp.hasIpv4DefaultRoute();
         ev.ipv6 |= lp.hasGlobalIpv6Address() && lp.hasIpv6DefaultRoute();
     }
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 2c06d82..1024556 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -20,11 +20,15 @@
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetdEventCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkStack;
 import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.os.Binder;
 import android.os.Process;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -361,6 +365,21 @@
             }
             return mNetdListener.removeNetdEventCallback(callerType);
         }
+
+        @Override
+        public void logDefaultNetworkValidity(boolean valid) {
+            mDefaultNetworkMetrics.logDefaultNetworkValidity(SystemClock.elapsedRealtime(), valid);
+        }
+
+        @Override
+        public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated,
+                LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork,
+                int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) {
+            final long timeMs = SystemClock.elapsedRealtime();
+            mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, defaultNetwork, score, validated,
+                    lp, nc,  previousDefaultNetwork, previousScore, previousLp, previousNc);
+        }
+
     };
 
     private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 7bde4d5..b0a73f1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -189,41 +189,46 @@
     // Set to true when partial connectivity was detected.
     public boolean partialConnectivity;
 
-    // Captive portal info of the network, if any.
+    // Captive portal info of the network from RFC8908, if any.
     // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
-    public CaptivePortalData captivePortalData;
+    public CaptivePortalData capportApiData;
 
     // The UID of the remote entity that created this Network.
     public final int creatorUid;
 
+    // Network agent portal info of the network, if any. This information is provided from
+    // non-RFC8908 sources, such as Wi-Fi Passpoint, which can provide information such as Venue
+    // URL, Terms & Conditions URL, and network friendly name.
+    public CaptivePortalData networkAgentPortalData;
+
     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
     // network is taken down.  This usually only happens to the default network. Lingering ends with
     // either the linger timeout expiring and the network being taken down, or the network
     // satisfying a request again.
     public static class LingerTimer implements Comparable<LingerTimer> {
-        public final NetworkRequest request;
+        public final int requestId;
         public final long expiryMs;
 
-        public LingerTimer(NetworkRequest request, long expiryMs) {
-            this.request = request;
+        public LingerTimer(int requestId, long expiryMs) {
+            this.requestId = requestId;
             this.expiryMs = expiryMs;
         }
         public boolean equals(Object o) {
             if (!(o instanceof LingerTimer)) return false;
             LingerTimer other = (LingerTimer) o;
-            return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
+            return (requestId == other.requestId) && (expiryMs == other.expiryMs);
         }
         public int hashCode() {
-            return Objects.hash(request.requestId, expiryMs);
+            return Objects.hash(requestId, expiryMs);
         }
         public int compareTo(LingerTimer other) {
             return (expiryMs != other.expiryMs) ?
                     Long.compare(expiryMs, other.expiryMs) :
-                    Integer.compare(request.requestId, other.request.requestId);
+                    Integer.compare(requestId, other.requestId);
         }
         public String toString() {
-            return String.format("%s, expires %dms", request.toString(),
+            return String.format("%s, expires %dms", requestId,
                     expiryMs - SystemClock.elapsedRealtime());
         }
     }
@@ -693,7 +698,7 @@
         updateRequestCounts(REMOVE, existing);
         mNetworkRequests.remove(requestId);
         if (existing.isRequest()) {
-            unlingerRequest(existing);
+            unlingerRequest(existing.requestId);
         }
     }
 
@@ -839,33 +844,33 @@
     }
 
     /**
-     * Sets the specified request to linger on this network for the specified time. Called by
+     * Sets the specified requestId to linger on this network for the specified time. Called by
      * ConnectivityService when the request is moved to another network with a higher score.
      */
-    public void lingerRequest(NetworkRequest request, long now, long duration) {
-        if (mLingerTimerForRequest.get(request.requestId) != null) {
+    public void lingerRequest(int requestId, long now, long duration) {
+        if (mLingerTimerForRequest.get(requestId) != null) {
             // Cannot happen. Once a request is lingering on a particular network, we cannot
             // re-linger it unless that network becomes the best for that request again, in which
             // case we should have unlingered it.
-            Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
+            Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
         }
         final long expiryMs = now + duration;
-        LingerTimer timer = new LingerTimer(request, expiryMs);
+        LingerTimer timer = new LingerTimer(requestId, expiryMs);
         if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
         mLingerTimers.add(timer);
-        mLingerTimerForRequest.put(request.requestId, timer);
+        mLingerTimerForRequest.put(requestId, timer);
     }
 
     /**
      * Cancel lingering. Called by ConnectivityService when a request is added to this network.
-     * Returns true if the given request was lingering on this network, false otherwise.
+     * Returns true if the given requestId was lingering on this network, false otherwise.
      */
-    public boolean unlingerRequest(NetworkRequest request) {
-        LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
+    public boolean unlingerRequest(int requestId) {
+        LingerTimer timer = mLingerTimerForRequest.get(requestId);
         if (timer != null) {
             if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
             mLingerTimers.remove(timer);
-            mLingerTimerForRequest.remove(request.requestId);
+            mLingerTimerForRequest.remove(requestId);
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
similarity index 82%
rename from services/core/java/com/android/server/connectivity/PacManager.java
rename to services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index 06721ae..5dc8c1a 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2013, The Android Open Source Project
+/*
+ * Copyright (C) 2013 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
+ *      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,
@@ -13,8 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.server.connectivity;
 
+import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -52,7 +54,7 @@
 /**
  * @hide
  */
-public class PacManager {
+public class PacProxyInstaller {
     private static final String PAC_PACKAGE = "com.android.pacprocessor";
     private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
     private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
@@ -60,7 +62,7 @@
     private static final String PROXY_PACKAGE = "com.android.proxyhandler";
     private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
 
-    private static final String TAG = "PacManager";
+    private static final String TAG = "PacProxyInstaller";
 
     private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
 
@@ -70,10 +72,6 @@
     private static final int DELAY_LONG = 4;
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
-    // Return values for #setCurrentProxyScriptUrl
-    public static final boolean DONT_SEND_BROADCAST = false;
-    public static final boolean DO_SEND_BROADCAST = true;
-
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
     private volatile Uri mPacUrl = Uri.EMPTY;
@@ -92,7 +90,7 @@
     private volatile boolean mHasSentBroadcast;
     private volatile boolean mHasDownloaded;
 
-    private Handler mConnectivityHandler;
+    private final Handler mConnectivityHandler;
     private final int mProxyMessage;
 
     /**
@@ -101,6 +99,13 @@
     private final Object mProxyLock = new Object();
 
     /**
+     * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
+     * last URL and port, and the broadcast message being sent with the correct arguments.
+     * TODO : this should probably protect all instances of these variables
+     */
+    private final Object mBroadcastStateLock = new Object();
+
+    /**
      * Runnable to download PAC script.
      * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
      * latest data fetched from mPacUrl is stored in mProxyService.
@@ -145,10 +150,10 @@
         }
     }
 
-    public PacManager(Context context, Handler handler, int proxyMessage) {
+    public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
         mContext = context;
         mLastPort = -1;
-        final HandlerThread netThread = new HandlerThread("android.pacmanager",
+        final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
                 android.os.Process.THREAD_PRIORITY_DEFAULT);
         netThread.start();
         mNetThreadHandler = new Handler(netThread.getLooper());
@@ -163,43 +168,39 @@
 
     private AlarmManager getAlarmManager() {
         if (mAlarmManager == null) {
-            mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+            mAlarmManager = mContext.getSystemService(AlarmManager.class);
         }
         return mAlarmManager;
     }
 
     /**
-     * Updates the PAC Manager with current Proxy information. This is called by
+     * Updates the PAC Proxy Installer with current Proxy information. This is called by
      * the ProxyTracker directly before a broadcast takes place to allow
-     * the PacManager to indicate that the broadcast should not be sent and the
-     * PacManager will trigger a new broadcast when it is ready.
+     * the PacProxyInstaller to indicate that the broadcast should not be sent and the
+     * PacProxyInstaller will trigger a new broadcast when it is ready.
      *
      * @param proxy Proxy information that is about to be broadcast.
-     * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
-        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
-            if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
-                // Allow to send broadcast, nothing to do.
-                return DO_SEND_BROADCAST;
-            }
-            mPacUrl = proxy.getPacFileUrl();
-            mCurrentDelay = DELAY_1;
-            mHasSentBroadcast = false;
-            mHasDownloaded = false;
-            getAlarmManager().cancel(mPacRefreshIntent);
-            bind();
-            return DONT_SEND_BROADCAST;
-        } else {
-            getAlarmManager().cancel(mPacRefreshIntent);
-            synchronized (mProxyLock) {
-                mPacUrl = Uri.EMPTY;
-                mCurrentPac = null;
-                if (mProxyService != null) {
-                    unbind();
+    public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
+        synchronized (mBroadcastStateLock) {
+            if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+                if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
+                mPacUrl = proxy.getPacFileUrl();
+                mCurrentDelay = DELAY_1;
+                mHasSentBroadcast = false;
+                mHasDownloaded = false;
+                getAlarmManager().cancel(mPacRefreshIntent);
+                bind();
+            } else {
+                getAlarmManager().cancel(mPacRefreshIntent);
+                synchronized (mProxyLock) {
+                    mPacUrl = Uri.EMPTY;
+                    mCurrentPac = null;
+                    if (mProxyService != null) {
+                        unbind();
+                    }
                 }
             }
-            return DO_SEND_BROADCAST;
         }
     }
 
@@ -233,10 +234,10 @@
     }
 
     private int getNextDelay(int currentDelay) {
-       if (++currentDelay > DELAY_4) {
-           return DELAY_4;
-       }
-       return currentDelay;
+        if (++currentDelay > DELAY_4) {
+            return DELAY_4;
+        }
+        return currentDelay;
     }
 
     private void longSchedule() {
@@ -274,6 +275,7 @@
         getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
     }
 
+    @GuardedBy("mProxyLock")
     private void setCurrentProxyScript(String script) {
         if (mProxyService == null) {
             Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -346,6 +348,9 @@
                             public void setProxyPort(int port) {
                                 if (mLastPort != -1) {
                                     // Always need to send if port changed
+                                    // TODO: Here lacks synchronization because this write cannot
+                                    // guarantee that it's visible from sendProxyIfNeeded() when
+                                    // it's called by a Runnable which is post by mNetThread.
                                     mHasSentBroadcast = false;
                                 }
                                 mLastPort = port;
@@ -385,13 +390,15 @@
         mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
     }
 
-    private synchronized void sendProxyIfNeeded() {
-        if (!mHasDownloaded || (mLastPort == -1)) {
-            return;
-        }
-        if (!mHasSentBroadcast) {
-            sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
-            mHasSentBroadcast = true;
+    private void sendProxyIfNeeded() {
+        synchronized (mBroadcastStateLock) {
+            if (!mHasDownloaded || (mLastPort == -1)) {
+                return;
+            }
+            if (!mHasSentBroadcast) {
+                sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+                mHasSentBroadcast = true;
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index f6ca152..b618d2b 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018, The Android Open Source Project
+ * Copyright (c) 2018 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.
@@ -67,7 +67,7 @@
     // is not set. Individual networks have their own settings that override this. This member
     // is set through setDefaultProxy, which is called when the default network changes proxies
     // in its LinkProperties, or when ConnectivityService switches to a new default network, or
-    // when PacManager resolves the proxy.
+    // when PacProxyInstaller resolves the proxy.
     @Nullable
     @GuardedBy("mProxyLock")
     private volatile ProxyInfo mDefaultProxy = null;
@@ -79,13 +79,14 @@
 
     // The object responsible for Proxy Auto Configuration (PAC).
     @NonNull
-    private final PacManager mPacManager;
+    private final PacProxyInstaller mPacProxyInstaller;
 
     public ProxyTracker(@NonNull final Context context,
             @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
         mContext = context;
         mConnectivityServiceHandler = connectivityServiceInternalHandler;
-        mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
+        mPacProxyInstaller = new PacProxyInstaller(
+                context, connectivityServiceInternalHandler, pacChangedEvent);
     }
 
     // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -181,7 +182,7 @@
 
             if (!TextUtils.isEmpty(pacFileUrl)) {
                 mConnectivityServiceHandler.post(
-                        () -> mPacManager.setCurrentProxyScriptUrl(proxyProperties));
+                        () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
             }
         }
     }
@@ -225,7 +226,9 @@
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ?
                 defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
-        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
+        mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
+
+        if (!shouldSendBroadcast(proxyInfo)) {
             return;
         }
         if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -241,6 +244,13 @@
         }
     }
 
+    private boolean shouldSendBroadcast(ProxyInfo proxy) {
+        if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
+        if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
+                && (proxy.getPort() > 0)) return true;
+        return true;
+    }
+
     /**
      * Sets the global proxy in memory. Also writes the values to the global settings of the device.
      *
@@ -305,10 +315,10 @@
                 return;
             }
 
-            // This call could be coming from the PacManager, containing the port of the local
-            // proxy. If this new proxy matches the global proxy then copy this proxy to the
+            // This call could be coming from the PacProxyInstaller, containing the port of the
+            // local proxy. If this new proxy matches the global proxy then copy this proxy to the
             // global (to get the correct local port), and send a broadcast.
-            // TODO: Switch PacManager to have its own message to send back rather than
+            // TODO: Switch PacProxyInstaller to have its own message to send back rather than
             // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
             if ((mGlobalProxy != null) && (proxyInfo != null)
                     && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 07a4b89..a65f809 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1229,7 +1229,8 @@
     private boolean canHaveRestrictedProfile(int userId) {
         final long token = Binder.clearCallingIdentity();
         try {
-            return UserManager.get(mContext).canHaveRestrictedProfile(userId);
+            final Context userContext = mContext.createContextAsUser(UserHandle.of(userId), 0);
+            return userContext.getSystemService(UserManager.class).canHaveRestrictedProfile();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1850,34 +1851,6 @@
         }
     }
 
-    /**
-     * @param uid The target uid.
-     *
-     * @return {@code true} if {@code uid} is included in one of the mBlockedUidsAsToldToNetd
-     * ranges and the VPN is not connected, or if the VPN is connected but does not apply to
-     * the {@code uid}.
-     *
-     * @apiNote This method don't check VPN lockdown status.
-     * @see #mBlockedUidsAsToldToConnectivity
-     */
-    public synchronized boolean isBlockingUid(int uid) {
-        if (mNetworkInfo.isConnected()) {
-            return !appliesToUid(uid);
-        } else {
-            return containsUid(mBlockedUidsAsToldToConnectivity, uid);
-        }
-    }
-
-    private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) {
-        if (ranges == null) return false;
-        for (UidRangeParcel range : ranges) {
-            if (range.start <= uid && uid <= range.stop) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void updateAlwaysOnNotification(DetailedState networkState) {
         final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
 
diff --git a/services/core/java/com/android/server/graphics/fonts/OWNERS b/services/core/java/com/android/server/graphics/fonts/OWNERS
new file mode 100644
index 0000000..34ac813
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/AesEncryptionUtil.java b/services/core/java/com/android/server/locksettings/AesEncryptionUtil.java
new file mode 100644
index 0000000..8e7e419
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/AesEncryptionUtil.java
@@ -0,0 +1,109 @@
+/*
+ * 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.locksettings;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+class AesEncryptionUtil {
+    /** The algorithm used for the encryption of the key blob. */
+    private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
+
+    private AesEncryptionUtil() {}
+
+    static byte[] decrypt(SecretKey key, DataInputStream cipherStream) throws IOException {
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(cipherStream);
+
+        int ivSize = cipherStream.readInt();
+        if (ivSize < 0 || ivSize > 32) {
+            throw new IOException("IV out of range: " + ivSize);
+        }
+        byte[] iv = new byte[ivSize];
+        cipherStream.readFully(iv);
+
+        int rawCipherTextSize = cipherStream.readInt();
+        if (rawCipherTextSize < 0) {
+            throw new IOException("Invalid cipher text size: " + rawCipherTextSize);
+        }
+
+        byte[] rawCipherText = new byte[rawCipherTextSize];
+        cipherStream.readFully(rawCipherText);
+
+        final byte[] plainText;
+        try {
+            Cipher c = Cipher.getInstance(CIPHER_ALGO);
+            c.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
+            plainText = c.doFinal(rawCipherText);
+        } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
+                | IllegalBlockSizeException | NoSuchPaddingException
+                | InvalidAlgorithmParameterException e) {
+            throw new IOException("Could not decrypt cipher text", e);
+        }
+
+        return plainText;
+    }
+
+    static byte[] decrypt(SecretKey key, byte[] cipherText) throws IOException {
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(cipherText);
+
+        DataInputStream cipherStream = new DataInputStream(new ByteArrayInputStream(cipherText));
+        return decrypt(key, cipherStream);
+    }
+
+    static byte[] encrypt(SecretKey key, byte[] plainText) throws IOException {
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(plainText);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        DataOutputStream dos = new DataOutputStream(bos);
+
+        final byte[] cipherText;
+        final byte[] iv;
+        try {
+            Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
+            cipher.init(Cipher.ENCRYPT_MODE, key);
+            cipherText = cipher.doFinal(plainText);
+            iv = cipher.getIV();
+        } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
+                | NoSuchPaddingException | InvalidKeyException e) {
+            throw new IOException("Could not encrypt input data", e);
+        }
+
+        dos.writeInt(iv.length);
+        dos.write(iv);
+        dos.writeInt(cipherText.length);
+        dos.write(cipherText);
+
+        return bos.toByteArray();
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
index 2b19079..38eeb88 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowData.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
@@ -16,22 +16,14 @@
 
 package com.android.server.locksettings;
 
-import com.android.internal.util.Preconditions;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
 
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.SecretKey;
 
 /**
  * Holds the data necessary to complete a reboot escrow of the Synthetic Password.
@@ -41,22 +33,17 @@
      * This is the current version of the escrow data format. This should be incremented if the
      * format on disk is changed.
      */
-    private static final int CURRENT_VERSION = 1;
+    private static final int CURRENT_VERSION = 2;
 
-    /** The algorithm used for the encryption of the key blob. */
-    private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
-
-    private RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob,
+    private RebootEscrowData(byte spVersion, byte[] syntheticPassword, byte[] blob,
             RebootEscrowKey key) {
         mSpVersion = spVersion;
-        mIv = iv;
         mSyntheticPassword = syntheticPassword;
         mBlob = blob;
         mKey = key;
     }
 
     private final byte mSpVersion;
-    private final byte[] mIv;
     private final byte[] mSyntheticPassword;
     private final byte[] mBlob;
     private final RebootEscrowKey mKey;
@@ -65,10 +52,6 @@
         return mSpVersion;
     }
 
-    public byte[] getIv() {
-        return mIv;
-    }
-
     public byte[] getSyntheticPassword() {
         return mSyntheticPassword;
     }
@@ -81,76 +64,43 @@
         return mKey;
     }
 
-    static RebootEscrowData fromEncryptedData(RebootEscrowKey key, byte[] blob)
+    static RebootEscrowData fromEncryptedData(RebootEscrowKey ks, byte[] blob, SecretKey kk)
             throws IOException {
-        Preconditions.checkNotNull(key);
-        Preconditions.checkNotNull(blob);
+        Objects.requireNonNull(ks);
+        Objects.requireNonNull(blob);
 
         DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
         int version = dis.readInt();
         if (version != CURRENT_VERSION) {
             throw new IOException("Unsupported version " + version);
         }
-
         byte spVersion = dis.readByte();
 
-        int ivSize = dis.readInt();
-        if (ivSize < 0 || ivSize > 32) {
-            throw new IOException("IV out of range: " + ivSize);
-        }
-        byte[] iv = new byte[ivSize];
-        dis.readFully(iv);
+        // Decrypt the blob with the key from keystore first, then decrypt again with the reboot
+        // escrow key.
+        byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis);
+        final byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob);
 
-        int cipherTextSize = dis.readInt();
-        if (cipherTextSize < 0) {
-            throw new IOException("Invalid cipher text size: " + cipherTextSize);
-        }
-
-        byte[] cipherText = new byte[cipherTextSize];
-        dis.readFully(cipherText);
-
-        final byte[] syntheticPassword;
-        try {
-            Cipher c = Cipher.getInstance(CIPHER_ALGO);
-            c.init(Cipher.DECRYPT_MODE, key.getKey(), new IvParameterSpec(iv));
-            syntheticPassword = c.doFinal(cipherText);
-        } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
-                | IllegalBlockSizeException | NoSuchPaddingException
-                | InvalidAlgorithmParameterException e) {
-            throw new IOException("Could not decrypt ciphertext", e);
-        }
-
-        return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, key);
+        return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
     }
 
-    static RebootEscrowData fromSyntheticPassword(RebootEscrowKey key, byte spVersion,
-            byte[] syntheticPassword)
+    static RebootEscrowData fromSyntheticPassword(RebootEscrowKey ks, byte spVersion,
+            byte[] syntheticPassword, SecretKey kk)
             throws IOException {
-        Preconditions.checkNotNull(syntheticPassword);
+        Objects.requireNonNull(syntheticPassword);
+
+        // Encrypt synthetic password with the escrow key first; then encrypt the blob again with
+        // the key from keystore.
+        byte[] ksEncryptedBlob = AesEncryptionUtil.encrypt(ks.getKey(), syntheticPassword);
+        byte[] kkEncryptedBlob = AesEncryptionUtil.encrypt(kk, ksEncryptedBlob);
 
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         DataOutputStream dos = new DataOutputStream(bos);
 
-        final byte[] cipherText;
-        final byte[] iv;
-        try {
-            Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
-            cipher.init(Cipher.ENCRYPT_MODE, key.getKey());
-            cipherText = cipher.doFinal(syntheticPassword);
-            iv = cipher.getIV();
-        } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
-                | NoSuchPaddingException | InvalidKeyException e) {
-            throw new IOException("Could not encrypt reboot escrow data", e);
-        }
-
         dos.writeInt(CURRENT_VERSION);
         dos.writeByte(spVersion);
-        dos.writeInt(iv.length);
-        dos.write(iv);
-        dos.writeInt(cipherText.length);
-        dos.write(cipherText);
+        dos.write(kkEncryptedBlob);
 
-        return new RebootEscrowData(spVersion, iv, syntheticPassword, bos.toByteArray(),
-                key);
+        return new RebootEscrowData(spVersion, syntheticPassword, bos.toByteArray(), ks);
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
new file mode 100644
index 0000000..bae029c
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
@@ -0,0 +1,134 @@
+/*
+ * 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.locksettings;
+
+import android.security.keystore.AndroidKeyStoreSpi;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
+import android.security.keystore2.AndroidKeyStoreProvider;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * This class loads and generates the key used for resume on reboot from android keystore.
+ */
+public class RebootEscrowKeyStoreManager {
+    private static final String TAG = "RebootEscrowKeyStoreManager";
+
+    /**
+     * The key alias in keystore. This key is used to wrap both escrow key and escrow data.
+     */
+    public static final String REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME =
+            "reboot_escrow_key_store_encryption_key";
+
+    public static final int KEY_LENGTH = 256;
+
+    /**
+     * Use keystore2 once it's installed.
+     */
+    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeystore";
+
+    /**
+     * The selinux namespace for resume_on_reboot_key
+     */
+    private static final int KEY_STORE_NAMESPACE = 120;
+
+    /**
+     * Hold this lock when getting or generating the encryption key in keystore.
+     */
+    private final Object mKeyStoreLock = new Object();
+
+    @GuardedBy("mKeyStoreLock")
+    private SecretKey getKeyStoreEncryptionKeyLocked() {
+        try {
+            KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+            KeyStore.LoadStoreParameter loadStoreParameter = null;
+            // Load from the specific namespace if keystore2 is enabled.
+            if (AndroidKeyStoreProvider.isInstalled()) {
+                loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+            }
+            keyStore.load(loadStoreParameter);
+            return (SecretKey) keyStore.getKey(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME,
+                    null);
+        } catch (IOException | GeneralSecurityException e) {
+            Slog.e(TAG, "Unable to get encryption key from keystore.", e);
+        }
+        return null;
+    }
+
+    protected SecretKey getKeyStoreEncryptionKey() {
+        synchronized (mKeyStoreLock) {
+            return getKeyStoreEncryptionKeyLocked();
+        }
+    }
+
+    protected void clearKeyStoreEncryptionKey() {
+        synchronized (mKeyStoreLock) {
+            try {
+                KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+                KeyStore.LoadStoreParameter loadStoreParameter = null;
+                // Load from the specific namespace if keystore2 is enabled.
+                if (AndroidKeyStoreProvider.isInstalled()) {
+                    loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+                }
+                keyStore.load(loadStoreParameter);
+                keyStore.deleteEntry(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME);
+            } catch (IOException | GeneralSecurityException e) {
+                Slog.e(TAG, "Unable to delete encryption key in keystore.", e);
+            }
+        }
+    }
+
+    protected SecretKey generateKeyStoreEncryptionKeyIfNeeded() {
+        synchronized (mKeyStoreLock) {
+            SecretKey kk = getKeyStoreEncryptionKeyLocked();
+            if (kk != null) {
+                return kk;
+            }
+
+            try {
+                KeyGenerator generator = KeyGenerator.getInstance(
+                        KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStoreSpi.NAME);
+                KeyGenParameterSpec.Builder parameterSpecBuilder = new KeyGenParameterSpec.Builder(
+                        REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME,
+                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                        .setKeySize(KEY_LENGTH)
+                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
+                // Generate the key with the correct namespace if keystore2 is enabled.
+                if (AndroidKeyStoreProvider.isInstalled()) {
+                    parameterSpecBuilder.setNamespace(KEY_STORE_NAMESPACE);
+                }
+                generator.init(parameterSpecBuilder.build());
+                return generator.generateKey();
+            } catch (GeneralSecurityException e) {
+                // Should never happen.
+                Slog.e(TAG, "Unable to generate key from keystore.", e);
+            }
+            return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 8d5f553..fbec915 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -40,6 +40,18 @@
 import java.util.List;
 import java.util.Locale;
 
+import javax.crypto.SecretKey;
+
+/**
+ * This class aims to persists the synthetic password(SP) across reboot in a secure way. In
+ * particular, it manages the encryption of the sp before reboot, and decryption of the sp after
+ * reboot. Here are the meaning of some terms.
+ *   SP: synthetic password
+ *   K_s: The RebootEscrowKey, i.e. AES-GCM key stored in memory
+ *   K_k: AES-GCM key in android keystore
+ *   RebootEscrowData: The synthetic password and its encrypted blob. We encrypt SP with K_s first,
+ *      then with K_k, i.e. E(K_k, E(K_s, SP))
+ */
 class RebootEscrowManager {
     private static final String TAG = "RebootEscrowManager";
 
@@ -101,6 +113,8 @@
 
     private final Callbacks mCallbacks;
 
+    private final RebootEscrowKeyStoreManager mKeyStoreManager;
+
     interface Callbacks {
         boolean isUserSecure(int userId);
 
@@ -109,11 +123,13 @@
 
     static class Injector {
         protected Context mContext;
-
+        private final RebootEscrowKeyStoreManager mKeyStoreManager;
         private final RebootEscrowProviderInterface mRebootEscrowProvider;
 
         Injector(Context context) {
             mContext = context;
+            mKeyStoreManager = new RebootEscrowKeyStoreManager();
+
             RebootEscrowProviderInterface rebootEscrowProvider = null;
             // TODO(xunchang) add implementation for server based ror.
             if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
@@ -138,6 +154,10 @@
             return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         }
 
+        public RebootEscrowKeyStoreManager getKeyStoreManager() {
+            return mKeyStoreManager;
+        }
+
         public RebootEscrowProviderInterface getRebootEscrowProvider() {
             return mRebootEscrowProvider;
         }
@@ -168,6 +188,7 @@
         mStorage = storage;
         mUserManager = injector.getUserManager();
         mEventLog = injector.getEventLog();
+        mKeyStoreManager = injector.getKeyStoreManager();
     }
 
     void loadRebootEscrowDataIfAvailable() {
@@ -183,8 +204,12 @@
             return;
         }
 
-        RebootEscrowKey escrowKey = getAndClearRebootEscrowKey();
-        if (escrowKey == null) {
+        // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is
+        // generated before reboot. Note that we will clear the escrow key even if the keystore key
+        // is null.
+        SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
+        RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(kk);
+        if (kk == null || escrowKey == null) {
             Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
             for (UserInfo user : users) {
                 mStorage.removeRebootEscrow(user.id);
@@ -197,8 +222,12 @@
 
         boolean allUsersUnlocked = true;
         for (UserInfo user : rebootEscrowUsers) {
-            allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey);
+            allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk);
         }
+
+        // Clear the old key in keystore. A new key will be generated by new RoR requests.
+        mKeyStoreManager.clearKeyStoreEncryptionKey();
+
         onEscrowRestoreComplete(allUsersUnlocked);
     }
 
@@ -212,7 +241,7 @@
         }
     }
 
-    private RebootEscrowKey getAndClearRebootEscrowKey() {
+    private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) {
         RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
         if (rebootEscrowProvider == null) {
             Slog.w(TAG,
@@ -220,14 +249,16 @@
             return null;
         }
 
-        RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(null);
+        // The K_s blob maybe encrypted by K_k as well.
+        RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(kk);
         if (key != null) {
             mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK);
         }
         return key;
     }
 
-    private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) {
+    private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey ks,
+            SecretKey kk) {
         if (!mStorage.hasRebootEscrow(userId)) {
             return false;
         }
@@ -236,7 +267,7 @@
             byte[] blob = mStorage.readRebootEscrow(userId);
             mStorage.removeRebootEscrow(userId);
 
-            RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(key, blob);
+            RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(ks, blob, kk);
 
             mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(),
                     escrowData.getSyntheticPassword(), userId);
@@ -267,11 +298,16 @@
             return;
         }
 
+        SecretKey kk = mKeyStoreManager.generateKeyStoreEncryptionKeyIfNeeded();
+        if (kk == null) {
+            Slog.e(TAG, "Failed to generate encryption key from keystore.");
+            return;
+        }
+
         final RebootEscrowData escrowData;
         try {
-            // TODO(xunchang) further wrap the escrowData with a key from keystore.
             escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion,
-                    syntheticPassword);
+                    syntheticPassword, kk);
         } catch (IOException e) {
             setRebootEscrowReady(false);
             Slog.w(TAG, "Could not escrow reboot data", e);
@@ -348,7 +384,13 @@
             return false;
         }
 
-        boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, null);
+        // We will use the same key from keystore to encrypt the escrow key and escrow data blob.
+        SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
+        if (kk == null) {
+            Slog.e(TAG, "Failed to get encryption key from keystore.");
+            return false;
+        }
+        boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
         if (armedRebootEscrow) {
             mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
             mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 5bd352c..676f421 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -78,6 +78,7 @@
     static final int NTWK_BLOCKED_BG_RESTRICT = 5;
     static final int NTWK_ALLOWED_DEFAULT = 6;
     static final int NTWK_ALLOWED_SYSTEM = 7;
+    static final int NTWK_BLOCKED_RESTRICTED_MODE = 8;
 
     private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
     private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -281,6 +282,8 @@
                 return "blocked when background is restricted";
             case NTWK_ALLOWED_DEFAULT:
                 return "allowed by default";
+            case NTWK_BLOCKED_RESTRICTED_MODE:
+                return "blocked by restricted networking mode";
             default:
                 return String.valueOf(reason);
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 407cedf..141fa6a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -44,12 +44,6 @@
     public abstract boolean isUidRestrictedOnMeteredNetworks(int uid);
 
     /**
-     * @return true if networking is blocked on the given interface for the given uid according
-     * to current networking policies.
-     */
-    public abstract boolean isUidNetworkingBlocked(int uid, String ifname);
-
-    /**
      * Figure out if networking is blocked for a given set of conditions.
      *
      * This is used by ConnectivityService via passing stale copies of conditions, so it must not
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index bd80bef..1c41dc0 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
@@ -44,6 +45,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
 import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.INetd.FIREWALL_RULE_DENY;
@@ -57,6 +59,7 @@
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
 import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
+import static android.net.NetworkPolicyManager.MASK_RESTRICTED_MODE_NETWORKS;
 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;
@@ -65,12 +68,14 @@
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.net.NetworkPolicyManager.resolveNetworkId;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.NetworkTemplate.MATCH_MOBILE;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
@@ -111,6 +116,7 @@
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_RESTRICTED_MODE;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -143,6 +149,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IConnectivityManager;
@@ -270,6 +277,7 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.IntConsumer;
 
 /**
  * Service that maintains low-level network policy rules, using
@@ -444,7 +452,10 @@
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
     @GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode;
     // Store whether user flipped restrict background in battery saver mode
-    @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackgroundChangedInBsm;
+    @GuardedBy("mUidRulesFirstLock")
+    volatile boolean mRestrictBackgroundChangedInBsm;
+    @GuardedBy("mUidRulesFirstLock")
+    volatile boolean mRestrictedNetworkingMode;
 
     private final boolean mSuppressDefaultPolicy;
 
@@ -478,6 +489,8 @@
     final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
     @GuardedBy("mUidRulesFirstLock")
     final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
+    @GuardedBy("mUidRulesFirstLock")
+    final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray();
 
     /** Set of states for the child firewall chains. True if the chain is active. */
     @GuardedBy("mUidRulesFirstLock")
@@ -597,6 +610,8 @@
     @GuardedBy("mUidRulesFirstLock")
     private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
 
+    private RestrictedModeObserver mRestrictedModeObserver;
+
     // TODO: keep allowlist of system-critical services that should never have
     // rules enforced, such as system, phone, and radio UIDs.
 
@@ -610,7 +625,35 @@
         int COUNT = IS_UID_NETWORKING_BLOCKED + 1;
     }
 
-    public final StatLogger mStatLogger = new StatLogger(new String[] {
+    private static class RestrictedModeObserver extends ContentObserver {
+        private final Context mContext;
+        private final RestrictedModeListener mListener;
+
+        RestrictedModeObserver(Context ctx, RestrictedModeListener listener) {
+            super(null);
+            mContext = ctx;
+            mListener = listener;
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.RESTRICTED_NETWORKING_MODE), false,
+                    this);
+        }
+
+        public boolean isRestrictedModeEnabled() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.RESTRICTED_NETWORKING_MODE, 0) != 0;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mListener.onChange(isRestrictedModeEnabled());
+        }
+
+        public interface RestrictedModeListener {
+            void onChange(boolean enabled);
+        }
+    }
+
+    public final StatLogger mStatLogger = new StatLogger(new String[]{
             "updateNetworkEnabledNL()",
             "isUidNetworkingBlocked()",
     });
@@ -785,6 +828,15 @@
                     mRestrictPower = mPowerManagerInternal.getLowPowerState(
                             ServiceType.NETWORK_FIREWALL).batterySaverEnabled;
 
+                    mRestrictedModeObserver = new RestrictedModeObserver(mContext,
+                            enabled -> {
+                                synchronized (mUidRulesFirstLock) {
+                                    mRestrictedNetworkingMode = enabled;
+                                    updateRestrictedModeAllowlistUL();
+                                }
+                            });
+                    mRestrictedNetworkingMode = mRestrictedModeObserver.isRestrictedModeEnabled();
+
                     mSystemReady = true;
 
                     waitForAdminData();
@@ -3500,6 +3552,7 @@
                 fout.print("Restrict background: "); fout.println(mRestrictBackground);
                 fout.print("Restrict power: "); fout.println(mRestrictPower);
                 fout.print("Device idle: "); fout.println(mDeviceIdleMode);
+                fout.print("Restricted networking mode: "); fout.println(mRestrictedNetworkingMode);
                 synchronized (mMeteredIfacesLock) {
                     fout.print("Metered ifaces: ");
                     fout.println(mMeteredIfaces);
@@ -3811,6 +3864,100 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isRestrictedModeEnabled() {
+        synchronized (mUidRulesFirstLock) {
+            return mRestrictedNetworkingMode;
+        }
+    }
+
+    /**
+     * updates restricted mode state / access for all apps
+     * Called on initialization and when restricted mode is enabled / disabled.
+     */
+    @VisibleForTesting
+    @GuardedBy("mUidRulesFirstLock")
+    void updateRestrictedModeAllowlistUL() {
+        mUidFirewallRestrictedModeRules.clear();
+        forEachUid("updateRestrictedModeAllowlist", uid -> {
+            final int oldUidRule = mUidRules.get(uid);
+            final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule);
+            final boolean hasUidRuleChanged = oldUidRule != newUidRule;
+            final int newFirewallRule = getRestrictedModeFirewallRule(newUidRule);
+
+            // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
+            // non-default rules.
+            if (newFirewallRule != FIREWALL_RULE_DEFAULT) {
+                mUidFirewallRestrictedModeRules.append(uid, newFirewallRule);
+            }
+
+            if (hasUidRuleChanged) {
+                mUidRules.put(uid, newUidRule);
+                mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
+            }
+        });
+        if (mRestrictedNetworkingMode) {
+            // firewall rules only need to be set when this mode is being enabled.
+            setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, mUidFirewallRestrictedModeRules);
+        }
+        enableFirewallChainUL(FIREWALL_CHAIN_RESTRICTED, mRestrictedNetworkingMode);
+    }
+
+    // updates restricted mode state / access for a single app / uid.
+    @VisibleForTesting
+    @GuardedBy("mUidRulesFirstLock")
+    void updateRestrictedModeForUidUL(int uid) {
+        final int oldUidRule = mUidRules.get(uid);
+        final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule);
+        final boolean hasUidRuleChanged = oldUidRule != newUidRule;
+
+        if (hasUidRuleChanged) {
+            mUidRules.put(uid, newUidRule);
+            mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
+        }
+
+        // if restricted networking mode is on, and the app has an access exemption, the uid rule
+        // will not change, but the firewall rule will have to be updated.
+        if (mRestrictedNetworkingMode) {
+            // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
+            // In this case, default firewall rules can also be added.
+            setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid,
+                    getRestrictedModeFirewallRule(newUidRule));
+        }
+    }
+
+    private int getNewRestrictedModeUidRule(int uid, int oldUidRule) {
+        int newRule = oldUidRule;
+        newRule &= ~MASK_RESTRICTED_MODE_NETWORKS;
+        if (mRestrictedNetworkingMode && !hasRestrictedModeAccess(uid)) {
+            newRule |= RULE_REJECT_RESTRICTED_MODE;
+        }
+        return newRule;
+    }
+
+    private static int getRestrictedModeFirewallRule(int uidRule) {
+        if ((uidRule & RULE_REJECT_RESTRICTED_MODE) != 0) {
+            // rejected in restricted mode, this is the default behavior.
+            return FIREWALL_RULE_DEFAULT;
+        } else {
+            return FIREWALL_RULE_ALLOW;
+        }
+    }
+
+    private boolean hasRestrictedModeAccess(int uid) {
+        try {
+            // TODO: this needs to be kept in sync with
+            // PermissionMonitor#hasRestrictedNetworkPermission
+            return mIPm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)
+                    == PERMISSION_GRANTED
+                    || mIPm.checkUidPermission(NETWORK_STACK, uid) == PERMISSION_GRANTED
+                    || mIPm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)
+                    == PERMISSION_GRANTED;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     @GuardedBy("mUidRulesFirstLock")
     void updateRulesForPowerSaveUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
@@ -4032,6 +4179,7 @@
             updateRulesForAppIdleUL();
             updateRulesForRestrictPowerUL();
             updateRulesForRestrictBackgroundUL();
+            updateRestrictedModeAllowlistUL();
 
             // If the set of restricted networks may have changed, re-evaluate those.
             if (restrictedNetworksChanged) {
@@ -4050,7 +4198,8 @@
         try {
             updateRulesForDeviceIdleUL();
             updateRulesForPowerSaveUL();
-            updateRulesForAllAppsUL(TYPE_RESTRICT_POWER);
+            forEachUid("updateRulesForRestrictPower",
+                    uid -> updateRulesForPowerRestrictionsUL(uid));
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -4060,31 +4209,19 @@
     private void updateRulesForRestrictBackgroundUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictBackgroundUL");
         try {
-            updateRulesForAllAppsUL(TYPE_RESTRICT_BACKGROUND);
+            forEachUid("updateRulesForRestrictBackground",
+                    uid -> updateRulesForDataUsageRestrictionsUL(uid));
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
     }
 
-    private static final int TYPE_RESTRICT_BACKGROUND = 1;
-    private static final int TYPE_RESTRICT_POWER = 2;
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = false, value = {
-            TYPE_RESTRICT_BACKGROUND,
-            TYPE_RESTRICT_POWER,
-    })
-    public @interface RestrictType {
-    }
-
-    // TODO: refactor / consolidate all those updateXyz methods, there are way too many of them...
-    @GuardedBy("mUidRulesFirstLock")
-    private void updateRulesForAllAppsUL(@RestrictType int type) {
+    private void forEachUid(String tag, IntConsumer consumer) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
-            Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL-" + type);
+            Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "forEachUid-" + tag);
         }
         try {
             // update rules for all installed applications
-
             final PackageManager pm = mContext.getPackageManager();
             final List<UserInfo> users;
             final List<ApplicationInfo> apps;
@@ -4112,16 +4249,7 @@
                 for (int j = 0; j < appsSize; j++) {
                     final ApplicationInfo app = apps.get(j);
                     final int uid = UserHandle.getUid(user.id, app.uid);
-                    switch (type) {
-                        case TYPE_RESTRICT_BACKGROUND:
-                            updateRulesForDataUsageRestrictionsUL(uid);
-                            break;
-                        case TYPE_RESTRICT_POWER:
-                            updateRulesForPowerRestrictionsUL(uid);
-                            break;
-                        default:
-                            Slog.w(TAG, "Invalid type for updateRulesForAllApps: " + type);
-                    }
+                    consumer.accept(uid);
                 }
             }
         } finally {
@@ -4268,6 +4396,7 @@
         mPowerSaveWhitelistAppIds.delete(uid);
         mPowerSaveTempWhitelistAppIds.delete(uid);
         mAppIdleTempWhitelistAppIds.delete(uid);
+        mUidFirewallRestrictedModeRules.delete(uid);
 
         // ...then update iptables asynchronously.
         mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -4293,6 +4422,10 @@
         updateRuleForAppIdleUL(uid);
         updateRuleForRestrictPowerUL(uid);
 
+        // If the uid has the necessary permissions, then it should be added to the restricted mode
+        // firewall allowlist.
+        updateRestrictedModeForUidUL(uid);
+
         // Update internal state for power-related modes.
         updateRulesForPowerRestrictionsUL(uid);
 
@@ -4365,26 +4498,26 @@
 
         final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
         final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
-        final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
-        int newRule = RULE_NONE;
+
+        // copy oldUidRules and clear out METERED_NETWORKS rules.
+        int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS);
 
         // First step: define the new rule based on user restrictions and foreground state.
         if (isRestrictedByAdmin) {
-            newRule = RULE_REJECT_METERED;
+            newUidRules |= RULE_REJECT_METERED;
         } else if (isForeground) {
             if (isDenied || (mRestrictBackground && !isAllowed)) {
-                newRule = RULE_TEMPORARY_ALLOW_METERED;
+                newUidRules |= RULE_TEMPORARY_ALLOW_METERED;
             } else if (isAllowed) {
-                newRule = RULE_ALLOW_METERED;
+                newUidRules |= RULE_ALLOW_METERED;
             }
         } else {
             if (isDenied) {
-                newRule = RULE_REJECT_METERED;
+                newUidRules |= RULE_REJECT_METERED;
             } else if (mRestrictBackground && isAllowed) {
-                newRule = RULE_ALLOW_METERED;
+                newUidRules |= RULE_ALLOW_METERED;
             }
         }
-        final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS);
 
         if (LOGV) {
             Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
@@ -4392,8 +4525,8 @@
                     + ", isDenied=" + isDenied
                     + ", isAllowed=" + isAllowed
                     + ", isRestrictedByAdmin=" + isRestrictedByAdmin
-                    + ", oldRule=" + uidRulesToString(oldRule)
-                    + ", newRule=" + uidRulesToString(newRule)
+                    + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS)
+                    + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS)
                     + ", newUidRules=" + uidRulesToString(newUidRules)
                     + ", oldUidRules=" + uidRulesToString(oldUidRules));
         }
@@ -4405,8 +4538,8 @@
         }
 
         // Second step: apply bw changes based on change of state.
-        if (newRule != oldRule) {
-            if (hasRule(newRule, RULE_TEMPORARY_ALLOW_METERED)) {
+        if (newUidRules != oldUidRules) {
+            if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) {
                 // Temporarily allow foreground app, removing from denylist if necessary
                 // (since bw_penalty_box prevails over bw_happy_box).
 
@@ -4417,7 +4550,7 @@
                 if (isDenied) {
                     setMeteredNetworkDenylist(uid, false);
                 }
-            } else if (hasRule(oldRule, RULE_TEMPORARY_ALLOW_METERED)) {
+            } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) {
                 // Remove temporary exemption from app that is not on foreground anymore.
 
                 // TODO: if statements below are used to avoid unnecessary calls to netd / iptables,
@@ -4430,18 +4563,18 @@
                 if (isDenied || isRestrictedByAdmin) {
                     setMeteredNetworkDenylist(uid, true);
                 }
-            } else if (hasRule(newRule, RULE_REJECT_METERED)
-                    || hasRule(oldRule, RULE_REJECT_METERED)) {
+            } else if (hasRule(newUidRules, RULE_REJECT_METERED)
+                    || hasRule(oldUidRules, RULE_REJECT_METERED)) {
                 // Flip state because app was explicitly added or removed to denylist.
                 setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin));
-                if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowed) {
+                if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) {
                     // Since denial prevails over allowance, we need to handle the special case
                     // where app is allowed and denied at the same time (although such
                     // scenario should be blocked by the UI), then it is removed from the denylist.
                     setMeteredNetworkAllowlist(uid, isAllowed);
                 }
-            } else if (hasRule(newRule, RULE_ALLOW_METERED)
-                    || hasRule(oldRule, RULE_ALLOW_METERED)) {
+            } else if (hasRule(newUidRules, RULE_ALLOW_METERED)
+                    || hasRule(oldUidRules, RULE_ALLOW_METERED)) {
                 // Flip state because app was explicitly added or removed to allowlist.
                 setMeteredNetworkAllowlist(uid, isAllowed);
             } else {
@@ -4527,8 +4660,9 @@
         final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
 
         final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
-        final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
-        int newRule = RULE_NONE;
+
+        // Copy existing uid rules and clear ALL_NETWORK rules.
+        int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS);
 
         // First step: define the new rule based on user restrictions and foreground state.
 
@@ -4536,14 +4670,12 @@
         // by considering the foreground and non-foreground states.
         if (isForeground) {
             if (restrictMode) {
-                newRule = RULE_ALLOW_ALL;
+                newUidRules |= RULE_ALLOW_ALL;
             }
         } else if (restrictMode) {
-            newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
+            newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
         }
 
-        final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule;
-
         if (LOGV) {
             Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
                     + ", isIdle: " + isUidIdle
@@ -4551,17 +4683,18 @@
                     + ", mDeviceIdleMode: " + mDeviceIdleMode
                     + ", isForeground=" + isForeground
                     + ", isWhitelisted=" + isWhitelisted
-                    + ", oldRule=" + uidRulesToString(oldRule)
-                    + ", newRule=" + uidRulesToString(newRule)
+                    + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS)
+                    + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS)
                     + ", newUidRules=" + uidRulesToString(newUidRules)
                     + ", oldUidRules=" + uidRulesToString(oldUidRules));
         }
 
         // Second step: notify listeners if state changed.
-        if (newRule != oldRule) {
-            if (newRule == RULE_NONE || hasRule(newRule, RULE_ALLOW_ALL)) {
+        if (newUidRules != oldUidRules) {
+            if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules,
+                    RULE_ALLOW_ALL)) {
                 if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid);
-            } else if (hasRule(newRule, RULE_REJECT_ALL)) {
+            } else if (hasRule(newUidRules, RULE_REJECT_ALL)) {
                 if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid);
             } else {
                 // All scenarios should have been covered above
@@ -5018,6 +5151,8 @@
                 mUidFirewallStandbyRules.put(uid, rule);
             } else if (chain == FIREWALL_CHAIN_POWERSAVE) {
                 mUidFirewallPowerSaveRules.put(uid, rule);
+            } else if (chain == FIREWALL_CHAIN_RESTRICTED) {
+                mUidFirewallRestrictedModeRules.put(uid, rule);
             }
 
             try {
@@ -5063,6 +5198,8 @@
             mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
             mNetworkManager
                     .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT);
+            mNetworkManager
+                    .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT);
             mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
             mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
         } catch (IllegalStateException e) {
@@ -5224,7 +5361,7 @@
     public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) {
         final long startTime = mStatLogger.getTime();
 
-        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+        enforceAnyPermissionOf(OBSERVE_NETWORK_POLICY, PERMISSION_MAINLINE_NETWORK_STACK);
         final int uidRules;
         final boolean isBackgroundRestricted;
         synchronized (mUidRulesFirstLock) {
@@ -5249,26 +5386,21 @@
         // Networks are never blocked for system components
         if (isSystem(uid)) {
             reason = NTWK_ALLOWED_SYSTEM;
-        }
-        else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+        } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) {
+            reason = NTWK_BLOCKED_RESTRICTED_MODE;
+        } else if (hasRule(uidRules, RULE_REJECT_ALL)) {
             reason = NTWK_BLOCKED_POWER;
-        }
-        else if (!isNetworkMetered) {
+        } else if (!isNetworkMetered) {
             reason = NTWK_ALLOWED_NON_METERED;
-        }
-        else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+        } else if (hasRule(uidRules, RULE_REJECT_METERED)) {
             reason = NTWK_BLOCKED_DENYLIST;
-        }
-        else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+        } else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
             reason = NTWK_ALLOWED_ALLOWLIST;
-        }
-        else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+        } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
             reason = NTWK_ALLOWED_TMP_ALLOWLIST;
-        }
-        else if (isBackgroundRestricted) {
+        } else if (isBackgroundRestricted) {
             reason = NTWK_BLOCKED_BG_RESTRICT;
-        }
-        else {
+        } else {
             reason = NTWK_ALLOWED_DEFAULT;
         }
 
@@ -5281,6 +5413,7 @@
             case NTWK_ALLOWED_SYSTEM:
                 blocked = false;
                 break;
+            case NTWK_BLOCKED_RESTRICTED_MODE:
             case NTWK_BLOCKED_POWER:
             case NTWK_BLOCKED_DENYLIST:
             case NTWK_BLOCKED_BG_RESTRICT:
@@ -5327,32 +5460,6 @@
                     && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
         }
 
-        /**
-         * @return true if networking is blocked on the given interface for the given uid according
-         * to current networking policies.
-         */
-        @Override
-        public boolean isUidNetworkingBlocked(int uid, String ifname) {
-            final long startTime = mStatLogger.getTime();
-
-            final int uidRules;
-            final boolean isBackgroundRestricted;
-            synchronized (mUidRulesFirstLock) {
-                uidRules = mUidRules.get(uid, RULE_NONE);
-                isBackgroundRestricted = mRestrictBackground;
-            }
-            final boolean isNetworkMetered;
-            synchronized (mMeteredIfacesLock) {
-                isNetworkMetered = mMeteredIfaces.contains(ifname);
-            }
-            final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
-                    isBackgroundRestricted, mLogger);
-
-            mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
-
-            return ret;
-        }
-
         @Override
         public void onTempPowerSaveWhitelistChange(int appId, boolean added) {
             synchronized (mUidRulesFirstLock) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 7bcf318..47bb8f0 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -119,6 +119,8 @@
         switch(type) {
             case "restrict-background":
                 return getRestrictBackground();
+            case "restricted-mode":
+                return getRestrictedModeState();
         }
         pw.println("Error: unknown get type '" + type + "'");
         return -1;
@@ -255,6 +257,13 @@
         return listUidList("App Idle whitelisted UIDs", uids);
     }
 
+    private int getRestrictedModeState() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.print("Restricted mode status: ");
+        pw.println(mInterface.isRestrictedModeEnabled() ? "enabled" : "disabled");
+        return 0;
+    }
+
     private int getRestrictBackground() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         pw.print("Restrict background status: ");
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 7a6792c..0f8c9c7 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,15 +24,11 @@
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_REASON;
-import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
-import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
 import static android.os.Trace.TRACE_TAG_RRO;
 import static android.os.Trace.traceBegin;
 import static android.os.Trace.traceEnd;
 
-import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -43,7 +39,6 @@
 import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
 import android.content.om.OverlayInfo;
-import android.content.om.OverlayManagerTransaction;
 import android.content.om.OverlayableInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -53,7 +48,6 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -70,6 +64,7 @@
 
 import com.android.internal.content.om.OverlayConfig;
 import com.android.server.FgThread;
+import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
@@ -87,15 +82,12 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Consumer;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Service to manage asset overlays.
@@ -244,14 +236,7 @@
 
     private final OverlayActorEnforcer mActorEnforcer;
 
-    private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
-        persistSettings();
-        FgThread.getHandler().post(() -> {
-            List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
-            updateActivityManager(affectedTargets, pair.userId);
-            broadcastActionOverlayChanged(affectedTargets, pair.userId);
-        });
-    };
+    private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
 
     public OverlayManagerService(@NonNull final Context context) {
         super(context);
@@ -264,19 +249,17 @@
             IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
             mSettings = new OverlayManagerSettings();
             mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
-                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
+                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
+                    new OverlayChangeListener());
             mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
 
-            HandlerThread packageReceiverThread = new HandlerThread(TAG);
-            packageReceiverThread.start();
-
             final IntentFilter packageFilter = new IntentFilter();
             packageFilter.addAction(ACTION_PACKAGE_ADDED);
             packageFilter.addAction(ACTION_PACKAGE_CHANGED);
             packageFilter.addAction(ACTION_PACKAGE_REMOVED);
             packageFilter.addDataScheme("package");
             getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
-                    packageFilter, null, packageReceiverThread.getThreadHandler());
+                    packageFilter, null, null);
 
             final IntentFilter userFilter = new IntentFilter();
             userFilter.addAction(ACTION_USER_ADDED);
@@ -309,11 +292,11 @@
             for (int i = 0; i < userCount; i++) {
                 final UserInfo userInfo = users.get(i);
                 if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
-                    // Initialize any users that can't be switched to, as their state would
+                    // Initialize any users that can't be switched to, as there state would
                     // never be setup in onSwitchUser(). We will switch to the system user right
                     // after this, and its state will be setup there.
                     final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
-                    updatePackageManager(targets, users.get(i).id);
+                    updateOverlayPaths(users.get(i).id, targets);
                 }
             }
         }
@@ -327,10 +310,9 @@
             // any asset changes to the rest of the system
             synchronized (mLock) {
                 final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
-                final List<String> affectedTargets = updatePackageManager(targets, newUserId);
-                updateActivityManager(affectedTargets, newUserId);
+                updateAssets(newUserId, targets);
             }
-            persistSettings();
+            schedulePersistSettings();
         } finally {
             traceEnd(TRACE_TAG_RRO);
         }
@@ -414,17 +396,10 @@
                                 false);
                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-
-                            try {
-                                if (pi.isOverlayPackage()) {
-                                    mImpl.onOverlayPackageAdded(packageName, userId)
-                                        .ifPresent(mPropagateOverlayChange);
-                                } else {
-                                    mImpl.onTargetPackageAdded(packageName, userId)
-                                        .ifPresent(mPropagateOverlayChange);
-                                }
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageAdded internal error", e);
+                            if (pi.isOverlayPackage()) {
+                                mImpl.onOverlayPackageAdded(packageName, userId);
+                            } else {
+                                mImpl.onTargetPackageAdded(packageName, userId);
                             }
                         }
                     }
@@ -444,17 +419,10 @@
                                 false);
                         if (pi != null && pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-
-                            try {
-                                if (pi.isOverlayPackage()) {
-                                    mImpl.onOverlayPackageChanged(packageName, userId)
-                                        .ifPresent(mPropagateOverlayChange);
-                                }  else {
-                                    mImpl.onTargetPackageChanged(packageName, userId)
-                                        .ifPresent(mPropagateOverlayChange);
-                                }
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageChanged internal error", e);
+                            if (pi.isOverlayPackage()) {
+                                mImpl.onOverlayPackageChanged(packageName, userId);
+                            }  else {
+                                mImpl.onTargetPackageChanged(packageName, userId);
                             }
                         }
                     }
@@ -473,12 +441,7 @@
                         mPackageManager.forgetPackageInfo(packageName, userId);
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
                         if (oi != null) {
-                            try {
-                                mImpl.onOverlayPackageReplacing(packageName, userId)
-                                    .ifPresent(mPropagateOverlayChange);
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageReplacing internal error", e);
-                            }
+                            mImpl.onOverlayPackageReplacing(packageName, userId);
                         }
                     }
                 }
@@ -497,16 +460,10 @@
                                 false);
                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            try {
-                                if (pi.isOverlayPackage()) {
-                                    mImpl.onOverlayPackageReplaced(packageName, userId)
-                                        .ifPresent(mPropagateOverlayChange);
-                                } else {
-                                    mImpl.onTargetPackageReplaced(packageName, userId)
-                                        .ifPresent(mPropagateOverlayChange);
-                                }
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageReplaced internal error", e);
+                            if (pi.isOverlayPackage()) {
+                                mImpl.onOverlayPackageReplaced(packageName, userId);
+                            } else {
+                                mImpl.onTargetPackageReplaced(packageName, userId);
                             }
                         }
                     }
@@ -524,17 +481,10 @@
                     synchronized (mLock) {
                         mPackageManager.forgetPackageInfo(packageName, userId);
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
-
-                        try {
-                            if (oi != null) {
-                                mImpl.onOverlayPackageRemoved(packageName, userId)
-                                    .ifPresent(mPropagateOverlayChange);
-                            } else {
-                                mImpl.onTargetPackageRemoved(packageName, userId)
-                                    .ifPresent(mPropagateOverlayChange);
-                            }
-                        } catch (OperationFailedException e) {
-                            Slog.e(TAG, "onPackageRemoved internal error", e);
+                        if (oi != null) {
+                            mImpl.onOverlayPackageRemoved(packageName, userId);
+                        } else {
+                            mImpl.onTargetPackageRemoved(packageName, userId);
                         }
                     }
                 }
@@ -557,7 +507,7 @@
                             synchronized (mLock) {
                                 targets = mImpl.updateOverlaysForUser(userId);
                             }
-                            updatePackageManager(targets, userId);
+                            updateOverlayPaths(userId, targets);
                         } finally {
                             traceEnd(TRACE_TAG_RRO);
                         }
@@ -652,13 +602,7 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        try {
-                            mImpl.setEnabled(packageName, enable, realUserId)
-                                .ifPresent(mPropagateOverlayChange);
-                            return true;
-                        } catch (OperationFailedException e) {
-                            return false;
-                        }
+                        return mImpl.setEnabled(packageName, enable, realUserId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -683,14 +627,8 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        try {
-                            mImpl.setEnabledExclusive(packageName,
-                                    false /* withinCategory */, realUserId)
-                                .ifPresent(mPropagateOverlayChange);
-                            return true;
-                        } catch (OperationFailedException e) {
-                            return false;
-                        }
+                        return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
+                                realUserId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -716,14 +654,8 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        try {
-                            mImpl.setEnabledExclusive(packageName,
-                                    true /* withinCategory */, realUserId)
-                                .ifPresent(mPropagateOverlayChange);
-                            return true;
-                        } catch (OperationFailedException e) {
-                            return false;
-                        }
+                        return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
+                                realUserId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -749,13 +681,7 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        try {
-                            mImpl.setPriority(packageName, parentPackageName, realUserId)
-                                .ifPresent(mPropagateOverlayChange);
-                            return true;
-                        } catch (OperationFailedException e) {
-                            return false;
-                        }
+                        return mImpl.setPriority(packageName, parentPackageName, realUserId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -779,13 +705,7 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        try {
-                            mImpl.setHighestPriority(packageName, realUserId)
-                                .ifPresent(mPropagateOverlayChange);
-                            return true;
-                        } catch (OperationFailedException e) {
-                            return false;
-                        }
+                        return mImpl.setHighestPriority(packageName, realUserId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -809,13 +729,7 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        try {
-                            mImpl.setLowestPriority(packageName, realUserId)
-                                .ifPresent(mPropagateOverlayChange);
-                            return true;
-                        } catch (OperationFailedException e) {
-                            return false;
-                        }
+                        return mImpl.setLowestPriority(packageName, realUserId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -864,120 +778,6 @@
         }
 
         @Override
-        public void commit(@NonNull final OverlayManagerTransaction transaction)
-                throws RemoteException {
-            try {
-                traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
-                try {
-                    executeAllRequests(transaction);
-                } catch (Exception e) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        restoreSettings();
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                    Slog.d(TAG, "commit failed: " + e.getMessage(), e);
-                    throw new SecurityException("commit failed"
-                            + (DEBUG ? ": " + e.getMessage() : ""));
-                }
-            } finally {
-                traceEnd(TRACE_TAG_RRO);
-            }
-        }
-
-        private Optional<PackageAndUser> executeRequest(
-                @NonNull final OverlayManagerTransaction.Request request) throws Exception {
-            final int realUserId = handleIncomingUser(request.userId, request.typeToString());
-            enforceActor(request.packageName, request.typeToString(), realUserId);
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                switch (request.type) {
-                    case TYPE_SET_ENABLED:
-                        Optional<PackageAndUser> opt1 =
-                                mImpl.setEnabled(request.packageName, true, request.userId);
-                        Optional<PackageAndUser> opt2 =
-                                mImpl.setHighestPriority(request.packageName, request.userId);
-                        // Both setEnabled and setHighestPriority affected the same
-                        // target package and user: if both return non-empty
-                        // Optionals, they are identical
-                        return opt1.isPresent() ? opt1 : opt2;
-                    case TYPE_SET_DISABLED:
-                        return mImpl.setEnabled(request.packageName, false, request.userId);
-                    default:
-                        throw new IllegalArgumentException("unsupported request: " + request);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
-                throws Exception {
-            if (DEBUG) {
-                Slog.d(TAG, "commit " + transaction);
-            }
-            if (transaction == null) {
-                throw new IllegalArgumentException("null transaction");
-            }
-
-            // map: userId -> list<targetPackageName>
-            SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>();
-
-            synchronized (mLock) {
-                // map: userId -> set<targetPackageName>
-                SparseArray<Set<String>> targetsToUpdate = new SparseArray<>();
-
-                // execute the requests (as calling user)
-                for (final OverlayManagerTransaction.Request request : transaction) {
-                    executeRequest(request).ifPresent(target -> {
-                        Set<String> userTargets = targetsToUpdate.get(target.userId);
-                        if (userTargets == null) {
-                            userTargets = new ArraySet<String>();
-                            targetsToUpdate.put(target.userId, userTargets);
-                        }
-                        userTargets.add(target.packageName);
-                    });
-                }
-
-                // past the point of no return: the entire transaction has been
-                // processed successfully, we can no longer fail: continue as
-                // system_server
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    persistSettings();
-
-                    // inform the package manager about the new paths
-                    for (int index = 0; index < targetsToUpdate.size(); index++) {
-                        final int userId = targetsToUpdate.keyAt(index);
-                        final List<String> affectedTargets =
-                                updatePackageManager(targetsToUpdate.valueAt(index), userId);
-                        affectedTargetsToUpdate.put(userId, affectedTargets);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            } // synchronized (mLock)
-
-            FgThread.getHandler().post(() -> {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    // schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents
-                    for (int index = 0; index < affectedTargetsToUpdate.size(); index++) {
-                        final int userId = affectedTargetsToUpdate.keyAt(index);
-                        final List<String> packageNames = affectedTargetsToUpdate.valueAt(index);
-
-                        updateActivityManager(packageNames, userId);
-                        broadcastActionOverlayChanged(packageNames, userId);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            });
-        }
-
-        @Override
         public void onShellCommand(@NonNull final FileDescriptor in,
                 @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
                 @NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -1098,7 +898,162 @@
         }
     };
 
-    private static final class PackageManagerHelperImpl implements PackageManagerHelper {
+    private final class OverlayChangeListener
+            implements OverlayManagerServiceImpl.OverlayChangeListener {
+        @Override
+        public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
+            schedulePersistSettings();
+            FgThread.getHandler().post(() -> {
+                updateAssets(userId, targetPackageName);
+
+                final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
+                        Uri.fromParts("package", targetPackageName, null));
+                intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+                if (DEBUG) {
+                    Slog.d(TAG, "send broadcast " + intent);
+                }
+
+                try {
+                    ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
+                            null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
+                            null, false, false, userId);
+                } catch (RemoteException e) {
+                    // Intentionally left empty.
+                }
+            });
+        }
+    }
+
+    /**
+     * Updates the target packages' set of enabled overlays in PackageManager.
+     */
+    private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
+            if (DEBUG) {
+                Slog.d(TAG, "Updating overlay assets");
+            }
+            final PackageManagerInternal pm =
+                    LocalServices.getService(PackageManagerInternal.class);
+            final boolean updateFrameworkRes = targetPackageNames.contains("android");
+            if (updateFrameworkRes) {
+                targetPackageNames = pm.getTargetPackageNames(userId);
+            }
+
+            final Map<String, List<String>> pendingChanges =
+                    new ArrayMap<>(targetPackageNames.size());
+            synchronized (mLock) {
+                final List<String> frameworkOverlays =
+                        mImpl.getEnabledOverlayPackageNames("android", userId);
+                final int n = targetPackageNames.size();
+                for (int i = 0; i < n; i++) {
+                    final String targetPackageName = targetPackageNames.get(i);
+                    List<String> list = new ArrayList<>();
+                    if (!"android".equals(targetPackageName)) {
+                        list.addAll(frameworkOverlays);
+                    }
+                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+                    pendingChanges.put(targetPackageName, list);
+                }
+            }
+
+            final HashSet<String> updatedPackages = new HashSet<>();
+            final int n = targetPackageNames.size();
+            for (int i = 0; i < n; i++) {
+                final String targetPackageName = targetPackageNames.get(i);
+                if (DEBUG) {
+                    Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
+                            + "] userId=" + userId);
+                }
+
+                if (!pm.setEnabledOverlayPackages(
+                        userId, targetPackageName, pendingChanges.get(targetPackageName),
+                        updatedPackages)) {
+                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+                            targetPackageName, userId));
+                }
+            }
+            return new ArrayList<>(updatedPackages);
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
+        }
+    }
+
+    private void updateAssets(final int userId, final String targetPackageName) {
+        updateAssets(userId, Collections.singletonList(targetPackageName));
+    }
+
+    private void updateAssets(final int userId, List<String> targetPackageNames) {
+        final IActivityManager am = ActivityManager.getService();
+        try {
+            final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
+            am.scheduleApplicationInfoChanged(updatedPaths, userId);
+        } catch (RemoteException e) {
+            // Intentionally left empty.
+        }
+    }
+
+    private void schedulePersistSettings() {
+        if (mPersistSettingsScheduled.getAndSet(true)) {
+            return;
+        }
+        IoThread.getHandler().post(() -> {
+            mPersistSettingsScheduled.set(false);
+            if (DEBUG) {
+                Slog.d(TAG, "Writing overlay settings");
+            }
+            synchronized (mLock) {
+                FileOutputStream stream = null;
+                try {
+                    stream = mSettingsFile.startWrite();
+                    mSettings.persist(stream);
+                    mSettingsFile.finishWrite(stream);
+                } catch (IOException | XmlPullParserException e) {
+                    mSettingsFile.failWrite(stream);
+                    Slog.e(TAG, "failed to persist overlay state", e);
+                }
+            }
+        });
+    }
+
+    private void restoreSettings() {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
+            synchronized (mLock) {
+                if (!mSettingsFile.getBaseFile().exists()) {
+                    return;
+                }
+                try (FileInputStream stream = mSettingsFile.openRead()) {
+                    mSettings.restore(stream);
+
+                    // We might have data for dying users if the device was
+                    // restarted before we received USER_REMOVED. Remove data for
+                    // users that will not exist after the system is ready.
+
+                    final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+                    final int[] liveUserIds = new int[liveUsers.size()];
+                    for (int i = 0; i < liveUsers.size(); i++) {
+                        liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+                    }
+                    Arrays.sort(liveUserIds);
+
+                    for (int userId : mSettings.getUsers()) {
+                        if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+                            mSettings.removeUser(userId);
+                        }
+                    }
+                } catch (IOException | XmlPullParserException e) {
+                    Slog.e(TAG, "failed to restore overlay state", e);
+                }
+            }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
+        }
+    }
+
+    private static final class PackageManagerHelperImpl implements PackageManagerHelper  {
 
         private final Context mContext;
         private final IPackageManager mPackageManager;
@@ -1308,151 +1263,4 @@
             }
         }
     }
-
-    // Helper methods to update other parts of the system or read/write
-    // settings: these methods should never call into each other!
-
-    private void broadcastActionOverlayChanged(@NonNull final Collection<String> packageNames,
-            final int userId) {
-        for (final String packageName : packageNames) {
-            broadcastActionOverlayChanged(packageName, userId);
-        }
-    }
-
-    private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
-            final int userId) {
-        final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
-                Uri.fromParts("package", targetPackageName, null));
-        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        try {
-            ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
-                    null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
-        } catch (RemoteException e) {
-            // Intentionally left empty.
-        }
-    }
-
-    /**
-     * Tell the activity manager to tell a set of packages to reload their
-     * resources.
-     */
-    private void updateActivityManager(List<String> targetPackageNames, final int userId) {
-        final IActivityManager am = ActivityManager.getService();
-        try {
-            am.scheduleApplicationInfoChanged(targetPackageNames, userId);
-        } catch (RemoteException e) {
-            // Intentionally left empty.
-        }
-    }
-
-    private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
-        return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
-    }
-
-    /**
-     * Updates the target packages' set of enabled overlays in PackageManager.
-     * @return the package names of affected targets (a superset of
-     *         targetPackageNames: the target themserlves and shared libraries)
-     */
-    private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
-            final int userId) {
-        try {
-            traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
-            if (DEBUG) {
-                Slog.d(TAG, "Update package manager about changed overlays");
-            }
-            final PackageManagerInternal pm =
-                    LocalServices.getService(PackageManagerInternal.class);
-            final boolean updateFrameworkRes = targetPackageNames.contains("android");
-            if (updateFrameworkRes) {
-                targetPackageNames = pm.getTargetPackageNames(userId);
-            }
-
-            final Map<String, List<String>> pendingChanges =
-                    new ArrayMap<>(targetPackageNames.size());
-            synchronized (mLock) {
-                final List<String> frameworkOverlays =
-                        mImpl.getEnabledOverlayPackageNames("android", userId);
-                for (final String targetPackageName : targetPackageNames) {
-                    List<String> list = new ArrayList<>();
-                    if (!"android".equals(targetPackageName)) {
-                        list.addAll(frameworkOverlays);
-                    }
-                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
-                    pendingChanges.put(targetPackageName, list);
-                }
-            }
-
-            final HashSet<String> updatedPackages = new HashSet<>();
-            for (final String targetPackageName : targetPackageNames) {
-                if (DEBUG) {
-                    Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
-                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
-                            + "] userId=" + userId);
-                }
-
-                if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName),
-                        updatedPackages)) {
-                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
-                            targetPackageName, userId));
-                }
-            }
-            return new ArrayList<>(updatedPackages);
-        } finally {
-            traceEnd(TRACE_TAG_RRO);
-        }
-    }
-
-    private void persistSettings() {
-        if (DEBUG) {
-            Slog.d(TAG, "Writing overlay settings");
-        }
-        synchronized (mLock) {
-            FileOutputStream stream = null;
-            try {
-                stream = mSettingsFile.startWrite();
-                mSettings.persist(stream);
-                mSettingsFile.finishWrite(stream);
-            } catch (IOException | XmlPullParserException e) {
-                mSettingsFile.failWrite(stream);
-                Slog.e(TAG, "failed to persist overlay state", e);
-            }
-        }
-    }
-
-    private void restoreSettings() {
-        try {
-            traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
-            synchronized (mLock) {
-                if (!mSettingsFile.getBaseFile().exists()) {
-                    return;
-                }
-                try (FileInputStream stream = mSettingsFile.openRead()) {
-                    mSettings.restore(stream);
-
-                    // We might have data for dying users if the device was
-                    // restarted before we received USER_REMOVED. Remove data for
-                    // users that will not exist after the system is ready.
-
-                    final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
-                    final int[] liveUserIds = new int[liveUsers.size()];
-                    for (int i = 0; i < liveUsers.size(); i++) {
-                        liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
-                    }
-                    Arrays.sort(liveUserIds);
-
-                    for (int userId : mSettings.getUsers()) {
-                        if (Arrays.binarySearch(liveUserIds, userId) < 0) {
-                            mSettings.removeUser(userId);
-                        }
-                    }
-                } catch (IOException | XmlPullParserException e) {
-                    Slog.e(TAG, "failed to restore overlay state", e);
-                }
-            }
-        } finally {
-            traceEnd(TRACE_TAG_RRO);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index e60411b..05a4a38 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -45,7 +45,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -72,6 +71,7 @@
     private final OverlayManagerSettings mSettings;
     private final OverlayConfig mOverlayConfig;
     private final String[] mDefaultOverlays;
+    private final OverlayChangeListener mListener;
 
     /**
      * Helper method to merge the overlay manager's (as read from overlays.xml)
@@ -114,12 +114,14 @@
             @NonNull final IdmapManager idmapManager,
             @NonNull final OverlayManagerSettings settings,
             @NonNull final OverlayConfig overlayConfig,
-            @NonNull final String[] defaultOverlays) {
+            @NonNull final String[] defaultOverlays,
+            @NonNull final OverlayChangeListener listener) {
         mPackageManager = packageManager;
         mIdmapManager = idmapManager;
         mSettings = settings;
         mOverlayConfig = overlayConfig;
         mDefaultOverlays = defaultOverlays;
+        mListener = listener;
     }
 
     /**
@@ -257,58 +259,52 @@
         mSettings.removeUser(userId);
     }
 
-    Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
         }
 
-        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
         }
 
-        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onTargetPackageReplacing(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
                     + userId);
         }
 
-        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onTargetPackageReplaced(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
         }
 
-        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     /**
      * Update the state of any overlays for this target.
      */
-    private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
-            @NonNull final String targetPackageName, final int userId, final int flags)
-            throws OperationFailedException {
+    private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
+            final int userId, final int flags) {
         final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
                 userId);
 
@@ -368,13 +364,11 @@
         }
 
         if (modified) {
-            return Optional.of(new PackageAndUser(targetPackageName, userId));
+            mListener.onOverlaysChanged(targetPackageName, userId);
         }
-        return Optional.empty();
     }
 
-    Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
         }
@@ -382,7 +376,8 @@
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
             Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
-            return onOverlayPackageRemoved(packageName, userId);
+            onOverlayPackageRemoved(packageName, userId);
+            return;
         }
 
         mSettings.init(packageName, userId, overlayPackage.overlayTarget,
@@ -394,17 +389,15 @@
                 overlayPackage.overlayCategory);
         try {
             if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
-                return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
+                mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
             }
-            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
+            Slog.e(TAG, "failed to update settings", e);
             mSettings.remove(packageName, userId);
-            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
         }
@@ -412,16 +405,14 @@
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (updateState(oi.targetPackageName, packageName, userId, 0)) {
-                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
             }
-            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            throw new OperationFailedException("failed to update settings", e);
+            Slog.e(TAG, "failed to update settings", e);
         }
     }
 
-    Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
                     + userId);
@@ -432,16 +423,14 @@
             if (updateState(oi.targetPackageName, packageName, userId,
                         FLAG_OVERLAY_IS_BEING_REPLACED)) {
                 removeIdmapIfPossible(oi);
-                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
             }
-            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            throw new OperationFailedException("failed to update settings", e);
+            Slog.e(TAG, "failed to update settings", e);
         }
     }
 
-    Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
                     + userId);
@@ -450,12 +439,16 @@
         final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
         if (pkg == null) {
             Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
-            return onOverlayPackageRemoved(packageName, userId);
+            onOverlayPackageRemoved(packageName, userId);
+            return;
         }
 
         try {
             final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
             if (mustReinitializeOverlay(pkg, oldOi)) {
+                if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
+                    mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+                }
                 mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
                         pkg.applicationInfo.getBaseCodePath(),
                         isPackageConfiguredMutable(pkg.packageName),
@@ -464,25 +457,22 @@
             }
 
             if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
-                return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
+                mListener.onOverlaysChanged(pkg.overlayTarget, userId);
             }
-            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            throw new OperationFailedException("failed to update settings", e);
+            Slog.e(TAG, "failed to update settings", e);
         }
     }
 
-    Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
         try {
             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
             if (mSettings.remove(packageName, userId)) {
                 removeIdmapIfPossible(overlayInfo);
-                return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
+                mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
             }
-            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            throw new OperationFailedException("failed to remove overlay", e);
+            Slog.e(TAG, "failed to remove overlay", e);
         }
     }
 
@@ -503,8 +493,8 @@
         return mSettings.getOverlaysForUser(userId);
     }
 
-    Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
-            final int userId) throws OperationFailedException {
+    boolean setEnabled(@NonNull final String packageName, final boolean enable,
+            final int userId) {
         if (DEBUG) {
             Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
                         packageName, enable, userId));
@@ -512,33 +502,30 @@
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            throw new OperationFailedException(
-                    String.format("failed to find overlay package %s for user %d",
-                        packageName, userId));
+            return false;
         }
 
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (!oi.isMutable) {
                 // Ignore immutable overlays.
-                throw new OperationFailedException(
-                        "cannot enable immutable overlay packages in runtime");
+                return false;
             }
 
             boolean modified = mSettings.setEnabled(packageName, userId, enable);
             modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
 
             if (modified) {
-                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
             }
-            return Optional.empty();
+            return true;
         } catch (OverlayManagerSettings.BadKeyException e) {
-            throw new OperationFailedException("failed to update settings", e);
+            return false;
         }
     }
 
-    Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
-            boolean withinCategory, final int userId) throws OperationFailedException {
+    boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
+            final int userId) {
         if (DEBUG) {
             Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
                     + " withinCategory=%s userId=%d", packageName, withinCategory, userId));
@@ -546,8 +533,7 @@
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            throw new OperationFailedException(String.format(
-                        "failed to find overlay package %s for user %d", packageName, userId));
+            return false;
         }
 
         try {
@@ -590,11 +576,11 @@
             modified |= updateState(targetPackageName, packageName, userId, 0);
 
             if (modified) {
-                return Optional.of(new PackageAndUser(targetPackageName, userId));
+                mListener.onOverlaysChanged(targetPackageName, userId);
             }
-            return Optional.empty();
+            return true;
         } catch (OverlayManagerSettings.BadKeyException e) {
-            throw new OperationFailedException("failed to update settings", e);
+            return false;
         }
     }
 
@@ -610,75 +596,66 @@
         return mOverlayConfig.isEnabled(packageName);
     }
 
-    Optional<PackageAndUser> setPriority(@NonNull final String packageName,
-            @NonNull final String newParentPackageName, final int userId)
-            throws OperationFailedException {
+    boolean setPriority(@NonNull final String packageName,
+            @NonNull final String newParentPackageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
                     + newParentPackageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            throw new OperationFailedException(String.format(
-                        "overlay package %s user %d is not updatable", packageName, userId));
+            return false;
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            throw new OperationFailedException(String.format(
-                        "failed to find overlay package %s for user %d", packageName, userId));
+            return false;
         }
 
         if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
-            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
+            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
         }
-        return Optional.empty();
+        return true;
     }
 
-    Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
-            final int userId) throws OperationFailedException {
+    boolean setHighestPriority(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            throw new OperationFailedException(String.format(
-                        "overlay package %s user %d is not updatable", packageName, userId));
+            return false;
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            throw new OperationFailedException(String.format(
-                        "failed to find overlay package %s for user %d", packageName, userId));
+            return false;
         }
 
         if (mSettings.setHighestPriority(packageName, userId)) {
-            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
+            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
         }
-        return Optional.empty();
+        return true;
     }
 
-    Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
-            throws OperationFailedException {
+    boolean setLowestPriority(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
             Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            throw new OperationFailedException(String.format(
-                        "overlay package %s user %d is not updatable", packageName, userId));
+            return false;
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            throw new OperationFailedException(String.format(
-                        "failed to find overlay package %s for user %d", packageName, userId));
+            return false;
         }
 
         if (mSettings.setLowestPriority(packageName, userId)) {
-            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
+            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
         }
-        return Optional.empty();
+        return true;
     }
 
     void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
@@ -820,13 +797,12 @@
         mIdmapManager.removeIdmap(oi, oi.userId);
     }
 
-    static final class OperationFailedException extends Exception {
-        OperationFailedException(@NonNull final String message) {
-            super(message);
-        }
+    interface OverlayChangeListener {
 
-        OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
-            super(message, cause);
-        }
+        /**
+         * An event triggered by changes made to overlay state or settings as well as changes that
+         * add or remove target packages of overlays.
+         **/
+        void onOverlaysChanged(@NonNull String targetPackage, int userId);
     }
 }
diff --git a/services/core/java/com/android/server/om/PackageAndUser.java b/services/core/java/com/android/server/om/PackageAndUser.java
deleted file mode 100644
index 5c38ba7..0000000
--- a/services/core/java/com/android/server/om/PackageAndUser.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.om;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-
-final class PackageAndUser {
-    public final @NonNull String packageName;
-    public final @UserIdInt int userId;
-
-    PackageAndUser(@NonNull String packageName, @UserIdInt int userId) {
-        this.packageName = packageName;
-        this.userId = userId;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof PackageAndUser)) {
-            return false;
-        }
-        PackageAndUser other = (PackageAndUser) obj;
-        return packageName.equals(other.packageName) && userId == other.userId;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + packageName.hashCode();
-        result = prime * result + userId;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId);
-    }
-}
diff --git a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS
new file mode 100644
index 0000000..a52e9cf
--- /dev/null
+++ b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS
@@ -0,0 +1,7 @@
+# OWNERS of Multiuser related files related to Enterprise
+
+include /MULTIUSER_OWNERS
+
+# Enterprise owners
+rubinxu@google.com
+sandness@google.com
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 004259b..43c5d5e 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -30,13 +30,12 @@
 per-file CrossProfileAppsService.java = omakoto@google.com, yamasani@google.com
 per-file CrossProfileIntentFilter.java = omakoto@google.com, yamasani@google.com
 per-file CrossProfileIntentResolver.java = omakoto@google.com, yamasani@google.com
-per-file RestrictionsSet.java = bookatz@google.com, omakoto@google.com, yamasani@google.com, rubinxu@google.com, sandness@google.com
-per-file UserManagerInternal.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserManagerService.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserRestrictionsUtils.java = omakoto@google.com, rubinxu@google.com, sandness@google.com, yamasani@google.com
-per-file UserSystemPackageInstaller.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserTypeDetails.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserTypeFactory.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
+per-file RestrictionsSet.java = file:MULTIUSER_AND_ENTERPRISE_OWNERS
+per-file UserManager* = file:/MULTIUSER_OWNERS
+per-file UserRestriction* = file:MULTIUSER_AND_ENTERPRISE_OWNERS
+per-file UserSystemPackageInstaller* = file:/MULTIUSER_OWNERS
+per-file UserTypeDetails.java = file:/MULTIUSER_OWNERS
+per-file UserTypeFactory.java = file:/MULTIUSER_OWNERS
 
 # security
 per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 7ea8e04..7024e67 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -16,33 +16,438 @@
 
 package com.android.server.vcn;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import static com.android.server.VcnManagementService.VDBG;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager;
+import android.net.IpSecManager.IpSecTunnelInterface;
+import android.net.IpSecManager.ResourceUnavailableException;
+import android.net.IpSecTransform;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.RouteInfo;
+import android.net.annotations.PolicyDirection;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
 import android.os.ParcelUuid;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
 
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A single VCN Gateway Connection, providing a single public-facing VCN network.
  *
  * <p>This class handles mobility events, performs retries, and tracks safe-mode conditions.
  *
+ * <pre>Internal state transitions are as follows:
+ *
+ * +----------------------------+                 +------------------------------+
+ * |     DisconnectedState      |    Teardown or  |      DisconnectingState      |
+ * |                            |<--no available--|                              |
+ * |       Initial state.       |    underlying   | Transitive state for tearing |
+ * +----------------------------+     networks    | tearing down an IKE session. |
+ *               |                                +------------------------------+
+ *               |                                         ^          |
+ *       Underlying Network            Teardown requested  |   Not tearing down
+ *            changed               +--or retriable error--+  and has available
+ *               |                  |      occurred           underlying network
+ *               |                  ^                                 |
+ *               v                  |                                 v
+ * +----------------------------+   |             +------------------------------+
+ * |      ConnectingState       |<----------------|      RetryTimeoutState       |
+ * |                            |   |             |                              |
+ * |    Transitive state for    |   |             |     Transitive state for     |
+ * |  starting IKE negotiation. |---+             |  handling retriable errors.  |
+ * +----------------------------+   |             +------------------------------+
+ *               |                  |
+ *          IKE session             |
+ *           negotiated             |
+ *               |                  |
+ *               v                  |
+ * +----------------------------+   ^
+ * |      ConnectedState        |   |
+ * |                            |   |
+ * |     Stable state where     |   |
+ * |  gateway connection is set |   |
+ * | up, and Android Network is |   |
+ * |         connected.         |---+
+ * +----------------------------+
+ * </pre>
+ *
  * @hide
  */
-public class VcnGatewayConnection extends Handler implements UnderlyingNetworkTrackerCallback {
+public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
+    private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
+    private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
+
+    private static final String DISCONNECT_REASON_INTERNAL_ERROR = "Uncaught exception: ";
+    private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST =
+            "Underlying Network lost";
+    private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel";
+    private static final int TOKEN_ANY = Integer.MIN_VALUE;
+
+    private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
+    private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+    private interface EventInfo {}
+
+    /**
+     * Sent when there are changes to the underlying network (per the UnderlyingNetworkTracker).
+     *
+     * <p>May indicate an entirely new underlying network, OR a change in network properties.
+     *
+     * <p>Relevant in ALL states.
+     *
+     * <p>In the Connected state, this MAY indicate a mobility even occurred.
+     *
+     * @param arg1 The "any" token; this event is always applicable.
+     * @param obj @NonNull An EventUnderlyingNetworkChangedInfo instance with relevant data.
+     */
+    private static final int EVENT_UNDERLYING_NETWORK_CHANGED = 1;
+
+    private static class EventUnderlyingNetworkChangedInfo implements EventInfo {
+        @Nullable public final UnderlyingNetworkRecord newUnderlying;
+
+        EventUnderlyingNetworkChangedInfo(@Nullable UnderlyingNetworkRecord newUnderlying) {
+            this.newUnderlying = newUnderlying;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(newUnderlying);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventUnderlyingNetworkChangedInfo)) {
+                return false;
+            }
+
+            final EventUnderlyingNetworkChangedInfo rhs = (EventUnderlyingNetworkChangedInfo) other;
+            return Objects.equals(newUnderlying, rhs.newUnderlying);
+        }
+    }
+
+    /**
+     * Sent (delayed) to trigger an attempt to reestablish the tunnel.
+     *
+     * <p>Only relevant in the Retry-timeout state, discarded in all other states.
+     *
+     * <p>Upon receipt of this signal, the state machine will transition from the Retry-timeout
+     * state to the Connecting state.
+     *
+     * @param arg1 The "any" token; no sessions are active in the RetryTimeoutState.
+     */
+    private static final int EVENT_RETRY_TIMEOUT_EXPIRED = 2;
+
+    /**
+     * Sent when a gateway connection has been lost, either due to a IKE or child failure.
+     *
+     * <p>Relevant in all states that have an IKE session.
+     *
+     * <p>Upon receipt of this signal, the state machine will (unless loss of the session is
+     * expected) transition to the Disconnecting state, to ensure IKE session closure before
+     * retrying, or fully shutting down.
+     *
+     * @param arg1 The session token for the IKE Session that was lost, used to prevent out-of-date
+     *     signals from propagating.
+     * @param obj @NonNull An EventSessionLostInfo instance with relevant data.
+     */
+    private static final int EVENT_SESSION_LOST = 3;
+
+    private static class EventSessionLostInfo implements EventInfo {
+        @Nullable public final Exception exception;
+
+        EventSessionLostInfo(@NonNull Exception exception) {
+            this.exception = exception;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(exception);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventSessionLostInfo)) {
+                return false;
+            }
+
+            final EventSessionLostInfo rhs = (EventSessionLostInfo) other;
+            return Objects.equals(exception, rhs.exception);
+        }
+    }
+
+    /**
+     * Sent when an IKE session has completely closed.
+     *
+     * <p>Relevant only in the Disconnecting State, used to identify that a session being torn down
+     * was fully closed. If this event is not fired within a timely fashion, the IKE session will be
+     * forcibly terminated.
+     *
+     * <p>Upon receipt of this signal, the state machine will (unless closure of the session is
+     * expected) transition to the Disconnected or RetryTimeout states, depending on whether the
+     * GatewayConnection is being fully torn down.
+     *
+     * @param arg1 The session token for the IKE Session that was lost, used to prevent out-of-date
+     *     signals from propagating.
+     * @param obj @NonNull An EventSessionLostInfo instance with relevant data.
+     */
+    private static final int EVENT_SESSION_CLOSED = 4;
+
+    /**
+     * Sent when an IKE Child Transform was created, and should be applied to the tunnel.
+     *
+     * <p>Only relevant in the Connecting, Connected and Migrating states. This callback MUST be
+     * handled in the Connected or Migrating states, and should be deferred if necessary.
+     *
+     * @param arg1 The session token for the IKE Session that had a new child created, used to
+     *     prevent out-of-date signals from propagating.
+     * @param obj @NonNull An EventTransformCreatedInfo instance with relevant data.
+     */
+    private static final int EVENT_TRANSFORM_CREATED = 5;
+
+    private static class EventTransformCreatedInfo implements EventInfo {
+        @PolicyDirection public final int direction;
+        @NonNull public final IpSecTransform transform;
+
+        EventTransformCreatedInfo(
+                @PolicyDirection int direction, @NonNull IpSecTransform transform) {
+            this.direction = direction;
+            this.transform = Objects.requireNonNull(transform);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(direction, transform);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventTransformCreatedInfo)) {
+                return false;
+            }
+
+            final EventTransformCreatedInfo rhs = (EventTransformCreatedInfo) other;
+            return direction == rhs.direction && Objects.equals(transform, rhs.transform);
+        }
+    }
+
+    /**
+     * Sent when an IKE Child Session was completely opened and configured successfully.
+     *
+     * <p>Only relevant in the Connected and Migrating states.
+     *
+     * @param arg1 The session token for the IKE Session for which a child was opened and configured
+     *     successfully, used to prevent out-of-date signals from propagating.
+     * @param obj @NonNull An EventSetupCompletedInfo instance with relevant data.
+     */
+    private static final int EVENT_SETUP_COMPLETED = 6;
+
+    private static class EventSetupCompletedInfo implements EventInfo {
+        @NonNull public final ChildSessionConfiguration childSessionConfig;
+
+        EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) {
+            this.childSessionConfig = Objects.requireNonNull(childSessionConfig);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(childSessionConfig);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventSetupCompletedInfo)) {
+                return false;
+            }
+
+            final EventSetupCompletedInfo rhs = (EventSetupCompletedInfo) other;
+            return Objects.equals(childSessionConfig, rhs.childSessionConfig);
+        }
+    }
+
+    /**
+     * Sent when conditions (internal or external) require a disconnect.
+     *
+     * <p>Relevant in all states except the Disconnected state.
+     *
+     * <p>This signal is often fired with a timeout in order to prevent disconnecting during
+     * transient conditions, such as network switches. Upon the transient passing, the signal is
+     * canceled based on the disconnect reason.
+     *
+     * <p>Upon receipt of this signal, the state machine MUST tear down all active sessions, cancel
+     * any pending work items, and move to the Disconnected state.
+     *
+     * @param arg1 The "any" token; this signal is always honored.
+     * @param obj @NonNull An EventDisconnectRequestedInfo instance with relevant data.
+     */
+    private static final int EVENT_DISCONNECT_REQUESTED = 7;
+
+    private static class EventDisconnectRequestedInfo implements EventInfo {
+        /** The reason why the disconnect was requested. */
+        @NonNull public final String reason;
+
+        EventDisconnectRequestedInfo(@NonNull String reason) {
+            this.reason = Objects.requireNonNull(reason);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(reason);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventDisconnectRequestedInfo)) {
+                return false;
+            }
+
+            final EventDisconnectRequestedInfo rhs = (EventDisconnectRequestedInfo) other;
+            return reason.equals(rhs.reason);
+        }
+    }
+
+    /**
+     * Sent (delayed) to trigger a forcible close of an IKE session.
+     *
+     * <p>Only relevant in the Disconnecting state, discarded in all other states.
+     *
+     * <p>Upon receipt of this signal, the state machine will transition from the Disconnecting
+     * state to the Disconnected state.
+     *
+     * @param arg1 The session token for the IKE Session that is being torn down, used to prevent
+     *     out-of-date signals from propagating.
+     */
+    private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
+
+    @NonNull private final DisconnectedState mDisconnectedState = new DisconnectedState();
+    @NonNull private final DisconnectingState mDisconnectingState = new DisconnectingState();
+    @NonNull private final ConnectingState mConnectingState = new ConnectingState();
+    @NonNull private final ConnectedState mConnectedState = new ConnectedState();
+    @NonNull private final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
     @NonNull private final Dependencies mDeps;
 
+    @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
+
+    @NonNull private final IpSecManager mIpSecManager;
+    @NonNull private final IpSecTunnelInterface mTunnelIface;
+
+    /** Running state of this VcnGatewayConnection. */
+    private boolean mIsRunning = true;
+
+    /**
+     * The token used by the primary/current/active session.
+     *
+     * <p>This token MUST be updated when a new stateful/async session becomes the
+     * primary/current/active session. Example cases where the session changes are:
+     *
+     * <ul>
+     *   <li>Switching to an IKE session as the primary session
+     * </ul>
+     *
+     * <p>In the migrating state, where two sessions may be active, this value MUST represent the
+     * primary session. This is USUALLY the existing session, and is only switched to the new
+     * session when:
+     *
+     * <ul>
+     *   <li>The new session connects successfully, and becomes the primary session
+     *   <li>The existing session is lost, and the remaining (new) session becomes the primary
+     *       session
+     * </ul>
+     */
+    private int mCurrentToken = -1;
+
+    /**
+     * The next usable token.
+     *
+     * <p>A new token MUST be used for all new IKE sessions.
+     */
+    private int mNextToken = 0;
+
+    /**
+     * The number of unsuccessful attempts since the last successful connection.
+     *
+     * <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
+     * each time the Connected state is entered.
+     */
+    private int mFailedAttempts = 0;
+
+    /**
+     * The current underlying network.
+     *
+     * <p>Set in any states, always @NonNull in all states except Disconnected, null otherwise.
+     */
+    private UnderlyingNetworkRecord mUnderlying;
+
+    /**
+     * The active IKE session.
+     *
+     * <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
+     * Migrating states, null otherwise.
+     */
+    private IkeSession mIkeSession;
+
+    /**
+     * The last known child configuration.
+     *
+     * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
+     * states, @Nullable otherwise.
+     */
+    private ChildSessionConfiguration mChildConfig;
+
+    /**
+     * The active network agent.
+     *
+     * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
+     * otherwise.
+     */
+    private NetworkAgent mNetworkAgent;
+
     public VcnGatewayConnection(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
@@ -55,30 +460,350 @@
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnGatewayConnectionConfig connectionConfig,
             @NonNull Dependencies deps) {
-        super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
+        super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
 
+        mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
+
         mUnderlyingNetworkTracker =
-                mDeps.newUnderlyingNetworkTracker(mVcnContext, subscriptionGroup, this);
+                mDeps.newUnderlyingNetworkTracker(
+                        mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback);
+        mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
+
+        IpSecTunnelInterface iface;
+        try {
+            iface =
+                    mIpSecManager.createIpSecTunnelInterface(
+                            DUMMY_ADDR, DUMMY_ADDR, new Network(-1));
+        } catch (IOException | ResourceUnavailableException e) {
+            teardownAsynchronously();
+            mTunnelIface = null;
+
+            return;
+        }
+
+        mTunnelIface = iface;
+
+        addState(mDisconnectedState);
+        addState(mDisconnectingState);
+        addState(mConnectingState);
+        addState(mConnectedState);
+        addState(mRetryTimeoutState);
+
+        setInitialState(mDisconnectedState);
+        setDbg(VDBG);
+        start();
     }
 
-    /** Asynchronously tears down this GatewayConnection, and any resources used */
+    /**
+     * Asynchronously tears down this GatewayConnection, and any resources used.
+     *
+     * <p>Once torn down, this VcnTunnel CANNOT be started again.
+     */
     public void teardownAsynchronously() {
         mUnderlyingNetworkTracker.teardown();
+
+        // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
+        if (mTunnelIface != null) {
+            mTunnelIface.close();
+        }
+
+        sendMessage(
+                EVENT_DISCONNECT_REQUESTED,
+                TOKEN_ANY,
+                new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
+        quit();
+
+        // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
+        // is also called asynchronously when a NetworkAgent becomes unwanted
     }
 
-    private static class Dependencies {
+    private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
+        @Override
+        public void onSelectedUnderlyingNetworkChanged(
+                @Nullable UnderlyingNetworkRecord underlying) {
+            // If underlying is null, all underlying networks have been lost. Disconnect VCN after a
+            // timeout.
+            if (underlying == null) {
+                sendMessageDelayed(
+                        EVENT_DISCONNECT_REQUESTED,
+                        TOKEN_ANY,
+                        new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST),
+                        TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS));
+                return;
+            }
+
+            // Cancel any existing disconnect due to loss of underlying network
+            // getHandler() can return null if the state machine has already quit. Since this is
+            // called
+            // from other classes, this condition must be verified.
+            if (getHandler() != null) {
+                getHandler()
+                        .removeEqualMessages(
+                                EVENT_DISCONNECT_REQUESTED,
+                                new EventDisconnectRequestedInfo(
+                                        DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+            }
+            sendMessage(
+                    EVENT_UNDERLYING_NETWORK_CHANGED,
+                    TOKEN_ANY,
+                    new EventUnderlyingNetworkChangedInfo(underlying));
+        }
+    }
+
+    private void sendMessage(int what, int token, EventInfo data) {
+        super.sendMessage(what, token, ARG_NOT_PRESENT, data);
+    }
+
+    private void sendMessage(int what, int token, int arg2, EventInfo data) {
+        super.sendMessage(what, token, arg2, data);
+    }
+
+    private void sendMessageDelayed(int what, int token, EventInfo data, long timeout) {
+        super.sendMessageDelayed(what, token, ARG_NOT_PRESENT, data, timeout);
+    }
+
+    private void sendMessageDelayed(int what, int token, int arg2, EventInfo data, long timeout) {
+        super.sendMessageDelayed(what, token, arg2, data, timeout);
+    }
+
+    private void sessionLost(int token, @Nullable Exception exception) {
+        sendMessage(EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
+    }
+
+    private void sessionClosed(int token, @Nullable Exception exception) {
+        // SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the
+        // Disconnecting state.
+        sessionLost(token, exception);
+        sendMessage(EVENT_SESSION_CLOSED, token);
+    }
+
+    private void childTransformCreated(
+            int token, @NonNull IpSecTransform transform, int direction) {
+        sendMessage(
+                EVENT_TRANSFORM_CREATED,
+                token,
+                new EventTransformCreatedInfo(direction, transform));
+    }
+
+    private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
+        sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
+    }
+
+    private abstract class BaseState extends State {
+        protected void enterState() throws Exception {}
+
+        protected abstract void processStateMsg(Message msg) throws Exception;
+    }
+    /**
+     * State representing the a disconnected VCN tunnel.
+     *
+     * <p>This is also is the initial state.
+     */
+    private class DisconnectedState extends BaseState {
+        @Override
+        protected void processStateMsg(Message msg) {}
+    }
+
+    private abstract class ActiveBaseState extends BaseState {}
+
+    /**
+     * Transitive state representing a VCN that is tearing down an IKE session.
+     *
+     * <p>In this state, the IKE session is in the process of being torn down. If the IKE session
+     * does not complete teardown in a timely fashion, it will be killed (forcibly closed).
+     */
+    private class DisconnectingState extends ActiveBaseState {
+        @Override
+        protected void processStateMsg(Message msg) {}
+    }
+
+    /**
+     * Transitive state representing a VCN that is making an primary (non-handover) connection.
+     *
+     * <p>This state starts IKE negotiation, but defers transform application & network setup to the
+     * Connected state.
+     */
+    private class ConnectingState extends ActiveBaseState {
+        @Override
+        protected void processStateMsg(Message msg) {}
+    }
+
+    private abstract class ConnectedStateBase extends ActiveBaseState {}
+
+    /**
+     * Stable state representing a VCN that has a functioning connection to the mobility anchor.
+     *
+     * <p>This state handles IPsec transform application (initial and rekey), NetworkAgent setup,
+     * and monitors for mobility events.
+     */
+    class ConnectedState extends ConnectedStateBase {
+        @Override
+        protected void processStateMsg(Message msg) {}
+    }
+
+    /**
+     * Transitive state representing a VCN that failed to establish a connection, and will retry.
+     *
+     * <p>This state will be exited upon a new underlying network being found, or timeout expiry.
+     */
+    class RetryTimeoutState extends ActiveBaseState {
+        @Override
+        protected void processStateMsg(Message msg) {}
+    }
+
+    // TODO: Remove this when migrating to new NetworkAgent API
+    private static NetworkInfo buildNetworkInfo(boolean isConnected) {
+        NetworkInfo info =
+                new NetworkInfo(
+                        ConnectivityManager.TYPE_MOBILE,
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                        "MOBILE",
+                        "VCN");
+        info.setDetailedState(
+                isConnected ? DetailedState.CONNECTED : DetailedState.DISCONNECTED, null, null);
+
+        return info;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static NetworkCapabilities buildNetworkCapabilities(
+            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+        final NetworkCapabilities caps = new NetworkCapabilities();
+
+        caps.addTransportType(TRANSPORT_CELLULAR);
+        caps.addCapability(NET_CAPABILITY_NOT_CONGESTED);
+        caps.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+
+        // Add exposed capabilities
+        for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
+            caps.addCapability(cap);
+        }
+
+        return caps;
+    }
+
+    private static LinkProperties buildConnectedLinkProperties(
+            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
+            @NonNull IpSecTunnelInterface tunnelIface,
+            @NonNull ChildSessionConfiguration childConfig) {
+        final LinkProperties lp = new LinkProperties();
+
+        lp.setInterfaceName(tunnelIface.getInterfaceName());
+        for (LinkAddress addr : childConfig.getInternalAddresses()) {
+            lp.addLinkAddress(addr);
+        }
+        for (InetAddress addr : childConfig.getInternalDnsServers()) {
+            lp.addDnsServer(addr);
+        }
+
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
+
+        lp.setMtu(gatewayConnectionConfig.getMaxMtu());
+
+        return lp;
+    }
+
+    private class IkeSessionCallbackImpl implements IkeSessionCallback {
+        private final int mToken;
+
+        IkeSessionCallbackImpl(int token) {
+            mToken = token;
+        }
+
+        @Override
+        public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
+            Slog.v(TAG, "IkeOpened for token " + mToken);
+            // Nothing to do here.
+        }
+
+        @Override
+        public void onClosed() {
+            Slog.v(TAG, "IkeClosed for token " + mToken);
+            sessionClosed(mToken, null);
+        }
+
+        @Override
+        public void onClosedExceptionally(@NonNull IkeException exception) {
+            Slog.v(TAG, "IkeClosedExceptionally for token " + mToken, exception);
+            sessionClosed(mToken, exception);
+        }
+
+        @Override
+        public void onError(@NonNull IkeProtocolException exception) {
+            Slog.v(TAG, "IkeError for token " + mToken, exception);
+            // Non-fatal, log and continue.
+        }
+    }
+
+    private class ChildSessionCallbackImpl implements ChildSessionCallback {
+        private final int mToken;
+
+        ChildSessionCallbackImpl(int token) {
+            mToken = token;
+        }
+
+        @Override
+        public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
+            Slog.v(TAG, "ChildOpened for token " + mToken);
+            childOpened(mToken, childConfig);
+        }
+
+        @Override
+        public void onClosed() {
+            Slog.v(TAG, "ChildClosed for token " + mToken);
+            sessionLost(mToken, null);
+        }
+
+        @Override
+        public void onClosedExceptionally(@NonNull IkeException exception) {
+            Slog.v(TAG, "ChildClosedExceptionally for token " + mToken, exception);
+            sessionLost(mToken, exception);
+        }
+
+        @Override
+        public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
+            Slog.v(TAG, "ChildTransformCreated; Direction: " + direction + "; token " + mToken);
+            childTransformCreated(mToken, transform, direction);
+        }
+
+        @Override
+        public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
+            // Nothing to be done; no references to the IpSecTransform are held, and this transform
+            // will be closed by the IKE library.
+            Slog.v(TAG, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken);
+        }
+    }
+
+    /** External dependencies used by VcnGatewayConnection, for injection in tests. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class Dependencies {
+        /** Builds a new UnderlyingNetworkTracker. */
         public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
                 VcnContext vcnContext,
                 ParcelUuid subscriptionGroup,
                 UnderlyingNetworkTrackerCallback callback) {
             return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
         }
-    }
 
-    @Override
-    public void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying) {}
+        /** Builds a new IkeSession. */
+        public IkeSession newIkeSession(
+                VcnContext vcnContext,
+                IkeSessionParams ikeSessionParams,
+                ChildSessionParams childSessionParams,
+                IkeSessionCallback ikeSessionCallback,
+                ChildSessionCallback childSessionCallback) {
+            return new IkeSession(
+                    vcnContext.getContext(),
+                    ikeSessionParams,
+                    childSessionParams,
+                    new HandlerExecutor(new Handler(vcnContext.getLooper())),
+                    ikeSessionCallback,
+                    childSessionCallback);
+        }
+    }
 }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4f95696..e0db93a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -168,7 +168,6 @@
 
     static_libs: [
         "android.hardware.broadcastradio@common-utils-1x-lib",
-        "libservice-connectivity-static",
     ],
 
     product_variables: {
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 43f50bf..729fa71 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -99,47 +99,17 @@
     android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
 }
 
-static int get_current_max_fd() {
-    // Not actually guaranteed to be the max, but close enough for our purposes.
-    int fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
-    LOG_ALWAYS_FATAL_IF(fd == -1, "failed to open /dev/null: %s", strerror(errno));
-    close(fd);
-    return fd;
-}
+static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) {
+    raise(BIONIC_SIGNAL_FDTRACK);
 
-static const char kFdLeakEnableThresholdProperty[] = "persist.sys.debug.fdtrack_enable_threshold";
-static const char kFdLeakAbortThresholdProperty[] = "persist.sys.debug.fdtrack_abort_threshold";
-static const char kFdLeakCheckIntervalProperty[] = "persist.sys.debug.fdtrack_interval";
+    // Wait for a bit to allow fdtrack to dump backtraces to logcat.
+    std::this_thread::sleep_for(5s);
 
-static void android_server_SystemServer_spawnFdLeakCheckThread(JNIEnv*, jobject) {
+    // Abort on a different thread to avoid ART dumping runtime stacks.
     std::thread([]() {
-        pthread_setname_np(pthread_self(), "FdLeakCheckThread");
-        bool loaded = false;
-        while (true) {
-            const int enable_threshold = GetIntProperty(kFdLeakEnableThresholdProperty, 1024);
-            const int abort_threshold = GetIntProperty(kFdLeakAbortThresholdProperty, 2048);
-            const int check_interval = GetIntProperty(kFdLeakCheckIntervalProperty, 120);
-            int max_fd = get_current_max_fd();
-            if (max_fd > enable_threshold && !loaded) {
-                loaded = true;
-                ALOGE("fd count above threshold of %d, starting fd backtraces", enable_threshold);
-                if (dlopen("libfdtrack.so", RTLD_GLOBAL) == nullptr) {
-                    ALOGE("failed to load libfdtrack.so: %s", dlerror());
-                }
-            } else if (max_fd > abort_threshold) {
-                raise(BIONIC_SIGNAL_FDTRACK);
-
-                // Wait for a bit to allow fdtrack to dump backtraces to logcat.
-                std::this_thread::sleep_for(5s);
-
-                LOG_ALWAYS_FATAL(
-                    "b/140703823: aborting due to fd leak: check logs for fd "
-                    "backtraces");
-            }
-
-            std::this_thread::sleep_for(std::chrono::seconds(check_interval));
-        }
-    }).detach();
+        LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd "
+                         "backtraces");
+    }).join();
 }
 
 static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass,
@@ -161,8 +131,7 @@
         {"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices},
         {"initZygoteChildHeapProfiling", "()V",
          (void*)android_server_SystemServer_initZygoteChildHeapProfiling},
-        {"spawnFdLeakCheckThread", "()V",
-         (void*)android_server_SystemServer_spawnFdLeakCheckThread},
+        {"fdtrackAbort", "()V", (void*)android_server_SystemServer_fdtrackAbort},
         {"startIncrementalService", "()J",
          (void*)android_server_SystemServer_startIncrementalService},
         {"setIncrementalServiceSystemReady", "(J)V",
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 8cb3e6d..ccf685c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -40,8 +40,6 @@
 int register_android_server_vr_VrManagerService(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
-int register_android_server_connectivity_Vpn(JNIEnv* env);
-int register_android_server_TestNetworkService(JNIEnv* env);
 int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
 int register_android_server_tv_TvUinputBridge(JNIEnv* env);
 int register_android_server_tv_TvInputHal(JNIEnv* env);
@@ -93,8 +91,6 @@
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GnssLocationProvider(env);
-    register_android_server_connectivity_Vpn(env);
-    register_android_server_TestNetworkService(env);
     register_android_server_devicepolicy_CryptoTestHelper(env);
     register_android_server_ConsumerIrService(env);
     register_android_server_BatteryStatsService(env);
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
index 9924708..a62e2c3 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -31,6 +31,7 @@
                 <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
                 <xs:attribute type="xs:int" name="enableSinceTargetSdk"/>
                 <xs:attribute type="xs:string" name="description"/>
+                <xs:attribute type="xs:boolean" name="overridable"/>
             </xs:extension>
         </xs:simpleContent>
     </xs:complexType>
@@ -48,7 +49,3 @@
         </xs:unique>
     </xs:element>
 </xs:schema>
-
-
-
-
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
index e3640ed..fb8bbef 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -10,6 +10,7 @@
     method public long getId();
     method public boolean getLoggingOnly();
     method public String getName();
+    method public boolean getOverridable();
     method public String getValue();
     method public void setDescription(String);
     method public void setDisabled(boolean);
@@ -18,6 +19,7 @@
     method public void setId(long);
     method public void setLoggingOnly(boolean);
     method public void setName(String);
+    method public void setOverridable(boolean);
     method public void setValue(String);
   }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 59b24f8..516c642 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -23,6 +23,8 @@
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myPid;
+import static android.system.OsConstants.O_CLOEXEC;
+import static android.system.OsConstants.O_RDONLY;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG;
@@ -75,6 +77,8 @@
 import android.provider.Settings;
 import android.server.ServerProtoEnums;
 import android.sysprop.VoldProperties;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -188,6 +192,7 @@
 import com.google.android.startop.iorap.IorapForwardingService;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.LinkedList;
 import java.util.Locale;
@@ -221,6 +226,8 @@
             "com.android.server.companion.CompanionDeviceManagerService";
     private static final String STATS_COMPANION_APEX_PATH =
             "/apex/com.android.os.statsd/javalib/service-statsd.jar";
+    private static final String CONNECTIVITY_SERVICE_APEX_PATH =
+            "/apex/com.android.tethering/javalib/service-connectivity.jar";
     private static final String STATS_COMPANION_LIFECYCLE_CLASS =
             "com.android.server.stats.StatsCompanion$Lifecycle";
     private static final String STATS_PULL_ATOM_SERVICE_CLASS =
@@ -392,11 +399,71 @@
      */
     private static native void initZygoteChildHeapProfiling();
 
+    private static final String SYSPROP_FDTRACK_ENABLE_THRESHOLD =
+            "persist.sys.debug.fdtrack_enable_threshold";
+    private static final String SYSPROP_FDTRACK_ABORT_THRESHOLD =
+            "persist.sys.debug.fdtrack_abort_threshold";
+    private static final String SYSPROP_FDTRACK_INTERVAL =
+            "persist.sys.debug.fdtrack_interval";
+
+    private static int getMaxFd() {
+        FileDescriptor fd = null;
+        try {
+            fd = Os.open("/dev/null", O_RDONLY | O_CLOEXEC, 0);
+            return fd.getInt$();
+        } catch (ErrnoException ex) {
+            Slog.e("System", "Failed to get maximum fd: " + ex);
+        } finally {
+            if (fd != null) {
+                try {
+                    Os.close(fd);
+                } catch (ErrnoException ex) {
+                    // If Os.close threw, something went horribly wrong.
+                    throw new RuntimeException(ex);
+                }
+            }
+        }
+
+        return Integer.MAX_VALUE;
+    }
+
+    private static native void fdtrackAbort();
 
     /**
      * Spawn a thread that monitors for fd leaks.
      */
-    private static native void spawnFdLeakCheckThread();
+    private static void spawnFdLeakCheckThread() {
+        final int enableThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ENABLE_THRESHOLD, 1024);
+        final int abortThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ABORT_THRESHOLD, 2048);
+        final int checkInterval = SystemProperties.getInt(SYSPROP_FDTRACK_INTERVAL, 120);
+
+        new Thread(() -> {
+            boolean enabled = false;
+            while (true) {
+                int maxFd = getMaxFd();
+                if (maxFd > enableThreshold) {
+                    // Do a manual GC to clean up fds that are hanging around as garbage.
+                    System.gc();
+                    maxFd = getMaxFd();
+                }
+
+                if (maxFd > enableThreshold && !enabled) {
+                    Slog.i("System", "fdtrack enable threshold reached, enabling");
+                    System.loadLibrary("fdtrack");
+                    enabled = true;
+                } else if (maxFd > abortThreshold) {
+                    Slog.i("System", "fdtrack abort threshold reached, dumping and aborting");
+                    fdtrackAbort();
+                }
+
+                try {
+                    Thread.sleep(checkInterval);
+                } catch (InterruptedException ex) {
+                    continue;
+                }
+            }
+        }).start();
+    }
 
     /**
      * Start native Incremental Service and get its handle.
@@ -1561,8 +1628,8 @@
             // This has to be called after NetworkManagementService, NetworkStatsService
             // and NetworkPolicyManager because ConnectivityService needs to take these
             // services to initialize.
-            // TODO: Dynamically load service-connectivity.jar by using startServiceFromJar.
-            mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_CLASS);
+            mSystemServiceManager.startServiceFromJar(CONNECTIVITY_SERVICE_INITIALIZER_CLASS,
+                    CONNECTIVITY_SERVICE_APEX_PATH);
             connectivity = IConnectivityManager.Stub.asInterface(
                     ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
             // TODO: Use ConnectivityManager instead of ConnectivityService.
diff --git a/services/net/Android.bp b/services/net/Android.bp
index eaf177e..e0bb67a 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -13,7 +13,7 @@
         ":services.net-sources",
     ],
     static_libs: [
-        "netd_aidl_interfaces-platform-java",
+        "netd-client",
         "netlink-client",
         "networkstack-client",
         "net-utils-services-common",
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index e779e21..c0f0ce0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -1 +1,3 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
+per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
+per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS
new file mode 100644
index 0000000..c2e27e0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/apphibernation/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index 4f4aa3f..f00edcc 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -40,78 +40,83 @@
     }
 
     CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, ""));
+        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) {
-        mChanges.add(new CompatChange(id, name, sdk, -1, false, false, ""));
+        mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", sdk, -1, true, false, ""));
+        mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id,
             String description) {
-        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description));
+        mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description, false));
         return this;
     }
 
     CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, sdk, false, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) {
-        mChanges.add(new CompatChange(id, "", -1, sdk, true, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id,
             String description) {
-        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description));
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description, false));
         return this;
     }
 
     CompatConfigBuilder addEnabledChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, -1, false, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, -1, false, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, -1, false, false, "", false));
         return this;
     }
     CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) {
-        mChanges.add(new CompatChange(id, "", -1, -1, false, false, description));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, false, description, false));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, -1, true, false, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) {
-        mChanges.add(new CompatChange(id, name, -1, -1, true, false, ""));
+        mChanges.add(new CompatChange(id, name, -1, -1, true, false, "", false));
         return this;
     }
 
     CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) {
-        mChanges.add(new CompatChange(id, "", -1, -1, true, false, description));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, description, false));
         return this;
     }
 
     CompatConfigBuilder addLoggingOnlyChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, -1, false, true, ""));
+        mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", false));
+        return this;
+    }
+
+    CompatConfigBuilder addOverridableChangeWithId(long id) {
+        mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true));
         return this;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index a70c510..a1b2dc8 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -97,17 +97,22 @@
                 .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
                 .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
+                .addOverridableChangeWithId(8L)
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
-                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
-                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
+                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
+                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
                 new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false,
-                        "desc"),
-                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
-                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""),
-                new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""),
-                new CompatibilityChangeInfo(7L, "", -1, -1, false, true, ""));
+                        "desc", false),
+                new CompatibilityChangeInfo(
+                        4L, "", Build.VERSION_CODES.P, -1, false, false, "", false),
+                new CompatibilityChangeInfo(
+                        5L, "", Build.VERSION_CODES.Q, -1, false, false, "", false),
+                new CompatibilityChangeInfo(
+                        6L, "", Build.VERSION_CODES.R, -1, false, false, "", false),
+                new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false),
+                new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true));
     }
 
     @Test
@@ -123,12 +128,12 @@
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
-                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
-                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
-                new CompatibilityChangeInfo(5L, "", /*enableAfter*/ -1,
-                        /*enableSince*/ Build.VERSION_CODES.Q, false, false, ""),
-                new CompatibilityChangeInfo(6L, "", /*enableAfter*/ -1,
-                        /*enableSince*/ Build.VERSION_CODES.R, false, false, ""));
+                new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
+                new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
+                new CompatibilityChangeInfo(
+                        5L, "", Build.VERSION_CODES.P, -1, false, false, "", false),
+                new CompatibilityChangeInfo(
+                        6L, "", Build.VERSION_CODES.Q, -1, false, false, "", false));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS b/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS
new file mode 100644
index 0000000..34ac813
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
index 46f43e7..32445fd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
@@ -19,22 +19,44 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.security.GeneralSecurityException;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
 /**
  * atest FrameworksServicesTests:RebootEscrowDataTest
  */
 @RunWith(AndroidJUnit4.class)
 public class RebootEscrowDataTest {
     private RebootEscrowKey mKey;
+    private SecretKey mKeyStoreEncryptionKey;
+
+    private SecretKey generateNewRebootEscrowEncryptionKey() throws GeneralSecurityException {
+        KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
+        generator.init(new KeyGenParameterSpec.Builder(
+                "reboot_escrow_data_test_key",
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setKeySize(256)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .build());
+        return generator.generateKey();
+    }
 
     @Before
     public void generateKey() throws Exception {
         mKey = RebootEscrowKey.generate();
+        mKeyStoreEncryptionKey = generateNewRebootEscrowEncryptionKey();
     }
 
     private static byte[] getTestSp() {
@@ -47,36 +69,49 @@
 
     @Test(expected = NullPointerException.class)
     public void fromEntries_failsOnNull() throws Exception {
-        RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, null);
+        RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, null, mKeyStoreEncryptionKey);
     }
 
     @Test(expected = NullPointerException.class)
     public void fromEncryptedData_failsOnNullData() throws Exception {
         byte[] testSp = getTestSp();
-        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp);
+        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp,
+                mKeyStoreEncryptionKey);
         RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes());
-        RebootEscrowData.fromEncryptedData(key, null);
+        RebootEscrowData.fromEncryptedData(key, null, mKeyStoreEncryptionKey);
     }
 
     @Test(expected = NullPointerException.class)
     public void fromEncryptedData_failsOnNullKey() throws Exception {
         byte[] testSp = getTestSp();
-        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp);
-        RebootEscrowData.fromEncryptedData(null, expected.getBlob());
+        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp,
+                mKeyStoreEncryptionKey);
+        RebootEscrowData.fromEncryptedData(null, expected.getBlob(), mKeyStoreEncryptionKey);
     }
 
     @Test
     public void fromEntries_loopback_success() throws Exception {
         byte[] testSp = getTestSp();
-        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp);
+        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp,
+                mKeyStoreEncryptionKey);
 
         RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes());
-        RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob());
+        RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob(),
+                mKeyStoreEncryptionKey);
 
         assertThat(actual.getSpVersion(), is(expected.getSpVersion()));
-        assertThat(actual.getIv(), is(expected.getIv()));
         assertThat(actual.getKey().getKeyBytes(), is(expected.getKey().getKeyBytes()));
         assertThat(actual.getBlob(), is(expected.getBlob()));
         assertThat(actual.getSyntheticPassword(), is(expected.getSyntheticPassword()));
     }
+
+    @Test
+    public void aesEncryptedBlob_loopback_success() throws Exception {
+        byte[] testSp = getTestSp();
+        byte [] encrypted = AesEncryptionUtil.encrypt(mKeyStoreEncryptionKey, testSp);
+        byte [] decrypted = AesEncryptionUtil.decrypt(mKeyStoreEncryptionKey, encrypted);
+
+        assertThat(decrypted, is(testSp));
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 98d6452..f74e45b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -61,6 +61,9 @@
 import java.io.File;
 import java.util.ArrayList;
 
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -77,15 +80,25 @@
             0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
     };
 
+    // Hex encoding of a randomly generated AES key for test.
+    private static final byte[] TEST_AES_KEY = new byte[] {
+            0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+            0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+            0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+            0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+    };
+
     private Context mContext;
     private UserManager mUserManager;
     private RebootEscrowManager.Callbacks mCallbacks;
     private IRebootEscrow mRebootEscrow;
+    private RebootEscrowKeyStoreManager mKeyStoreManager;
 
     LockSettingsStorageTestable mStorage;
 
     private MockableRebootEscrowInjected mInjected;
     private RebootEscrowManager mService;
+    private SecretKey mAesKey;
 
     public interface MockableRebootEscrowInjected {
         int getBootCount();
@@ -98,9 +111,11 @@
         private final RebootEscrowProviderInterface mRebootEscrowProvider;
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
+        private final RebootEscrowKeyStoreManager mKeyStoreManager;
 
         MockInjector(Context context, UserManager userManager,
                 IRebootEscrow rebootEscrow,
+                RebootEscrowKeyStoreManager keyStoreManager,
                 MockableRebootEscrowInjected injected) {
             super(context);
             mRebootEscrow = rebootEscrow;
@@ -114,6 +129,7 @@
                     };
             mRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
             mUserManager = userManager;
+            mKeyStoreManager = keyStoreManager;
             mInjected = injected;
         }
 
@@ -128,6 +144,11 @@
         }
 
         @Override
+        public RebootEscrowKeyStoreManager getKeyStoreManager() {
+            return mKeyStoreManager;
+        }
+
+        @Override
         public int getBootCount() {
             return mInjected.getBootCount();
         }
@@ -144,6 +165,11 @@
         mUserManager = mock(UserManager.class);
         mCallbacks = mock(RebootEscrowManager.Callbacks.class);
         mRebootEscrow = mock(IRebootEscrow.class);
+        mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
+        mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
+
+        when(mKeyStoreManager.getKeyStoreEncryptionKey()).thenReturn(mAesKey);
+        when(mKeyStoreManager.generateKeyStoreEncryptionKeyIfNeeded()).thenReturn(mAesKey);
 
         mStorage = new LockSettingsStorageTestable(mContext,
                 new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
@@ -160,7 +186,7 @@
         when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
         mInjected = mock(MockableRebootEscrowInjected.class);
         mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
-                mInjected), mCallbacks, mStorage);
+                mKeyStoreManager, mInjected), mCallbacks, mStorage);
     }
 
     @Test
@@ -213,6 +239,7 @@
         assertNotNull(
                 mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
         verify(mRebootEscrow).storeKey(any());
+        verify(mKeyStoreManager).getKeyStoreEncryptionKey();
 
         assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
         assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
@@ -300,6 +327,7 @@
         ArgumentCaptor<byte[]> keyByteCaptor = ArgumentCaptor.forClass(byte[].class);
         assertTrue(mService.armRebootEscrowIfNeeded());
         verify(mRebootEscrow).storeKey(keyByteCaptor.capture());
+        verify(mKeyStoreManager).getKeyStoreEncryptionKey();
 
         assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
         assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
@@ -314,6 +342,7 @@
         mService.loadRebootEscrowDataIfAvailable();
         verify(mRebootEscrow).retrieveKey();
         assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
     }
 
     @Test
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 fec0273..4db7ce2 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -16,13 +16,18 @@
 
 package com.android.server.net;
 
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 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.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;
@@ -34,6 +39,7 @@
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
@@ -74,6 +80,7 @@
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -97,6 +104,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
+import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
@@ -123,6 +131,7 @@
 import android.os.SimpleClock;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -131,6 +140,7 @@
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Log;
 import android.util.Pair;
@@ -187,6 +197,7 @@
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.TimeZone;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -240,6 +251,7 @@
     private @Mock SubscriptionManager mSubscriptionManager;
     private @Mock CarrierConfigManager mCarrierConfigManager;
     private @Mock TelephonyManager mTelephonyManager;
+    private @Mock UserManager mUserManager;
 
     private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
@@ -351,6 +363,8 @@
                         return mNotifManager;
                     case Context.CONNECTIVITY_SERVICE:
                         return mConnectivityManager;
+                    case Context.USER_SERVICE:
+                        return mUserManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -407,11 +421,14 @@
         when(mPackageManager.getPackagesForUid(UID_B)).thenReturn(new String[] {PKG_NAME_B});
         when(mPackageManager.getPackagesForUid(UID_C)).thenReturn(new String[] {PKG_NAME_C});
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_A), anyInt()))
-                .thenReturn(buildApplicationInfo(PKG_NAME_A));
+                .thenReturn(buildApplicationInfo(PKG_NAME_A, UID_A));
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_B), anyInt()))
-                .thenReturn(buildApplicationInfo(PKG_NAME_B));
+                .thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B));
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt()))
-                .thenReturn(buildApplicationInfo(PKG_NAME_C));
+                .thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C));
+        when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
+                buildInstalledApplicationInfoList());
+        when(mUserManager.getUsers()).thenReturn(buildUserInfoList());
         when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
         when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
         doNothing().when(mConnectivityManager)
@@ -1874,6 +1891,66 @@
         }
     }
 
+    private void enableRestrictedMode(boolean enable) throws Exception {
+        mService.mRestrictedNetworkingMode = enable;
+        mService.updateRestrictedModeAllowlistUL();
+        verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_RESTRICTED,
+                enable);
+    }
+
+    @Test
+    public void testUpdateRestrictedModeAllowlist() throws Exception {
+        // initialization calls setFirewallChainEnabled, so we want to reset the invocations.
+        clearInvocations(mNetworkManager);
+        expectHasUseRestrictedNetworksPermission(UID_A, true);
+        expectHasUseRestrictedNetworksPermission(UID_B, false);
+
+        Map<Integer, Integer> firewallUidRules = new ArrayMap<>();
+        doAnswer(arg -> {
+            int[] uids = arg.getArgument(1);
+            int[] rules = arg.getArgument(2);
+            assertTrue(uids.length == rules.length);
+
+            for (int i = 0; i < uids.length; ++i) {
+                firewallUidRules.put(uids[i], rules[i]);
+            }
+            return null;
+        }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_RESTRICTED),
+                any(int[].class), any(int[].class));
+
+        enableRestrictedMode(true);
+        assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A).intValue());
+        assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+        assertTrue(mService.isUidNetworkingBlocked(UID_B, false));
+
+        enableRestrictedMode(false);
+        assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+        assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
+    }
+
+    @Test
+    public void testUpdateRestrictedModeForUid() throws Exception {
+        // initialization calls setFirewallChainEnabled, so we want to reset the invocations.
+        clearInvocations(mNetworkManager);
+        expectHasUseRestrictedNetworksPermission(UID_A, true);
+        expectHasUseRestrictedNetworksPermission(UID_B, false);
+        enableRestrictedMode(true);
+
+        // UID_D and UID_E are not part of installed applications list, so it won't have any
+        // firewall rules set yet
+        expectHasUseRestrictedNetworksPermission(UID_D, false);
+        mService.updateRestrictedModeForUidUL(UID_D);
+        verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_D,
+                FIREWALL_RULE_DEFAULT);
+        assertTrue(mService.isUidNetworkingBlocked(UID_D, false));
+
+        expectHasUseRestrictedNetworksPermission(UID_E, true);
+        mService.updateRestrictedModeForUidUL(UID_E);
+        verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_E,
+                FIREWALL_RULE_ALLOW);
+        assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
+    }
+
     private String formatBlockedStateError(int uid, int rule, boolean metered,
             boolean backgroundRestricted) {
         return String.format(
@@ -1888,12 +1965,27 @@
                 .build();
     }
 
-    private ApplicationInfo buildApplicationInfo(String label) {
+    private ApplicationInfo buildApplicationInfo(String label, int uid) {
         final ApplicationInfo ai = new ApplicationInfo();
         ai.nonLocalizedLabel = label;
+        ai.uid = uid;
         return ai;
     }
 
+    private List<ApplicationInfo> buildInstalledApplicationInfoList() {
+        final List<ApplicationInfo> installedApps = new ArrayList<>();
+        installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A));
+        installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B));
+        installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C));
+        return installedApps;
+    }
+
+    private List<UserInfo> buildUserInfoList() {
+        final List<UserInfo> users = new ArrayList<>();
+        users.add(new UserInfo(USER_ID, "user1", 0));
+        return users;
+    }
+
     private NetworkInfo buildNetworkInfo() {
         final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
                 TelephonyManager.NETWORK_TYPE_LTE, null, null);
@@ -1967,6 +2059,15 @@
                 hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
     }
 
+    private void expectHasUseRestrictedNetworksPermission(int uid, boolean hasIt) throws Exception {
+        when(mIpm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)).thenReturn(
+                hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+        when(mIpm.checkUidPermission(NETWORK_STACK, uid)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+        when(mIpm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+    }
+
     private void expectNetworkState(boolean roaming) throws Exception {
         when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
                 .thenReturn(mCarrierConfig);
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
index 9c8a382..ac9316e 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
@@ -24,6 +24,9 @@
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetdEventCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -106,6 +109,16 @@
             counter--;
             return true;
         }
+
+        // TODO: mark @Override when aosp/1541935 automerges to master.
+        public void logDefaultNetworkValidity(boolean valid) {
+        }
+
+        // TODO: mark @Override when aosp/1541935 automerges to master.
+        public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated,
+                LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork,
+                int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) {
+        }
     };
 
     ServiceThread mHandlerThread;
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index 5468fba..391611b 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -78,7 +78,7 @@
     }
 
     @Test
-    public void testImmutableEnabledChange() throws Exception {
+    public void testImmutableEnabledChange() {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -106,7 +106,7 @@
     }
 
     @Test
-    public void testMutableEnabledChangeHasNoEffect() throws Exception {
+    public void testMutableEnabledChangeHasNoEffect() {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -134,7 +134,7 @@
     }
 
     @Test
-    public void testMutableEnabledToImmutableEnabled() throws Exception {
+    public void testMutableEnabledToImmutableEnabled() {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -178,7 +178,7 @@
     }
 
     @Test
-    public void testMutablePriorityChange() throws Exception {
+    public void testMutablePriorityChange() {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -218,7 +218,7 @@
     }
 
     @Test
-    public void testImmutablePriorityChange() throws Exception {
+    public void testImmutablePriorityChange() {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 33dbcc0..4f882ce 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -22,14 +22,11 @@
 import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.testng.Assert.assertThrows;
 
 import android.content.om.OverlayInfo;
-import android.util.Pair;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -38,7 +35,6 @@
 
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
@@ -59,7 +55,7 @@
     private static final String CERT_CONFIG_NOK = "config_certificate_nok";
 
     @Test
-    public void testGetOverlayInfo() throws Exception {
+    public void testGetOverlayInfo() {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
 
         final OverlayManagerServiceImpl impl = getImpl();
@@ -71,7 +67,7 @@
     }
 
     @Test
-    public void testGetOverlayInfosForTarget() throws Exception {
+    public void testGetOverlayInfosForTarget() {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
         installNewPackage(overlay(OVERLAY3, TARGET), USER2);
@@ -96,7 +92,7 @@
     }
 
     @Test
-    public void testGetOverlayInfosForUser() throws Exception {
+    public void testGetOverlayInfosForUser() {
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
@@ -123,7 +119,7 @@
     }
 
     @Test
-    public void testPriority() throws Exception {
+    public void testPriority() {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
         installNewPackage(overlay(OVERLAY3, TARGET), USER);
@@ -135,21 +131,18 @@
 
         assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
-        assertEquals(impl.setLowestPriority(OVERLAY3, USER),
-                Optional.of(new PackageAndUser(TARGET, USER)));
+        assertTrue(impl.setLowestPriority(OVERLAY3, USER));
         assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
 
-        assertEquals(impl.setHighestPriority(OVERLAY3, USER),
-                Optional.of(new PackageAndUser(TARGET, USER)));
+        assertTrue(impl.setHighestPriority(OVERLAY3, USER));
         assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
-        assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
-                Optional.of(new PackageAndUser(TARGET, USER)));
+        assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
         assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
     }
 
     @Test
-    public void testOverlayInfoStateTransitions() throws Exception {
+    public void testOverlayInfoStateTransitions() {
         final OverlayManagerServiceImpl impl = getImpl();
         assertNull(impl.getOverlayInfo(OVERLAY, USER));
 
@@ -160,8 +153,7 @@
         installNewPackage(target, USER);
         assertState(STATE_DISABLED, OVERLAY, USER);
 
-        assertEquals(impl.setEnabled(OVERLAY, true, USER),
-                Optional.of(new PackageAndUser(TARGET, USER)));
+        impl.setEnabled(OVERLAY, true, USER);
         assertState(STATE_ENABLED, OVERLAY, USER);
 
         // target upgrades do not change the state of the overlay
@@ -176,40 +168,50 @@
     }
 
     @Test
-    public void testOnOverlayPackageUpgraded() throws Exception {
+    public void testOnOverlayPackageUpgraded() {
+        final FakeListener listener = getListener();
         final FakeDeviceState.PackageBuilder target = target(TARGET);
         final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
         installNewPackage(target, USER);
         installNewPackage(overlay, USER);
+        listener.count = 0;
         upgradePackage(overlay, USER);
+        assertEquals(2, listener.count);
 
         // upgrade to a version where the overlay has changed its target
+        // expect once for the old target package, once for the new target package
+        listener.count = 0;
         final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
-        final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
-                upgradePackage(overlay2, USER);
-        assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
-        assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
+        upgradePackage(overlay2, USER);
+        assertEquals(3, listener.count);
+
+        listener.count = 0;
+        upgradePackage(overlay2, USER);
+        assertEquals(2, listener.count);
     }
 
     @Test
-    public void testSetEnabledAtVariousConditions() throws Exception {
+    public void testListener() {
         final OverlayManagerServiceImpl impl = getImpl();
-        assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
-                () -> impl.setEnabled(OVERLAY, true, USER));
-
-        // request succeeded, and there was a change that needs to be
-        // propagated to the rest of the system
-        installNewPackage(target(TARGET), USER);
+        final FakeListener listener = getListener();
         installNewPackage(overlay(OVERLAY, TARGET), USER);
-        assertEquals(impl.setEnabled(OVERLAY, true, USER),
-                Optional.of(new PackageAndUser(TARGET, USER)));
+        assertEquals(1, listener.count);
+        listener.count = 0;
 
-        // request succeeded, but nothing changed
-        assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
+        installNewPackage(target(TARGET), USER);
+        assertEquals(1, listener.count);
+        listener.count = 0;
+
+        impl.setEnabled(OVERLAY, true, USER);
+        assertEquals(1, listener.count);
+        listener.count = 0;
+
+        impl.setEnabled(OVERLAY, true, USER);
+        assertEquals(0, listener.count);
     }
 
     @Test
-    public void testConfigSignaturePolicyOk() throws Exception {
+    public void testConfigSignaturePolicyOk() {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
@@ -227,7 +229,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyCertNok() throws Exception {
+    public void testConfigSignaturePolicyCertNok() {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
@@ -245,7 +247,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyNoConfig() throws Exception {
+    public void testConfigSignaturePolicyNoConfig() {
         addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -260,7 +262,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyNoRefPkg() throws Exception {
+    public void testConfigSignaturePolicyNoRefPkg() {
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
@@ -274,7 +276,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
+    public void testConfigSignaturePolicyRefPkgNotSystem() {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 2c477c8..006dda0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -16,8 +16,6 @@
 
 package com.android.server.om;
 
-import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
-
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
@@ -32,7 +30,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Pair;
 
 import androidx.annotation.Nullable;
 
@@ -46,13 +43,13 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
 class OverlayManagerServiceImplTestsBase {
     private OverlayManagerServiceImpl mImpl;
     private FakeDeviceState mState;
+    private FakeListener mListener;
     private FakePackageManagerHelper mPackageManager;
     private FakeIdmapDaemon mIdmapDaemon;
     private OverlayConfig mOverlayConfig;
@@ -61,6 +58,7 @@
     @Before
     public void setUp() {
         mState = new FakeDeviceState();
+        mListener = new FakeListener();
         mPackageManager = new FakePackageManagerHelper(mState);
         mIdmapDaemon = new FakeIdmapDaemon(mState);
         mOverlayConfig = mock(OverlayConfig.class);
@@ -75,13 +73,18 @@
                 new IdmapManager(mIdmapDaemon, mPackageManager),
                 new OverlayManagerSettings(),
                 mOverlayConfig,
-                new String[0]);
+                new String[0],
+                mListener);
     }
 
     OverlayManagerServiceImpl getImpl() {
         return mImpl;
     }
 
+    FakeListener getListener() {
+        return mListener;
+    }
+
     FakeIdmapDaemon getIdmapd() {
         return mIdmapDaemon;
     }
@@ -152,8 +155,7 @@
      *
      * @throws IllegalStateException if the package is currently installed
      */
-    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
-            throws OperationFailedException {
+    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
         if (mState.select(pkg.packageName, userId) != null) {
             throw new IllegalStateException("package " + pkg.packageName + " already installed");
         }
@@ -174,30 +176,23 @@
      * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
      * {@link android.content.Intent#EXTRA_REPLACING} extra.
      *
-     * @return the two Optional<PackageAndUser> objects from starting and finishing the upgrade
-     *
      * @throws IllegalStateException if the package is not currently installed
      */
-    Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
-            FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
+    void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
         final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
         if (replacedPackage == null) {
             throw new IllegalStateException("package " + pkg.packageName + " not installed");
         }
-        Optional<PackageAndUser> opt1 = Optional.empty();
         if (replacedPackage.targetPackageName != null) {
-            opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
+            mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
         }
 
         mState.add(pkg, userId);
-        Optional<PackageAndUser> opt2;
         if (pkg.targetPackage == null) {
-            opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
+            mImpl.onTargetPackageReplaced(pkg.packageName, userId);
         } else {
-            opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
+            mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
         }
-
-        return Pair.create(opt1, opt2);
     }
 
     /**
@@ -208,7 +203,7 @@
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void uninstallPackage(String packageName, int userId) throws OperationFailedException {
+    void uninstallPackage(String packageName, int userId) {
         final FakeDeviceState.Package pkg = mState.select(packageName, userId);
         if (pkg == null) {
             throw new IllegalStateException("package " + packageName+ " not installed");
@@ -490,4 +485,12 @@
             }
         }
     }
+
+    static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
+        public int count;
+
+        public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
+            count++;
+        }
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 98b9dcd..477592b 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -170,7 +170,10 @@
     // Delay for debouncing USB disconnects.
     // We often get rapid connect/disconnect events when enabling USB functions,
     // which need debouncing.
-    private static final int UPDATE_DELAY = 1000;
+    private static final int DEVICE_STATE_UPDATE_DELAY = 3000;
+
+    // Delay for debouncing USB disconnects on Type-C ports in host mode
+    private static final int HOST_STATE_UPDATE_DELAY = 1000;
 
     // Timeout for entering USB request mode.
     // Request is cancelled if host does not configure device within 10 seconds.
@@ -583,7 +586,7 @@
             msg.arg1 = connected;
             msg.arg2 = configured;
             // debounce disconnects to avoid problems bringing up USB tethering
-            sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
+            sendMessageDelayed(msg, (connected == 0) ? DEVICE_STATE_UPDATE_DELAY : 0);
         }
 
         public void updateHostState(UsbPort port, UsbPortStatus status) {
@@ -598,7 +601,7 @@
             removeMessages(MSG_UPDATE_PORT_STATE);
             Message msg = obtainMessage(MSG_UPDATE_PORT_STATE, args);
             // debounce rapid transitions of connect/disconnect on type-c ports
-            sendMessageDelayed(msg, UPDATE_DELAY);
+            sendMessageDelayed(msg, HOST_STATE_UPDATE_DELAY);
         }
 
         private void setAdbEnabled(boolean enable) {
diff --git a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
new file mode 100644
index 0000000..c7e7cd5
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
@@ -0,0 +1,238 @@
+/*
+ * 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.internal.telephony;
+
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Utility methods for parsing parts of {@link android.telephony.ims.SipMessage}s.
+ * See RFC 3261 for more information.
+ * @hide
+ */
+// Note: This is lightweight in order to avoid a full SIP stack import in frameworks/base.
+public class SipMessageParsingUtils {
+    private static final String TAG = "SipMessageParsingUtils";
+    // "Method" in request-line
+    // Request-Line = Method SP Request-URI SP SIP-Version CRLF
+    private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
+            "BYE", "CANCEL", "REGISTER", "PRACK", "SUBSCRIBE", "NOTIFY", "PUBLISH", "INFO", "REFER",
+            "MESSAGE", "UPDATE"};
+
+    // SIP Version 2.0 (corresponding to RCS 3261), set in "SIP-Version" of Status-Line and
+    // Request-Line
+    //
+    // Request-Line = Method SP Request-URI SP SIP-Version CRLF
+    // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
+    private static final String SIP_VERSION_2 = "SIP/2.0";
+
+    // headers are formatted Key:Value
+    private static final String HEADER_KEY_VALUE_SEPARATOR = ":";
+    // Multiple of the same header can be concatenated and put into one header Key:Value pair, for
+    // example "v: XX1;branch=YY1,XX2;branch=YY2". This needs to be treated as two "v:" headers.
+    private static final String SUBHEADER_VALUE_SEPARATOR = ",";
+
+    // SIP header parameters have the format ";paramName=paramValue"
+    private static final String PARAM_SEPARATOR = ";";
+    // parameters are formatted paramName=ParamValue
+    private static final String PARAM_KEY_VALUE_SEPARATOR = "=";
+
+    // The via branch parameter definition
+    private static final String BRANCH_PARAM_KEY = "branch";
+
+    // via header key
+    private static final String VIA_SIP_HEADER_KEY = "via";
+    // compact form of the via header key
+    private static final String VIA_SIP_HEADER_KEY_COMPACT = "v";
+
+    /**
+     * @return true if the SIP message start line is considered a request (based on known request
+     * methods).
+     */
+    public static boolean isSipRequest(String startLine) {
+        String[] splitLine = splitStartLineAndVerify(startLine);
+        if (splitLine == null) return false;
+        return verifySipRequest(splitLine);
+    }
+
+    /**
+     * Return the via branch parameter, which is used to identify the transaction ID (request and
+     * response pair) in a SIP transaction.
+     * @param headerString The string containing the headers of the SIP message.
+     */
+    public static String getTransactionId(String headerString) {
+        // search for Via: or v: parameter, we only care about the first one.
+        List<Pair<String, String>> headers = parseHeaders(headerString, true,
+                VIA_SIP_HEADER_KEY, VIA_SIP_HEADER_KEY_COMPACT);
+        for (Pair<String, String> header : headers) {
+            // Headers can also be concatenated together using a "," between each header value.
+            // format becomes v: XX1;branch=YY1,XX2;branch=YY2. Need to extract only the first ID's
+            // branch param YY1.
+            String[] subHeaders = header.second.split(SUBHEADER_VALUE_SEPARATOR);
+            for (String subHeader : subHeaders) {
+                // Search for ;branch=z9hG4bKXXXXXX and return parameter value
+                String[] params = subHeader.split(PARAM_SEPARATOR);
+                if (params.length < 2) {
+                    // This param doesn't include a branch param, move to next param.
+                    Log.w(TAG, "getTransactionId: via detected without branch param:"
+                            + subHeader);
+                    continue;
+                }
+                // by spec, each param can only appear once in a header.
+                for (String param : params) {
+                    String[] pair = param.split(PARAM_KEY_VALUE_SEPARATOR);
+                    if (pair.length < 2) {
+                        // ignore info before the first parameter
+                        continue;
+                    }
+                    if (pair.length > 2) {
+                        Log.w(TAG,
+                                "getTransactionId: unexpected parameter" + Arrays.toString(pair));
+                    }
+                    // Trim whitespace in parameter
+                    pair[0] = pair[0].trim();
+                    pair[1] = pair[1].trim();
+                    if (BRANCH_PARAM_KEY.equalsIgnoreCase(pair[0])) {
+                        // There can be multiple "Via" headers in the SIP message, however we want
+                        // to return the first once found, as this corresponds with the transaction
+                        // that is relevant here.
+                        return pair[1];
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String[] splitStartLineAndVerify(String startLine) {
+        String[] splitLine = startLine.split(" ");
+        if (isStartLineMalformed(splitLine)) return null;
+        return splitLine;
+    }
+
+    private static boolean isStartLineMalformed(String[] startLine) {
+        if (startLine == null || startLine.length == 0)  {
+            return true;
+        }
+        // start lines contain three segments separated by spaces (SP):
+        // Request-Line  =  Method SP Request-URI SP SIP-Version CRLF
+        // Status-Line  =  SIP-Version SP Status-Code SP Reason-Phrase CRLF
+        return (startLine.length != 3);
+    }
+
+    private static boolean verifySipRequest(String[] request) {
+        // Request-Line  =  Method SP Request-URI SP SIP-Version CRLF
+        boolean verified = request[2].contains(SIP_VERSION_2);
+        verified &= (Uri.parse(request[1]).getScheme() != null);
+        verified &= Arrays.stream(SIP_REQUEST_METHODS).anyMatch(s -> request[0].contains(s));
+        return verified;
+    }
+
+    private static boolean verifySipResponse(String[] response) {
+        // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
+        boolean verified = response[0].contains(SIP_VERSION_2);
+        int statusCode = Integer.parseInt(response[1]);
+        verified &= (statusCode >= 100  && statusCode < 700);
+        return verified;
+    }
+
+    /**
+     * Parse a String representation of the Header portion of the SIP Message and re-structure it
+     * into a List of key->value pairs representing each header in the order that they appeared in
+     * the message.
+     *
+     * @param headerString The raw string containing all headers
+     * @param stopAtFirstMatch Return early when the first match is found from matching header keys.
+     * @param matchingHeaderKeys An optional list of Strings containing header keys that should be
+     *                           returned if they exist. If none exist, all keys will be returned.
+     *                           (This is internally an equalsIgnoreMatch comparison).
+     * @return the matched header keys and values.
+     */
+    private static List<Pair<String, String>> parseHeaders(String headerString,
+            boolean stopAtFirstMatch, String... matchingHeaderKeys) {
+        // Ensure there is no leading whitespace
+        headerString = removeLeadingWhitespace(headerString);
+
+        List<Pair<String, String>> result = new ArrayList<>();
+        // Split the string line-by-line.
+        String[] headerLines = headerString.split("\\r?\\n");
+        if (headerLines.length == 0) {
+            return Collections.emptyList();
+        }
+
+        String headerKey = null;
+        StringBuilder headerValueSegment = new StringBuilder();
+        // loop through each line, either parsing a "key: value" pair or appending values that span
+        // multiple lines.
+        for (String line : headerLines) {
+            // This line is a continuation of the last line if it starts with whitespace or tab
+            if (line.startsWith("\t") || line.startsWith(" ")) {
+                headerValueSegment.append(removeLeadingWhitespace(line));
+                continue;
+            }
+            // This line is the start of a new key, If headerKey/value is already populated from a
+            // previous key/value pair, add it to list of parsed header pairs.
+            if (headerKey != null) {
+                final String key = headerKey;
+                if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0
+                        || Arrays.stream(matchingHeaderKeys).anyMatch(
+                                (s) -> s.equalsIgnoreCase(key))) {
+                    result.add(new Pair<>(key, headerValueSegment.toString()));
+                    if (stopAtFirstMatch) {
+                        return result;
+                    }
+                }
+                headerKey = null;
+                headerValueSegment = new StringBuilder();
+            }
+
+            // Format is "Key:Value", ignore any ":" after the first.
+            String[] pair = line.split(HEADER_KEY_VALUE_SEPARATOR, 2);
+            if (pair.length < 2) {
+                // malformed line, skip
+                Log.w(TAG, "parseHeaders - received malformed line: " + line);
+                continue;
+            }
+
+            headerKey = pair[0].trim();
+            for (int i = 1; i < pair.length; i++) {
+                headerValueSegment.append(removeLeadingWhitespace(pair[i]));
+            }
+        }
+        // Pick up the last pending header being parsed, if it exists.
+        if (headerKey != null) {
+            final String key = headerKey;
+            if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0
+                    || Arrays.stream(matchingHeaderKeys).anyMatch(
+                            (s) -> s.equalsIgnoreCase(key))) {
+                result.add(new Pair<>(key, headerValueSegment.toString()));
+            }
+        }
+
+        return result;
+    }
+
+    private static String removeLeadingWhitespace(String line) {
+        return line.replaceFirst("^\\s*", "");
+    }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index ed09d53..1273aa3a 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -478,7 +478,9 @@
 
     /**
      * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
+     * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead
      */
+    @Deprecated
     public static boolean compare(String a, String b) {
         // We've used loose comparation at least Eclair, which may change in the future.
 
@@ -489,7 +491,9 @@
      * Compare phone numbers a and b, and return true if they're identical
      * enough for caller ID purposes. Checks a resource to determine whether
      * to use a strict or loose comparison algorithm.
+     * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead
      */
+    @Deprecated
     public static boolean compare(Context context, String a, String b) {
         boolean useStrict = context.getResources().getBoolean(
                com.android.internal.R.bool.config_use_strict_phone_number_comparation);
@@ -3218,7 +3222,7 @@
         }
 
         // The conversion map is not defined (this is default). Skip conversion.
-        if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) {
+        if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0) {
             return number;
         }
 
@@ -3254,4 +3258,47 @@
         }
         return number;
     }
+
+    /**
+     * Determines if two phone numbers are the same.
+     * <p>
+     * Matching is based on <a href="https://github.com/google/libphonenumber>libphonenumber</a>.
+     * Unlike {@link #compare(String, String)}, matching takes into account national
+     * dialing plans rather than simply matching the last 7 digits of the two phone numbers. As a
+     * result, it is expected that some numbers which would match using the previous method will no
+     * longer match using this new approach.
+     *
+     * @param number1
+     * @param number2
+     * @param defaultCountryIso The lowercase two letter ISO 3166-1 country code. Used when parsing
+     *                          the phone numbers where it is not possible to determine the country
+     *                          associated with a phone number based on the number alone. It
+     *                          is recommended to pass in
+     *                          {@link TelephonyManager#getNetworkCountryIso()}.
+     * @return True if the two given phone number are same.
+     */
+    public static boolean areSamePhoneNumber(@NonNull String number1,
+            @NonNull String number2, @NonNull String defaultCountryIso) {
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        PhoneNumber n1;
+        PhoneNumber n2;
+        defaultCountryIso = defaultCountryIso.toUpperCase();
+        try {
+            n1 = util.parseAndKeepRawInput(number1, defaultCountryIso);
+            n2 = util.parseAndKeepRawInput(number2, defaultCountryIso);
+        } catch (NumberParseException e) {
+            return false;
+        }
+
+        PhoneNumberUtil.MatchType matchType = util.isNumberMatch(n1, n2);
+        if (matchType == PhoneNumberUtil.MatchType.EXACT_MATCH
+                || matchType == PhoneNumberUtil.MatchType.NSN_MATCH) {
+            return true;
+        } else if (matchType == PhoneNumberUtil.MatchType.SHORT_NSN_MATCH) {
+            return (n1.getNationalNumber() == n2.getNationalNumber()
+                    && n1.getCountryCode() == n2.getCountryCode());
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index f6f6d75..8a472ad 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -30,99 +30,117 @@
  * Defines the threshold value of the signal strength.
  * @hide
  */
-public class SignalThresholdInfo implements Parcelable {
+public final class SignalThresholdInfo implements Parcelable {
+
+    /**
+     * Unknown signal measurement type.
+     * @hide
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0;
+
     /**
      * Received Signal Strength Indication.
      * Range: -113 dBm and -51 dBm
-     * Used RAN: GERAN, CDMA2000
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN},
+     *           {@link AccessNetworkConstants.AccessNetworkType#CDMA2000}
      * Reference: 3GPP TS 27.007 section 8.5.
+     * @hide
      */
-    public static final int SIGNAL_RSSI = 1;
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1;
 
     /**
      * Received Signal Code Power.
      * Range: -120 dBm to -25 dBm;
-     * Used RAN: UTRAN
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
      * Reference: 3GPP TS 25.123, section 9.1.1.1
+     * @hide
      */
-    public static final int SIGNAL_RSCP = 2;
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2;
 
     /**
      * Reference Signal Received Power.
      * Range: -140 dBm to -44 dBm;
-     * Used RAN: EUTRAN
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
      * Reference: 3GPP TS 36.133 9.1.4
+     * @hide
      */
-    public static final int SIGNAL_RSRP = 3;
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3;
 
     /**
      * Reference Signal Received Quality
-     * Range: -20 dB to -3 dB;
-     * Used RAN: EUTRAN
+     * Range: -34 dB to 3 dB;
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
      * Reference: 3GPP TS 36.133 9.1.7
+     * @hide
      */
-    public static final int SIGNAL_RSRQ = 4;
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4;
 
     /**
      * Reference Signal Signal to Noise Ratio
      * Range: -20 dB to 30 dB;
-     * Used RAN: EUTRAN
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+     * @hide
      */
-    public static final int SIGNAL_RSSNR = 5;
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5;
 
     /**
      * 5G SS reference signal received power.
      * Range: -140 dBm to -44 dBm.
-     * Used RAN: NGRAN
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
      * Reference: 3GPP TS 38.215.
+     * @hide
      */
-    public static final int SIGNAL_SSRSRP = 6;
+    public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6;
 
     /**
      * 5G SS reference signal received quality.
-     * Range: -20 dB to -3 dB.
-     * Used RAN: NGRAN
-     * Reference: 3GPP TS 38.215.
+     * Range: -43 dB to 20 dB.
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+     * Reference: 3GPP TS 38.133 section 10.1.11.1.
+     * @hide
      */
-    public static final int SIGNAL_SSRSRQ = 7;
+    public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7;
 
     /**
      * 5G SS signal-to-noise and interference ratio.
      * Range: -23 dB to 40 dB
-     * Used RAN: NGRAN
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
      * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+     * @hide
      */
-    public static final int SIGNAL_SSSINR = 8;
+    public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
 
     /** @hide */
-    @IntDef(prefix = { "SIGNAL_" }, value = {
-        SIGNAL_RSSI,
-        SIGNAL_RSCP,
-        SIGNAL_RSRP,
-        SIGNAL_RSRQ,
-        SIGNAL_RSSNR,
-        SIGNAL_SSRSRP,
-        SIGNAL_SSRSRQ,
-        SIGNAL_SSSINR
+    @IntDef(prefix = {"SIGNAL_MEASUREMENT_TYPE_"}, value = {
+            SIGNAL_MEASUREMENT_TYPE_UNKNOWN,
+            SIGNAL_MEASUREMENT_TYPE_RSSI,
+            SIGNAL_MEASUREMENT_TYPE_RSCP,
+            SIGNAL_MEASUREMENT_TYPE_RSRP,
+            SIGNAL_MEASUREMENT_TYPE_RSRQ,
+            SIGNAL_MEASUREMENT_TYPE_RSSNR,
+            SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+            SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+            SIGNAL_MEASUREMENT_TYPE_SSSINR
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface SignalMeasurementType {}
+    public @interface SignalMeasurementType {
+    }
 
     @SignalMeasurementType
-    private int mSignalMeasurement;
+    private final int mSignalMeasurementType;
 
     /**
      * A hysteresis time in milliseconds to prevent flapping.
      * A value of 0 disables hysteresis
      */
-    private int mHysteresisMs;
+    private final int mHysteresisMs;
 
     /**
      * An interval in dB defining the required magnitude change between reports.
      * hysteresisDb must be smaller than the smallest threshold delta.
      * An interval value of 0 disables hysteresis.
      */
-    private int mHysteresisDb;
+    private final int mHysteresisDb;
 
     /**
      * List of threshold values.
@@ -130,60 +148,323 @@
      * The threshold values for which to apply criteria.
      * A vector size of 0 disables the use of thresholds for reporting.
      */
-    private int[] mThresholds = null;
+    private final int[] mThresholds;
 
     /**
      * {@code true} means modem must trigger the report based on the criteria;
      * {@code false} means modem must not trigger the report based on the criteria.
      */
-    private boolean mIsEnabled = true;
+    private final boolean mIsEnabled;
+
+    /**
+     * The radio access network type associated with the signal thresholds.
+     */
+    @AccessNetworkConstants.RadioAccessNetworkType
+    private final int mRan;
 
     /**
      * Indicates the hysteresisMs is disabled.
+     *
+     * @hide
      */
     public static final int HYSTERESIS_MS_DISABLED = 0;
 
     /**
      * Indicates the hysteresisDb is disabled.
+     *
+     * @hide
      */
     public static final int HYSTERESIS_DB_DISABLED = 0;
 
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSI_MIN_VALUE = -113;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSI_MAX_VALUE = -51;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSCP_MIN_VALUE = -120;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSCP_MAX_VALUE = -25;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRP_MIN_VALUE = -140;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRP_MAX_VALUE = -44;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRQ_MIN_VALUE = -34;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRQ_MAX_VALUE = 3;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSNR_MIN_VALUE = -20;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSNR_MAX_VALUE = 30;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRP_MIN_VALUE = -140;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRP_MAX_VALUE = -44;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRQ_MIN_VALUE = -43;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRQ_MAX_VALUE = 20;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSSINR_MIN_VALUE = -23;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSSINR_MAX_VALUE = 40;
+
     /**
      * Constructor
      *
-     * @param signalMeasurement Signal Measurement Type
-     * @param hysteresisMs hysteresisMs
-     * @param hysteresisDb hysteresisDb
-     * @param thresholds threshold value
-     * @param isEnabled isEnabled
+     * @param ran               Radio Access Network type
+     * @param signalMeasurementType Signal Measurement Type
+     * @param hysteresisMs      hysteresisMs
+     * @param hysteresisDb      hysteresisDb
+     * @param thresholds        threshold value
+     * @param isEnabled         isEnabled
      */
-    public SignalThresholdInfo(@SignalMeasurementType int signalMeasurement,
-            int hysteresisMs, int hysteresisDb, @NonNull int [] thresholds, boolean isEnabled) {
-        mSignalMeasurement = signalMeasurement;
+    private SignalThresholdInfo(@AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalMeasurementType int signalMeasurementType, int hysteresisMs, int hysteresisDb,
+            @NonNull int[] thresholds, boolean isEnabled) {
+        Objects.requireNonNull(thresholds, "thresholds must not be null");
+        validateRanWithMeasurementType(ran, signalMeasurementType);
+        validateThresholdRange(signalMeasurementType, thresholds);
+
+        mRan = ran;
+        mSignalMeasurementType = signalMeasurementType;
         mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs;
         mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb;
-        mThresholds = thresholds == null ? null : thresholds.clone();
+        mThresholds = thresholds;
         mIsEnabled = isEnabled;
     }
 
-    public @SignalMeasurementType int getSignalMeasurement() {
-        return mSignalMeasurement;
+    /**
+     * Builder class to create {@link SignalThresholdInfo} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+        private int mSignalMeasurementType = SIGNAL_MEASUREMENT_TYPE_UNKNOWN;
+        private int mHysteresisMs = HYSTERESIS_MS_DISABLED;
+        private int mHysteresisDb = HYSTERESIS_DB_DISABLED;
+        private int[] mThresholds = null;
+        private boolean mIsEnabled = false;
+
+        /**
+         * Set the radio access network type for the builder instance.
+         *
+         * @param ran The radio access network type
+         * @return the builder to facilitate the chaining
+         */
+        public @NonNull Builder setRadioAccessNetworkType(
+                @AccessNetworkConstants.RadioAccessNetworkType int ran) {
+            mRan = ran;
+            return this;
+        }
+
+        /**
+         * Set the signal measurement type for the builder instance.
+         *
+         * @param signalMeasurementType The signal measurement type
+         * @return the builder to facilitate the chaining
+         */
+        public @NonNull Builder setSignalMeasurementType(
+                @SignalMeasurementType int signalMeasurementType) {
+            mSignalMeasurementType = signalMeasurementType;
+            return this;
+        }
+
+        /**
+         * Set the hysteresis time in milliseconds to prevent flapping. A value of 0 disables
+         * hysteresis.
+         *
+         * @param hysteresisMs the hysteresis time in milliseconds
+         * @return the builder to facilitate the chaining
+         * @hide
+         */
+        public @NonNull Builder setHysteresisMs(int hysteresisMs) {
+            mHysteresisMs = hysteresisMs;
+            return this;
+        }
+
+        /**
+         * Set the interval in dB defining the required magnitude change between reports. A value of
+         * zero disabled dB-based hysteresis restrictions.
+         *
+         * @param hysteresisDb the interval in dB
+         * @return the builder to facilitate the chaining
+         * @hide
+         */
+        public @NonNull Builder setHysteresisDb(int hysteresisDb) {
+            mHysteresisDb = hysteresisDb;
+            return this;
+        }
+
+        /**
+         * Set the signal threshold values of the corresponding signal measurement type.
+         *
+         * The range and unit must reference specific SignalMeasurementType.
+         *
+         * @param thresholds array of integer as the signal threshold values
+         * @return the builder to facilitate the chaining
+         */
+        public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
+            Objects.requireNonNull(thresholds, "thresholds must not be null");
+            mThresholds = thresholds.clone();
+            Arrays.sort(mThresholds);
+            return this;
+        }
+
+        /**
+         * Set if the modem should trigger the report based on the criteria.
+         *
+         * @param isEnabled true if the modem should trigger the report based on the criteria
+         * @return the builder to facilitate the chaining
+         * @hide
+         */
+        public @NonNull Builder setIsEnabled(boolean isEnabled) {
+            mIsEnabled = isEnabled;
+            return this;
+        }
+
+        /**
+         * Build {@link SignalThresholdInfo} object.
+         *
+         * @return the SignalThresholdInfo object build out
+         *
+         * @throws IllegalArgumentException if the signal measurement type is invalid, any value in
+         * the thresholds is out of range, or the RAN is not allowed to set with the signal
+         * measurement type
+         */
+        public @NonNull SignalThresholdInfo build() {
+            return new SignalThresholdInfo(mRan, mSignalMeasurementType, mHysteresisMs,
+                    mHysteresisDb, mThresholds, mIsEnabled);
+        }
     }
 
+    /**
+     * Get the radio access network type.
+     *
+     * @return radio access network type
+     *
+     * @hide
+     */
+    public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() {
+        return mRan;
+    }
+
+    /**
+     * Get the signal measurement type.
+     *
+     * @return the SignalMeasurementType value
+     *
+     * @hide
+     */
+    public @SignalMeasurementType int getSignalMeasurementType() {
+        return mSignalMeasurementType;
+    }
+
+    /** @hide */
     public int getHysteresisMs() {
         return mHysteresisMs;
     }
 
+    /** @hide */
     public int getHysteresisDb() {
         return mHysteresisDb;
     }
 
+    /** @hide */
     public boolean isEnabled() {
         return mIsEnabled;
     }
 
-    public int[] getThresholds() {
-        return mThresholds == null ? null : mThresholds.clone();
+    /**
+     * Get the signal threshold values.
+     *
+     * @return array of integer of the signal thresholds
+     *
+     * @hide
+     */
+    public @NonNull int[] getThresholds() {
+        return mThresholds.clone();
     }
 
     @Override
@@ -192,8 +473,9 @@
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSignalMeasurement);
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mRan);
+        out.writeInt(mSignalMeasurementType);
         out.writeInt(mHysteresisMs);
         out.writeInt(mHysteresisDb);
         out.writeIntArray(mThresholds);
@@ -201,7 +483,8 @@
     }
 
     private SignalThresholdInfo(Parcel in) {
-        mSignalMeasurement = in.readInt();
+        mRan = in.readInt();
+        mSignalMeasurementType = in.readInt();
         mHysteresisMs = in.readInt();
         mHysteresisDb = in.readInt();
         mThresholds = in.createIntArray();
@@ -217,7 +500,8 @@
         }
 
         SignalThresholdInfo other = (SignalThresholdInfo) o;
-        return mSignalMeasurement == other.mSignalMeasurement
+        return mRan == other.mRan
+                && mSignalMeasurementType == other.mSignalMeasurementType
                 && mHysteresisMs == other.mHysteresisMs
                 && mHysteresisDb == other.mHysteresisDb
                 && Arrays.equals(mThresholds, other.mThresholds)
@@ -226,8 +510,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(
-                mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled);
+        return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb, mThresholds,
+                mIsEnabled);
     }
 
     public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR =
@@ -246,11 +530,83 @@
     @Override
     public String toString() {
         return new StringBuilder("SignalThresholdInfo{")
-            .append("mSignalMeasurement=").append(mSignalMeasurement)
-            .append("mHysteresisMs=").append(mSignalMeasurement)
-            .append("mHysteresisDb=").append(mHysteresisDb)
-            .append("mThresholds=").append(Arrays.toString(mThresholds))
-            .append("mIsEnabled=").append(mIsEnabled)
-            .append("}").toString();
+                .append("mRan=").append(mRan)
+                .append(" mSignalMeasurementType=").append(mSignalMeasurementType)
+                .append(" mHysteresisMs=").append(mHysteresisMs)
+                .append(" mHysteresisDb=").append(mHysteresisDb)
+                .append(" mThresholds=").append(Arrays.toString(mThresholds))
+                .append(" mIsEnabled=").append(mIsEnabled)
+                .append("}").toString();
+    }
+
+    /**
+     * Return true if signal measurement type is valid and the threshold value is in range.
+     */
+    private static boolean isValidThreshold(@SignalMeasurementType int type, int threshold) {
+        switch (type) {
+            case SIGNAL_MEASUREMENT_TYPE_RSSI:
+                return threshold >= SIGNAL_RSSI_MIN_VALUE && threshold <= SIGNAL_RSSI_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSCP:
+                return threshold >= SIGNAL_RSCP_MIN_VALUE && threshold <= SIGNAL_RSCP_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSRP:
+                return threshold >= SIGNAL_RSRP_MIN_VALUE && threshold <= SIGNAL_RSRP_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSRQ:
+                return threshold >= SIGNAL_RSRQ_MIN_VALUE && threshold <= SIGNAL_RSRQ_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSSNR:
+                return threshold >= SIGNAL_RSSNR_MIN_VALUE && threshold <= SIGNAL_RSSNR_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+                return threshold >= SIGNAL_SSRSRP_MIN_VALUE && threshold <= SIGNAL_SSRSRP_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+                return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_SSSINR:
+                return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Return true if the radio access type is allowed to set with the measurement type.
+     */
+    private static boolean isValidRanWithMeasurementType(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalMeasurementType int type) {
+        switch (type) {
+            case SIGNAL_MEASUREMENT_TYPE_RSSI:
+                return ran == AccessNetworkConstants.AccessNetworkType.GERAN
+                        || ran == AccessNetworkConstants.AccessNetworkType.CDMA2000;
+            case SIGNAL_MEASUREMENT_TYPE_RSCP:
+                return ran == AccessNetworkConstants.AccessNetworkType.UTRAN;
+            case SIGNAL_MEASUREMENT_TYPE_RSRP:
+            case SIGNAL_MEASUREMENT_TYPE_RSRQ:
+            case SIGNAL_MEASUREMENT_TYPE_RSSNR:
+                return ran == AccessNetworkConstants.AccessNetworkType.EUTRAN;
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+            case SIGNAL_MEASUREMENT_TYPE_SSSINR:
+                return ran == AccessNetworkConstants.AccessNetworkType.NGRAN;
+            default:
+                return false;
+        }
+    }
+
+    private void validateRanWithMeasurementType(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalMeasurementType int signalMeasurement) {
+        if (!isValidRanWithMeasurementType(ran, signalMeasurement)) {
+            throw new IllegalArgumentException(
+                    "invalid RAN: " + ran + " with signal measurement type: " + signalMeasurement);
+        }
+    }
+
+    private void validateThresholdRange(@SignalMeasurementType int signalMeasurement,
+            int[] thresholds) {
+        for (int threshold : thresholds) {
+            if (!isValidThreshold(signalMeasurement, threshold)) {
+                throw new IllegalArgumentException(
+                        "invalid signal measurement type: " + signalMeasurement
+                                + " with threshold: " + threshold);
+            }
+        }
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c05e90b..cedd6f3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8689,11 +8689,18 @@
      */
     public static final int CALL_COMPOSER_STATUS_ON = 1;
 
+    /**
+     * Call composer status indicating that sending/receiving pictures is disabled.
+     * All other attachments are still enabled in this state.
+     */
+    public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2;
+
     /** @hide */
     @IntDef(prefix = {"CALL_COMPOSER_STATUS_"},
             value = {
                 CALL_COMPOSER_STATUS_ON,
                 CALL_COMPOSER_STATUS_OFF,
+                CALL_COMPOSER_STATUS_ON_NO_PICTURES,
             })
     public @interface CallComposerStatus {}
 
@@ -8701,8 +8708,9 @@
      * Set the user-set status for enriched calling with call composer.
      *
      * @param status user-set status for enriched calling with call composer;
-     *               it must be a value of either {@link #CALL_COMPOSER_STATUS_ON}
-     *               or {@link #CALL_COMPOSER_STATUS_OFF}.
+     *               it must be any of {@link #CALL_COMPOSER_STATUS_ON}
+     *               {@link #CALL_COMPOSER_STATUS_OFF},
+     *               or {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -8712,7 +8720,8 @@
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setCallComposerStatus(@CallComposerStatus int status) {
-        if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) {
+        if (status > CALL_COMPOSER_STATUS_ON_NO_PICTURES
+                || status < CALL_COMPOSER_STATUS_OFF) {
             throw new IllegalArgumentException("requested status is invalid");
         }
         try {
@@ -8734,8 +8743,9 @@
      *
      * @throws SecurityException if the caller does not have the permission.
      *
-     * @return the user-set status for enriched calling with call composer either
-     * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+     * @return the user-set status for enriched calling with call composer, any of
+     * {@link #CALL_COMPOSER_STATUS_ON}, {@link #CALL_COMPOSER_STATUS_OFF}, or
+     * {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @CallComposerStatus int getCallComposerStatus() {
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index 66281ed..fd206c1 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -320,4 +320,11 @@
     public int hashCode() {
         return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags);
     }
+
+    @Override
+    public String toString() {
+        return "DelegateRegistrationState{ registered={" + mRegisteredTags
+                + "}, deregistering={" + mDeregisteringTags + "}, deregistered={"
+                + mDeregisteredTags + "}}";
+    }
 }
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index e4d20e9..519d016 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -34,7 +34,7 @@
  * network during a SUBSCRIBE request. See RFC3863 for more information.
  * @hide
  */
-public class RcsContactPresenceTuple implements Parcelable {
+public final class RcsContactPresenceTuple implements Parcelable {
 
     /** The service id of the MMTEL */
     public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
@@ -61,7 +61,7 @@
      * An optional addition to the PIDF Presence Tuple containing service capabilities, which is
      * defined in the servcaps element. See RFC5196, section 3.2.1.
      */
-    public static class ServiceCapabilities implements Parcelable {
+    public static final class ServiceCapabilities implements Parcelable {
 
         /** The service can simultaneously send and receive data. */
         public static final String DUPLEX_MODE_FULL = "full";
@@ -88,7 +88,7 @@
         /**
          * Builder to help construct {@link ServiceCapabilities} instances.
          */
-        public static class Builder {
+        public static final class Builder {
 
             private ServiceCapabilities mCapabilities;
 
@@ -106,7 +106,7 @@
              * Add the supported duplex mode.
              * @param mode The supported duplex mode
              */
-            public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
+            public @NonNull Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
                 mCapabilities.mSupportedDuplexModeList.add(mode);
                 return this;
             }
@@ -115,7 +115,7 @@
              * Add the unsupported duplex mode.
              * @param mode The unsupported duplex mode
              */
-            public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
+            public @NonNull Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
                 mCapabilities.mUnsupportedDuplexModeList.add(mode);
                 return this;
             }
@@ -123,7 +123,7 @@
             /**
              * @return the ServiceCapabilities instance.
              */
-            public ServiceCapabilities build() {
+            public @NonNull ServiceCapabilities build() {
                 return mCapabilities;
             }
         }
@@ -211,9 +211,9 @@
     /**
      * Builder to help construct {@link RcsContactPresenceTuple} instances.
      */
-    public static class Builder {
+    public static final class Builder {
 
-        private RcsContactPresenceTuple mPresenceTuple;
+        private final RcsContactPresenceTuple mPresenceTuple;
 
         /**
          * Builds a RcsContactPresenceTuple instance.
@@ -230,7 +230,7 @@
         /**
          * The optional SIP Contact URI associated with the PIDF tuple element.
          */
-        public Builder addContactUri(@NonNull Uri contactUri) {
+        public @NonNull Builder addContactUri(@NonNull Uri contactUri) {
             mPresenceTuple.mContactUri = contactUri;
             return this;
         }
@@ -239,7 +239,7 @@
          * The optional timestamp indicating the data and time of the status change of this tuple.
          * See RFC3863, section 4.1.7 for more information on the expected format.
          */
-        public Builder addTimeStamp(@NonNull String timestamp) {
+        public @NonNull Builder addTimeStamp(@NonNull String timestamp) {
             mPresenceTuple.mTimestamp = timestamp;
             return this;
         }
@@ -248,7 +248,7 @@
          * An optional parameter containing the description element of the service-description. See
          * OMA Presence SIMPLE specification v1.1
          */
-        public Builder addDescription(@NonNull String description) {
+        public @NonNull Builder addDescription(@NonNull String description) {
             mPresenceTuple.mServiceDescription = description;
             return this;
         }
@@ -257,7 +257,7 @@
          * An optional parameter containing the service capabilities of the presence tuple if they
          * are present in the servcaps element.
          */
-        public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+        public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
             mPresenceTuple.mServiceCapabilities = caps;
             return this;
         }
@@ -265,7 +265,7 @@
         /**
          * @return the constructed instance.
          */
-        public RcsContactPresenceTuple build() {
+        public @NonNull RcsContactPresenceTuple build() {
             return mPresenceTuple;
         }
     }
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 5848be8..d4715bf 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -144,7 +144,7 @@
          * @param tag the supported feature tag
          * @return this OptionBuilder
          */
-        public @NonNull OptionsBuilder addFeatureTag(String tag) {
+        public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) {
             mCapabilities.mFeatureTags.add(tag);
             return this;
         }
@@ -154,7 +154,7 @@
          * @param tags the list of the supported feature tags
          * @return this OptionBuilder
          */
-        public @NonNull OptionsBuilder addFeatureTags(List<String> tags) {
+        public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) {
             mCapabilities.mFeatureTags.addAll(tags);
             return this;
         }
@@ -195,7 +195,7 @@
          * @param tuple The {@link RcsContactPresenceTuple} to be added into.
          * @return this PresenceBuilder
          */
-        public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) {
+        public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) {
             mCapabilities.mPresenceTuples.add(tuple);
             return this;
         }
@@ -205,7 +205,8 @@
          * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into.
          * @return this PresenceBuilder
          */
-        public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) {
+        public @NonNull PresenceBuilder addCapabilityTuples(
+                @NonNull List<RcsContactPresenceTuple> tuples) {
             mCapabilities.mPresenceTuples.addAll(tuples);
             return this;
         }
@@ -282,7 +283,7 @@
      * @return The feature tags present in the OPTIONS response from the network.
      * <p>
      * Note: this is only populated if {@link #getCapabilityMechanism} is
-     * {@link CAPABILITY_MECHANISM_OPTIONS}
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
      */
     public @NonNull List<String> getOptionsFeatureTags() {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
@@ -296,7 +297,7 @@
      * contained in the NOTIFY response from the network.
      * <p>
      * Note: this is only populated if {@link #getCapabilityMechanism} is
-     * {@link CAPABILITY_MECHANISM_PRESENCE}
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
      */
     public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
@@ -312,9 +313,9 @@
      *
      * <p>
      * Note: this is only populated if {@link #getCapabilityMechanism} is
-     * {@link CAPABILITY_MECHANISM_PRESENCE}
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
      */
-    public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) {
+    public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
             return null;
         }
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 8d7742b7..6c31466 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -36,7 +36,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Executor;
 
 /**
@@ -110,7 +112,7 @@
     public static final int ERROR_FORBIDDEN = 6;
 
     /**
-     * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
+     * The contact URI requested is not provisioned for voice or it is not known as an IMS
      * subscriber to the carrier network.
      * @hide
      */
@@ -128,26 +130,26 @@
      * The network did not respond to the capabilities request before the request timed out.
      * @hide
      */
-    public static final int ERROR_REQUEST_TIMEOUT = 10;
+    public static final int ERROR_REQUEST_TIMEOUT = 9;
 
     /**
      * The request failed due to the service having insufficient memory.
      * @hide
      */
-    public static final int ERROR_INSUFFICIENT_MEMORY = 11;
+    public static final int ERROR_INSUFFICIENT_MEMORY = 10;
 
     /**
      * The network was lost while trying to complete the request.
      * @hide
      */
-    public static final int ERROR_LOST_NETWORK = 12;
+    public static final int ERROR_LOST_NETWORK = 11;
 
     /**
      * The network is temporarily unavailable or busy. Retries should only be done after the retry
      * time returned in {@link CapabilitiesCallback#onError} has elapsed.
      * @hide
      */
-    public static final int ERROR_SERVER_UNAVAILABLE = 13;
+    public static final int ERROR_SERVER_UNAVAILABLE = 12;
 
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
@@ -168,69 +170,93 @@
     public @interface ErrorCode {}
 
     /**
+     * A capability update has been requested but the reason is unknown.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0;
+
+    /**
      * A capability update has been requested due to the Entity Tag (ETag) expiring.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1;
+
     /**
      * A capability update has been requested due to moving to LTE with VoPS disabled.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2;
+
     /**
      * A capability update has been requested due to moving to LTE with VoPS enabled.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3;
+
     /**
      * A capability update has been requested due to moving to eHRPD.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4;
+
     /**
      * A capability update has been requested due to moving to HSPA+.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5;
+
     /**
      * A capability update has been requested due to moving to 3G.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6;
+
     /**
      * A capability update has been requested due to moving to 2G.
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7;
+
     /**
      * A capability update has been requested due to moving to WLAN
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8;
+
     /**
      * A capability update has been requested due to moving to IWLAN
      * @hide
      */
-    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8;
-    /**
-     * A capability update has been requested but the reason is unknown.
-     * @hide
-     */
-    public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9;
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9;
+
     /**
      * A capability update has been requested due to moving to 5G NR with VoPS disabled.
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
+
     /**
      * A capability update has been requested due to moving to 5G NR with VoPS enabled.
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
 
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "ERROR_", value = {
+            CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
             CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED,
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
@@ -240,7 +266,6 @@
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G,
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN,
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN,
-            CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED,
             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED
     })
@@ -251,32 +276,37 @@
      * UCE.
      * @hide
      */
+    @SystemApi
     public static final int PUBLISH_STATE_OK = 1;
 
     /**
      * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
      * @hide
      */
+    @SystemApi
     public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
 
     /**
      * The device has tried to publish its capabilities, which has resulted in an error. This error
-     * is related to the fact that the device is not VoLTE provisioned.
+     * is related to the fact that the device is not provisioned for voice.
      * @hide
      */
-    public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
+    @SystemApi
+    public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3;
 
     /**
      * The device has tried to publish its capabilities, which has resulted in an error. This error
      * is related to the fact that the device is not RCS or UCE provisioned.
      * @hide
      */
+    @SystemApi
     public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
 
     /**
      * The last publish resulted in a "408 Request Timeout" response.
      * @hide
      */
+    @SystemApi
     public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
 
     /**
@@ -286,6 +316,7 @@
      * Device shall retry with exponential back-off.
      * @hide
      */
+    @SystemApi
     public static final int PUBLISH_STATE_OTHER_ERROR = 6;
 
     /**@hide*/
@@ -293,7 +324,7 @@
     @IntDef(prefix = "PUBLISH_STATE_", value = {
             PUBLISH_STATE_OK,
             PUBLISH_STATE_NOT_PUBLISHED,
-            PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+            PUBLISH_STATE_VOICE_PROVISION_ERROR,
             PUBLISH_STATE_RCS_PROVISION_ERROR,
             PUBLISH_STATE_REQUEST_TIMEOUT,
             PUBLISH_STATE_OTHER_ERROR
@@ -301,55 +332,61 @@
     public @interface PublishState {}
 
     /**
-     * An application can use {@link #registerPublishStateCallback} to register a
-     * {@link PublishStateCallback), which will notify the user when the publish state to the
-     * network changes.
+     * An application can use {@link #addOnPublishStateChangedListener} to register a
+     * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
+     * the network changes.
      * @hide
      */
-    public static class PublishStateCallback {
-
-        private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
-
-            private final PublishStateCallback mLocalCallback;
-            private Executor mExecutor;
-
-            PublishStateBinder(PublishStateCallback c) {
-                mLocalCallback = c;
-            }
-
-            @Override
-            public void onPublishStateChanged(int publishState) {
-                if (mLocalCallback == null) return;
-
-                final long callingIdentity = Binder.clearCallingIdentity();
-                try {
-                    mExecutor.execute(() -> mLocalCallback.onChanged(publishState));
-                } finally {
-                    restoreCallingIdentity(callingIdentity);
-                }
-            }
-
-            private void setExecutor(Executor executor) {
-                mExecutor = executor;
-            }
-        }
-
-        private final PublishStateBinder mBinder = new PublishStateBinder(this);
-
-        /**@hide*/
-        public final IRcsUcePublishStateCallback getBinder() {
-            return mBinder;
-        }
-
-        private void setExecutor(Executor executor) {
-            mBinder.setExecutor(executor);
-        }
-
+    @SystemApi
+    public interface OnPublishStateChangedListener {
         /**
          * Notifies the callback when the publish state has changed.
          * @param publishState The latest update to the publish state.
          */
-        public void onChanged(@PublishState int publishState) {
+        void onPublishStateChange(@PublishState int publishState);
+    }
+
+    /**
+     * An application can use {@link #addOnPublishStateChangedListener} to register a
+     * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
+     * the network changes.
+     * @hide
+     */
+    public static class PublishStateCallbackAdapter {
+
+        private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
+            private final OnPublishStateChangedListener mPublishStateChangeListener;
+            private final Executor mExecutor;
+
+            PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) {
+                mExecutor = executor;
+                mPublishStateChangeListener = listener;
+            }
+
+            @Override
+            public void onPublishStateChanged(int publishState) {
+                if (mPublishStateChangeListener == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mPublishStateChangeListener.onPublishStateChange(publishState));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        }
+
+        private final PublishStateBinder mBinder;
+
+        public PublishStateCallbackAdapter(@NonNull Executor executor,
+                @NonNull OnPublishStateChangedListener listener) {
+            mBinder = new PublishStateBinder(executor, listener);
+        }
+
+        /**@hide*/
+        public final IRcsUcePublishStateCallback getBinder() {
+            return mBinder;
         }
     }
 
@@ -395,6 +432,8 @@
 
     private final Context mContext;
     private final int mSubId;
+    private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter>
+            mPublishStateCallbacks;
 
     /**
      * Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate
@@ -404,6 +443,7 @@
     RcsUceAdapter(Context context, int subId) {
         mContext = context;
         mSubId = subId;
+        mPublishStateCallbacks = new HashMap<>();
     }
 
     /**
@@ -588,6 +628,7 @@
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @PublishState int getUcePublishState() throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
@@ -609,81 +650,90 @@
     }
 
     /**
-     * Registers a {@link PublishStateCallback} with the system, which will provide publish state
-     * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
+     * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish
+     * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
      * <p>
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
      * changed events and call {@link #unregisterPublishStateCallback} to clean up.
      * <p>
-     * The registered {@link PublishStateCallback} will also receive a callback when it is
+     * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is
      * registered with the current publish state.
      *
      * @param executor The executor the listener callback events should be run on.
-     * @param c The {@link PublishStateCallback} to be added.
+     * @param listener The {@link OnPublishStateChangedListener} to be added.
      * @throws ImsException if the subscription associated with this callback is valid, but
      * the {@link ImsService} associated with the subscription is not available. This can happen if
      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
      * reason.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PublishStateCallback c) throws ImsException {
-        if (c == null) {
-            throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
-        }
+    public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPublishStateChangedListener listener) throws ImsException {
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+        if (listener == null) {
+            throw new IllegalArgumentException(
+                    "Must include a non-null OnPublishStateChangedListener.");
+        }
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null");
+            Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
-                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+                ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
-        c.setExecutor(executor);
+        PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
         try {
-            imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder());
+            imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder());
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
             throw new ImsException("Remote IMS Service is not available",
-                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+                ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
     /**
-     * Removes an existing {@link PublishStateCallback}.
+     * Removes an existing {@link OnPublishStateChangedListener}.
      * <p>
      * When the subscription associated with this callback is removed
      * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method
      * is called for an inactive subscription, it will result in a no-op.
      *
-     * @param c The callback to be unregistered.
+     * @param listener The callback to be unregistered.
      * @throws ImsException if the subscription associated with this callback is valid, but
      * the {@link ImsService} associated with the subscription is not available. This can happen if
      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
      * reason.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void unregisterPublishStateCallback(@NonNull PublishStateCallback c)
-            throws ImsException {
-        if (c == null) {
-            throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
+    public void removeOnPublishStateChangedListener(
+            @NonNull OnPublishStateChangedListener listener) throws ImsException {
+        if (listener == null) {
+            throw new IllegalArgumentException(
+                    "Must include a non-null OnPublishStateChangedListener.");
         }
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null");
+            Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
+        PublishStateCallbackAdapter callback = removePublishStateCallback(listener);
+        if (callback == null) {
+            return;
+        }
+
         try {
-            imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder());
+            imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder());
         } catch (android.os.ServiceSpecificException e) {
             throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
@@ -763,6 +813,36 @@
         }
     }
 
+    /**
+     * Add the {@link OnPublishStateChangedListener} to collection for tracking.
+     * @param executor The executor that will be used when the publish state is changed and the
+     * {@link OnPublishStateChangedListener} is called.
+     * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed.
+     * @return The {@link PublishStateCallbackAdapter} to wrapper the
+     * {@link OnPublishStateChangedListener}
+     */
+    private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor,
+            @NonNull OnPublishStateChangedListener listener) {
+        PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener);
+        synchronized (mPublishStateCallbacks) {
+            mPublishStateCallbacks.put(listener, adapter);
+        }
+        return adapter;
+    }
+
+    /**
+     * Remove the existing {@link OnPublishStateChangedListener}.
+     * @param listener The {@link OnPublishStateChangedListener} to remove from the collection.
+     * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the
+     * {@link OnPublishStateChangedListener}.
+     */
+    private PublishStateCallbackAdapter removePublishStateCallback(
+            @NonNull OnPublishStateChangedListener listener) {
+        synchronized (mPublishStateCallbacks) {
+            return mPublishStateCallbacks.remove(listener);
+        }
+    }
+
     private IImsRcsController getIImsRcsController() {
         IBinder binder = TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 1539224..006cca8 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.telephony.SipMessageParsingUtils;
+
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -38,9 +40,6 @@
     // Should not be set to true for production!
     private static final boolean IS_DEBUGGING = Build.IS_ENG;
 
-    private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
-            "BYE", "CANCEL", "REGISTER"};
-
     private final String mStartLine;
     private final String mHeaderSection;
     private final byte[] mContent;
@@ -72,6 +71,7 @@
         mContent = new byte[source.readInt()];
         source.readByteArray(mContent);
     }
+
     /**
      * @return The start line of the SIP message, which contains either the request-line or
      * status-line.
@@ -128,34 +128,25 @@
         } else {
             b.append(sanitizeStartLineRequest(mStartLine));
         }
-        b.append("], [");
-        b.append("Header: [");
+        b.append("], Header: [");
         if (IS_DEBUGGING) {
             b.append(mHeaderSection);
         } else {
             // only identify transaction id/call ID when it is available.
             b.append("***");
         }
-        b.append("], ");
-        b.append("Content: [NOT SHOWN]");
+        b.append("], Content: ");
+        b.append(getContent().length == 0 ? "[NONE]" : "[NOT SHOWN]");
         return b.toString();
     }
 
     /**
-     * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF.
      * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII.
      */
     private String sanitizeStartLineRequest(String startLine) {
+        if (!SipMessageParsingUtils.isSipRequest(startLine)) return startLine;
         String[] splitLine = startLine.split(" ");
-        if (splitLine == null || splitLine.length == 0)  {
-            return "(INVALID STARTLINE)";
-        }
-        for (String method : SIP_REQUEST_METHODS) {
-            if (splitLine[0].contains(method)) {
-                return splitLine[0] + " <Request-URI> " + splitLine[2];
-            }
-        }
-        return startLine;
+        return splitLine[0] + " <Request-URI> " + splitLine[2];
     }
 
     @Override
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
new file mode 100644
index 0000000..4435640e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -0,0 +1,115 @@
+/*
+ * 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 android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by
+ * the framework. This wrapper class also delivers the request to the framework when receive the
+ * request from the network.
+ * @hide
+ */
+public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventListener {
+
+    private static final String LOG_TAG = "CapExchangeListener";
+
+    private final ICapabilityExchangeEventListener mListenerBinder;
+
+    public CapabilityExchangeAidlWrapper(@Nullable ICapabilityExchangeEventListener listener) {
+        mListenerBinder = listener;
+    }
+
+    /**
+     * Receives the request of publishing capabilities from the network and deliver this request
+     * to the framework via the registered capability exchange event listener.
+     */
+    public void onRequestPublishCapabilities(int publishTriggerType) {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            listener.onRequestPublishCapabilities(publishTriggerType);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "request publish capabilities exception: " + e);
+        }
+    }
+
+    /**
+     * Receives the unpublish notification and deliver this callback to the framework.
+     */
+    public void onUnpublish() {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            listener.onUnpublish();
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Unpublish exception: " + e);
+        }
+    }
+
+    /**
+     * Receives the callback of the remote capability request from the network and deliver this
+     * request to the framework.
+     */
+    public void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+
+        IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() {
+            @Override
+            public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    callback.onRespondToCapabilityRequest(ownCapabilities);
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void respondToCapabilityRequestWithError(int code, String reason) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    callback.onRespondToCapabilityRequestWithError(code, reason);
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        };
+
+        try {
+            listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote capability request exception: " + e);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
index a4ffbef..078ac91 100644
--- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
@@ -22,54 +22,15 @@
 import java.util.List;
 
 /**
- * Listener interface for the ImsService to use to notify the framework of UCE events.
+ * Listener interface for the ImsService to use to notify the framework of UCE
+ * events.
+ *
+ * See CapabilityExchangeEventListener for more information.
  * {@hide}
  */
 oneway interface ICapabilityExchangeEventListener {
-    /**
-     * Trigger the framework to provide a capability update using
-     * {@link RcsCapabilityExchangeImplBase#publishCapabilities}.
-     * <p>
-     * This is typically used when trying to generate an initial PUBLISH for a new
-     * subscription to the network. The device will cache all presence publications
-     * after boot until this method is called the first time.
-     * @param publishTriggerType {@link StackPublishTriggerType} The reason for the
-     * capability update request.
-     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is
-     * not currently connected to the framework. This can happen if the
-     * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
-     * {@link RcsFeature} has not received the
-     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
-     * cases when the Telephony stack has crashed.
-     */
     void onRequestPublishCapabilities(int publishTriggerType);
-
-    /**
-     * Notify the framework that the device's capabilities have been unpublished from the network.
-     *
-     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
-     * connected to the framework. This can happen if the {@link RcsFeature} is not
-     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
-     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
-     * Telephony stack has crashed.
-     */
     void onUnpublish();
-
-    /**
-     * Inform the framework of a query for this device's UCE capabilities.
-     * <p>
-     * The framework will respond via the
-     * {@link IOptionsRequestCallback#respondToCapabilityRequest} or
-     * {@link IOptionsRequestCallback#respondToCapabilityRequestWithError} method.
-     * @param contactUri The URI associated with the remote contact that is requesting capabilities.
-     * @param remoteCapabilities The remote contact's capability information.
-     * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
-     * connected to the framework. This can happen if the {@link RcsFeature} is not
-     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
-     * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when
-     * the Telephony stack has crashed.
-     */
     void onRemoteCapabilityRequest(in Uri contactUri,
-            in List<String> remoteCapabilities,
-            IOptionsRequestCallback cb);
+            in List<String> remoteCapabilities, IOptionsRequestCallback cb);
 }
diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
index d55670d..d4d5301 100644
--- a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
@@ -33,7 +33,6 @@
     /**
      * Respond to a remote capability request from the contact specified with the
      * specified error.
-     * @param contactUri A URI containing the remote contact.
      * @param code The SIP response code to respond with.
      * @param reason A non-null String containing the reason associated with the SIP code.
      */
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 522ad81..9d91901 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -28,6 +28,10 @@
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.SipMessage;
 import android.telephony.ims.stub.SipDelegate;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
 
 import java.util.ArrayList;
 import java.util.Set;
@@ -40,6 +44,7 @@
  * @hide
  */
 public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback {
+    private static final String LOG_TAG = "SipDelegateAW";
 
     private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
         @Override
@@ -183,11 +188,15 @@
     }
 
     private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
-        //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
-        // transaction ID can not be parsed.
+        String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+        if (TextUtils.isEmpty(transactionId)) {
+            Log.w(LOG_TAG, "failure to parse SipMessage.");
+            throw new IllegalArgumentException("Malformed SipMessage, can not determine "
+                    + "transaction ID.");
+        }
         SipDelegate d = mDelegate;
         if (d != null) {
-            mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason));
+            mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason));
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index a35039b..c877aca 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -28,9 +28,12 @@
 import android.telephony.ims.stub.DelegateConnectionMessageCallback;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
 import android.telephony.ims.stub.SipDelegate;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.telephony.SipMessageParsingUtils;
+
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.Executor;
@@ -265,9 +268,13 @@
     }
 
     private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
-        //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
-        // transaction ID can not be parsed.
+        String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+        if (TextUtils.isEmpty(transactionId)) {
+            Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a "
+                    + "transaction ID.");
+            throw new IllegalArgumentException("Could not send SipMessage due to malformed header");
+        }
         mExecutor.execute(() ->
-                mMessageCallback.onMessageSendFailure(null, reason));
+                mMessageCallback.onMessageSendFailure(transactionId, reason));
     }
 }
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 96ca022..8b26c3b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -336,7 +336,7 @@
     /**
      * @hide
      */
-    public final void initialize(Context context, int slotId) {
+    public void initialize(Context context, int slotId) {
         mContext = context;
         mSlotId = slotId;
     }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index cde7067..22df921 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -21,9 +21,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.Context;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper;
 import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsFeature;
@@ -33,6 +35,7 @@
 import android.telephony.ims.aidl.RcsOptionsResponseAidlWrapper;
 import android.telephony.ims.aidl.RcsPublishResponseAidlWrapper;
 import android.telephony.ims.aidl.RcsSubscribeResponseAidlWrapper;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback;
@@ -114,8 +117,10 @@
         @Override
         public void setCapabilityExchangeEventListener(
                 @Nullable ICapabilityExchangeEventListener listener) throws RemoteException {
-            executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listener),
-                    "setCapabilityExchangeEventListener");
+            CapabilityExchangeEventListener listenerWrapper =
+                    new CapabilityExchangeAidlWrapper(listener);
+            executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(
+                    mExecutor, listenerWrapper), "setCapabilityExchangeEventListener");
         }
 
         @Override
@@ -245,9 +250,10 @@
         }
     }
 
+    private final Executor mExecutor;
     private final RcsFeatureBinder mImsRcsBinder;
     private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
-    private ICapabilityExchangeEventListener mCapExchangeEventListener;
+    private CapabilityExchangeEventListener mCapExchangeEventListener;
 
     /**
      * Create a new RcsFeature.
@@ -255,26 +261,45 @@
      * Method stubs called from the framework will be called asynchronously. To specify the
      * {@link Executor} that the methods stubs will be called, use
      * {@link RcsFeature#RcsFeature(Executor)} instead.
+     *
+     * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature.
      */
+    @Deprecated
     public RcsFeature() {
         super();
+        mExecutor = Runnable::run;
         // Run on the Binder threads that call them.
-        mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
+        mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
     }
 
     /**
      * Create a new RcsFeature using the Executor specified for methods being called by the
      * framework.
-     * @param executor The executor for the framework to use when making calls to this service.
-     * @hide
+     * @param executor The executor for the framework to use when executing the methods overridden
+     * by the implementation of RcsFeature.
      */
     public RcsFeature(@NonNull Executor executor) {
         super();
         if (executor == null) {
             throw new IllegalArgumentException("executor can not be null.");
         }
+        mExecutor = executor;
         // Run on the Binder thread by default.
-        mImsRcsBinder = new RcsFeatureBinder(this, executor);
+        mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
+    }
+
+    /**
+     * Called when the RcsFeature is initialized.
+     *
+     * @param context The context that is used in the ImsService.
+     * @param slotId The slot ID associated with the RcsFeature.
+     * @hide
+     */
+    @Override
+    public void initialize(Context context, int slotId) {
+        super.initialize(context, slotId);
+        // Notify that the RcsFeature is ready.
+        mExecutor.execute(() -> onFeatureReady());
     }
 
     /**
@@ -348,13 +373,26 @@
      * operation and the RcsFeature sets the status of the capability to true using
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
      *
-     * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements presence
+     * @param executor The executor for the framework to use when request RCS resquests to this
+     * service.
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
      * exchange if it is supported by the device.
-     * @hide
      */
-    public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl() {
+    public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+            @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) {
         // Base Implementation, override to implement functionality
-        return new RcsCapabilityExchangeImplBase();
+        return new RcsCapabilityExchangeImplBase(executor);
+    }
+
+    /**
+     * Remove the given CapabilityExchangeImplBase instance.
+     * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed.
+     */
+    public void removeCapabilityExchangeImpl(
+            @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
+        // Override to implement the process of removing RcsCapabilityExchangeImplBase instance.
     }
 
     /**{@inheritDoc}*/
@@ -377,18 +415,58 @@
         return mImsRcsBinder;
     }
 
-    private void setCapabilityExchangeEventListener(ICapabilityExchangeEventListener listener) {
-        mCapExchangeEventListener = listener;
-        if (mCapExchangeEventListener != null) {
-            onFeatureReady();
+    /**
+     * Set the capability exchange listener.
+     * @param executor The executor for the framework to use when request RCS requests to this
+     * service.
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     */
+    private void setCapabilityExchangeEventListener(@NonNull Executor executor,
+            @Nullable CapabilityExchangeEventListener listener) {
+        synchronized (mLock) {
+            mCapExchangeEventListener = listener;
+            if (mCapExchangeEventListener != null) {
+                initRcsCapabilityExchangeImplBase(executor, mCapExchangeEventListener);
+            } else {
+                // Remove the RcsCapabilityExchangeImplBase instance when the capability exchange
+                // instance has been removed in the framework.
+                if (mCapabilityExchangeImpl != null) {
+                    removeCapabilityExchangeImpl(mCapabilityExchangeImpl);
+                }
+                mCapabilityExchangeImpl = null;
+            }
         }
     }
 
-    private RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() {
+    /**
+     * Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance
+     * has already been created in the framework.
+     * @param executor The executor for the framework to use when request RCS requests to this
+     * service.
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     */
+    private void initRcsCapabilityExchangeImplBase(@NonNull Executor executor,
+            @NonNull CapabilityExchangeEventListener listener) {
         synchronized (mLock) {
+            // Remove the original instance
+            if (mCapabilityExchangeImpl != null) {
+                removeCapabilityExchangeImpl(mCapabilityExchangeImpl);
+            }
+            mCapabilityExchangeImpl = createCapabilityExchangeImpl(executor, listener);
+        }
+    }
+
+    /**
+     * @return the {@link RcsCapabilityExchangeImplBase} associated with the RcsFeature.
+     */
+    private @NonNull RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() {
+        synchronized (mLock) {
+            // The method should not be called if the instance of RcsCapabilityExchangeImplBase has
+            // not been created yet.
             if (mCapabilityExchangeImpl == null) {
-                mCapabilityExchangeImpl = createCapabilityExchangeImpl();
-                mCapabilityExchangeImpl.setEventListener(mCapExchangeEventListener);
+                throw new IllegalStateException("Session is not available.");
             }
             return mCapabilityExchangeImpl;
         }
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
new file mode 100644
index 0000000..d9734a7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -0,0 +1,84 @@
+/*
+ * 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 android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+/**
+ * The interface of the capabilities event listener for ImsService to notify the framework of the
+ * UCE request and status updated.
+ * @hide
+ */
+@SystemApi
+public interface CapabilityExchangeEventListener {
+    /**
+     * Interface used by the framework to respond to OPTIONS requests.
+     * @hide
+     */
+    interface OptionsRequestCallback {
+        /**
+         * Respond to a remote capability request from the contact specified with the
+         * capabilities of this device.
+         * @param ownCapabilities The capabilities of this device.
+         */
+        void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities);
+
+        /**
+         * Respond to a remote capability request from the contact specified with the
+         * specified error.
+         * @param code The SIP response code to respond with.
+         * @param reason A non-null String containing the reason associated with the SIP code.
+         */
+        void onRespondToCapabilityRequestWithError(int code, @NonNull String reason);
+    }
+
+    /**
+     * Trigger the framework to provide a capability update using
+     * {@link RcsCapabilityExchangeImplBase#publishCapabilities}.
+     * <p>
+     * This is typically used when trying to generate an initial PUBLISH for a new subscription to
+     * the network. The device will cache all presence publications after boot until this method is
+     * called the first time.
+     * @param publishTriggerType {@link RcsUceAdapter#StackPublishTriggerType} The reason for the
+     * capability update request.
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
+     */
+    void onRequestPublishCapabilities(
+            @RcsUceAdapter.StackPublishTriggerType int publishTriggerType) throws ImsException;
+
+    /**
+     * Notify the framework that the device's capabilities have been unpublished
+     * from the network.
+     *
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
+     */
+    void onUnpublish() throws ImsException;
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 3a0fb6e..c84e23c 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -20,20 +20,28 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.telephony.ims.ImsException;
-import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 import android.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
- * Base class for different types of Capability exchange.
+ * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform
+ * using the vendor ImsService.
+ * <p>
+ * See RCC.07 for more details on UCE as well as how UCE should be implemented.
  * @hide
  */
+@SystemApi
 public class RcsCapabilityExchangeImplBase {
 
     private static final String LOG_TAG = "RcsCapExchangeImplBase";
@@ -70,13 +78,11 @@
 
     /**
      * Network connection is lost.
-     * @hide
      */
     public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6;
 
     /**
      * Requested feature/resource is not supported.
-     * @hide
      */
     public static final int COMMAND_CODE_NOT_SUPPORTED = 7;
 
@@ -117,7 +123,8 @@
      */
     public interface PublishResponseCallback {
         /**
-         * Notify the framework that the command associated with this callback has failed.
+         * Notify the framework that the command associated with the
+         * {@link #publishCapabilities(String, PublishResponseCallback)} has failed.
          *
          * @param code The reason why the associated command has failed.
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
@@ -128,15 +135,15 @@
          */
         void onCommandError(@CommandCode int code) throws ImsException;
 
-
         /**
          * Provide the framework with a subsequent network response update to
          * {@link #publishCapabilities(String, PublishResponseCallback)}.
          *
          * @param code The SIP response code sent from the network for the operation
          * token specified.
-         * @param reason The optional reason response from the network. If the network
-         *  provided no reason with the code, the string should be empty.
+         * @param reason The optional reason response from the network. If there is a reason header
+         * included in the response, that should take precedence over the reason provided in the
+         * status line. If the network provided no reason with the code, the string should be empty.
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
          * not currently connected to the framework. This can happen if the {@link RcsFeature}
          * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
@@ -149,6 +156,7 @@
 
     /**
      * Interface used by the framework to respond to OPTIONS requests.
+     * @hide
      */
     public interface OptionsResponseCallback {
         /**
@@ -171,7 +179,7 @@
          * If none was sent, this should be an empty string.
          * @param theirCaps the contact's UCE capabilities associated with the
          * capability request.
-         * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
          * currently connected to the framework. This can happen if the
          * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
          * {@link RcsFeature} has not received the
@@ -184,6 +192,7 @@
 
     /**
      * Interface used by the framework to receive the response of the subscribe request.
+     * @hide
      */
     public interface SubscribeResponseCallback {
         /**
@@ -219,17 +228,16 @@
         /**
          * Provides the framework with latest XML PIDF documents included in the
          * network response for the requested  contacts' capabilities requested by the
-         * Framework  using {@link #requestCapabilities(List, int)}. This should be
+         * Framework using {@link #requestCapabilities(List, int)}. This should be
          * called every time a new NOTIFY event is received with new capability
          * information.
          *
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
-         * not currently
-         * connected to the framework. This can happen if the {@link RcsFeature} is not
-         * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
-         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
-         * rare cases when the
-         * Telephony stack has crashed.
+         * not currently connected to the framework.
+         * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+         * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
          */
         void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
 
@@ -250,24 +258,21 @@
          * This allows the framework to know that there will no longer be any
          * capability updates for the requested operationToken.
          */
-        void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException;
+        void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
     }
 
-
-    private ICapabilityExchangeEventListener mListener;
+    private final Executor mBinderExecutor;
 
     /**
-     * Set the event listener to send the request to Framework.
+     * Create a new RcsCapabilityExchangeImplBase instance.
+     *
+     * @param executor The executor that remote calls from the framework will be called on.
      */
-    public void setEventListener(ICapabilityExchangeEventListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Get the event listener.
-     */
-    public ICapabilityExchangeEventListener getEventListener() {
-        return mListener;
+    public RcsCapabilityExchangeImplBase(@NonNull Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+        mBinderExecutor = executor;
     }
 
     /**
@@ -284,7 +289,10 @@
      * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
      * capabilities for.
      * @param cb The callback of the subscribe request.
+     * @hide
      */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
     public void subscribeForCapabilities(@NonNull List<Uri> uris,
             @NonNull SubscribeResponseCallback cb) {
         // Stub - to be implemented by service
@@ -300,11 +308,13 @@
      * The capabilities of this device have been updated and should be published to the network.
      * <p>
      * If this operation succeeds, network response updates should be sent to the framework using
-     * {@link #onNetworkResponse(int, String)}.
+     * {@link PublishResponseCallback#onNetworkResponse(int, String)}.
      * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent
      * to the carrier’s presence server.
      * @param cb The callback of the publish request
      */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
     public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "publishCapabilities called with no implementation.");
@@ -324,7 +334,10 @@
      * @param contactUri The URI of the remote user that we wish to get the capabilities of.
      * @param myCapabilities The capabilities of this device to send to the remote user.
      * @param callback The callback of this request which is sent from the remote user.
+     * @hide
      */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
     public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
             @NonNull List<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
         // Stub - to be implemented by service
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c60a44c..541ec04 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -629,7 +629,7 @@
      *            successful iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      */
-    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     boolean iccCloseLogicalChannel(int subId, int channel);
 
     /**
@@ -671,7 +671,7 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      */
-    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 171933273)
     String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction,
             int p1, int p2, int p3, String data);
 
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
similarity index 98%
rename from tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
rename to tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
index 2df0024..56db4f9 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
@@ -35,7 +35,7 @@
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
-public class DummyBlobData {
+public class FakeBlobData {
     private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
 
     private final Random mRandom;
@@ -47,7 +47,7 @@
     byte[] mFileDigest;
     long mExpiryTimeMs;
 
-    private DummyBlobData(Builder builder) {
+    private FakeBlobData(Builder builder) {
         mRandom = new Random(builder.getRandomSeed());
         mFile = new File(builder.getContext().getFilesDir(), builder.getFileName());
         mFileSize = builder.getFileSize();
@@ -116,8 +116,8 @@
             return mExpiryDurationMs;
         }
 
-        public DummyBlobData build() {
-            return new DummyBlobData(this);
+        public FakeBlobData build() {
+            return new FakeBlobData(this);
         }
     }
 
diff --git a/tests/StagedInstallTest/OWNERS b/tests/StagedInstallTest/OWNERS
index d825dfd..aac68e9 100644
--- a/tests/StagedInstallTest/OWNERS
+++ b/tests/StagedInstallTest/OWNERS
@@ -1 +1,5 @@
 include /services/core/java/com/android/server/pm/OWNERS
+
+dariofreni@google.com
+ioffe@google.com
+olilan@google.com
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index a762219..f6a2846 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -70,4 +70,7 @@
         "android.test.base",
         "android.test.mock",
     ],
+    jni_libs: [
+        "libservice-connectivity",
+    ],
 }
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 373aac6..c271f49 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -24,6 +24,7 @@
         "androidx.test.rules",
         "junit",
         "mockito-target-minus-junit4",
+        "modules-utils-build",
         "net-tests-utils",
         "net-utils-framework-common",
         "platform-test-annotations",
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
index bd1847b..2cb16d3 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -18,12 +18,15 @@
 
 import android.os.Build
 import androidx.test.filters.SmallTest
+import com.android.modules.utils.build.SdkLevel
 import com.android.testutils.assertParcelSane
 import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
@@ -33,6 +36,9 @@
 @RunWith(DevSdkIgnoreRunner::class)
 @IgnoreUpTo(Build.VERSION_CODES.Q)
 class CaptivePortalDataTest {
+    @Rule @JvmField
+    val ignoreRule = DevSdkIgnoreRule()
+
     private val data = CaptivePortalData.Builder()
             .setRefreshTime(123L)
             .setUserPortalUrl(Uri.parse("https://portal.example.com/test"))
@@ -41,13 +47,18 @@
             .setBytesRemaining(456L)
             .setExpiryTime(789L)
             .setCaptive(true)
+            .apply {
+                if (SdkLevel.isAtLeastS()) {
+                    setVenueFriendlyName("venue friendly name")
+                }
+            }
             .build()
 
     private fun makeBuilder() = CaptivePortalData.Builder(data)
 
     @Test
     fun testParcelUnparcel() {
-        assertParcelSane(data, fieldCount = 7)
+        assertParcelSane(data, fieldCount = 8)
 
         assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
         assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -66,6 +77,11 @@
         assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
         assertNotEqualsAfterChange { it.setExpiryTime(12L) }
         assertNotEqualsAfterChange { it.setCaptive(false) }
+
+        if (SdkLevel.isAtLeastS()) {
+            assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
+            assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
+        }
     }
 
     @Test
@@ -108,6 +124,11 @@
         assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    fun testVenueFriendlyName() {
+        assertEquals("venue friendly name", data.venueFriendlyName)
+    }
+
     private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
             CaptivePortalData.Builder(this).apply { mutator(this) }.build()
 
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6b7ea66..5d0e016 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -42,9 +42,11 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+import static android.os.Process.INVALID_UID;
 
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
+import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -53,18 +55,19 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
+import android.net.wifi.WifiInfo;
 import android.net.wifi.aware.DiscoverySession;
 import android.net.wifi.aware.PeerHandle;
 import android.net.wifi.aware.WifiAwareNetworkSpecifier;
 import android.os.Build;
-import android.os.Process;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
-import androidx.core.os.BuildCompat;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
@@ -89,10 +92,11 @@
     private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
 
     private boolean isAtLeastR() {
-        // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
-        // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
-        // releasing Android R.
-        return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q;
+        return SdkLevel.isAtLeastR();
+    }
+
+    private boolean isAtLeastS() {
+        return SdkLevel.isAtLeastS();
     }
 
     @Test
@@ -324,8 +328,59 @@
         testParcelSane(netCap);
     }
 
+    private NetworkCapabilities createNetworkCapabilitiesWithWifiInfo() {
+        // uses a real WifiInfo to test parceling of sensitive data.
+        final WifiInfo wifiInfo = new WifiInfo.Builder()
+                .setSsid("sssid1234".getBytes())
+                .setBssid("00:11:22:33:44:55")
+                .build();
+        return new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_EIMS)
+                .addCapability(NET_CAPABILITY_NOT_METERED)
+                .setSSID(TEST_SSID)
+                .setTransportInfo(wifiInfo)
+                .setRequestorPackageName("com.android.test")
+                .setRequestorUid(9304);
+    }
+
+    @Test
+    public void testParcelNetworkCapabilitiesWithLocationSensitiveFields() {
+        assumeTrue(isAtLeastS());
+
+        final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
+        final NetworkCapabilities netCapWithLocationSensitiveFields =
+                new NetworkCapabilities(netCap, true);
+
+        assertParcelingIsLossless(netCapWithLocationSensitiveFields);
+        testParcelSane(netCapWithLocationSensitiveFields);
+
+        assertEquals(netCapWithLocationSensitiveFields,
+                parcelingRoundTrip(netCapWithLocationSensitiveFields));
+    }
+
+    @Test
+    public void testParcelNetworkCapabilitiesWithoutLocationSensitiveFields() {
+        assumeTrue(isAtLeastS());
+
+        final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
+        final NetworkCapabilities netCapWithoutLocationSensitiveFields =
+                new NetworkCapabilities(netCap, false);
+
+        final NetworkCapabilities sanitizedNetCap =
+                new NetworkCapabilities(netCapWithoutLocationSensitiveFields);
+        final WifiInfo sanitizedWifiInfo = new WifiInfo.Builder()
+                .setSsid(new byte[0])
+                .setBssid(WifiInfo.DEFAULT_MAC_ADDRESS)
+                .build();
+        sanitizedNetCap.setTransportInfo(sanitizedWifiInfo);
+        assertEquals(sanitizedNetCap, parcelingRoundTrip(netCapWithoutLocationSensitiveFields));
+    }
+
     private void testParcelSane(NetworkCapabilities cap) {
-        if (isAtLeastR()) {
+        if (isAtLeastS()) {
+            assertParcelSane(cap, 16);
+        } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
             assertParcelSane(cap, 11);
@@ -639,26 +694,23 @@
         // Sequence 1: Transport + Transport + TransportInfo
         NetworkCapabilities nc1 = new NetworkCapabilities();
         nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
-                .setTransportInfo(new TransportInfo() {});
+                .setTransportInfo(new TestTransportInfo());
 
         // Sequence 2: Transport + NetworkSpecifier + Transport
         NetworkCapabilities nc2 = new NetworkCapabilities();
-        nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {})
+        nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TestTransportInfo())
                 .addTransportType(TRANSPORT_WIFI);
     }
 
     @Test
     public void testCombineTransportInfo() {
         NetworkCapabilities nc1 = new NetworkCapabilities();
-        nc1.setTransportInfo(new TransportInfo() {
-            // empty
-        });
+        nc1.setTransportInfo(new TestTransportInfo());
+
         NetworkCapabilities nc2 = new NetworkCapabilities();
         // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
         // combine fails)
-        nc2.setTransportInfo(new TransportInfo() {
-            // empty
-        });
+        nc2.setTransportInfo(new TestTransportInfo());
 
         try {
             nc1.combineCapabilities(nc2);
@@ -761,7 +813,7 @@
         // Test default owner uid.
         // If the owner uid is not set, the default value should be Process.INVALID_UID.
         final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
-        assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+        assertEquals(INVALID_UID, nc1.getOwnerUid());
         // Test setAdministratorUids and getAdministratorUids.
         final int[] administratorUids = {1001, 10001};
         final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
@@ -906,6 +958,16 @@
     private class TestTransportInfo implements TransportInfo {
         TestTransportInfo() {
         }
+
+        @Override
+        public TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+            return this;
+        }
+
+        @Override
+        public boolean hasLocationSensitiveFields() {
+            return false;
+        }
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index d74a621..f2dd27e 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
@@ -31,16 +32,21 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkRequest.Type.REQUEST;
+import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -49,9 +55,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
-import android.net.NetworkCapabilities;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Handler;
@@ -213,9 +217,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(
-                any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class)))
-                .thenReturn(request);
+        when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
+                any(), nullable(String.class))).thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
         // callback triggers
@@ -242,9 +245,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(
-                any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class)))
-                .thenReturn(req1);
+        when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
+                any(), nullable(String.class))).thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
         // callback triggers
@@ -261,9 +263,8 @@
         verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
 
         // callback can be registered again
-        when(mService.requestNetwork(
-                any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class)))
-                .thenReturn(req2);
+        when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
+                any(), nullable(String.class))).thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
         // callback triggers
@@ -286,7 +287,7 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any(),
+        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(),
                 nullable(String.class))).thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
@@ -340,6 +341,35 @@
         }
     }
 
+    @Test
+    public void testRequestType() throws Exception {
+        final String testPkgName = "MyPackage";
+        final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
+        when(mCtx.getOpPackageName()).thenReturn(testPkgName);
+        final NetworkRequest request = makeRequest(1);
+        final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
+
+        manager.requestNetwork(request, callback);
+        verify(mService).requestNetwork(eq(request.networkCapabilities),
+                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(testPkgName), eq(null));
+        reset(mService);
+
+        // Verify that register network callback does not calls requestNetwork at all.
+        manager.registerNetworkCallback(request, callback);
+        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
+                anyInt(), any(), any());
+        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
+                eq(testPkgName));
+        reset(mService);
+
+        manager.registerDefaultNetworkCallback(callback);
+        verify(mService).requestNetwork(eq(null),
+                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(testPkgName), eq(null));
+        reset(mService);
+    }
+
     static Message makeMessage(NetworkRequest req, int messageType) {
         Bundle bundle = new Bundle();
         bundle.putParcelable(NetworkRequest.class.getSimpleName(), req);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9421acd..4a282e8 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -161,7 +161,6 @@
 import android.net.EthernetManager;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IDnsResolver;
-import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
@@ -202,6 +201,7 @@
 import android.net.shared.NetworkMonitorUtils;
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
+import android.net.wifi.WifiInfo;
 import android.os.BadParcelableException;
 import android.os.Binder;
 import android.os.Build;
@@ -290,13 +290,16 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -344,6 +347,11 @@
 
     private static final String INTERFACE_NAME = "interface";
 
+    private static final String TEST_VENUE_URL_NA = "https://android.com/";
+    private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/";
+    private static final String TEST_FRIENDLY_NAME = "Network friendly name";
+    private static final String TEST_REDIRECT_URL = "http://example.com/firstPath";
+
     private MockContext mServiceContext;
     private HandlerThread mCsHandlerThread;
     private ConnectivityService.Dependencies mDeps;
@@ -359,7 +367,6 @@
     private HandlerThread mAlarmManagerThread;
     private TestNetIdManager mNetIdManager;
 
-    @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
     @Mock DeviceIdleInternal mDeviceIdleInternal;
@@ -407,9 +414,6 @@
 
     private class MockContext extends BroadcastInterceptingContext {
         private final MockContentResolver mContentResolver;
-        // Contains all registered receivers since this object was created. Useful to clear
-        // them when needed, as BroadcastInterceptingContext does not provide this facility.
-        private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>();
 
         @Spy private Resources mResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
@@ -546,19 +550,6 @@
         public void setPermission(String permission, Integer granted) {
             mMockedPermissions.put(permission, granted);
         }
-
-        @Override
-        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-            mRegisteredReceivers.add(receiver);
-            return super.registerReceiver(receiver, filter);
-        }
-
-        public void clearRegisteredReceivers() {
-            // super.unregisterReceiver is a no-op for receivers that are not registered (because
-            // they haven't been registered or because they have already been unregistered).
-            // For the same reason, don't bother clearing mRegisteredReceivers.
-            for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv);
-        }
     }
 
     private void waitForIdle() {
@@ -587,10 +578,10 @@
         }
 
         // Bring up a network that we can use to send messages to ConnectivityService.
-        ConditionVariable cv = registerConnectivityBroadcast(1);
+        ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        waitFor(cv);
+        b.expectBroadcast();
         Network n = mWiFiNetworkAgent.getNetwork();
         assertNotNull(n);
 
@@ -607,10 +598,10 @@
     @Ignore
     public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
         // Bring up a network that we can use to send messages to ConnectivityService.
-        ConditionVariable cv = registerConnectivityBroadcast(1);
+        ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        waitFor(cv);
+        b.expectBroadcast();
         Network n = mWiFiNetworkAgent.getNetwork();
         assertNotNull(n);
 
@@ -869,7 +860,7 @@
             mProbesSucceeded = probesSucceeded;
         }
 
-        void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+        void notifyCapportApiDataChanged(CaptivePortalData data) {
             try {
                 mNmCallbacks.notifyCaptivePortalDataChanged(data);
             } catch (RemoteException e) {
@@ -1199,6 +1190,8 @@
                 updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect");
             }
             mAgentRegistered = false;
+            setUids(null);
+            mInterface = null;
         }
 
         @Override
@@ -1371,7 +1364,6 @@
         doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
         doReturn(mMetricsService).when(deps).getMetricsLogger();
         doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
-        doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
         doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
         doAnswer(inv -> {
             mPolicyTracker = new WrappedMultinetworkPolicyTracker(
@@ -1509,29 +1501,79 @@
     }
 
     /**
-     * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
-     * broadcasts are received.
+     * Class to simplify expecting broadcasts using BroadcastInterceptingContext.
+     * Ensures that the receiver is unregistered after the expected broadcast is received. This
+     * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs
+     * the receivers' receive method while iterating over the list of receivers, and unregistering
+     * the receiver during iteration throws ConcurrentModificationException.
      */
-    private ConditionVariable registerConnectivityBroadcast(final int count) {
+    private class ExpectedBroadcast extends CompletableFuture<Intent>  {
+        private final BroadcastReceiver mReceiver;
+
+        ExpectedBroadcast(BroadcastReceiver receiver) {
+            mReceiver = receiver;
+        }
+
+        public Intent expectBroadcast(int timeoutMs) throws Exception {
+            try {
+                return get(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                fail("Expected broadcast not received after " + timeoutMs + " ms");
+                return null;
+            } finally {
+                mServiceContext.unregisterReceiver(mReceiver);
+            }
+        }
+
+        public Intent expectBroadcast() throws Exception {
+            return expectBroadcast(TIMEOUT_MS);
+        }
+
+        public void expectNoBroadcast(int timeoutMs) throws Exception {
+            waitForIdle();
+            try {
+                final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
+                fail("Unexpected broadcast: " + intent.getAction());
+            } catch (TimeoutException expected) {
+            } finally {
+                mServiceContext.unregisterReceiver(mReceiver);
+            }
+        }
+    }
+
+    /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */
+    private ExpectedBroadcast registerConnectivityBroadcast(final int count) {
         return registerConnectivityBroadcastThat(count, intent -> true);
     }
 
-    private ConditionVariable registerConnectivityBroadcastThat(final int count,
+    private ExpectedBroadcast registerConnectivityBroadcastThat(final int count,
             @NonNull final Predicate<Intent> filter) {
-        final ConditionVariable cv = new ConditionVariable();
         final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
+        // AtomicReference allows receiver to access expected even though it is constructed later.
+        final AtomicReference<ExpectedBroadcast> expectedRef = new AtomicReference<>();
         final BroadcastReceiver receiver = new BroadcastReceiver() {
-                    private int remaining = count;
-                    public void onReceive(Context context, Intent intent) {
-                        if (!filter.test(intent)) return;
-                        if (--remaining == 0) {
-                            cv.open();
-                            mServiceContext.unregisterReceiver(this);
-                        }
-                    }
-                };
+            private int mRemaining = count;
+            public void onReceive(Context context, Intent intent) {
+                final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+                final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+                Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni);
+                if (!filter.test(intent)) return;
+                if (--mRemaining == 0) {
+                    expectedRef.get().complete(intent);
+                }
+            }
+        };
+        final ExpectedBroadcast expected = new ExpectedBroadcast(receiver);
+        expectedRef.set(expected);
         mServiceContext.registerReceiver(receiver, intentFilter);
-        return cv;
+        return expected;
+    }
+
+    private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
+        return registerConnectivityBroadcastThat(1, intent ->
+                type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
+                        ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
+                                .getDetailedState()));
     }
 
     @Test
@@ -1555,10 +1597,9 @@
         // Connect the cell agent and wait for the connected broadcast.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL);
-        final ConditionVariable cv1 = registerConnectivityBroadcastThat(1,
-                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE);
+        ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
         mCellNetworkAgent.connect(true);
-        waitFor(cv1);
+        b.expectBroadcast();
 
         // Build legacy request for SUPL.
         final NetworkCapabilities legacyCaps = new NetworkCapabilities();
@@ -1568,27 +1609,17 @@
                 ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
 
         // File request, withdraw it and make sure no broadcast is sent
-        final ConditionVariable cv2 = registerConnectivityBroadcast(1);
+        b = registerConnectivityBroadcast(1);
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.requestNetwork(legacyRequest, callback);
         callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
         mCm.unregisterNetworkCallback(callback);
-        assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent
-        // As the broadcast did not fire, the receiver was not unregistered. Do this now.
-        mServiceContext.clearRegisteredReceivers();
+        b.expectNoBroadcast(800);  // 800ms long enough to at least flake if this is sent
 
-        // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to
-        // check that has been sent.
-        final AtomicBoolean vanillaAction = new AtomicBoolean(false);
-        final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> {
-            if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
-                vanillaAction.set(true);
-            }
-            return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
-        });
+        // Disconnect the network and expect mobile disconnected broadcast.
+        b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
         mCellNetworkAgent.disconnect();
-        waitFor(cv3);
-        assertTrue(vanillaAction.get());
+        b.expectBroadcast();
     }
 
     @Test
@@ -1599,9 +1630,9 @@
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
         // Test bringing up validated cellular.
-        ConditionVariable cv = registerConnectivityBroadcast(1);
+        ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
         mCellNetworkAgent.connect(true);
-        waitFor(cv);
+        b.expectBroadcast();
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         assertLength(2, mCm.getAllNetworks());
         assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
@@ -1609,9 +1640,9 @@
         assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
                 mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
         // Test bringing up validated WiFi.
-        cv = registerConnectivityBroadcast(2);
+        b = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.connect(true);
-        waitFor(cv);
+        b.expectBroadcast();
         verifyActiveNetwork(TRANSPORT_WIFI);
         assertLength(2, mCm.getAllNetworks());
         assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
@@ -1626,9 +1657,9 @@
         assertLength(1, mCm.getAllNetworks());
         assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
         // Test WiFi disconnect.
-        cv = registerConnectivityBroadcast(1);
+        b = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.disconnect();
-        waitFor(cv);
+        b.expectBroadcast();
         verifyNoNetwork();
     }
 
@@ -1636,9 +1667,9 @@
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        ConditionVariable cv = registerConnectivityBroadcast(1);
+        ExpectedBroadcast b = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(false);
-        waitFor(cv);
+        b.expectBroadcast();
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up unvalidated cellular
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -1651,19 +1682,19 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up validated cellular
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        cv = registerConnectivityBroadcast(2);
+        b = registerConnectivityBroadcast(2);
         mCellNetworkAgent.connect(true);
-        waitFor(cv);
+        b.expectBroadcast();
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test cellular