Merge "Fix log message errors in RangingManager"
diff --git a/Android.bp b/Android.bp
index 6a0bdc3..6a2bd95 100644
--- a/Android.bp
+++ b/Android.bp
@@ -678,6 +678,7 @@
     srcs: [
         ":ipconnectivity-proto-src",
         ":libstats_atom_enum_protos",
+        ":libtombstone_proto-src",
         "core/proto/**/*.proto",
         "libs/incident/**/*.proto",
     ],
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 6afed7a..367b427 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -79,12 +79,13 @@
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
         "stable.core.platform.api.stubs",
-        // There are a few classes from modules used as type arguments that
-        // need to be resolved by metalava. For now, we can use a previously
-        // finalized stub library to resolve them. If a new class gets added,
-        // this may be need to be revisited to use a manually maintained stub
-        // library with empty classes in order to resolve those references.
-        "sdk_system_30_android",
+        // There are a few classes from modules used by the core that
+        // need to be resolved by metalava. We use a prebuilt stub of the
+        // full sdk to ensure we can resolve them. If a new class gets added,
+        // the prebuilts/sdk/current needs to be updated.
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
     ],
     high_mem: true, // Lots of sources => high memory use, see b/170701554
     installable: false,
@@ -392,7 +393,11 @@
         "android_defaults_stubs_current",
         "android_stubs_dists_default",
     ],
-    libs: ["sdk_system_29_android"],
+    libs: [
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
+    ],
     static_libs: ["art.module.public.api.stubs"],
     dist: {
         dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/apct-tests/perftests/core/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/apex/Android.bp b/apex/Android.bp
index 1876110..8310ba7 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,189 +15,3 @@
 package {
     default_visibility: [":__subpackages__"],
 }
-
-mainline_stubs_args =
-    "--error UnhiddenSystemApi " +
-    "--hide BroadcastBehavior " +
-    "--hide CallbackInterface " +
-    "--hide DeprecationMismatch " +
-    "--hide HiddenSuperclass " +
-    "--hide HiddenTypedefConstant " +
-    "--hide HiddenTypeParameter " +
-    "--hide MissingPermission " +
-    "--hide RequiresPermission " +
-    "--hide SdkConstant " +
-    "--hide Todo " +
-    "--hide Typo " +
-    "--hide UnavailableSymbol "
-
-// TODO: modularize this so not every module has the same whitelist
-framework_packages_to_document = [
-    "android",
-    "dalvik",
-    "java",
-    "javax",
-    "junit",
-    "org.apache.http",
-    "org.json",
-    "org.w3c.dom",
-    "org.xml.sax",
-    "org.xmlpull",
-]
-
-// TODO: remove the hiding when server classes are cleaned up.
-mainline_framework_stubs_args =
-    mainline_stubs_args +
-    "--hide-package com.android.server "
-
-priv_apps = " " +
-    "--show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
-    "\\) "
-
-module_libs = " " +
-    " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
-    "\\)" +
-    " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
-    "\\) "
-
-mainline_service_stubs_args =
-    mainline_stubs_args +
-    "--show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.SYSTEM_SERVER" +
-    "\\) " +
-    "--hide-annotation android.annotation.Hide " +
-    "--hide InternalClasses " // com.android.* classes are okay in this interface
-
-// Defaults common to all mainline module java_sdk_library instances.
-java_defaults {
-    name: "framework-module-common-defaults",
-
-    // Additional annotations used for compiling both the implementation and the
-    // stubs libraries.
-    libs: ["framework-annotations-lib"],
-
-    // Framework modules are not generally shared libraries, i.e. they are not
-    // intended, and must not be allowed, to be used in a <uses-library> manifest
-    // entry.
-    shared_library: false,
-
-    // Prevent dependencies that do not specify an sdk_version from accessing the
-    // implementation library by default and force them to use stubs instead.
-    default_to_stubs: true,
-
-    // Enable api lint. This will eventually become the default for java_sdk_library
-    // but it cannot yet be turned on because some usages have not been cleaned up.
-    // TODO(b/156126315) - Remove when no longer needed.
-    api_lint: {
-        enabled: true,
-    },
-
-    // The API scope specific properties.
-    public: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-
-    // installable implies we'll create a non-apex (platform) variant, which
-    // we shouldn't ordinarily need (and it can create issues), so disable that.
-    installable: false,
-
-    // Configure framework module specific metalava options.
-    droiddoc_options: [mainline_stubs_args],
-
-    annotations_enabled: true,
-
-    // Allow access to the stubs from anywhere
-    visibility: ["//visibility:public"],
-    stubs_library_visibility: ["//visibility:public"],
-
-    // Hide impl library and stub sources
-    impl_library_visibility: [
-        ":__pkg__",
-        "//frameworks/base", // For framework-all
-    ],
-    stubs_source_visibility: ["//visibility:private"],
-
-    defaults_visibility: ["//visibility:private"],
-
-    // Collates API usages from each module for further analysis.
-    plugins: ["java_api_finder"],
-
-    // Mainline modules should only rely on 'module_lib' APIs provided by other modules
-    // and the non updatable parts of the platform.
-    sdk_version: "module_current",
-}
-
-// Defaults for mainline module provided java_sdk_library instances.
-java_defaults {
-    name: "framework-module-defaults",
-    defaults: ["framework-module-common-defaults"],
-
-    system: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-    module_lib: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-    defaults_visibility: [
-        ":__subpackages__",
-        "//frameworks/base/libs/hwui",
-        "//frameworks/base/wifi",
-        "//packages/modules:__subpackages__",
-        "//packages/providers/MediaProvider:__subpackages__",
-    ],
-}
-
-// Defaults for mainline module system server provided java_sdk_library instances.
-java_defaults {
-    name: "framework-system-server-module-defaults",
-    defaults: ["framework-module-common-defaults"],
-
-    system_server: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-    defaults_visibility: [
-        ":__subpackages__",
-        "//packages/modules:__subpackages__",
-    ],
-}
-
-stubs_defaults {
-    name: "service-module-stubs-srcs-defaults",
-    args: mainline_service_stubs_args,
-    installable: false,
-    annotations_enabled: true,
-    merge_annotations_dirs: [
-        "metalava-manual",
-    ],
-    filter_packages: ["com.android."],
-    check_api: {
-        current: {
-            api_file: "api/current.txt",
-            removed_api_file: "api/removed.txt",
-        },
-        api_lint: {
-            enabled: true,
-        },
-    },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system-server/api",
-    },
-}
-
-// Empty for now, but a convenient place to add rules for all
-// module java_library system_server stub libs.
-java_defaults {
-    name: "service-module-stubs-defaults",
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system-server",
-    },
-}
diff --git a/apex/OWNERS b/apex/OWNERS
index bde2bec..b3e81b9 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,8 +1 @@
-# Mainline modularization team
-
-andreionea@google.com
-dariofreni@google.com
-hansson@google.com
-mathewi@google.com
-pedroql@google.com
-satayev@google.com
+file:platform/packages/modules/common:/OWNERS
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index bb94275..15052f8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.job.controllers;
 
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 
@@ -325,7 +324,7 @@
         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
-            if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+            if (bandwidth > 0) {
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -343,7 +342,7 @@
         if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
-            if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+            if (bandwidth > 0) {
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -373,18 +372,16 @@
 
     private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
-        final NetworkCapabilities required;
         // A restricted job that's out of quota MUST use an unmetered network.
         if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
                 && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
-            required = new NetworkCapabilities(
+            final NetworkCapabilities required = new NetworkCapabilities.Builder(
                     jobStatus.getJob().getRequiredNetwork().networkCapabilities)
-                    .addCapability(NET_CAPABILITY_NOT_METERED);
+                    .addCapability(NET_CAPABILITY_NOT_METERED).build();
+            return required.satisfiedByNetworkCapabilities(capabilities);
         } else {
-            required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+            return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
         }
-
-        return required.satisfiedByNetworkCapabilities(capabilities);
     }
 
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -395,9 +392,9 @@
         }
 
         // See if we match after relaxing any unmetered request
-        final NetworkCapabilities relaxed = new NetworkCapabilities(
+        final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
                 jobStatus.getJob().getRequiredNetwork().networkCapabilities)
-                        .removeCapability(NET_CAPABILITY_NOT_METERED);
+                        .removeCapability(NET_CAPABILITY_NOT_METERED).build();
         if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
             // TODO: treat this as "maybe" response; need to check quotas
             return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 8ab5273..2360ef12 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,4 +4,3 @@
 android.os.NullVibrator
 android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
 android.widget.Magnifier
-com.android.server.BootReceiver$2
diff --git a/core/api/current.txt b/core/api/current.txt
index 94f4d99..976ec56 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9140,7 +9140,7 @@
     method public boolean getIncludeTxPowerLevel();
     method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
     method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
-    method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
+    method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
     method public java.util.List<android.os.ParcelUuid> getServiceUuids();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -34325,6 +34325,55 @@
     field public static final String PATH_SETTING_INTENT = "intent";
   }
 
+  public final class SimPhonebookContract {
+    field public static final String AUTHORITY = "com.android.simphonebook";
+    field @NonNull public static final android.net.Uri AUTHORITY_URI;
+  }
+
+  public static final class SimPhonebookContract.ElementaryFiles {
+    method @NonNull public static android.net.Uri getItemUri(int, int);
+    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-elementary-file";
+    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+    field @NonNull public static final android.net.Uri CONTENT_URI;
+    field public static final int EF_ADN = 1; // 0x1
+    field public static final int EF_FDN = 2; // 0x2
+    field public static final int EF_SDN = 3; // 0x3
+    field public static final String EF_TYPE = "ef_type";
+    field public static final int EF_UNKNOWN = 0; // 0x0
+    field public static final String MAX_RECORDS = "max_records";
+    field public static final String NAME_MAX_LENGTH = "name_max_length";
+    field public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+    field public static final String RECORD_COUNT = "record_count";
+    field public static final String SLOT_INDEX = "slot_index";
+    field public static final String SUBSCRIPTION_ID = "subscription_id";
+  }
+
+  public static final class SimPhonebookContract.SimRecords {
+    method @NonNull public static android.net.Uri getContentUri(int, int);
+    method @NonNull public static android.net.Uri getItemUri(int, int, int);
+    method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
+    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+    field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+    field public static final String NAME = "name";
+    field public static final String PHONE_NUMBER = "phone_number";
+    field public static final String RECORD_NUMBER = "record_number";
+    field public static final String SUBSCRIPTION_ID = "subscription_id";
+  }
+
+  public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
+    ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
+    method public int describeContents();
+    method public int getEncodedLength();
+    method public int getMaxEncodedLength();
+    method @NonNull public String getName();
+    method @NonNull public String getSanitizedName();
+    method public boolean isSupportedCharacter(int);
+    method public boolean isValid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
+  }
+
   public class SyncStateContract {
     ctor public SyncStateContract();
   }
@@ -39687,6 +39736,7 @@
     field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
     field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
     field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+    field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
@@ -41163,7 +41213,9 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
+    method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
     method public void setSubscriptionOverrideUnmetered(int, boolean, long);
+    method public void setSubscriptionOverrideUnmetered(int, boolean, @NonNull int[], long);
     method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent);
     field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 72fcc72..fa712ba 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2131,6 +2131,7 @@
     field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+    field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
     field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
     field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
@@ -7592,6 +7593,7 @@
     method @NonNull public static String formatUid(int);
     method public static int getAppId(int);
     method public int getIdentifier();
+    method public static int getUid(@NonNull android.os.UserHandle, int);
     method @Deprecated public boolean isOwner();
     method public boolean isSystem();
     method public static int myUserId();
@@ -8289,6 +8291,24 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
   }
 
+  public final class SimPhonebookContract {
+    method @NonNull public static String getEfUriPath(int);
+    field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+  }
+
+  public static final class SimPhonebookContract.ElementaryFiles {
+    field public static final String EF_ADN_PATH_SEGMENT = "adn";
+    field public static final String EF_FDN_PATH_SEGMENT = "fdn";
+    field public static final String EF_SDN_PATH_SEGMENT = "sdn";
+    field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+  }
+
+  public static final class SimPhonebookContract.SimRecords {
+    field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
+    field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+    field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+  }
+
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final String APN_SET_ID = "apn_set_id";
     field public static final int CARRIER_EDITED = 4; // 0x4
@@ -8416,6 +8436,11 @@
     method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
   }
 
+  public abstract class KeyProperties {
+    field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff
+    field public static final int NAMESPACE_WIFI = 102; // 0x66
+  }
+
 }
 
 package android.security.keystore.recovery {
@@ -12375,11 +12400,13 @@
   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;
+    method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
   }
 
   public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
     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;
+    method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
     method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
     method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
     method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 546e72b..668b588 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
     field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
+    field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 573b932..fa7ac2b 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -44,7 +44,7 @@
     @Nullable
     private final List<ParcelUuid> mServiceUuids;
 
-    @Nullable
+    @NonNull
     private final List<ParcelUuid> mServiceSolicitationUuids;
 
     private final SparseArray<byte[]> mManufacturerSpecificData;
@@ -77,7 +77,7 @@
     /**
      * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
      */
-    @Nullable
+    @NonNull
     public List<ParcelUuid> getServiceSolicitationUuids() {
         return mServiceSolicitationUuids;
     }
@@ -221,7 +221,7 @@
     public static final class Builder {
         @Nullable
         private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
-        @Nullable
+        @NonNull
         private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
         private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
         private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 80a7b16..0581ed5 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.os.Parcel;
@@ -62,6 +63,7 @@
      * List of allowlisted activities.
      */
     @Nullable
+    @SuppressLint("NullableCollection")
     public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
 
     /**
@@ -73,6 +75,7 @@
      * The disabled Activities of the package. key is component name string, value is when they
      * will be enabled.
      */
+    @SuppressLint("NullableCollection")
     @Nullable
     public ArrayMap<String, Long> disabledActivities;
 
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index ef49e02..c296bb5 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.os.Parcel;
@@ -73,6 +74,7 @@
      * for all acitivites in the package).
      */
     @Nullable
+    @SuppressLint("NullableCollection")
     public final ArraySet<ComponentName> whitelistedComponents;
 
     /**
@@ -96,6 +98,7 @@
      */
     public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
             int textChangeFlushingFrequencyMs, int logHistorySize,
+            @SuppressLint("NullableCollection")
             @Nullable ArraySet<ComponentName> whitelistedComponents) {
         this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
                 textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 742f48c..04e0468 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -59,6 +59,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -71,6 +72,12 @@
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.permission.PermissionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaService;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDelegateManager;
 import android.util.AndroidException;
 import android.util.Log;
 
@@ -2602,6 +2609,37 @@
     public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports a single IMS registration as defined by carrier networks in the IMS service
+     * implementation using the {@link ImsService} API, {@link GbaService} API, and IRadio 1.6 HAL.
+     * <p>
+     * When set, the device must fully support the following APIs for an application to implement
+     * IMS single registration:
+     * <ul>
+     * <li> Updating RCS provisioning status using the {@link ProvisioningManager} API to supply an
+     * RCC.14 defined XML and notify IMS applications of Auto Configuration Server (ACS) or
+     * proprietary server provisioning updates.</li>
+     * <li>Opening a delegate in the device IMS service to forward SIP traffic to the carrier's
+     * network using the {@link SipDelegateManager} API</li>
+     * <li>Listening to EPS dedicated bearer establishment via the
+     * {@link ConnectivityManager#registerQosCallback}
+     * API to indicate to the application when to start/stop media traffic.</li>
+     * <li>Implementing Generic Bootstrapping Architecture (GBA) and providing the associated
+     * authentication keys to applications
+     * requesting this information via the {@link TelephonyManager#bootstrapAuthenticationRequest}
+     * API</li>
+     * <li>Implementing RCS User Capability Exchange using the {@link RcsUceAdapter} API</li>
+     * </ul>
+     * <p>
+     * This feature should only be defined if {@link #FEATURE_TELEPHONY_IMS} is also defined.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION =
+            "android.hardware.telephony.ims.singlereg";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * other devices via ultra wideband.
@@ -4963,7 +5001,7 @@
      *
      * @hide
      */
-    @SuppressWarnings("HiddenAbstractMethod")
+    @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"})
     @TestApi
     public abstract @Nullable String[] getNamesForUids(int[] uids);
 
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/inputmethodservice/OWNERS
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -2,3 +2,5 @@
 set noparent
 
 include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/net/DataUsageRequest.java b/core/java/android/net/DataUsageRequest.java
index 0ac8f7e7..b06d515 100644
--- a/core/java/android/net/DataUsageRequest.java
+++ b/core/java/android/net/DataUsageRequest.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.net.NetworkTemplate;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -95,7 +96,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof DataUsageRequest == false) return false;
         DataUsageRequest that = (DataUsageRequest) obj;
         return that.requestId == this.requestId
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index ef0a436..82ba156 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -160,7 +160,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof DhcpResults)) return false;
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index fe9141c..dfb1e99 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -23,6 +23,6 @@
     void onMeteredIfacesChanged(in String[] meteredIfaces);
     void onRestrictBackgroundChanged(boolean restrictBackground);
     void onUidPoliciesChanged(int uid, int uidPolicies);
-    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes);
     void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 29a3fdf..b016ed6 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,10 +76,11 @@
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
-    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
+    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long timeoutMillis, String callingPackage);
 
     void factoryReset(String subscriber);
 
     boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
     boolean isUidRestrictedOnMeteredNetworks(int uid);
+    boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
 }
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 63fc5f2..183f500 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -360,7 +360,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof Ikev2VpnProfile)) {
             return false;
         }
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index aa7811a..b48c1fd 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -19,6 +19,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -150,7 +151,7 @@
     /**
      * Standard equals.
      */
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) return true;
         if (!(other instanceof IpSecTransform)) return false;
         final IpSecTransform rhs = (IpSecTransform) other;
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index 011a04c..9a11be0 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -44,7 +45,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return o instanceof MatchAllNetworkSpecifier;
     }
 
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 521ed9b..5d8122b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -18,6 +18,7 @@
 
 import static android.net.ConnectivityManager.TYPE_WIFI;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
@@ -67,7 +68,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkIdentity) {
             final NetworkIdentity ident = (NetworkIdentity) obj;
             return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 4f05c9b..8a0211c 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -205,7 +206,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkPolicy) {
             final NetworkPolicy other = (NetworkPolicy) obj;
             return warningBytes == other.warningBytes
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 82b035b..11146bd 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -34,6 +34,7 @@
 import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
+import android.telephony.Annotation;
 import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
 import android.util.Pair;
@@ -377,6 +378,8 @@
      * @param overrideMask the bitmask that specifies which of the overrides is being
      *            set or cleared.
      * @param overrideValue the override values to set or clear.
+     * @param networkTypes the network types this override applies to.
+     *            {@see TelephonyManager#getAllNetworkTypes()}
      * @param timeoutMillis the timeout after which the requested override will
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
@@ -385,11 +388,12 @@
      * @hide
      */
     public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
-            @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
-                    @NonNull String callingPackage) {
+            @SubscriptionOverrideMask int overrideValue,
+            @NonNull @Annotation.NetworkType int[] networkTypes, long timeoutMillis,
+            @NonNull String callingPackage) {
         try {
-            mService.setSubscriptionOverride(subId, overrideMask, overrideValue, timeoutMillis,
-                    callingPackage);
+            mService.setSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes,
+                    timeoutMillis, callingPackage);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -460,6 +464,31 @@
     }
 
     /**
+     * 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
+     * take any locks.
+     *
+     * @param uid The target uid.
+     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+     * @param isNetworkMetered True if the network is metered.
+     * @param isBackgroundRestricted True if data saver is enabled.
+     *
+     * @return true if networking is blocked for the UID under the specified conditions.
+     *
+     * @hide
+     */
+    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+            boolean isNetworkMetered, boolean isBackgroundRestricted) {
+        try {
+            return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+                    isBackgroundRestricted);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check that the given uid is restricted from doing networking on metered networks.
      *
      * @param uid The target uid.
@@ -613,9 +642,10 @@
          * @param subId the subscriber this override applies to.
          * @param overrideMask a bitmask that specifies which of the overrides is set.
          * @param overrideValue a bitmask that specifies the override values.
+         * @param networkTypes the network types this override applies to.
          */
         public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
-                @SubscriptionOverrideMask int overrideValue) {}
+                @SubscriptionOverrideMask int overrideValue, int[] networkTypes) {}
 
         /**
          * Notify of subscription plans change about a given subscription.
@@ -639,8 +669,8 @@
 
         @Override
         public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
-                @SubscriptionOverrideMask int overrideValue) {
-            mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue);
+                @SubscriptionOverrideMask int overrideValue, int[] networkTypes) {
+            mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
         }
 
         @Override
@@ -656,7 +686,7 @@
         @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
         @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
         @Override public void onSubscriptionOverride(int subId, int overrideMask,
-                int overrideValue) { }
+                int overrideValue, int[] networkTypes) { }
         @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
     }
 }
diff --git a/core/java/android/net/NetworkScorerAppData.java b/core/java/android/net/NetworkScorerAppData.java
index 116e39e..caa6e45 100644
--- a/core/java/android/net/NetworkScorerAppData.java
+++ b/core/java/android/net/NetworkScorerAppData.java
@@ -110,7 +110,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NetworkScorerAppData that = (NetworkScorerAppData) o;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index cf40ce5..d42beae 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -412,7 +412,7 @@
 
         /** @hide */
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (o instanceof Entry) {
                 final Entry e = (Entry) o;
                 return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 2322048..aa61e03 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -329,7 +329,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkTemplate) {
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 6a8e3f9..5e56164 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -18,14 +18,14 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Parcelable;
-import android.util.SparseArray;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
 
 /** @hide */
@@ -60,16 +60,16 @@
     public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
 
     @NonNull
-    private final SparseArray<List<String>> mNetworkMappings;
+    private final Bundle mNetworkMappings;
 
     @NonNull
-    public SparseArray<List<String>> getNetworkPreferences() {
-        return mNetworkMappings.clone();
+    public Map<String, Integer> getNetworkPreferences() {
+        return convertToUnmodifiableMap(mNetworkMappings);
     }
 
-    private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) {
+    private OemNetworkPreferences(@NonNull final Bundle networkMappings) {
         Objects.requireNonNull(networkMappings);
-        mNetworkMappings = networkMappings.clone();
+        mNetworkMappings = (Bundle) networkMappings.clone();
     }
 
     @Override
@@ -99,26 +99,45 @@
      * @hide
      */
     public static final class Builder {
-        private final SparseArray<List<String>> mNetworkMappings;
+        private final Bundle mNetworkMappings;
 
         public Builder() {
-            mNetworkMappings = new SparseArray<>();
+            mNetworkMappings = new Bundle();
+        }
+
+        public Builder(@NonNull final OemNetworkPreferences preferences) {
+            Objects.requireNonNull(preferences);
+            mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
         }
 
         /**
-         * Add a network preference for a list of packages.
+         * Add a network preference for a given package. Previously stored values for the given
+         * package will be overwritten.
          *
-         * @param preference the desired network preference to use
-         * @param packages   full package names (e.g.: "com.google.apps.contacts") for apps to use
-         *                   the given preference
+         * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app
+         *                    to use the given preference
+         * @param preference  the desired network preference to use
          * @return The builder to facilitate chaining.
          */
         @NonNull
-        public Builder addNetworkPreference(@OemNetworkPreference final int preference,
-                @NonNull List<String> packages) {
-            Objects.requireNonNull(packages);
-            mNetworkMappings.put(preference,
-                    Collections.unmodifiableList(new ArrayList<>(packages)));
+        public Builder addNetworkPreference(@NonNull final String packageName,
+                @OemNetworkPreference final int preference) {
+            Objects.requireNonNull(packageName);
+            mNetworkMappings.putInt(packageName, preference);
+            return this;
+        }
+
+        /**
+         * Remove a network preference for a given package.
+         *
+         * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to
+         *                    remove a preference for.
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        public Builder removeNetworkPreference(@NonNull final String packageName) {
+            Objects.requireNonNull(packageName);
+            mNetworkMappings.remove(packageName);
             return this;
         }
 
@@ -131,6 +150,14 @@
         }
     }
 
+    private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) {
+        final Map<String, Integer> networkPreferences = new HashMap<>();
+        for (final String key : bundle.keySet()) {
+            networkPreferences.put(key, bundle.getInt(key));
+        }
+        return Collections.unmodifiableMap(networkPreferences);
+    }
+
     /** @hide */
     @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
             OEM_NETWORK_PREFERENCE_DEFAULT,
@@ -168,7 +195,7 @@
 
     @Override
     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        dest.writeSparseArray(mNetworkMappings);
+        dest.writeBundle(mNetworkMappings);
     }
 
     @Override
@@ -187,7 +214,7 @@
                 @Override
                 public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) {
                     return new OemNetworkPreferences(
-                            in.readSparseArray(getClass().getClassLoader()));
+                            in.readBundle(getClass().getClassLoader()));
                 }
             };
 }
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 3f2aa17..012410b 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -45,7 +46,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof StringNetworkSpecifier)) return false;
         return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier);
     }
diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java
index 33c71d5..3348233 100644
--- a/core/java/android/net/TelephonyNetworkSpecifier.java
+++ b/core/java/android/net/TelephonyNetworkSpecifier.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -74,7 +75,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) {
             return true;
         }
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index d75c43d..3bc0f9c 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -18,6 +18,7 @@
 
 import static android.os.UserHandle.PER_USER_RANGE;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -81,7 +82,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (this == o) {
             return true;
         }
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 1cb4fe8..ffe4f3c 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -343,7 +343,7 @@
      * default port explicitly and the other leaves it implicit, they will not
      * be considered equal.
      */
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof Uri)) {
             return false;
         }
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index bef876f9..3d539a6 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -295,6 +295,19 @@
     }
 
     /**
+     * Returns the uid that is composed from the userHandle and the appId.
+     *
+     * @param userHandle the UserHandle to compose the uid
+     * @param appId the AppId to compose the uid
+     * @return the uid that is composed from the userHandle and the appId
+     * @hide
+     */
+    @SystemApi
+    public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) {
+        return getUid(userHandle.getIdentifier(), appId);
+    }
+
+    /**
      * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
      * @hide
      */
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index cb1509a..cb06515 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,5 +1,6 @@
 per-file *BlockedNumber* = file:/telephony/OWNERS
 per-file *Telephony* = file:/telephony/OWNERS
+per-file *SimPhonebook* = file:/telephony/OWNERS
 
 per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
 per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
new file mode 100644
index 0000000..2efc212
--- /dev/null
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -0,0 +1,506 @@
+/*
+ * 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.provider;
+
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The contract between the provider of contact records on the device's SIM cards and applications.
+ * Contains definitions of the supported URIs and columns.
+ *
+ * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
+ * IllegalArgumentException will be thrown if these are included.
+ */
+public final class SimPhonebookContract {
+
+    /** The authority for the SIM phonebook provider. */
+    public static final String AUTHORITY = "com.android.simphonebook";
+    /** The content:// style uri to the authority for the SIM phonebook provider. */
+    @NonNull
+    public static final Uri AUTHORITY_URI = Uri.parse("content://com.android.simphonebook");
+    /**
+     * The Uri path element used to indicate that the following path segment is a subscription ID
+     * for the SIM card that will be operated on.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+
+    private SimPhonebookContract() {
+    }
+
+    /**
+     * Returns the Uri path segment used to reference the specified elementary file type for Uris
+     * returned by this API.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
+        switch (efType) {
+            case EF_ADN:
+                return EF_ADN_PATH_SEGMENT;
+            case EF_FDN:
+                return EF_FDN_PATH_SEGMENT;
+            case EF_SDN:
+                return EF_SDN_PATH_SEGMENT;
+            default:
+                throw new IllegalArgumentException("Unsupported EfType " + efType);
+        }
+    }
+
+    /** Constants for the contact records on a SIM card. */
+    public static final class SimRecords {
+
+        /**
+         * The subscription ID of the SIM the record is from.
+         *
+         * @see SubscriptionInfo#getSubscriptionId()
+         */
+        public static final String SUBSCRIPTION_ID = "subscription_id";
+        /**
+         * The type of the elementary file the record is from.
+         *
+         * @see ElementaryFiles#EF_ADN
+         * @see ElementaryFiles#EF_FDN
+         * @see ElementaryFiles#EF_SDN
+         */
+        public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+        /**
+         * The 1-based offset of the record in the elementary file that contains it.
+         *
+         * <p>This can be used to access individual SIM records by appending it to the
+         * elementary file URIs but it is not like a normal database ID because it is not
+         * auto-incrementing and it is not unique across SIM cards or elementary files. Hence, care
+         * should be taken when using it to ensure that it is applied to the correct SIM and EF.
+         *
+         * @see #getItemUri(int, int, int)
+         */
+        public static final String RECORD_NUMBER = "record_number";
+        /**
+         * The name for this record.
+         *
+         * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+         * exceeds the maximum supported length or contains unsupported characters.
+         * {@link #validateName(ContentResolver, int, int, String)} )} can be used to
+         * check whether the name is supported.
+         *
+         * @see ElementaryFiles#NAME_MAX_LENGTH
+         * @see #validateName(ContentResolver, int, int, String) )
+         */
+        public static final String NAME = "name";
+        /**
+         * The phone number for this record.
+         *
+         * <p>Only dialable characters are supported.
+         *
+         * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+         * exceeds the maximum supported length or contains unsupported characters.
+         *
+         * @see ElementaryFiles#PHONE_NUMBER_MAX_LENGTH
+         * @see android.telephony.PhoneNumberUtils#isDialable(char)
+         */
+        public static final String PHONE_NUMBER = "phone_number";
+
+        /** The MIME type of a CONTENT_URI subdirectory of a single SIM record. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+        /** The MIME type of CONTENT_URI providing a directory of SIM records. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+
+        /**
+         * The path segment that is appended to {@link #getContentUri(int, int)} which indicates
+         * that the following path segment contains a name to be validated.
+         *
+         * @hide
+         * @see #validateName(ContentResolver, int, int, String)
+         */
+        @SystemApi
+        public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+
+        /**
+         * The key for a cursor extra that contains the result of a validate name query.
+         *
+         * @hide
+         * @see #validateName(ContentResolver, int, int, String)
+         */
+        @SystemApi
+        public static final String EXTRA_NAME_VALIDATION_RESULT =
+                "android.provider.extra.NAME_VALIDATION_RESULT";
+
+
+        /**
+         * Key for the PIN2 needed to modify FDN record that should be passed in the Bundle
+         * passed to {@link ContentResolver#insert(Uri, ContentValues, Bundle)},
+         * {@link ContentResolver#update(Uri, ContentValues, Bundle)}
+         * and {@link ContentResolver#delete(Uri, Bundle)}.
+         *
+         * <p>Modifying FDN records also requires either
+         * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
+         * {@link TelephonyManager#hasCarrierPrivileges()}
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+
+        private SimRecords() {
+        }
+
+        /**
+         * Returns the content Uri for the specified elementary file on the specified SIM.
+         *
+         * <p>When queried this Uri will return all of the contact records in the specified
+         * elementary file on the specified SIM. The available subscriptionIds and efTypes can
+         * be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
+         *
+         * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
+         * subscription ID doesn't support the specified entity file then queries will return
+         * and empty cursor and inserts will throw an {@link IllegalArgumentException}
+         *
+         * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
+         * @param efType         the elementary file on the SIM that this Uri will reference
+         * @see ElementaryFiles#EF_ADN
+         * @see ElementaryFiles#EF_FDN
+         * @see ElementaryFiles#EF_SDN
+         */
+        @NonNull
+        public static Uri getContentUri(int subscriptionId, @ElementaryFiles.EfType int efType) {
+            return buildContentUri(subscriptionId, efType).build();
+        }
+
+        /**
+         * Content Uri for the specific SIM record with the provided {@link #RECORD_NUMBER}.
+         *
+         * <p>When queried this will return the record identified by the provided arguments.
+         *
+         * <p>For a non-existent record:
+         * <ul>
+         *     <li>query will return an empty cursor</li>
+         *     <li>update will return 0</li>
+         *     <li>delete will return 0</li>
+         * </ul>
+         *
+         * @param subscriptionId the subscription ID of the SIM containing the record. If no SIM
+         *                       with this subscription ID exists then it will be treated as a
+         *                       non-existent record
+         * @param efType         the elementary file type containing the record. If the specified
+         *                       SIM doesn't support this elementary file then it will be treated
+         *                       as a non-existent record.
+         * @param recordNumber   the record number of the record this Uri should reference. This
+         *                       must be greater than 0. If there is no record with this record
+         *                       number in the specified entity file then it will be treated as a
+         *                       non-existent record.
+         */
+        @NonNull
+        public static Uri getItemUri(
+                int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+            // Elementary file record indices are 1-based.
+            Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
+
+            return buildContentUri(subscriptionId, efType)
+                    .appendPath(String.valueOf(recordNumber))
+                    .build();
+        }
+
+        /**
+         * Validates a value that is being provided for the {@link #NAME} column.
+         *
+         * <p>The return value can be used to check if the name is valid. If it is not valid then
+         * inserts and updates to the specified elementary file that use the provided name value
+         * will throw an {@link IllegalArgumentException}.
+         *
+         * <p>If the specified SIM or elementary file don't exist then
+         * {@link NameValidationResult#getMaxEncodedLength()} will be zero and
+         * {@link NameValidationResult#isValid()} will return false.
+         */
+        @NonNull
+        @WorkerThread
+        public static NameValidationResult validateName(
+                @NonNull ContentResolver resolver, int subscriptionId,
+                @ElementaryFiles.EfType int efType,
+                @NonNull String name) {
+            Bundle queryArgs = new Bundle();
+            queryArgs.putString(SimRecords.NAME, name);
+            try (Cursor cursor =
+                         resolver.query(buildContentUri(subscriptionId, efType)
+                                 .appendPath(VALIDATE_NAME_PATH_SEGMENT)
+                                 .build(), null, queryArgs, null)) {
+                NameValidationResult result = cursor.getExtras()
+                        .getParcelable(EXTRA_NAME_VALIDATION_RESULT);
+                return result != null ? result : new NameValidationResult(name, "", 0, 0);
+            }
+        }
+
+        private static Uri.Builder buildContentUri(
+                int subscriptionId, @ElementaryFiles.EfType int efType) {
+            return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(AUTHORITY)
+                    .appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+                    .appendPath(String.valueOf(subscriptionId))
+                    .appendPath(getEfUriPath(efType));
+        }
+
+        /** Contains details about the validity of a value provided for the {@link #NAME} column. */
+        public static final class NameValidationResult implements Parcelable {
+
+            @NonNull
+            public static final Creator<NameValidationResult> CREATOR =
+                    new Creator<NameValidationResult>() {
+
+                        @Override
+                        public NameValidationResult createFromParcel(@NonNull Parcel in) {
+                            return new NameValidationResult(in);
+                        }
+
+                        @NonNull
+                        @Override
+                        public NameValidationResult[] newArray(int size) {
+                            return new NameValidationResult[size];
+                        }
+                    };
+
+            private final String mName;
+            private final String mSanitizedName;
+            private final int mEncodedLength;
+            private final int mMaxEncodedLength;
+
+            /** Creates a new instance from the provided values. */
+            public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
+                    int encodedLength, int maxEncodedLength) {
+                this.mName = Objects.requireNonNull(name);
+                this.mSanitizedName = Objects.requireNonNull(sanitizedName);
+                this.mEncodedLength = encodedLength;
+                this.mMaxEncodedLength = maxEncodedLength;
+            }
+
+            private NameValidationResult(Parcel in) {
+                this(in.readString(), in.readString(), in.readInt(), in.readInt());
+            }
+
+            /** Returns the original name that is being validated. */
+            @NonNull
+            public String getName() {
+                return mName;
+            }
+
+            /**
+             * Returns a sanitized copy of the original name with all unsupported characters
+             * replaced with spaces.
+             */
+            @NonNull
+            public String getSanitizedName() {
+                return mSanitizedName;
+            }
+
+            /**
+             * Returns whether the original name isValid.
+             *
+             * <p>If this returns false then inserts and updates using the name will throw an
+             * {@link IllegalArgumentException}
+             */
+            public boolean isValid() {
+                return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
+                        && Objects.equals(
+                        mName, mSanitizedName);
+            }
+
+            /** Returns whether the character at the specified position is supported by the SIM. */
+            public boolean isSupportedCharacter(int position) {
+                return mName.charAt(position) == mSanitizedName.charAt(position);
+            }
+
+            /**
+             * Returns the number of bytes required to save the name.
+             *
+             * <p>This may be more than the number of characters in the name.
+             */
+            public int getEncodedLength() {
+                return mEncodedLength;
+            }
+
+            /**
+             * Returns the maximum number of bytes that are supported for the name.
+             *
+             * @see ElementaryFiles#NAME_MAX_LENGTH
+             */
+            public int getMaxEncodedLength() {
+                return mMaxEncodedLength;
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+
+            @Override
+            public void writeToParcel(@NonNull Parcel dest, int flags) {
+                dest.writeString(mName);
+                dest.writeString(mSanitizedName);
+                dest.writeInt(mEncodedLength);
+                dest.writeInt(mMaxEncodedLength);
+            }
+        }
+    }
+
+    /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+    public static final class ElementaryFiles {
+
+        /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
+        public static final String SLOT_INDEX = "slot_index";
+        /** {@link SubscriptionInfo#getSubscriptionId()} of the SIM for this row. */
+        public static final String SUBSCRIPTION_ID = "subscription_id";
+        /**
+         * The elementary file type for this row.
+         *
+         * @see ElementaryFiles#EF_ADN
+         * @see ElementaryFiles#EF_FDN
+         * @see ElementaryFiles#EF_SDN
+         */
+        public static final String EF_TYPE = "ef_type";
+        /** The maximum number of records supported by the elementary file. */
+        public static final String MAX_RECORDS = "max_records";
+        /** Count of the number of records that are currently stored in the elementary file. */
+        public static final String RECORD_COUNT = "record_count";
+        /** The maximum length supported for the name of a record in the elementary file. */
+        public static final String NAME_MAX_LENGTH = "name_max_length";
+        /**
+         * The maximum length supported for the phone number of a record in the elementary file.
+         */
+        public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+
+        /**
+         * A value for an elementary file that is not recognized.
+         *
+         * <p>Generally this should be ignored. If new values are added then this will be used
+         * for apps that target SDKs where they aren't defined.
+         */
+        public static final int EF_UNKNOWN = 0;
+        /**
+         * Type for accessing records in the "abbreviated dialing number" (ADN) elementary file on
+         * the SIM.
+         *
+         * <p>ADN records are typically user created.
+         */
+        public static final int EF_ADN = 1;
+        /**
+         * Type for accessing records in the "fixed dialing number" (FDN) elementary file on the
+         * SIM.
+         *
+         * <p>FDN numbers are the numbers that are allowed to dialed for outbound calls when FDN is
+         * enabled.
+         *
+         * <p>FDN records cannot be modified by applications. Hence, insert, update and
+         * delete methods operating on this Uri will throw UnsupportedOperationException
+         */
+        public static final int EF_FDN = 2;
+        /**
+         * Type for accessing records in the "service dialing number" (SDN) elementary file on the
+         * SIM.
+         *
+         * <p>Typically SDNs are preset numbers provided by the carrier for common operations (e.g.
+         * voicemail, check balance, etc).
+         *
+         * <p>SDN records cannot be modified by applications. Hence, insert, update and delete
+         * methods operating on this Uri will throw UnsupportedOperationException
+         */
+        public static final int EF_SDN = 3;
+        /** @hide */
+        @SystemApi
+        public static final String EF_ADN_PATH_SEGMENT = "adn";
+        /** @hide */
+        @SystemApi
+        public static final String EF_FDN_PATH_SEGMENT = "fdn";
+        /** @hide */
+        @SystemApi
+        public static final String EF_SDN_PATH_SEGMENT = "sdn";
+        /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+        /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/sim-elementary-file";
+        /**
+         * The Uri path segment used to construct Uris for the metadata defined in this class.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+
+        /** Content URI for the ADN-like elementary files available on the device. */
+        @NonNull
+        public static final Uri CONTENT_URI = AUTHORITY_URI
+                .buildUpon()
+                .appendPath(ELEMENTARY_FILES_PATH_SEGMENT).build();
+
+        private ElementaryFiles() {
+        }
+
+        /**
+         * Returns a content uri for a specific elementary file.
+         *
+         * <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
+         * If the SIM doesn't support the specified elementary file it will have a zero value for
+         * {@link #MAX_RECORDS}.
+         */
+        @NonNull
+        public static Uri getItemUri(int subscriptionId, @EfType int efType) {
+            return CONTENT_URI.buildUpon().appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+                    .appendPath(String.valueOf(subscriptionId))
+                    .appendPath(getEfUriPath(efType))
+                    .build();
+        }
+
+        /**
+         * Annotation for the valid elementary file types.
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                prefix = {"EF"},
+                value = {EF_UNKNOWN, EF_ADN, EF_FDN, EF_SDN})
+        public @interface EfType {
+        }
+    }
+}
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96..ad49ffd 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@
      * Implementation for wrapping the opaque blob used for resume-on-reboot prior to
      * reboot. The service should not assume any structure of the blob to be wrapped. The
      * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
-     * if it's unable to complete the action.
+     * if it's unable to complete the action due to retry-able errors (e.g network errors)
+     * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+     * (e.g corrupted blob).
      *
      * @param blob             The opaque blob with size on the order of 100 bytes.
      * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@
      *                         this function after expiration should
      *                         fail.
      * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
-     * @throws IOException if the implementation is unable to wrap the blob successfully.
+     * @throws IOException if the implementation is unable to wrap the blob successfully due to
+     * retry-able errors.
      */
     @NonNull
     public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@
      * operation would happen after reboot during direct boot mode (i.e before device is unlocked
      * for the first time). The implementation should unwrap the wrapped blob in a reasonable time
      * and returns the result or throw {@link IOException} if it's unable to complete the action
-     * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
-     * stale.
+     * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+     * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
      *
      * @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
      * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
-     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+     * due to retry-able errors.
      */
     @NonNull
     public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index f8500fa..6511a94 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -149,7 +149,7 @@
      * <p>
      * The time value that was used in all the vsync listeners and drawing for
      * the frame (Choreographer frame callbacks, animations,
-     * {@link View#getDrawingTime()}, etc…)
+     * {@link View#getDrawingTime()}, etc.)
      * </p>
      */
     public static final int VSYNC_TIMESTAMP = 11;
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 1c703ec..a17c012 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -82,6 +82,7 @@
     public static InlineSuggestionInfo newInlineSuggestionInfo(
             @NonNull InlinePresentationSpec presentationSpec,
             @NonNull @Source String source,
+            @SuppressLint("NullableCollection")
             @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
         return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
     }
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/view/inputmethod/OWNERS
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -2,3 +2,5 @@
 set noparent
 
 include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 502680d..7b4fcbf 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.os.RemoteException;
@@ -101,6 +102,7 @@
     /** Gets direct child tasks (ordered from top-to-bottom) */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     @Nullable
+    @SuppressLint("NullableCollection")
     public static List<ActivityManager.RunningTaskInfo> getChildTasks(
             @NonNull WindowContainerToken parent, @NonNull int[] activityTypes) {
         try {
@@ -113,6 +115,7 @@
     /** Gets all root tasks on a display (ordered from top-to-bottom) */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     @Nullable
+    @SuppressLint("NullableCollection")
     public static List<ActivityManager.RunningTaskInfo> getRootTasks(
             int displayId, @NonNull int[] activityTypes) {
         try {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 16991b4..1270185 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1210,25 +1210,6 @@
                         if (TextUtils.isEmpty(packageName)) {
                             pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId);
                         }
-                    } else {
-                        // Update Domain Verification status
-                        ComponentName cn = intent.getComponent();
-                        String packageName = cn.getPackageName();
-                        String dataScheme = (data != null) ? data.getScheme() : null;
-
-                        boolean isHttpOrHttps = (dataScheme != null) &&
-                                (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
-                                        dataScheme.equals(IntentFilter.SCHEME_HTTPS));
-
-                        boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
-                        boolean hasCategoryBrowsable = (categories != null) &&
-                                categories.contains(Intent.CATEGORY_BROWSABLE);
-
-                        if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
-                            pm.updateIntentVerificationStatusAsUser(packageName,
-                                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
-                                    userId);
-                        }
                     }
                 } else {
                     try {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0381a75..527d10f 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -103,7 +103,7 @@
      */
     public static final int PROFILE_FROM_SHELL = 1 << 15;
 
-    /*
+    /**
      * Enable using the ART app image startup cache
      */
     public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
@@ -116,13 +116,6 @@
      */
     public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
 
-    /**
-     * Disable runtime access to {@link android.annotation.TestApi} annotated members.
-     *
-     * <p>This only takes effect if Hidden API access restrictions are enabled as well.
-     */
-    public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
-
     public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
     /**
      * Enable pointer tagging in this process.
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 0dd8b59..db7cbbc 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -492,10 +492,12 @@
                 long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
 
                 if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
-                    // Normalize the poll timeout value when the time between one poll event and the
-                    // next pushes us over the delay value.  This prevents poll receiving a 0
-                    // timeout value, which would result in it returning immediately.
-                    pollTimeoutMs = -1;
+                    // The refill delay has elapsed during the period between poll invocations.
+                    // We will now check for any currently ready file descriptors before refilling
+                    // the USAP pool.
+                    pollTimeoutMs = 0;
+                    mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+                    mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
 
                 } else if (elapsedTimeMs <= 0) {
                     // This can occur if the clock used by currentTimeMillis is reset, which is
@@ -517,9 +519,11 @@
             }
 
             if (pollReturnValue == 0) {
-                // The poll timeout has been exceeded.  This only occurs when we have finished the
-                // USAP pool refill delay period.
-
+                // The poll returned zero results either when the timeout value has been exceeded
+                // or when a non-blocking poll is issued and no FDs are ready.  In either case it
+                // is time to refill the pool.  This will result in a duplicate assignment when
+                // the non-blocking poll returns zero results, but it avoids an additional
+                // conditional in the else branch.
                 mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                 mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
 
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index 3fc3933..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -11,6 +11,7 @@
 per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
 per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index ac2361d..c46d708 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 import android.os.DropBoxManager;
 import android.os.Environment;
-import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.RecoverySystem;
 import android.os.RemoteException;
@@ -75,7 +74,6 @@
         SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
     private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
 
-    private static final File TOMBSTONE_DIR = new File("/data/tombstones");
     private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
 
     // The pre-froyo package and class of the system updater, which
@@ -86,9 +84,6 @@
     private static final String OLD_UPDATER_CLASS =
         "com.google.android.systemupdater.SystemUpdateReceiver";
 
-    // Keep a reference to the observer so the finalizer doesn't disable it.
-    private static FileObserver sTombstoneObserver = null;
-
     private static final String LOG_FILES_FILE = "log-files.xml";
     private static final AtomicFile sFile = new AtomicFile(new File(
             Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -154,7 +149,7 @@
         Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
     }
 
-    private String getPreviousBootHeaders() {
+    private static String getPreviousBootHeaders() {
         try {
             return FileUtils.readTextFile(lastHeaderFile, 0, null);
         } catch (IOException e) {
@@ -162,7 +157,7 @@
         }
     }
 
-    private String getCurrentBootHeaders() throws IOException {
+    private static String getCurrentBootHeaders() throws IOException {
         return new StringBuilder(512)
             .append("Build: ").append(Build.FINGERPRINT).append("\n")
             .append("Hardware: ").append(Build.BOARD).append("\n")
@@ -176,7 +171,7 @@
     }
 
 
-    private String getBootHeadersToLogAndUpdate() throws IOException {
+    private static String getBootHeadersToLogAndUpdate() throws IOException {
         final String oldHeaders = getPreviousBootHeaders();
         final String newHeaders = getCurrentBootHeaders();
 
@@ -248,38 +243,27 @@
         logFsMountTime();
         addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
         logSystemServerShutdownTimeMetrics();
-
-        // Scan existing tombstones (in case any new ones appeared)
-        File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
-        for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
-            if (tombstoneFiles[i].isFile()) {
-                addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
-                        LOG_SIZE, "SYSTEM_TOMBSTONE");
-            }
-        }
-
         writeTimestamps(timestamps);
+    }
 
-        // Start watching for new tombstone files; will record them as they occur.
-        // This gets registered with the singleton file observer thread.
-        sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
-            @Override
-            public void onEvent(int event, String path) {
-                HashMap<String, Long> timestamps = readTimestamps();
-                try {
-                    File file = new File(TOMBSTONE_DIR, path);
-                    if (file.isFile() && file.getName().startsWith("tombstone_")) {
-                        addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
-                                TAG_TOMBSTONE);
-                    }
-                } catch (IOException e) {
-                    Slog.e(TAG, "Can't log tombstone", e);
-                }
-                writeTimestamps(timestamps);
-            }
-        };
-
-        sTombstoneObserver.startWatching();
+    /**
+     * Add a tombstone to the DropBox.
+     *
+     * @param ctx Context
+     * @param tombstone path to the tombstone
+     */
+    public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+        final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+        final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+        HashMap<String, Long> timestamps = readTimestamps();
+        try {
+            final String headers = getBootHeadersToLogAndUpdate();
+            addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+                    TAG_TOMBSTONE);
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't log tombstone", e);
+        }
+        writeTimestamps(timestamps);
     }
 
     private static void addLastkToDropBox(
@@ -764,7 +748,7 @@
         }
     }
 
-    private void writeTimestamps(HashMap<String, Long> timestamps) {
+    private static void writeTimestamps(HashMap<String, Long> timestamps) {
         synchronized (sFile) {
             final FileOutputStream stream;
             try {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 748b4b4..99fd215 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,7 @@
 jjaggi@google.com
 roosa@google.com
 per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 
 # Biometrics
 kchyn@google.com
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
new file mode 100644
index 0000000..d341c4b
--- /dev/null
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.server.apphibernation;
+
+option java_multiple_files = true;
+
+// Proto for hibernation states for all packages for a user.
+message UserLevelHibernationStatesProto {
+  repeated UserLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.UserLevelState.
+message UserLevelHibernationStateProto {
+  optional string package_name = 1;
+  optional bool hibernated = 2;
+}
+
+// Proto for global hibernation states for all packages.
+message GlobalLevelHibernationStatesProto {
+  repeated GlobalLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.GlobalLevelState
+message GlobalLevelHibernationStateProto {
+  optional string package_name = 1;
+  optional bool hibernated = 2;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0e80c7b..af6b973 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2499,6 +2499,10 @@
     <permission android:name="android.permission.CREATE_USERS"
         android:protectionLevel="signature" />
 
+    <!-- @TestApi @hide Allows an application to query user info for all users on the device. -->
+    <permission android:name="android.permission.QUERY_USERS"
+                android:protectionLevel="signature" />
+
     <!-- @hide Allows an application to set the profile owners and the device owner.
          This permission is not available to third party applications.-->
     <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
diff --git a/core/tests/coretests/assets/fonts/OWNERS b/core/tests/coretests/assets/fonts/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/OWNERS b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/res/font/OWNERS b/core/tests/coretests/res/font/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/res/font/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
new file mode 100644
index 0000000..be38260
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimPhonebookContractTest {
+
+    @Test
+    public void getContentUri_invalidEfType_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getContentUri(1, 100)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getContentUri(1, -1)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getContentUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_UNKNOWN)
+        );
+    }
+
+    @Test
+    public void getItemUri_invalidEfType_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1, 100, 1)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1, -1, 1)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_UNKNOWN, 1)
+        );
+    }
+
+    @Test
+    public void getItemUri_invalidRecordIndex_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_ADN, 0)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
+        );
+    }
+
+    @Test
+    public void nameValidationResult_isValid_validNames() {
+        assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
+        assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
+        assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
+        assertThat(
+                new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
+    }
+
+    @Test
+    public void nameValidationResult_isValid_invalidNames() {
+        assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
+        assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
+        NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
+                "A b c", 5, 5);
+        assertThat(unsupportedCharactersResult.isValid()).isFalse();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
+    }
+
+    @Test
+    public void nameValidationResult_parcel() {
+        ContentValues values = new ContentValues();
+        values.put("name", "Name");
+        values.put("phone_number", "123");
+
+        NameValidationResult result;
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
+            parcel.setDataPosition(0);
+            result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
+        } finally {
+            parcel.recycle();
+        }
+
+        assertThat(result.getName()).isEqualTo("name");
+        assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
+        assertThat(result.getEncodedLength()).isEqualTo(1);
+        assertThat(result.getMaxEncodedLength()).isEqualTo(2);
+    }
+}
+
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a664ee3..e6ab5c4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -446,6 +446,7 @@
         <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
         <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
         <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
+        <permission name="android.permission.MODIFY_QUIET_MODE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 97da3cc..1ae6a63 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -46,6 +46,7 @@
     boolean installKeyPair(
         in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
     boolean removeKeyPair(String alias);
+    boolean containsKeyPair(String alias);
 
     // APIs used by Settings
     boolean deleteCaCertificate(String alias);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b1b6306..16bf546 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,7 +23,6 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterDefs;
-import android.sysprop.Keystore2Properties;
 
 import java.io.IOException;
 import java.security.KeyFactory;
@@ -117,8 +116,6 @@
         putSecretKeyFactoryImpl("HmacSHA512");
     }
 
-    private static boolean sKeystore2Enabled;
-
     /**
      * This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
      * Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
@@ -133,10 +130,9 @@
      * @hide
      */
     public static boolean isKeystore2Enabled() {
-        return sKeystore2Enabled;
+        return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
     }
 
-
     /**
      * Installs a new instance of this provider (and the
      * {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -164,11 +160,6 @@
             // priority.
             Security.addProvider(workaroundProvider);
         }
-
-        // {@code install()} is run by zygote when this property is still accessible. We store its
-        // value so that the Keystore SPI can act accordingly without having to access an internal
-        // property.
-        sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
     }
 
     private void putSecretKeyFactoryImpl(String algorithm) {
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index fa4f8b1..3ebca6a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Process;
 import android.security.KeyStore;
 import android.security.keymaster.KeymasterDefs;
 
@@ -874,9 +876,18 @@
      * which it must be configured in SEPolicy.
      * @hide
      */
+    @SystemApi
     public static final int NAMESPACE_APPLICATION = -1;
 
     /**
+     * The namespace identifier for the WIFI Keystore namespace.
+     * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
+     * @hide
+     */
+    @SystemApi
+    public static final int NAMESPACE_WIFI = 102;
+
+    /**
      * For legacy support, translate namespaces into known UIDs.
      * @hide
      */
@@ -884,6 +895,8 @@
         switch (namespace) {
             case NAMESPACE_APPLICATION:
                 return KeyStore.UID_SELF;
+            case NAMESPACE_WIFI:
+                return Process.WIFI_UID;
             // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
             //  b/171305388 and b/171305607
             default:
@@ -900,6 +913,8 @@
         switch (uid) {
             case KeyStore.UID_SELF:
                 return NAMESPACE_APPLICATION;
+            case Process.WIFI_UID:
+                return NAMESPACE_WIFI;
             // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
             //  b/171305388 and b/171305607
             default:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 75ac61a..e101115 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -352,14 +352,17 @@
         try {
             response = keyStore.getKeyEntry(descriptor);
         } catch (android.security.KeyStoreException e) {
-            if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
-                throw new KeyPermanentlyInvalidatedException(
-                        "User changed or deleted their auth credentials",
-                        e);
-            } else {
-                throw (UnrecoverableKeyException)
-                        new UnrecoverableKeyException("Failed to obtain information about key")
-                                .initCause(e);
+            switch (e.getErrorCode()) {
+                case ResponseCode.KEY_NOT_FOUND:
+                    return null;
+                case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+                    throw new KeyPermanentlyInvalidatedException(
+                            "User changed or deleted their auth credentials",
+                            e);
+                default:
+                    throw (UnrecoverableKeyException)
+                            new UnrecoverableKeyException("Failed to obtain information about key")
+                                    .initCause(e);
             }
         }
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 33c6951..ccb74eb 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1445,6 +1445,7 @@
     @TestApi
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     @Nullable
+    @SuppressWarnings("NullableCollection")
     public List<String> getProviderPackages(@NonNull String provider) {
         try {
             return mService.getProviderPackages(provider);
diff --git a/native/android/OWNERS b/native/android/OWNERS
index ac5a895..d414ed4 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,3 +1,4 @@
 per-file libandroid_net.map.txt, net.c = set noparent
 per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
 per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
+per-file system_fonts.cpp = file:/graphics/java/android/graphics/fonts/OWNERS
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index 18467fa..9b56b23 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -276,7 +276,7 @@
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof CaptivePortalData)) return false;
         final CaptivePortalData other = (CaptivePortalData) obj;
         return mRefreshTimeMillis == other.mRefreshTimeMillis
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 987dcc4..fef4152 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3231,32 +3231,6 @@
         }
     }
 
-    /** {@hide} - returns the factory serial number */
-    @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public int registerNetworkFactory(Messenger messenger, String name) {
-        try {
-            return mService.registerNetworkFactory(messenger, name);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** {@hide} */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public void unregisterNetworkFactory(Messenger messenger) {
-        try {
-            mService.unregisterNetworkFactory(messenger);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     /**
      * Registers the specified {@link NetworkProvider}.
      * Each listener must only be registered once. The listener can be unregistered with
@@ -4871,9 +4845,13 @@
         }
     }
 
-    private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
-        Log.d(TAG, "setOemNetworkPreference called with preference: "
-                + preference.toString());
+    private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+        try {
+            mService.setOemNetworkPreference(preference);
+        } catch (RemoteException e) {
+            Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     @NonNull
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e4..e2672c4 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
+import android.net.OemNetworkPreferences;
 import android.net.ProxyInfo;
 import android.net.UidRange;
 import android.net.QosSocketInfo;
@@ -156,9 +157,6 @@
 
     boolean requestBandwidthUpdate(in Network network);
 
-    int registerNetworkFactory(in Messenger messenger, in String name);
-    void unregisterNetworkFactory(in Messenger messenger);
-
     int registerNetworkProvider(in Messenger messenger, in String name);
     void unregisterNetworkProvider(in Messenger messenger);
 
@@ -243,4 +241,6 @@
 
     void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
     void unregisterQosCallback(in IQosCallback callback);
+
+    void setOemNetworkPreference(in OemNetworkPreferences preference);
 }
diff --git a/packages/Connectivity/framework/src/android/net/IpConfiguration.java b/packages/Connectivity/framework/src/android/net/IpConfiguration.java
index 0b20564..d5f8b2e 100644
--- a/packages/Connectivity/framework/src/android/net/IpConfiguration.java
+++ b/packages/Connectivity/framework/src/android/net/IpConfiguration.java
@@ -167,7 +167,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o == this) {
             return true;
         }
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
index e7c8014..bcb65fa 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -124,7 +125,7 @@
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof IpPrefix)) {
             return false;
         }
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
index 44d25a1..d1bdaa0 100644
--- a/packages/Connectivity/framework/src/android/net/LinkAddress.java
+++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java
@@ -349,7 +349,7 @@
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof LinkAddress)) {
             return false;
         }
diff --git a/packages/Connectivity/framework/src/android/net/LinkProperties.java b/packages/Connectivity/framework/src/android/net/LinkProperties.java
index 486e2d7..e41ed72 100644
--- a/packages/Connectivity/framework/src/android/net/LinkProperties.java
+++ b/packages/Connectivity/framework/src/android/net/LinkProperties.java
@@ -1613,7 +1613,7 @@
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof LinkProperties)) return false;
diff --git a/packages/Connectivity/framework/src/android/net/MacAddress.java b/packages/Connectivity/framework/src/android/net/MacAddress.java
index c7116b4..c83c23a 100644
--- a/packages/Connectivity/framework/src/android/net/MacAddress.java
+++ b/packages/Connectivity/framework/src/android/net/MacAddress.java
@@ -161,7 +161,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
index b07bd68..46141e0 100644
--- a/packages/Connectivity/framework/src/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -510,7 +511,7 @@
     };
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof Network)) return false;
         Network other = (Network)obj;
         return this.netId == other.netId;
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index d22d82d..27aa15d 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -775,7 +776,8 @@
      * @param underlyingNetworks the new list of underlying networks.
      * @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
      */
-    public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+    public final void setUnderlyingNetworks(
+            @SuppressLint("NullableCollection") @Nullable List<Network> underlyingNetworks) {
         final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
                 ? new ArrayList<>(underlyingNetworks) : null;
         queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 8bfa77a..55b2c3c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -1786,6 +1786,15 @@
         return 0;
     }
 
+    private <T extends Parcelable> void writeParcelableArraySet(Parcel in,
+            @Nullable ArraySet<T> val, int flags) {
+        final int size = (val != null) ? val.size() : -1;
+        in.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            in.writeParcelable(val.valueAt(i), flags);
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mNetworkCapabilities);
@@ -1796,7 +1805,7 @@
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
         dest.writeParcelable((Parcelable) mTransportInfo, flags);
         dest.writeInt(mSignalStrength);
-        dest.writeArraySet(mUids);
+        writeParcelableArraySet(dest, mUids, flags);
         dest.writeString(mSSID);
         dest.writeBoolean(mPrivateDnsBroken);
         dest.writeIntArray(getAdministratorUids());
@@ -1819,8 +1828,7 @@
                 netCap.mNetworkSpecifier = in.readParcelable(null);
                 netCap.mTransportInfo = in.readParcelable(null);
                 netCap.mSignalStrength = in.readInt();
-                netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
-                        null /* ClassLoader, null for default */);
+                netCap.mUids = readParcelableArraySet(in, null /* ClassLoader, null for default */);
                 netCap.mSSID = in.readString();
                 netCap.mPrivateDnsBroken = in.readBoolean();
                 netCap.setAdministratorUids(in.createIntArray());
@@ -1833,6 +1841,20 @@
             public NetworkCapabilities[] newArray(int size) {
                 return new NetworkCapabilities[size];
             }
+
+            private @Nullable <T extends Parcelable> ArraySet<T> readParcelableArraySet(Parcel in,
+                    @Nullable ClassLoader loader) {
+                final int size = in.readInt();
+                if (size < 0) {
+                    return null;
+                }
+                final ArraySet<T> result = new ArraySet<>(size);
+                for (int i = 0; i < size; i++) {
+                    final T value = in.readParcelable(loader);
+                    result.append(value);
+                }
+                return result;
+            }
         };
 
     @Override
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 04011fc..b9ef4c2 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -435,25 +435,7 @@
      * @hide
      */
     public boolean isRequest() {
-        return isForegroundRequest() || isBackgroundRequest();
-    }
-
-    /**
-     * Returns true iff. the contained NetworkRequest is one that:
-     *
-     *     - should be associated with at most one satisfying network
-     *       at a time;
-     *
-     *     - should cause a network to be kept up and in the foreground if
-     *       it is the best network which can satisfy the NetworkRequest.
-     *
-     * For full detail of how isRequest() is used for pairing Networks with
-     * NetworkRequests read rematchNetworkAndRequests().
-     *
-     * @hide
-     */
-    public boolean isForegroundRequest() {
-        return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+        return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST;
     }
 
     /**
@@ -567,7 +549,7 @@
         proto.end(token);
     }
 
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
         return (that.legacyType == this.legacyType &&
diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03b07e0..9cd7ab2 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -30,8 +30,6 @@
 import java.net.ProxySelector;
 import java.net.URI;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * A convenience class for accessing the user and default proxy
@@ -64,40 +62,9 @@
     @Deprecated
     public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
 
-    /** @hide */
-    public static final int PROXY_VALID             = 0;
-    /** @hide */
-    public static final int PROXY_HOSTNAME_EMPTY    = 1;
-    /** @hide */
-    public static final int PROXY_HOSTNAME_INVALID  = 2;
-    /** @hide */
-    public static final int PROXY_PORT_EMPTY        = 3;
-    /** @hide */
-    public static final int PROXY_PORT_INVALID      = 4;
-    /** @hide */
-    public static final int PROXY_EXCLLIST_INVALID  = 5;
-
     private static ConnectivityManager sConnectivityManager = null;
 
-    // Hostname / IP REGEX validation
-    // Matches blank input, ips, and domain names
-    private static final String NAME_IP_REGEX =
-        "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
-
-    private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
-
-    private static final Pattern HOSTNAME_PATTERN;
-
-    private static final String EXCL_REGEX =
-        "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
-
-    private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
-
-    private static final Pattern EXCLLIST_PATTERN;
-
     static {
-        HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
-        EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
         sDefaultProxySelector = ProxySelector.getDefault();
     }
 
@@ -216,33 +183,6 @@
         return false;
     }
 
-    /**
-     * Validate syntax of hostname, port and exclusion list entries
-     * {@hide}
-     */
-    public static int validate(String hostname, String port, String exclList) {
-        Matcher match = HOSTNAME_PATTERN.matcher(hostname);
-        Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
-
-        if (!match.matches()) return PROXY_HOSTNAME_INVALID;
-
-        if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
-
-        if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
-
-        if (port.length() > 0) {
-            if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
-            int portVal = -1;
-            try {
-                portVal = Integer.parseInt(port);
-            } catch (NumberFormatException ex) {
-                return PROXY_PORT_INVALID;
-            }
-            if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
-        }
-        return PROXY_VALID;
-    }
-
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static final void setHttpProxySystemProperty(ProxyInfo p) {
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index c9bca28..229db0d 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -23,6 +23,8 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.net.module.util.ProxyUtils;
+
 import java.net.InetSocketAddress;
 import java.net.URLConnection;
 import java.util.List;
@@ -233,7 +235,7 @@
      */
     public boolean isValid() {
         if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
-        return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+        return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
                 mPort == 0 ? "" : Integer.toString(mPort),
                 mExclusionList == null ? "" : mExclusionList);
     }
@@ -275,7 +277,7 @@
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (!(o instanceof ProxyInfo)) return false;
         ProxyInfo p = (ProxyInfo)o;
         // If PAC URL is present in either then they must be equal.
diff --git a/packages/Connectivity/framework/src/android/net/RouteInfo.java b/packages/Connectivity/framework/src/android/net/RouteInfo.java
index 94f849f..5b6684a 100644
--- a/packages/Connectivity/framework/src/android/net/RouteInfo.java
+++ b/packages/Connectivity/framework/src/android/net/RouteInfo.java
@@ -534,7 +534,7 @@
      * Compares this RouteInfo object against the specified object and indicates if they are equal.
      * @return {@code true} if the objects are equal, {@code false} otherwise.
      */
-    public boolean equals(Object obj) {
+    public boolean equals(@Nullable Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof RouteInfo)) return false;
@@ -570,7 +570,7 @@
         }
 
         @Override
-        public boolean equals(Object o) {
+        public boolean equals(@Nullable Object o) {
             if (!(o instanceof RouteKey)) {
                 return false;
             }
diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
index c87b827..1812509 100644
--- a/packages/Connectivity/framework/src/android/net/VpnManager.java
+++ b/packages/Connectivity/framework/src/android/net/VpnManager.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,6 +29,8 @@
 import android.content.res.Resources;
 import android.os.RemoteException;
 
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 
 import java.io.IOException;
@@ -161,4 +164,104 @@
             throw e.rethrowFromSystemServer();
         }
     }
-}
+
+    /**
+     * Return the VPN configuration for the given user ID.
+     * @hide
+     */
+    @Nullable
+    public VpnConfig getVpnConfig(@UserIdInt int userId) {
+        try {
+            return mService.getVpnConfig(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Prepare for a VPN application.
+     * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+     * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+     *
+     * @param oldPackage Package name of the application which currently controls VPN, which will
+     *                   be replaced. If there is no such application, this should should either be
+     *                   {@code null} or {@link VpnConfig.LEGACY_VPN}.
+     * @param newPackage Package name of the application which should gain control of VPN, or
+     *                   {@code null} to disable.
+     * @param userId User for whom to prepare the new VPN.
+     *
+     * @hide
+     */
+    public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+            int userId) {
+        try {
+            return mService.prepareVpn(oldPackage, newPackage, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+     * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+     * class. If the caller is not {@code userId}, {@link
+     * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+     *
+     * @param packageName The package for which authorization state should change.
+     * @param userId User for whom {@code packageName} is installed.
+     * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+     *     permissions should be granted. When unauthorizing an app, {@link
+     *     VpnManager.TYPE_VPN_NONE} should be used.
+     * @hide
+     */
+    public void setVpnPackageAuthorization(
+            String packageName, int userId, @VpnManager.VpnType int vpnType) {
+        try {
+            mService.setVpnPackageAuthorization(packageName, userId, vpnType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the legacy VPN information for the specified user ID.
+     * @hide
+     */
+    public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
+        try {
+            return mService.getLegacyVpnInfo(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts a legacy VPN.
+     * @hide
+     */
+    public void startLegacyVpn(VpnProfile profile) {
+        try {
+            mService.startLegacyVpn(profile);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
+     * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
+     * with a reload of its profile.
+     *
+     * <p>This method can only be called by the system UID
+     * @return a boolean indicating success
+     *
+     * @hide
+     */
+    public boolean updateLockdownVpn() {
+        try {
+            return mService.updateLockdownVpn();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c5d4fa9..cb610fc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -89,15 +89,7 @@
                         return value == null || value.length() < MAX_LENGTH;
                     }
                 });
-        VALIDATORS.put(
-                System.FONT_SCALE,
-                value -> {
-                    try {
-                        return Float.parseFloat(value) >= 0;
-                    } catch (NumberFormatException | NullPointerException e) {
-                        return false;
-                    }
-                });
+        VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
         VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 System.DISPLAY_COLOR_MODE,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 135356c..39be9cb 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -121,6 +121,8 @@
     <uses-permission android:name="android.permission.CREATE_USERS" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+    <uses-permission android:name="android.permission.QUERY_USERS" />
+    <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
     <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
     <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef1..6dcad25 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@
 
     private static final String TAG = "VpnDisconnected";
 
-    private IConnectivityManager mService;
+    private ConnectivityManager mService;
     private int mUserId;
     private String mVpnPackage;
 
@@ -53,10 +50,9 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
         mUserId = UserHandle.myUserId();
-        mVpnPackage = getAlwaysOnVpnPackage();
+        final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+        mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
         if (mVpnPackage == null) {
             finish();
             return;
@@ -102,15 +98,6 @@
         }
     }
 
-    private String getAlwaysOnVpnPackage() {
-        try {
-            return mService.getAlwaysOnVpnPackage(mUserId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
-            return null;
-        }
-    }
-
     private CharSequence getVpnLabel() {
         try {
             return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc..aab01d0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@
 
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
 import android.net.VpnManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.Html;
@@ -48,7 +45,8 @@
 
     private String mPackage;
 
-    private IConnectivityManager mService;
+    private ConnectivityManager mCm;  // TODO: switch entirely to VpnManager once VPN code moves
+    private VpnManager mVm;
 
     public ConfirmDialog() {
         this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mPackage = getCallingPackage();
-        mService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+        mCm = getSystemService(ConnectivityManager.class);
+        mVm = getSystemService(VpnManager.class);
 
-        if (prepareVpn()) {
+        if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
             setResult(RESULT_OK);
             finish();
             return;
@@ -74,7 +72,7 @@
             finish();
             return;
         }
-        final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+        final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
         // Can't prepare new vpn app when another vpn is always-on
         if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
             finish();
@@ -97,24 +95,6 @@
         button.setFilterTouchesWhenObscured(true);
     }
 
-    private String getAlwaysOnVpnPackage() {
-        try {
-           return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
-        } catch (RemoteException e) {
-            Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
-            // Fallback to null to show the dialog
-            return null;
-        }
-    }
-
-    private boolean prepareVpn() {
-        try {
-            return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
-        } catch (RemoteException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
     private CharSequence getVpnLabel() {
         try {
             return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         try {
-            if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+            if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
                 // Authorize this app to initiate VPN connections in the future without user
                 // intervention.
-                mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+                mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
                 setResult(RESULT_OK);
             }
         } catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e..1fc74f7 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
 
 package com.android.vpndialogs;
 
-import android.content.Context;
 import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
@@ -41,7 +39,7 @@
 
     private VpnConfig mConfig;
 
-    private IConnectivityManager mService;
+    private VpnManager mVm;
 
     private TextView mDuration;
     private TextView mDataTransmitted;
@@ -55,11 +53,9 @@
         super.onCreate(savedInstanceState);
 
         try {
+            mVm = getSystemService(VpnManager.class);
 
-            mService = IConnectivityManager.Stub.asInterface(
-                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
-            mConfig = mService.getVpnConfig(UserHandle.myUserId());
+            mConfig = mVm.getVpnConfig(UserHandle.myUserId());
 
             // mConfig can be null if we are a restricted user, in that case don't show this dialog
             if (mConfig == null) {
@@ -118,9 +114,9 @@
             } else if (which == DialogInterface.BUTTON_NEUTRAL) {
                 final int myUserId = UserHandle.myUserId();
                 if (mConfig.legacy) {
-                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+                    mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
                 } else {
-                    mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+                    mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
                 }
             }
         } catch (Exception e) {
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 3c5268c..ba2a63a 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -3,6 +3,7 @@
 aabhinav@google.com
 bryanmawhinney@google.com
 jstemmer@google.com
+millmore@google.com
 nathch@google.com
 niagra@google.com
 niamhfw@google.com
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..5eed0b5
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 0000000..d0a2daf
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f2e1920..3933e37 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -120,6 +120,7 @@
 import android.net.NetworkTestResultParcelable;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
+import android.net.OemNetworkPreferences;
 import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
 import android.net.QosCallbackException;
@@ -222,6 +223,7 @@
 import java.io.PrintWriter;
 import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -280,15 +282,18 @@
     // connect anyway?" dialog after the user selects a network that doesn't validate.
     private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
 
-    // Default to 30s linger time-out. Modifiable only for testing.
+    // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+    private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
 
     // The maximum number of network request allowed per uid before an exception is thrown.
     private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
 
     @VisibleForTesting
     protected int mLingerDelayMs;  // Can't be final, or test subclass constructors can't change it.
+    @VisibleForTesting
+    protected int mNascentDelayMs;
 
     // How long to delay to removal of a pending intent based request.
     // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -739,11 +744,11 @@
         }
 
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
-                boolean isFallbackNetwork) {
+                boolean isDefaultNetwork) {
             if (DBG) {
                 log("Sending " + state
                         + " broadcast for type " + type + " " + nai.toShortString()
-                        + " isFallbackNetwork=" + isFallbackNetwork);
+                        + " isDefaultNetwork=" + isDefaultNetwork);
             }
         }
 
@@ -762,10 +767,10 @@
                 list.add(nai);
             }
 
-            // Send a broadcast if this is the first network of its type or if it's the fallback.
-            final boolean isFallbackNetwork = mService.isFallbackNetwork(nai);
-            if ((list.size() == 1) || isFallbackNetwork) {
-                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isFallbackNetwork);
+            // Send a broadcast if this is the first network of its type or if it's the default.
+            final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
+            if ((list.size() == 1) || isDefaultNetwork) {
+                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
                 mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
             }
         }
@@ -794,7 +799,7 @@
                               ", sending connected broadcast");
                 final NetworkAgentInfo replacement = list.get(0);
                 maybeLogBroadcast(replacement, DetailedState.CONNECTED, type,
-                        mService.isFallbackNetwork(replacement));
+                        mService.isDefaultNetwork(replacement));
                 mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type);
             }
         }
@@ -810,14 +815,14 @@
         // send out another legacy broadcast - currently only used for suspend/unsuspend
         // toggle
         public void update(NetworkAgentInfo nai) {
-            final boolean isFallback = mService.isFallbackNetwork(nai);
+            final boolean isDefault = mService.isDefaultNetwork(nai);
             final DetailedState state = nai.networkInfo.getDetailedState();
             for (int type = 0; type < mTypeLists.length; type++) {
                 final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
                 final boolean contains = (list != null && list.contains(nai));
                 final boolean isFirst = contains && (nai == list.get(0));
-                if (isFirst || contains && isFallback) {
-                    maybeLogBroadcast(nai, state, type, isFallback);
+                if (isFirst || contains && isDefault) {
+                    maybeLogBroadcast(nai, state, type, isDefault);
                     mService.sendLegacyNetworkBroadcast(nai, state, type);
                 }
             }
@@ -990,6 +995,15 @@
         }
 
         /**
+         * Gets the UID that owns a socket connection. Needed because opening SOCK_DIAG sockets
+         * requires CAP_NET_ADMIN, which the unit tests do not have.
+         */
+        public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+                InetSocketAddress remote) {
+            return InetDiagMessage.getConnectionOwnerUid(protocol, local, remote);
+        }
+
+        /**
          * @see MultinetworkPolicyTracker
          */
         public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
@@ -1022,12 +1036,12 @@
 
         mMetricsLog = logger;
         mNetworkRanker = new NetworkRanker();
-        final NetworkRequest fallbackRequest = createDefaultInternetRequestForTransport(
+        final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
                 -1, NetworkRequest.Type.REQUEST);
-        mFallbackRequest = new NetworkRequestInfo(null, fallbackRequest, new Binder());
-        mNetworkRequests.put(fallbackRequest, mFallbackRequest);
-        mDefaultNetworkRequests.add(mFallbackRequest);
-        mNetworkRequestInfoLogs.log("REGISTER " + mFallbackRequest);
+        mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+        mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
+        mDefaultNetworkRequests.add(mDefaultRequest);
+        mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
 
         mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1053,6 +1067,8 @@
                 Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+        // TODO: Consider making the timer customizable.
+        mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
 
         mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
         mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
@@ -1217,6 +1233,14 @@
 
         mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
+
+        mNoServiceNetwork =  new NetworkAgentInfo(null,
+                new Network(NO_SERVICE_NET_ID),
+                new NetworkInfo(TYPE_NONE, 0, "", ""),
+                new LinkProperties(), new NetworkCapabilities(), 0, mContext,
+                null, new NetworkAgentConfig(), this, null,
+                null, null, 0, INVALID_UID,
+                mQosCallbackTracker);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1366,7 +1390,7 @@
     }
 
     private NetworkState getUnfilteredActiveNetworkState(int uid) {
-        NetworkAgentInfo nai = getFallbackNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
 
         final Network[] networks = getVpnUnderlyingNetworks(uid);
         if (networks != null) {
@@ -1499,7 +1523,7 @@
             }
         }
 
-        NetworkAgentInfo nai = getFallbackNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
         if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
                 ignoreBlocked)) {
             return null;
@@ -1638,21 +1662,28 @@
 
         HashMap<Network, NetworkCapabilities> result = new HashMap<>();
 
-        final NetworkAgentInfo nai = getFallbackNetwork();
-        NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
-        if (nc != null) {
-            result.put(
-                    nai.network,
-                    createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                            nc, mDeps.getCallingUid(), callingPackageName));
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            if (!nri.isBeingSatisfied()) {
+                continue;
+            }
+            final NetworkAgentInfo nai = nri.getSatisfier();
+            final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+            if (null != nc
+                    && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                    && !result.containsKey(nai.network)) {
+                result.put(
+                        nai.network,
+                        createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                                nc, mDeps.getCallingUid(), callingPackageName));
+            }
         }
 
         // No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
         final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
-        if (networks != null) {
-            for (Network network : networks) {
-                nc = getNetworkCapabilitiesInternal(network);
-                if (nc != null) {
+        if (null != networks) {
+            for (final Network network : networks) {
+                final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+                if (null != nc) {
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@@ -1674,9 +1705,7 @@
 
     /**
      * Return LinkProperties for the active (i.e., connected) default
-     * network interface.  It is assumed that at most one default network
-     * is active at a time. If more than one is active, it is indeterminate
-     * which will be returned.
+     * network interface for the calling uid.
      * @return the ip properties for the active network, or {@code null} if
      * none is active
      */
@@ -2007,7 +2036,7 @@
                 mHandler.sendMessage(mHandler.obtainMessage(
                         EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
                         new PrivateDnsValidationUpdate(netId,
-                                InetAddress.parseNumericAddress(ipAddress),
+                                InetAddresses.parseNumericAddress(ipAddress),
                                 hostname, validated)));
             } catch (IllegalArgumentException e) {
                 loge("Error parsing ip address in validation event");
@@ -2025,7 +2054,7 @@
             // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
             // callback from each caller type. Need to re-factor NetdEventListenerService to allow
             // multiple NetworkMonitor registrants.
-            if (nai != null && nai.satisfies(mFallbackRequest.mRequests.get(0))) {
+            if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
                 nai.networkMonitor().notifyDnsResponse(returnCode);
             }
         }
@@ -2119,8 +2148,8 @@
 
     private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
             boolean isBackgroundRestricted) {
-        return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
-                isNetworkMetered, isBackgroundRestricted);
+        return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted);
     }
 
     /**
@@ -2582,12 +2611,12 @@
         pw.println();
         pw.println();
 
-        final NetworkAgentInfo fallbackNai = getFallbackNetwork();
+        final NetworkAgentInfo defaultNai = getDefaultNetwork();
         pw.print("Active default network: ");
-        if (fallbackNai == null) {
+        if (defaultNai == null) {
             pw.println("none");
         } else {
-            pw.println(fallbackNai.network.getNetId());
+            pw.println(defaultNai.network.getNetId());
         }
         pw.println();
 
@@ -2703,9 +2732,9 @@
                 pw.println(nai.requestAt(i).toString());
             }
             pw.decreaseIndent();
-            pw.println("Lingered:");
+            pw.println("Inactivity Timers:");
             pw.increaseIndent();
-            nai.dumpLingerTimers(pw);
+            nai.dumpInactivityTimers(pw);
             pw.decreaseIndent();
             pw.decreaseIndent();
         }
@@ -2970,7 +2999,7 @@
 
             final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
             final boolean wasValidated = nai.lastValidated;
-            final boolean wasFallback = isFallbackNetwork(nai);
+            final boolean wasDefault = isDefaultNetwork(nai);
 
             if (DBG) {
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -2979,7 +3008,7 @@
                 log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
-                if (wasFallback) {
+                if (wasDefault) {
                     mMetricsLog.logDefaultNetworkValidity(valid);
                 }
                 final int oldScore = nai.getCurrentScore();
@@ -3300,27 +3329,27 @@
     }
 
     /**
-     * Updates the linger state from the network requests inside the NAI.
+     * Updates the inactivity state from the network requests inside the NAI.
      * @param nai the agent info to update
      * @param now the timestamp of the event causing this update
-     * @return whether the network was lingered as a result of this update
+     * @return whether the network was inactive as a result of this update
      */
-    private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
-        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
-        // 2. If the network was lingering and there are now requests, unlinger it.
+    private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+        // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+        // 2. If the network was inactive and there are now requests, unset inactive.
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
-        //    one lingered request, start lingering.
-        nai.updateLingerTimer();
-        if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
-            if (DBG) log("Unlingering " + nai.toShortString());
-            nai.unlinger();
+        //    one lingered request, set inactive.
+        nai.updateInactivityTimer();
+        if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) {
+            if (DBG) log("Unsetting inactive " + nai.toShortString());
+            nai.unsetInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
-        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
             if (DBG) {
-                final int lingerTime = (int) (nai.getLingerExpiry() - now);
-                log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+                final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+                log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
             }
-            nai.linger();
+            nai.setInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
             return true;
         }
@@ -3334,7 +3363,6 @@
                 if (VDBG) log("NetworkFactory connected");
                 // Finish setting up the full connection
                 NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                npi.completeConnection();
                 sendAllRequestsToProvider(npi);
             } else {
                 loge("Error connecting NetworkFactory");
@@ -3355,13 +3383,13 @@
             loge("Error connecting NetworkAgent");
             mNetworkAgentInfos.remove(nai);
             if (nai != null) {
-                final boolean wasFallback = isFallbackNetwork(nai);
+                final boolean wasDefault = isDefaultNetwork(nai);
                 synchronized (mNetworkForNetId) {
                     mNetworkForNetId.remove(nai.network.getNetId());
                 }
                 mNetIdManager.releaseNetId(nai.network.getNetId());
                 // Just in case.
-                mLegacyTypeTracker.remove(nai, wasFallback);
+                mLegacyTypeTracker.remove(nai, wasDefault);
             }
         }
     }
@@ -3400,8 +3428,8 @@
             nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
                     null, null);
         }
-        final boolean wasFallback = isFallbackNetwork(nai);
-        if (wasFallback) {
+        final boolean wasDefault = isDefaultNetwork(nai);
+        if (wasDefault) {
             mDefaultInetConditionPublished = 0;
             // Log default network disconnection before required book-keeping.
             // Let rematchAllNetworksAndRequests() below record a new default network event
@@ -3436,17 +3464,20 @@
         propagateUnderlyingNetworkCapabilities(nai.network);
         // Remove all previously satisfied requests.
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
-            NetworkRequest request = nai.requestAt(i);
+            final NetworkRequest request = nai.requestAt(i);
             final NetworkRequestInfo nri = mNetworkRequests.get(request);
             final NetworkAgentInfo currentNetwork = nri.getSatisfier();
             if (currentNetwork != null
                     && currentNetwork.network.getNetId() == nai.network.getNetId()) {
+                // uid rules for this network will be removed in destroyNativeNetwork(nai).
                 nri.setSatisfier(null, null);
-                sendUpdatedScoreToFactories(request, null);
+                if (request.isRequest()) {
+                    sendUpdatedScoreToFactories(request, null);
+                }
 
-                if (mFallbackRequest == nri) {
+                if (mDefaultRequest == nri) {
                     // TODO : make battery stats aware that since 2013 multiple interfaces may be
-                    //  active at the same time. For now keep calling this with the fallback
+                    //  active at the same time. For now keep calling this with the default
                     //  network, because while incorrect this is the closest to the old (also
                     //  incorrect) behavior.
                     mNetworkActivityTracker.updateDataActivityTracking(
@@ -3456,11 +3487,11 @@
                 }
             }
         }
-        nai.clearLingerState();
+        nai.clearInactivityState();
         // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
-        //  Currently, deleting it breaks tests that check for the fallback network disconnecting.
+        //  Currently, deleting it breaks tests that check for the default network disconnecting.
         //  Find out why, fix the rematch code, and delete this.
-        mLegacyTypeTracker.remove(nai, wasFallback);
+        mLegacyTypeTracker.remove(nai, wasDefault);
         rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
         if (nai.created) {
@@ -3468,10 +3499,9 @@
             // (routing rules, DNS, etc).
             // This may be slow as it requires a lot of netd shelling out to ip and
             // ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
-            // after we've rematched networks with requests which should make a potential
-            // fallback network the default or requested a new network from the
-            // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
-            // long time.
+            // after we've rematched networks with requests (which might change the default
+            // network or service a new request from an app), so network traffic isn't interrupted
+            // for an unnecessarily long time.
             destroyNativeNetwork(nai);
             mDnsManager.removeNetwork(nai.network);
         }
@@ -3556,18 +3586,15 @@
             }
         }
         rematchAllNetworksAndRequests();
-        // If an active request exists, return as its score has already been sent if needed.
-        if (null != nri.getActiveRequest()) {
+        // If the nri is satisfied, return as its score has already been sent if needed.
+        if (nri.isBeingSatisfied()) {
             return;
         }
 
         // As this request was not satisfied on rematch and thus never had any scores sent to the
         // factories, send null now for each request of type REQUEST.
         for (final NetworkRequest req : nri.mRequests) {
-            if (!req.isRequest()) {
-                continue;
-            }
-            sendUpdatedScoreToFactories(req, null);
+            if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
         }
     }
 
@@ -3607,7 +3634,7 @@
                 return true;
         }
 
-        if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
+        if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
             return false;
         }
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -3700,7 +3727,7 @@
         if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
             return;
         }
-        if (nri.getSatisfier() != null) {
+        if (nri.isBeingSatisfied()) {
             return;
         }
         if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
@@ -3744,7 +3771,7 @@
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
 
         if (null != nri.getActiveRequest()) {
-            if (nri.getActiveRequest().isRequest()) {
+            if (!nri.getActiveRequest().isListen()) {
                 removeSatisfiedNetworkRequestFromNetwork(nri);
             } else {
                 nri.setSatisfier(null, null);
@@ -3799,7 +3826,7 @@
             // If there are still lingered requests on this network, don't tear it down,
             // but resume lingering instead.
             final long now = SystemClock.elapsedRealtime();
-            if (updateLingerState(nai, now)) {
+            if (updateInactivityState(nai, now)) {
                 notifyNetworkLosing(nai, now);
             }
             if (unneeded(nai, UnneededFor.TEARDOWN)) {
@@ -4260,7 +4287,7 @@
 
     @Override
     public NetworkRequest getDefaultRequest() {
-        return mFallbackRequest.mRequests.get(0);
+        return mDefaultRequest.mRequests.get(0);
     }
 
     private class InternalHandler extends Handler {
@@ -4506,7 +4533,7 @@
         // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getFallbackNetwork();
+            nai = getDefaultNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4525,7 +4552,7 @@
             Network network, int uid, boolean hasConnectivity) {
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getFallbackNetwork();
+            nai = getDefaultNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4891,7 +4918,8 @@
         // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
         // the underlyingNetworks list.
         if (underlyingNetworks == null) {
-            final NetworkAgentInfo defaultNai = getFallbackNetwork();
+            final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
+                    nai.networkCapabilities.getOwnerUid());
             if (defaultNai != null) {
                 underlyingNetworks = new Network[] { defaultNai.network };
             }
@@ -4942,8 +4970,10 @@
         }
     }
 
-    private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
-        final Network defaultNetwork = getNetwork(getFallbackNetwork());
+    // TODO This needs to be the default network that applies to the NAI.
+    private Network[] underlyingNetworksOrDefault(final int ownerUid,
+            Network[] underlyingNetworks) {
+        final Network defaultNetwork = getNetwork(getDefaultNetworkForUid(ownerUid));
         if (underlyingNetworks == null && defaultNetwork != null) {
             // null underlying networks means to track the default.
             underlyingNetworks = new Network[] { defaultNetwork };
@@ -4956,7 +4986,8 @@
         // TODO: support more than one level of underlying networks, either via a fixed-depth search
         // (e.g., 2 levels of underlying networks), or via loop detection, or....
         if (!nai.supportsUnderlyingNetworks()) return false;
-        final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+        final Network[] underlying = underlyingNetworksOrDefault(
+                nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
         return ArrayUtils.contains(underlying, network);
     }
 
@@ -5414,27 +5445,21 @@
     private static class NetworkProviderInfo {
         public final String name;
         public final Messenger messenger;
-        private final AsyncChannel mAsyncChannel;
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
         NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, IBinder.DeathRecipient deathRecipient) {
+                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
-            mAsyncChannel = asyncChannel;
             mDeathRecipient = deathRecipient;
 
-            if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
-                throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+            if (mDeathRecipient == null) {
+                throw new AssertionError("Must pass a deathRecipient");
             }
         }
 
-        boolean isLegacyNetworkFactory() {
-            return mAsyncChannel != null;
-        }
-
         void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
             try {
                 messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5445,38 +5470,19 @@
         }
 
         void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
-                        servingProviderId, request);
-            } else {
-                sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+            sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
                             servingProviderId, request);
-            }
         }
 
         void cancelRequest(NetworkRequest request) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
-            } else {
-                sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
-            }
+            sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
         }
 
         void connect(Context context, Handler handler) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.connect(context, handler, messenger);
-            } else {
-                try {
-                    messenger.getBinder().linkToDeath(mDeathRecipient, 0);
-                } catch (RemoteException e) {
-                    mDeathRecipient.binderDied();
-                }
-            }
-        }
-
-        void completeConnection() {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+            try {
+                messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+                mDeathRecipient.binderDied();
             }
         }
     }
@@ -5512,9 +5518,8 @@
             mActiveRequest = activeRequest;
         }
 
-        // The network currently satisfying this request, or null if none. Must only be touched
-        // on the handler thread. This only makes sense for network requests and not for listens,
-        // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+        // The network currently satisfying this NRI. Only one request in an NRI can have a
+        // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier.
         @Nullable
         private NetworkAgentInfo mSatisfier;
         NetworkAgentInfo getSatisfier() {
@@ -5537,6 +5542,18 @@
         final int mUid;
         final Messenger messenger;
 
+        /**
+         * Get the list of UIDs this nri applies to.
+         */
+        @NonNull
+        private Set<UidRange> getUids() {
+            // networkCapabilities.getUids() returns a defensive copy.
+            // multilayer requests will all have the same uids so return the first one.
+            final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
+                    ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
+            return uids;
+        }
+
         NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
             mRequests = initializeRequests(r);
             ensureAllNetworkRequestsHaveType(mRequests);
@@ -5570,6 +5587,13 @@
             this(r, null);
         }
 
+        // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
+        // set to the mNoServiceNetwork in which case mActiveRequest will be null thus returning
+        // false.
+        boolean isBeingSatisfied() {
+            return (null != mSatisfier && null != mActiveRequest);
+        }
+
         boolean isMultilayerRequest() {
             return mRequests.size() > 1;
         }
@@ -5595,7 +5619,9 @@
 
         @Override
         public String toString() {
-            return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+            return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+                    + (mActiveRequest == null ? null : mActiveRequest.requestId)
+                    + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
         }
     }
@@ -5951,15 +5977,6 @@
                 EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
     }
 
-    @Override
-    public int registerNetworkFactory(Messenger messenger, String name) {
-        enforceNetworkFactoryPermission();
-        NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
-                nextNetworkProviderId(), null /* deathRecipient */);
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
-        return npi.providerId;
-    }
-
     private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
         if (mNetworkProviderInfos.containsKey(npi.messenger)) {
             // Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -5973,10 +5990,7 @@
         if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
         mNetworkProviderInfos.put(npi.messenger, npi);
         npi.connect(mContext, mTrackerHandler);
-        if (!npi.isLegacyNetworkFactory()) {
-            // Legacy NetworkFactories get their requests when their AsyncChannel connects.
-            sendAllRequestsToProvider(npi);
-        }
+        sendAllRequestsToProvider(npi);
     }
 
     @Override
@@ -5995,11 +6009,6 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
     }
 
-    @Override
-    public void unregisterNetworkFactory(Messenger messenger) {
-        unregisterNetworkProvider(messenger);
-    }
-
     private void handleUnregisterNetworkProvider(Messenger messenger) {
         NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
         if (npi == null) {
@@ -6050,11 +6059,31 @@
     // The always-on request for an Internet-capable network that apps without a specific default
     // fall back to.
     @NonNull
-    private final NetworkRequestInfo mFallbackRequest;
+    private final NetworkRequestInfo mDefaultRequest;
     // Collection of NetworkRequestInfo's used for default networks.
     @NonNull
     private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
 
+    private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
+        return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
+    }
+
+    /**
+     * Determine if an nri is a managed default request that disallows default networking.
+     * @param nri the request to evaluate
+     * @return true if device-default networking is disallowed
+     */
+    private boolean isDefaultBlocked(@NonNull final NetworkRequestInfo nri) {
+        // Check if this nri is a managed default that supports the default network at its
+        // lowest priority request.
+        final NetworkRequest defaultNetworkRequest = mDefaultRequest.mRequests.get(0);
+        final NetworkCapabilities lowestPriorityNetCap =
+                nri.mRequests.get(nri.mRequests.size() - 1).networkCapabilities;
+        return isPerAppDefaultRequest(nri)
+                && !(defaultNetworkRequest.networkCapabilities.equalRequestableCapabilities(
+                        lowestPriorityNetCap));
+    }
+
     // Request used to optionally keep mobile data active even when higher
     // priority networks like Wi-Fi are active.
     private final NetworkRequest mDefaultMobileDataRequest;
@@ -6066,10 +6095,37 @@
     // Request used to optionally keep vehicle internal network always active
     private final NetworkRequest mDefaultVehicleRequest;
 
-    // TODO: b/178729499 update this in favor of a method taking in a UID.
-    // The NetworkAgentInfo currently satisfying the fallback request, if any.
-    private NetworkAgentInfo getFallbackNetwork() {
-        return mFallbackRequest.mSatisfier;
+    // TODO replace with INetd.DUMMY_NET_ID when available.
+    private static final int NO_SERVICE_NET_ID = 51;
+    // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
+    // network with no service. This NAI should never be matched against, nor should any public API
+    // ever return the associated network. For this reason, this NAI is not in the list of available
+    // NAIs. It is used in computeNetworkReassignment() to be set as the satisfier for non-device
+    // default requests that don't support using the device default network which will ultimately
+    // allow ConnectivityService to use this no-service network when calling makeDefaultForApps().
+    @VisibleForTesting
+    final NetworkAgentInfo mNoServiceNetwork;
+
+    // The NetworkAgentInfo currently satisfying the default request, if any.
+    private NetworkAgentInfo getDefaultNetwork() {
+        return mDefaultRequest.mSatisfier;
+    }
+
+    private NetworkAgentInfo getDefaultNetworkForUid(final int uid) {
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            // Currently, all network requests will have the same uids therefore checking the first
+            // one is sufficient. If/when uids are tracked at the nri level, this can change.
+            final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+            if (null == uids) {
+                continue;
+            }
+            for (final UidRange range : uids) {
+                if (range.contains(uid)) {
+                    return nri.getSatisfier();
+                }
+            }
+        }
+        return getDefaultNetwork();
     }
 
     @Nullable
@@ -6086,8 +6142,8 @@
     }
 
     @VisibleForTesting
-    protected boolean isFallbackNetwork(NetworkAgentInfo nai) {
-        return nai == getFallbackNetwork();
+    protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
+        return nai == getDefaultNetwork();
     }
 
     // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
@@ -6156,8 +6212,6 @@
 
         LinkProperties lp = new LinkProperties(linkProperties);
 
-        // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network
-        // satisfies mFallbackRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6234,7 +6288,7 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        if (isFallbackNetwork(networkAgent)) {
+        if (isDefaultNetwork(networkAgent)) {
             updateTcpBufferSizes(newLp.getTcpBufferSizes());
         }
 
@@ -6246,7 +6300,7 @@
         // updateDnses will fetch the private DNS configuration from DnsManager.
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
-        if (isFallbackNetwork(networkAgent)) {
+        if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
             updateProxy(newLp, oldLp);
@@ -6580,7 +6634,8 @@
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
             @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
-        underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+        underlyingNetworks = underlyingNetworksOrDefault(
+                agentCaps.getOwnerUid(), underlyingNetworks);
         int[] transportTypes = agentCaps.getTransportTypes();
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -6952,8 +7007,8 @@
     private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest nr = nai.requestAt(i);
-            // Don't send listening requests to factories. b/17393458
-            if (nr.isListen()) continue;
+            // Don't send listening or track default request to factories. b/17393458
+            if (!nr.isRequest()) continue;
             sendUpdatedScoreToFactories(nr, nai);
         }
     }
@@ -7015,10 +7070,10 @@
         ensureRunningOnConnectivityServiceThread();
         for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
             for (final NetworkRequest req : nri.mRequests) {
-                if (req.isListen() && nri.getActiveRequest() == req) {
+                if (!req.isRequest() && nri.getActiveRequest() == req) {
                     break;
                 }
-                if (req.isListen()) {
+                if (!req.isRequest()) {
                     continue;
                 }
                 // Only set the nai for the request it is satisfying.
@@ -7168,8 +7223,8 @@
         if (nai.numRequestNetworkRequests() != 0) {
             for (int i = 0; i < nai.numNetworkRequests(); i++) {
                 NetworkRequest nr = nai.requestAt(i);
-                // Ignore listening requests.
-                if (nr.isListen()) continue;
+                // Ignore listening and track default requests.
+                if (!nr.isRequest()) continue;
                 loge("Dead network still had at least " + nr);
                 break;
             }
@@ -7186,13 +7241,13 @@
 
         // If we get here it means that the last linger timeout for this network expired. So there
         // must be no other active linger timers, and we must stop lingering.
-        oldNetwork.clearLingerState();
+        oldNetwork.clearInactivityState();
 
         if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
             // Tear the network down.
             teardownUnneededNetwork(oldNetwork);
         } else {
-            // Put the network in the background.
+            // Put the network in the background if it doesn't satisfy any foreground request.
             updateCapabilitiesForNetwork(oldNetwork);
         }
     }
@@ -7224,21 +7279,20 @@
             log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
         }
 
-        try {
-            // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
-            if (mFallbackRequest != nri) {
-                return;
-            }
-
-            if (null != newDefaultNetwork) {
-                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
-            } else {
-                mNetd.networkClearDefault();
-            }
-        } catch (RemoteException | ServiceSpecificException e) {
-            loge("Exception setting default network :" + e);
+        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+        if (newDefaultNetwork != null) {
+            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
         }
 
+        // Set an app level managed default and return since further processing only applies to the
+        // default network.
+        if (mDefaultRequest != nri) {
+            makeDefaultForApps(nri, oldDefaultNetwork, newDefaultNetwork);
+            return;
+        }
+
+        makeDefaultNetwork(newDefaultNetwork);
+
         if (oldDefaultNetwork != null) {
             mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
         }
@@ -7249,10 +7303,6 @@
         updateTcpBufferSizes(null != newDefaultNetwork
                 ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
         notifyIfacesChangedForNetworkStats();
-        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
-        if (newDefaultNetwork != null) {
-            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
-        }
 
         // Log 0 -> X and Y -> X default network transitions, where X is the new default.
         final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
@@ -7276,6 +7326,49 @@
                 prevNetwork, prevScore, prevLp, prevNc);
     }
 
+    private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
+            @Nullable final NetworkAgentInfo oldDefaultNetwork,
+            @Nullable final NetworkAgentInfo newDefaultNetwork) {
+        try {
+            if (VDBG) {
+                log("Setting default network for " + nri
+                        + " using UIDs " + nri.getUids()
+                        + " with old network " + (oldDefaultNetwork != null
+                        ? oldDefaultNetwork.network().getNetId() : "null")
+                        + " and new network " + (newDefaultNetwork != null
+                        ? newDefaultNetwork.network().getNetId() : "null"));
+            }
+            if (nri.getUids().isEmpty()) {
+                throw new IllegalStateException("makeDefaultForApps called without specifying"
+                        + " any applications to set as the default." + nri);
+            }
+            if (null != newDefaultNetwork) {
+                mNetd.networkAddUidRanges(
+                        newDefaultNetwork.network.getNetId(),
+                        toUidRangeStableParcels(nri.getUids()));
+            }
+            if (null != oldDefaultNetwork) {
+                mNetd.networkRemoveUidRanges(
+                        oldDefaultNetwork.network.getNetId(),
+                        toUidRangeStableParcels(nri.getUids()));
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception setting OEM network preference default network :" + e);
+        }
+    }
+
+    private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+        try {
+            if (null != newDefaultNetwork) {
+                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
+            } else {
+                mNetd.networkClearDefault();
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception setting default network :" + e);
+        }
+    }
+
     private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
         // For consistency with previous behaviour, send onLost callbacks before onAvailable.
         processNewlyLostListenRequests(nai);
@@ -7397,9 +7490,9 @@
             @Nullable final NetworkAgentInfo previousSatisfier,
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
-        if (newSatisfier != null) {
+        if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
             if (VDBG) log("rematch for " + newSatisfier.toShortString());
-            if (previousSatisfier != null) {
+            if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
                 if (VDBG || DDBG) {
                     log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
@@ -7408,12 +7501,20 @@
             } else {
                 if (VDBG || DDBG) log("   accepting network in place of null");
             }
+
+            // To prevent constantly CPU wake up for nascent timer, if a network comes up
+            // and immediately satisfies a request then remove the timer. This will happen for
+            // all networks except in the case of an underlying network for a VCN.
+            if (newSatisfier.isNascent()) {
+                newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
+            }
+
             newSatisfier.unlingerRequest(newRequest.requestId);
             if (!newSatisfier.addRequest(newRequest)) {
                 Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
                         + newRequest);
             }
-        } else {
+        } else if (null != previousSatisfier) {
             if (DBG) {
                 log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + previousRequest.requestId);
@@ -7464,7 +7565,11 @@
                     break;
                 }
             }
-            if (bestNetwork != nri.mSatisfier) {
+            if (null == bestNetwork && isDefaultBlocked(nri)) {
+                // Remove default networking if disallowed for managed default requests.
+                bestNetwork = mNoServiceNetwork;
+            }
+            if (nri.getSatisfier() != bestNetwork) {
                 // bestNetwork may be null if no network can satisfy this request.
                 changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
                         nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
@@ -7546,19 +7651,19 @@
             }
         }
 
-        // Update the linger state before processing listen callbacks, because the background
-        // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+        // Update the inactivity state before processing listen callbacks, because the background
+        // computation depends on whether the network is inactive. Don't send the LOSING callbacks
         // just yet though, because they have to be sent after the listens are processed to keep
         // backward compatibility.
-        final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+        final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>();
         for (final NetworkAgentInfo nai : nais) {
-            // Rematching may have altered the linger state of some networks, so update all linger
-            // timers. updateLingerState reads the state from the network agent and does nothing
-            // if the state has not changed : the source of truth is controlled with
-            // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
-            // called while rematching the individual networks above.
-            if (updateLingerState(nai, now)) {
-                lingeredNetworks.add(nai);
+            // Rematching may have altered the inactivity state of some networks, so update all
+            // inactivity timers. updateInactivityState reads the state from the network agent
+            // and does nothing if the state has not changed : the source of truth is controlled
+            // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which
+            // have been called while rematching the individual networks above.
+            if (updateInactivityState(nai, now)) {
+                inactiveNetworks.add(nai);
             }
         }
 
@@ -7575,7 +7680,11 @@
             processNewlySatisfiedListenRequests(nai);
         }
 
-        for (final NetworkAgentInfo nai : lingeredNetworks) {
+        for (final NetworkAgentInfo nai : inactiveNetworks) {
+            // For nascent networks, if connecting with no foreground request, skip broadcasting
+            // LOSING for backward compatibility. This is typical when mobile data connected while
+            // wifi connected with mobile data always-on enabled.
+            if (nai.isNascent()) continue;
             notifyNetworkLosing(nai, now);
         }
 
@@ -7584,7 +7693,7 @@
         // Tear down all unneeded networks.
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
             if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                if (nai.getLingerExpiry() > 0) {
+                if (nai.getInactivityExpiry() > 0) {
                     // This network has active linger timers and no requests, but is not
                     // lingering. Linger it.
                     //
@@ -7592,7 +7701,7 @@
                     // and became unneeded due to another network improving its score to the
                     // point where this network will no longer be able to satisfy any requests
                     // even if it validates.
-                    if (updateLingerState(nai, now)) {
+                    if (updateInactivityState(nai, now)) {
                         notifyNetworkLosing(nai, now);
                     }
                 } else {
@@ -7624,34 +7733,34 @@
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
             @NonNull final NetworkReassignment changes,
             @NonNull final Collection<NetworkAgentInfo> nais) {
-        final NetworkReassignment.RequestReassignment fallbackReassignment =
-                changes.getReassignment(mFallbackRequest);
-        final NetworkAgentInfo oldFallbackNetwork =
-                null != fallbackReassignment ? fallbackReassignment.mOldNetwork : null;
-        final NetworkAgentInfo newFallbackNetwork =
-                null != fallbackReassignment ? fallbackReassignment.mNewNetwork : null;
+        final NetworkReassignment.RequestReassignment reassignmentOfDefault =
+                changes.getReassignment(mDefaultRequest);
+        final NetworkAgentInfo oldDefaultNetwork =
+                null != reassignmentOfDefault ? reassignmentOfDefault.mOldNetwork : null;
+        final NetworkAgentInfo newDefaultNetwork =
+                null != reassignmentOfDefault ? reassignmentOfDefault.mNewNetwork : null;
 
-        if (oldFallbackNetwork != newFallbackNetwork) {
+        if (oldDefaultNetwork != newDefaultNetwork) {
             // Maintain the illusion : since the legacy API only understands one network at a time,
             // if the default network changed, apps should see a disconnected broadcast for the
             // old default network before they see a connected broadcast for the new one.
-            if (oldFallbackNetwork != null) {
-                mLegacyTypeTracker.remove(oldFallbackNetwork.networkInfo.getType(),
-                        oldFallbackNetwork, true);
+            if (oldDefaultNetwork != null) {
+                mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
+                        oldDefaultNetwork, true);
             }
-            if (newFallbackNetwork != null) {
+            if (newDefaultNetwork != null) {
                 // The new default network can be newly null if and only if the old default
                 // network doesn't satisfy the default request any more because it lost a
                 // capability.
-                mDefaultInetConditionPublished = newFallbackNetwork.lastValidated ? 100 : 0;
+                mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
                 mLegacyTypeTracker.add(
-                        newFallbackNetwork.networkInfo.getType(), newFallbackNetwork);
+                        newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
                 // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
                 // to reflect the NetworkInfo of this new network. This broadcast has to be sent
                 // after the disconnect broadcasts above, but before the broadcasts sent by the
                 // legacy type tracker below.
                 // TODO : refactor this, it's too complex
-                notifyLockdownVpn(newFallbackNetwork);
+                notifyLockdownVpn(newDefaultNetwork);
             }
         }
 
@@ -7686,7 +7795,7 @@
         }
 
         // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
-        // because usually there are no NetworkRequests it satisfies (e.g., mFallbackRequest
+        // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
         // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
         // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
         if (nai.isVPN()) {
@@ -7697,9 +7806,9 @@
     private void updateInetCondition(NetworkAgentInfo nai) {
         // Don't bother updating until we've graduated to validated at least once.
         if (!nai.everValidated) return;
-        // For now only update icons for the fallback connection.
+        // For now only update icons for the default connection.
         // TODO: Update WiFi and cellular icons separately. b/17237507
-        if (!isFallbackNetwork(nai)) return;
+        if (!isDefaultNetwork(nai)) return;
 
         int newInetCondition = nai.lastValidated ? 100 : 0;
         // Don't repeat publish.
@@ -7816,6 +7925,15 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
+            // Before first rematching networks, put an inactivity timer without any request, this
+            // allows {@code updateInactivityState} to update the state accordingly and prevent
+            // tearing down for any {@code unneeded} evaluation in this period.
+            // Note that the timer will not be rescheduled since the expiry time is
+            // fixed after connection regardless of the network satisfying other requests or not.
+            // But it will be removed as soon as the network satisfies a request for the first time.
+            networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
+                    SystemClock.elapsedRealtime(), mNascentDelayMs);
+
             // Consider network even though it is not yet validated.
             rematchAllNetworksAndRequests();
 
@@ -7869,7 +7987,7 @@
 
     // Notify the requests on this NAI that the network is now lingered.
     private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
-        final int lingerTime = (int) (nai.getLingerExpiry() - now);
+        final int lingerTime = (int) (nai.getInactivityExpiry() - now);
         notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
     }
 
@@ -7967,8 +8085,8 @@
                 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
             }
             NetworkAgentInfo newDefaultAgent = null;
-            if (nai.isSatisfyingRequest(mFallbackRequest.mRequests.get(0).requestId)) {
-                newDefaultAgent = getFallbackNetwork();
+            if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
+                newDefaultAgent = mDefaultRequest.getSatisfier();
                 if (newDefaultAgent != null) {
                     intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
                             newDefaultAgent.networkInfo);
@@ -8016,9 +8134,14 @@
     private Network[] getDefaultNetworks() {
         ensureRunningOnConnectivityServiceThread();
         final ArrayList<Network> defaultNetworks = new ArrayList<>();
-        final NetworkAgentInfo fallbackNetwork = getFallbackNetwork();
+        final Set<Integer> activeNetIds = new ArraySet<>();
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            if (nri.isBeingSatisfied()) {
+                activeNetIds.add(nri.getSatisfier().network().netId);
+            }
+        }
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
-            if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) {
+            if (nai.everConnected && (activeNetIds.contains(nai.network().netId) || nai.isVPN())) {
                 defaultNetworks.add(nai.network);
             }
         }
@@ -8350,7 +8473,7 @@
             throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
         }
 
-        final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+        final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
                 connectionInfo.local, connectionInfo.remote);
 
         /* Filter out Uids not associated with the VPN. */
@@ -9001,6 +9124,7 @@
             }
         }
     }
+
     /**
      * Registers {@link QosSocketFilter} with {@link IQosCallback}.
      *
@@ -9050,4 +9174,10 @@
     public void unregisterQosCallback(@NonNull final IQosCallback callback) {
         mQosCallbackTracker.unregisterCallback(callback);
     }
+
+    @Override
+    public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+        // TODO http://b/176495594 track multiple default networks with networkPreferences
+        if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+    }
 }
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e96fd39..96f832d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -50,6 +50,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.NetdUtils;
+import com.android.net.module.util.NetworkStackConstants;
 
 import java.io.UncheckedIOException;
 import java.net.Inet4Address;
@@ -280,10 +281,12 @@
 
         // Add global routes (but as non-default, non-internet providing network)
         if (allowIPv4) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
         }
         if (allowIPv6) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
         }
 
         final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 6a72010..4dce59f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -290,8 +291,10 @@
         public Vcn newVcn(
                 @NonNull VcnContext vcnContext,
                 @NonNull ParcelUuid subscriptionGroup,
-                @NonNull VcnConfig config) {
-            return new Vcn(vcnContext, subscriptionGroup, config);
+                @NonNull VcnConfig config,
+                @NonNull TelephonySubscriptionSnapshot snapshot,
+                @NonNull VcnSafemodeCallback safemodeCallback) {
+            return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
         }
 
         /** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -382,6 +385,7 @@
                 // delay)
                 for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
                     final VcnConfig config = mConfigs.get(entry.getKey());
+
                     if (config == null
                             || !snapshot.packageHasPermissionsForSubscriptionGroup(
                                     entry.getKey(), config.getProvisioningPackageName())) {
@@ -395,10 +399,13 @@
                                 // correct instance is torn down. This could happen as a result of a
                                 // Carrier App manually removing/adding a VcnConfig.
                                 if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
-                                    mVcns.remove(uuidToTeardown).teardownAsynchronously();
+                                    stopVcnLocked(uuidToTeardown);
                                 }
                             }
                         }, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+                    } else {
+                        // If this VCN's status has not changed, update it with the new snapshot
+                        entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
                     }
                 }
             }
@@ -406,14 +413,44 @@
     }
 
     @GuardedBy("mLock")
+    private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
+        final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
+        if (vcnToTeardown == null) {
+            return;
+        }
+
+        vcnToTeardown.teardownAsynchronously();
+
+        // Now that the VCN is removed, notify all registered listeners to refresh their
+        // UnderlyingNetworkPolicy.
+        notifyAllPolicyListenersLocked();
+    }
+
+    @GuardedBy("mLock")
+    private void notifyAllPolicyListenersLocked() {
+        for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
+            Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
+        }
+    }
+
+    @GuardedBy("mLock")
     private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
         Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
 
         // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
         //                    VCN.
 
-        final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config);
+        final VcnSafemodeCallbackImpl safemodeCallback =
+                new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+        final Vcn newInstance =
+                mDeps.newVcn(
+                        mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
         mVcns.put(subscriptionGroup, newInstance);
+
+        // Now that a new VCN has started, notify all registered listeners to refresh their
+        // UnderlyingNetworkPolicy.
+        notifyAllPolicyListenersLocked();
     }
 
     @GuardedBy("mLock")
@@ -476,9 +513,7 @@
             synchronized (mLock) {
                 mConfigs.remove(subscriptionGroup);
 
-                if (mVcns.containsKey(subscriptionGroup)) {
-                    mVcns.remove(subscriptionGroup).teardownAsynchronously();
-                }
+                stopVcnLocked(subscriptionGroup);
 
                 writeConfigsToDiskLocked();
             }
@@ -508,7 +543,7 @@
         }
     }
 
-    /** Get current configuration list for testing purposes */
+    /** Get current VCNs for testing purposes */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public Map<ParcelUuid, Vcn> getAllVcns() {
         synchronized (mLock) {
@@ -610,8 +645,8 @@
             synchronized (mLock) {
                 ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
 
-                // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
-                if (mVcns.containsKey(subGroup)) {
+                Vcn vcn = mVcns.get(subGroup);
+                if (vcn != null && vcn.isActive()) {
                     isVcnManagedNetwork = true;
                 }
             }
@@ -623,4 +658,31 @@
 
         return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
     }
+
+    /** Callback for signalling when a Vcn has entered Safemode. */
+    public interface VcnSafemodeCallback {
+        /** Called by a Vcn to signal that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+    private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+        @NonNull private final ParcelUuid mSubGroup;
+
+        private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+            mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+        }
+
+        @Override
+        public void onEnteredSafemode() {
+            synchronized (mLock) {
+                // Ignore if this subscription group doesn't exist anymore
+                if (!mVcns.containsKey(mSubGroup)) {
+                    return;
+                }
+
+                notifyAllPolicyListenersLocked();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 686adbb..8f1e4e9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7144,67 +7144,68 @@
                         "getContentProviderImpl: after checkContentProviderPermission");
 
                 final long origId = Binder.clearCallingIdentity();
+                try {
+                    checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
 
-                checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
-
-                // In this case the provider instance already exists, so we can
-                // return it right away.
-                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
-                        stable);
-                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
-                    if (cpr.proc != null
-                            && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
-                        // If this is a perceptible app accessing the provider,
-                        // make sure to count it as being accessed and thus
-                        // back up on the LRU list.  This is good because
-                        // content providers are often expensive to start.
-                        checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
-                        mProcessList.updateLruProcessLocked(cpr.proc, false, null);
-                        checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
+                    // Return the provider instance right away since it already exists.
+                    conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
+                            callingTag, stable);
+                    if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
+                        if (cpr.proc != null
+                                && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+                            // If this is a perceptible app accessing the provider,
+                            // make sure to count it as being accessed and thus
+                            // back up on the LRU list.  This is good because
+                            // content providers are often expensive to start.
+                            checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
+                            mProcessList.updateLruProcessLocked(cpr.proc, false, null);
+                            checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
+                        }
                     }
-                }
 
-                checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
-                final int verifiedAdj = cpr.proc.verifiedAdj;
-                boolean success = updateOomAdjLocked(cpr.proc, true,
-                        OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
-                // XXX things have changed so updateOomAdjLocked doesn't actually tell us
-                // if the process has been successfully adjusted.  So to reduce races with
-                // it, we will check whether the process still exists.  Note that this doesn't
-                // completely get rid of races with LMK killing the process, but should make
-                // them much smaller.
-                if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
-                    success = false;
-                }
-                maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
-                checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
-                if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
-                // NOTE: there is still a race here where a signal could be
-                // pending on the process even though we managed to update its
-                // adj level.  Not sure what to do about this, but at least
-                // the race is now smaller.
-                if (!success) {
-                    // Uh oh...  it looks like the provider's process
-                    // has been killed on us.  We need to wait for a new
-                    // process to be started, and make sure its death
-                    // doesn't kill our process.
-                    Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
-                            + " is crashing; detaching " + r);
-                    boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
-                    if (!lastRef) {
-                        // This wasn't the last ref our process had on
-                        // the provider...  we will be killed during cleaning up, bail.
-                        return null;
+                    checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
+                    final int verifiedAdj = cpr.proc.verifiedAdj;
+                    boolean success = updateOomAdjLocked(cpr.proc, true,
+                            OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
+                    // XXX things have changed so updateOomAdjLocked doesn't actually tell us
+                    // if the process has been successfully adjusted.  So to reduce races with
+                    // it, we will check whether the process still exists.  Note that this doesn't
+                    // completely get rid of races with LMK killing the process, but should make
+                    // them much smaller.
+                    if (success && verifiedAdj != cpr.proc.setAdj
+                            && !isProcessAliveLocked(cpr.proc)) {
+                        success = false;
                     }
-                    // We'll just start a new process to host the content provider
-                    providerRunning = false;
-                    conn = null;
-                    dyingProc = cpr.proc;
-                } else {
-                    cpr.proc.verifiedAdj = cpr.proc.setAdj;
+                    maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
+                    checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
+                    if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
+                    // NOTE: there is still a race here where a signal could be
+                    // pending on the process even though we managed to update its
+                    // adj level.  Not sure what to do about this, but at least
+                    // the race is now smaller.
+                    if (!success) {
+                        // Uh oh...  it looks like the provider's process
+                        // has been killed on us.  We need to wait for a new
+                        // process to be started, and make sure its death
+                        // doesn't kill our process.
+                        Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+                                + " is crashing; detaching " + r);
+                        boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+                        if (!lastRef) {
+                            // This wasn't the last ref our process had on
+                            // the provider...  we will be killed during cleaning up, bail.
+                            return null;
+                        }
+                        // We'll just start a new process to host the content provider
+                        providerRunning = false;
+                        conn = null;
+                        dyingProc = cpr.proc;
+                    } else {
+                        cpr.proc.verifiedAdj = cpr.proc.setAdj;
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
                 }
-
-                Binder.restoreCallingIdentity(origId);
             }
 
             if (!providerRunning) {
@@ -8211,20 +8212,11 @@
                 false /* mountExtStorageFull */, abiOverride, zygotePolicyFlags);
     }
 
+    // TODO: Move to ProcessList?
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride,
             int zygotePolicyFlags) {
-        return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
-                false /* disableTestApiChecks */, mountExtStorageFull, abiOverride,
-                zygotePolicyFlags);
-    }
-
-    // TODO: Move to ProcessList?
-    @GuardedBy("this")
-    final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
-            boolean disableHiddenApiChecks, boolean disableTestApiChecks,
-            boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -8259,8 +8251,7 @@
             mPersistentStartingProcesses.add(app);
             mProcessList.startProcessLocked(app, new HostingRecord("added application",
                     customProcess != null ? customProcess : app.processName),
-                    zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
-                    mountExtStorageFull, abiOverride);
+                    zygotePolicyFlags, disableHiddenApiChecks, mountExtStorageFull, abiOverride);
         }
 
         return app;
@@ -16943,11 +16934,12 @@
                     || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
             boolean disableTestApiChecks = disableHiddenApiChecks
                     || (flags & INSTR_FLAG_DISABLE_TEST_API_CHECKS) != 0;
+
             if (disableHiddenApiChecks || disableTestApiChecks) {
                 enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
                         "disable hidden API checks");
 
-                enableTestApiAccess(ii.packageName);
+                enableTestApiAccess(ai.packageName);
             }
 
             // TODO(b/158750470): remove
@@ -16965,8 +16957,7 @@
             }
 
             ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
-                    disableTestApiChecks, mountExtStorageFull, abiOverride,
-                    ZYGOTE_POLICY_FLAG_EMPTY);
+                    mountExtStorageFull, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
             app.setActiveInstrumentation(activeInstr);
             activeInstr.mFinished = false;
             activeInstr.mSourceUid = callingUid;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6e632d..e2c020af 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1769,8 +1769,8 @@
      */
     @GuardedBy("mService")
     boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
-            int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
-            boolean mountExtStorageFull, String abiOverride) {
+            int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean mountExtStorageFull,
+            String abiOverride) {
         if (app.pendingStart) {
             return true;
         }
@@ -1914,10 +1914,6 @@
                     throw new IllegalStateException("Invalid API policy: " + policy);
                 }
                 runtimeFlags |= policyBits;
-
-                if (disableTestApiChecks) {
-                    runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
-                }
             }
 
             String useAppImageCache = SystemProperties.get(
@@ -2360,8 +2356,7 @@
     final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
             int zygotePolicyFlags, String abiOverride) {
         return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
-                false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
-                false /* mountExtStorageFull */, abiOverride);
+                false /* disableHiddenApiChecks */, false /* mountExtStorageFull */, abiOverride);
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 28afcbb..c20a01d 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@
     static final String[] sDeviceConfigScopes = new String[] {
         DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_CONFIGURATION,
+        DeviceConfig.NAMESPACE_CONNECTIVITY,
         DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index fded85c..e97f0b4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -18,11 +18,9 @@
 
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.Intent.ACTION_USER_ADDED;
-import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
 import static android.content.Intent.EXTRA_REPLACING;
-import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
 
 import android.annotation.NonNull;
@@ -36,8 +34,9 @@
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -48,16 +47,21 @@
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemService;
 
+import java.io.File;
 import java.io.FileDescriptor;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 
 /**
  * System service that manages app hibernation state, a state apps can enter that means they are
@@ -66,6 +70,11 @@
  */
 public final class AppHibernationService extends SystemService {
     private static final String TAG = "AppHibernationService";
+    private static final int PACKAGE_MATCH_FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE
+                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                    | PackageManager.MATCH_DISABLED_COMPONENTS;
 
     /**
      * Lock for accessing any in-memory hibernation state
@@ -76,9 +85,13 @@
     private final IActivityManager mIActivityManager;
     private final UserManager mUserManager;
     @GuardedBy("mLock")
-    private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+    private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
+    private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores =
+            new SparseArray<>();
     @GuardedBy("mLock")
-    private final Set<String> mGloballyHibernatedPackages = new ArraySet<>();
+    private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
+    private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
+    private final Injector mInjector;
 
     /**
      * Initializes the system service.
@@ -90,28 +103,22 @@
      * @param context The system server context.
      */
     public AppHibernationService(@NonNull Context context) {
-        this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
-                ActivityManager.getService(),
-                context.getSystemService(UserManager.class));
+        this(new InjectorImpl(context));
     }
 
     @VisibleForTesting
-    AppHibernationService(@NonNull Context context, IPackageManager packageManager,
-            IActivityManager activityManager, UserManager userManager) {
-        super(context);
-        mContext = context;
-        mIPackageManager = packageManager;
-        mIActivityManager = activityManager;
-        mUserManager = userManager;
+    AppHibernationService(@NonNull Injector injector) {
+        super(injector.getContext());
+        mContext = injector.getContext();
+        mIPackageManager = injector.getPackageManager();
+        mIActivityManager = injector.getActivityManager();
+        mUserManager = injector.getUserManager();
+        mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
+        mInjector = injector;
 
         final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
 
         IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_USER_ADDED);
-        intentFilter.addAction(ACTION_USER_REMOVED);
-        userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
-
-        intentFilter = new IntentFilter();
         intentFilter.addAction(ACTION_PACKAGE_ADDED);
         intentFilter.addAction(ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
@@ -126,12 +133,10 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_BOOT_COMPLETED) {
+            List<GlobalLevelState> states =
+                    mGlobalLevelHibernationDiskStore.readHibernationStates();
             synchronized (mLock) {
-                final List<UserInfo> users = mUserManager.getUsers();
-                // TODO: Pull from persistent disk storage. For now, just make from scratch.
-                for (UserInfo user : users) {
-                    addUserPackageStatesL(user.id);
-                }
+                initializeGlobalHibernationStates(states);
             }
         }
     }
@@ -145,12 +150,14 @@
      */
     boolean isHibernatingForUser(String packageName, int userId) {
         userId = handleIncomingUser(userId, "isHibernating");
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+            Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
+                    + userId);
+            return false;
+        }
         synchronized (mLock) {
-            final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
-            if (packageStates == null) {
-                throw new IllegalArgumentException("No user associated with user id " + userId);
-            }
-            final UserPackageState pkgState = packageStates.get(packageName);
+            final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+            final UserLevelState pkgState = packageStates.get(packageName);
             if (pkgState == null) {
                 throw new IllegalArgumentException(
                         String.format("Package %s is not installed for user %s",
@@ -168,7 +175,12 @@
      */
     boolean isHibernatingGlobally(String packageName) {
         synchronized (mLock) {
-            return mGloballyHibernatedPackages.contains(packageName);
+            GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+            if (state == null) {
+                throw new IllegalArgumentException(
+                        String.format("Package %s is not installed", packageName));
+            }
+            return state.hibernated;
         }
     }
 
@@ -181,12 +193,14 @@
      */
     void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
         userId = handleIncomingUser(userId, "setHibernating");
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+            Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
+                    + userId);
+            return;
+        }
         synchronized (mLock) {
-            if (!mUserStates.contains(userId)) {
-                throw new IllegalArgumentException("No user associated with user id " + userId);
-            }
-            Map<String, UserPackageState> packageStates = mUserStates.get(userId);
-            UserPackageState pkgState = packageStates.get(packageName);
+            final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+            final UserLevelState pkgState = packageStates.get(packageName);
             if (pkgState == null) {
                 throw new IllegalArgumentException(
                         String.format("Package %s is not installed for user %s",
@@ -198,10 +212,12 @@
             }
 
             if (isHibernating) {
-                hibernatePackageForUserL(packageName, userId, pkgState);
+                hibernatePackageForUser(packageName, userId, pkgState);
             } else {
-                unhibernatePackageForUserL(packageName, userId, pkgState);
+                unhibernatePackageForUser(packageName, userId, pkgState);
             }
+            List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
+            mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
         }
     }
 
@@ -213,25 +229,32 @@
      * @param isHibernating new hibernation state
      */
     void setHibernatingGlobally(String packageName, boolean isHibernating) {
-        if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
-            synchronized (mLock) {
+        synchronized (mLock) {
+            GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+            if (state == null) {
+                throw new IllegalArgumentException(
+                        String.format("Package %s is not installed for any user", packageName));
+            }
+            if (state.hibernated != isHibernating) {
                 if (isHibernating) {
-                    hibernatePackageGloballyL(packageName);
+                    hibernatePackageGlobally(packageName, state);
                 } else {
-                    unhibernatePackageGloballyL(packageName);
+                    unhibernatePackageGlobally(packageName, state);
                 }
+                List<GlobalLevelState> states = new ArrayList<>(mGlobalHibernationStates.values());
+                mGlobalLevelHibernationDiskStore.scheduleWriteHibernationStates(states);
             }
         }
     }
 
     /**
      * Put an app into hibernation for a given user, allowing user-level optimizations to occur.
-     * The caller should hold {@link #mLock}
      *
      * @param pkgState package hibernation state
      */
-    private void hibernatePackageForUserL(@NonNull String packageName, int userId,
-            @NonNull UserPackageState pkgState) {
+    @GuardedBy("mLock")
+    private void hibernatePackageForUser(@NonNull String packageName, int userId,
+            @NonNull UserLevelState pkgState) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
         final long caller = Binder.clearCallingIdentity();
         try {
@@ -249,12 +272,13 @@
     }
 
     /**
-     * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
+     * Remove a package from hibernation for a given user.
      *
      * @param pkgState package hibernation state
      */
-    private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
-            UserPackageState pkgState) {
+    @GuardedBy("mLock")
+    private void unhibernatePackageForUser(@NonNull String packageName, int userId,
+            UserLevelState pkgState) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
         final long caller = Binder.clearCallingIdentity();
         try {
@@ -271,60 +295,140 @@
 
     /**
      * Put a package into global hibernation, optimizing its storage at a package / APK level.
-     * The caller should hold {@link #mLock}.
      */
-    private void hibernatePackageGloballyL(@NonNull String packageName) {
+    @GuardedBy("mLock")
+    private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
         // TODO(175830194): Delete vdex/odex when DexManager API is built out
-        mGloballyHibernatedPackages.add(packageName);
+        state.hibernated = true;
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     /**
-     * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
+     * Unhibernate a package from global hibernation.
      */
-    private void unhibernatePackageGloballyL(@NonNull String packageName) {
+    @GuardedBy("mLock")
+    private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
-        mGloballyHibernatedPackages.remove(packageName);
+        state.hibernated = false;
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     /**
-     * Populates {@link #mUserStates} with the users installed packages. The caller should hold
-     * {@link #mLock}.
+     * Initializes in-memory store of user-level hibernation states for the given user
      *
      * @param userId user id to add installed packages for
+     * @param diskStates states pulled from disk, if available
      */
-    private void addUserPackageStatesL(int userId) {
-        Map<String, UserPackageState> packages = new ArrayMap<>();
-        List<PackageInfo> packageList;
+    @GuardedBy("mLock")
+    private void initializeUserHibernationStates(int userId,
+            @Nullable List<UserLevelState> diskStates) {
+        List<PackageInfo> packages;
         try {
-            packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+            packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList();
         } catch (RemoteException e) {
-            throw new IllegalStateException("Package manager not available.", e);
+            throw new IllegalStateException("Package manager not available", e);
         }
 
-        for (int i = 0, size = packageList.size(); i < size; i++) {
-            packages.put(packageList.get(i).packageName, new UserPackageState());
+        Map<String, UserLevelState> userLevelStates = new ArrayMap<>();
+
+        for (int i = 0, size = packages.size(); i < size; i++) {
+            String packageName = packages.get(i).packageName;
+            UserLevelState state = new UserLevelState();
+            state.packageName = packageName;
+            userLevelStates.put(packageName, state);
         }
-        mUserStates.put(userId, packages);
+
+        if (diskStates != null) {
+            Set<String> installedPackages = new ArraySet<>();
+            for (int i = 0, size = packages.size(); i < size; i++) {
+                installedPackages.add(packages.get(i).packageName);
+            }
+            for (int i = 0, size = diskStates.size(); i < size; i++) {
+                String packageName = diskStates.get(i).packageName;
+                if (!installedPackages.contains(packageName)) {
+                    Slog.w(TAG, String.format(
+                            "No hibernation state associated with package %s user %d. Maybe"
+                                    + "the package was uninstalled? ", packageName, userId));
+                    continue;
+                }
+                userLevelStates.put(packageName, diskStates.get(i));
+            }
+        }
+        mUserStates.put(userId, userLevelStates);
     }
 
-    private void onUserAdded(int userId) {
-        synchronized (mLock) {
-            addUserPackageStatesL(userId);
+    /**
+     * Initialize in-memory store of global level hibernation states.
+     *
+     * @param diskStates global level hibernation states pulled from disk, if available
+     */
+    @GuardedBy("mLock")
+    private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) {
+        List<PackageInfo> packages;
+        try {
+            packages = mIPackageManager.getInstalledPackages(
+                    PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList();
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Package manager not available", e);
+        }
+
+        for (int i = 0, size = packages.size(); i < size; i++) {
+            String packageName = packages.get(i).packageName;
+            GlobalLevelState state = new GlobalLevelState();
+            state.packageName = packageName;
+            mGlobalHibernationStates.put(packageName, state);
+        }
+        if (diskStates != null) {
+            Set<String> installedPackages = new ArraySet<>();
+            for (int i = 0, size = packages.size(); i < size; i++) {
+                installedPackages.add(packages.get(i).packageName);
+            }
+            for (int i = 0, size = diskStates.size(); i < size; i++) {
+                GlobalLevelState state = diskStates.get(i);
+                if (!installedPackages.contains(state.packageName)) {
+                    Slog.w(TAG, String.format(
+                            "No hibernation state associated with package %s. Maybe the "
+                                    + "package was uninstalled? ", state.packageName));
+                    continue;
+                }
+                mGlobalHibernationStates.put(state.packageName, state);
+            }
         }
     }
 
-    private void onUserRemoved(int userId) {
+    @Override
+    public void onUserUnlocking(@NonNull TargetUser user) {
+        int userId = user.getUserIdentifier();
+        HibernationStateDiskStore<UserLevelState> diskStore =
+                mInjector.getUserLevelDiskStore(userId);
+        mUserDiskStores.put(userId, diskStore);
+        List<UserLevelState> storedStates = diskStore.readHibernationStates();
         synchronized (mLock) {
+            initializeUserHibernationStates(userId, storedStates);
+        }
+    }
+
+    @Override
+    public void onUserStopping(@NonNull TargetUser user) {
+        int userId = user.getUserIdentifier();
+        // TODO: Flush any scheduled writes to disk immediately on user stopping / power off.
+        synchronized (mLock) {
+            mUserDiskStores.remove(userId);
             mUserStates.remove(userId);
         }
     }
 
     private void onPackageAdded(@NonNull String packageName, int userId) {
         synchronized (mLock) {
-            mUserStates.get(userId).put(packageName, new UserPackageState());
+            UserLevelState userState = new UserLevelState();
+            userState.packageName = packageName;
+            mUserStates.get(userId).put(packageName, userState);
+            if (!mGlobalHibernationStates.containsKey(packageName)) {
+                GlobalLevelState globalState = new GlobalLevelState();
+                globalState.packageName = packageName;
+                mGlobalHibernationStates.put(packageName, globalState);
+            }
         }
     }
 
@@ -336,7 +440,7 @@
 
     private void onPackageRemovedForAllUsers(@NonNull String packageName) {
         synchronized (mLock) {
-            mGloballyHibernatedPackages.remove(packageName);
+            mGlobalHibernationStates.remove(packageName);
         }
     }
 
@@ -395,7 +499,7 @@
         }
     }
 
-    // Broadcast receiver for user and package add/removal events
+    // Broadcast receiver for package add/removal events
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -405,12 +509,6 @@
             }
 
             final String action = intent.getAction();
-            if (ACTION_USER_ADDED.equals(action)) {
-                onUserAdded(userId);
-            }
-            if (ACTION_USER_REMOVED.equals(action)) {
-                onUserRemoved(userId);
-            }
             if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
                 final String packageName = intent.getData().getSchemeSpecificPart();
                 if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
@@ -443,10 +541,66 @@
     }
 
     /**
-     * Data class that contains hibernation state info of a package for a user.
+     * Dependency injector for {@link #AppHibernationService)}.
      */
-    private static final class UserPackageState {
-        public boolean hibernated;
-        // TODO: Track whether hibernation is exempted by the user
+    interface Injector {
+        Context getContext();
+
+        IPackageManager getPackageManager();
+
+        IActivityManager getActivityManager();
+
+        UserManager getUserManager();
+
+        HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
+
+        HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
+    }
+
+    private static final class InjectorImpl implements Injector {
+        private static final String HIBERNATION_DIR_NAME = "hibernation";
+        private final Context mContext;
+        private final ScheduledExecutorService mScheduledExecutorService;
+        private final UserLevelHibernationProto mUserLevelHibernationProto;
+
+        InjectorImpl(Context context) {
+            mContext = context;
+            mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+            mUserLevelHibernationProto = new UserLevelHibernationProto();
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        public IPackageManager getPackageManager() {
+            return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+        }
+
+        @Override
+        public IActivityManager getActivityManager() {
+            return ActivityManager.getService();
+        }
+
+        @Override
+        public UserManager getUserManager() {
+            return mContext.getSystemService(UserManager.class);
+        }
+
+        @Override
+        public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+            File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
+            return new HibernationStateDiskStore<>(
+                    dir, new GlobalLevelHibernationProto(), mScheduledExecutorService);
+        }
+
+        @Override
+        public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+            File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME);
+            return new HibernationStateDiskStore<>(
+                    dir, mUserLevelHibernationProto, mScheduledExecutorService);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
new file mode 100644
index 0000000..79e995b
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link GlobalLevelState} hiberation states.
+ */
+final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLevelState>> {
+    private static final String TAG = "GlobalLevelHibernationProtoReadWriter";
+
+    @Override
+    public void writeToProto(@NonNull ProtoOutputStream stream,
+            @NonNull List<GlobalLevelState> data) {
+        for (int i = 0, size = data.size(); i < size; i++) {
+            long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+            GlobalLevelState state = data.get(i);
+            stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+            stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+            stream.end(token);
+        }
+    }
+
+    @Override
+    public @Nullable List<GlobalLevelState> readFromProto(@NonNull ProtoInputStream stream)
+            throws IOException {
+        List<GlobalLevelState> list = new ArrayList<>();
+        while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            if (stream.getFieldNumber()
+                    != (int) GlobalLevelHibernationStatesProto.HIBERNATION_STATE) {
+                continue;
+            }
+            GlobalLevelState state = new GlobalLevelState();
+            long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (stream.getFieldNumber()) {
+                    case (int) GlobalLevelHibernationStateProto.PACKAGE_NAME:
+                        state.packageName =
+                                stream.readString(GlobalLevelHibernationStateProto.PACKAGE_NAME);
+                        break;
+                    case (int) GlobalLevelHibernationStateProto.HIBERNATED:
+                        state.hibernated =
+                                stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
+                        break;
+                    default:
+                        Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+                }
+            }
+            stream.end(token);
+            list.add(state);
+        }
+        return list;
+    }
+}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
new file mode 100644
index 0000000..4f75675
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Data class that contains global hibernation state for a package.
+ */
+final class GlobalLevelState {
+    public String packageName;
+    public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
new file mode 100644
index 0000000..c83659d
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
@@ -0,0 +1,162 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Disk store utility class for hibernation states.
+ *
+ * @param <T> the type of hibernation state data
+ */
+class HibernationStateDiskStore<T> {
+    private static final String TAG = "HibernationStateDiskStore";
+
+    // Time to wait before actually writing. Saves extra writes if data changes come in batches.
+    private static final long DISK_WRITE_DELAY = 1L * DateUtils.MINUTE_IN_MILLIS;
+    private static final String STATES_FILE_NAME = "states";
+
+    private final File mHibernationFile;
+    private final ScheduledExecutorService mExecutorService;
+    private final ProtoReadWriter<List<T>> mProtoReadWriter;
+    private List<T> mScheduledStatesToWrite = new ArrayList<>();
+    private ScheduledFuture<?> mFuture;
+
+    /**
+     * Initialize a disk store for hibernation states in the given directory.
+     *
+     * @param hibernationDir directory to write/read states file
+     * @param readWriter writer/reader of states proto
+     * @param executorService scheduled executor for writing data
+     */
+    HibernationStateDiskStore(@NonNull File hibernationDir,
+            @NonNull ProtoReadWriter<List<T>> readWriter,
+            @NonNull ScheduledExecutorService executorService) {
+        this(hibernationDir, readWriter, executorService, STATES_FILE_NAME);
+    }
+
+    @VisibleForTesting
+    HibernationStateDiskStore(@NonNull File hibernationDir,
+            @NonNull ProtoReadWriter<List<T>> readWriter,
+            @NonNull ScheduledExecutorService executorService,
+            @NonNull String fileName) {
+        mHibernationFile = new File(hibernationDir, fileName);
+        mExecutorService = executorService;
+        mProtoReadWriter = readWriter;
+    }
+
+    /**
+     * Schedule a full write of all the hibernation states to the file on disk. Does not run
+     * immediately and subsequent writes override previous ones.
+     *
+     * @param hibernationStates list of hibernation states to write to disk
+     */
+    void scheduleWriteHibernationStates(@NonNull List<T> hibernationStates) {
+        synchronized (this) {
+            mScheduledStatesToWrite = hibernationStates;
+            if (mExecutorService.isShutdown()) {
+                Slog.e(TAG, "Scheduled executor service is shut down.");
+                return;
+            }
+
+            // Already have write scheduled
+            if (mFuture != null) {
+                Slog.i(TAG, "Write already scheduled. Skipping schedule.");
+                return;
+            }
+
+            mFuture = mExecutorService.schedule(this::writeHibernationStates, DISK_WRITE_DELAY,
+                    TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /**
+     * Read hibernation states from disk.
+     *
+     * @return the parsed list of hibernation states, null if file does not exist
+     */
+    @Nullable
+    List<T> readHibernationStates() {
+        synchronized (this) {
+            if (!mHibernationFile.exists()) {
+                Slog.i(TAG, "No hibernation file on disk for file " + mHibernationFile.getPath());
+                return null;
+            }
+            AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+            try {
+                FileInputStream inputStream = atomicFile.openRead();
+                ProtoInputStream protoInputStream = new ProtoInputStream(inputStream);
+                return mProtoReadWriter.readFromProto(protoInputStream);
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to read states protobuf.", e);
+                return null;
+            }
+        }
+    }
+
+    @WorkerThread
+    private void writeHibernationStates() {
+        synchronized (this) {
+            writeStateProto(mScheduledStatesToWrite);
+            mScheduledStatesToWrite.clear();
+            mFuture = null;
+        }
+    }
+
+    @WorkerThread
+    private void writeStateProto(List<T> states) {
+        AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+        FileOutputStream fileOutputStream;
+        try {
+            fileOutputStream = atomicFile.startWrite();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to start write to states protobuf.", e);
+            return;
+        }
+
+        try {
+            ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
+            mProtoReadWriter.writeToProto(protoOutputStream, states);
+            protoOutputStream.flush();
+            atomicFile.finishWrite(fileOutputStream);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to finish write to states protobuf.", e);
+            atomicFile.failWrite(fileOutputStream);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
new file mode 100644
index 0000000..0cbc09a
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+/**
+ * Proto utility that reads and writes proto for some data.
+ *
+ * @param <T> data that can be written and read from a proto
+ */
+interface ProtoReadWriter<T> {
+
+    /**
+     * Write data to a proto stream
+     */
+    void writeToProto(@NonNull ProtoOutputStream stream, @NonNull T data);
+
+    /**
+     * Parse data from the proto stream and return
+     */
+    @Nullable T readFromProto(@NonNull ProtoInputStream stream) throws IOException;
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
new file mode 100644
index 0000000..a24c4c5
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link UserLevelState} hiberation states.
+ */
+final class UserLevelHibernationProto implements ProtoReadWriter<List<UserLevelState>> {
+    private static final String TAG = "UserLevelHibernationProtoReadWriter";
+
+    @Override
+    public void writeToProto(@NonNull ProtoOutputStream stream,
+            @NonNull List<UserLevelState> data) {
+        for (int i = 0, size = data.size(); i < size; i++) {
+            long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+            UserLevelState state = data.get(i);
+            stream.write(UserLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+            stream.write(UserLevelHibernationStateProto.HIBERNATED, state.hibernated);
+            stream.end(token);
+        }
+    }
+
+    @Override
+    public @Nullable List<UserLevelState> readFromProto(@NonNull ProtoInputStream stream)
+            throws IOException {
+        List<UserLevelState> list = new ArrayList<>();
+        while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            if (stream.getFieldNumber()
+                    != (int) UserLevelHibernationStatesProto.HIBERNATION_STATE) {
+                continue;
+            }
+            UserLevelState state = new UserLevelState();
+            long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (stream.getFieldNumber()) {
+                    case (int) UserLevelHibernationStateProto.PACKAGE_NAME:
+                        state.packageName =
+                                stream.readString(UserLevelHibernationStateProto.PACKAGE_NAME);
+                        break;
+                    case (int) UserLevelHibernationStateProto.HIBERNATED:
+                        state.hibernated =
+                                stream.readBoolean(UserLevelHibernationStateProto.HIBERNATED);
+                        break;
+                    default:
+                        Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+                }
+            }
+            stream.end(token);
+            list.add(state);
+        }
+        return list;
+    }
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
new file mode 100644
index 0000000..c66dad8
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+final class UserLevelState {
+    public String packageName;
+    public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c70bb08..43d9ade 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.IDnsResolver;
+import android.net.InetAddresses;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.ResolverOptionsParcel;
@@ -190,7 +191,7 @@
             for (String ipAddress : ipAddresses) {
                 try {
                     latestDnses.add(new Pair(hostname,
-                            InetAddress.parseNumericAddress(ipAddress)));
+                            InetAddresses.parseNumericAddress(ipAddress)));
                 } catch (IllegalArgumentException e) {}
             }
             // Remove <hostname, ipAddress> pairs that should not be tracked.
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 952193b..46c49e7 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -34,9 +34,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.net.BaseNetworkObserver;
 
-import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.util.Objects;
 
@@ -433,7 +433,7 @@
         // clat IPv4 address itself (for those apps, it doesn't matter what
         // the IP of the gateway is, only that there is one).
         RouteInfo ipv4Default = new RouteInfo(
-                new LinkAddress(Inet4Address.ANY, 0),
+                new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0),
                 clatAddress.getAddress(), mIface);
         stacked.addRoute(ipv4Default);
         stacked.addLinkAddress(clatAddress);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7..bff1a5c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -122,6 +122,13 @@
 //
 // When ConnectivityService disconnects a network:
 // -----------------------------------------------
+// If a network is just connected, ConnectivityService will think it will be used soon, but might
+// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately.
+// This "nascent" state is implemented by the "lingering" logic below without relating to any
+// request, and is used in some cases where network requests race with network establishment. The
+// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a
+// request, whichever is earlier. In this state, the network is considered in the background.
+//
 // If a network has no chance of satisfying any requests (even if it were to become validated
 // and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
 //
@@ -210,23 +217,23 @@
     // 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 static class InactivityTimer implements Comparable<InactivityTimer> {
         public final int requestId;
         public final long expiryMs;
 
-        public LingerTimer(int requestId, long expiryMs) {
+        public InactivityTimer(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;
+            if (!(o instanceof InactivityTimer)) return false;
+            InactivityTimer other = (InactivityTimer) o;
             return (requestId == other.requestId) && (expiryMs == other.expiryMs);
         }
         public int hashCode() {
             return Objects.hash(requestId, expiryMs);
         }
-        public int compareTo(LingerTimer other) {
+        public int compareTo(InactivityTimer other) {
             return (expiryMs != other.expiryMs) ?
                     Long.compare(expiryMs, other.expiryMs) :
                     Integer.compare(requestId, other.requestId);
@@ -269,30 +276,32 @@
      */
     public static final int ARG_AGENT_SUCCESS = 1;
 
-    // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+    // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
     // a request is moved to a network with a better score, regardless of whether the network is or
-    // was lingering or not.
+    // was lingering or not. An inactivity timer is also added when a network connects
+    // without immediately satisfying any requests.
     // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
     // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
-    private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+    private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
 
-    // For fast lookups. Indexes into mLingerTimers by request ID.
-    private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+    // For fast lookups. Indexes into mInactivityTimers by request ID.
+    private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
 
-    // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
-    // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
-    // When the timer fires, all linger state is cleared, and if the network has no requests, it is
-    // torn down.
-    private WakeupMessage mLingerMessage;
+    // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+    // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+    // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+    // has no requests, it is torn down.
+    private WakeupMessage mInactivityMessage;
 
-    // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
-    private long mLingerExpiryMs;
+    // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+    // armed.
+    private long mInactivityExpiryMs;
 
-    // Whether the network is lingering or not. Must be maintained separately from the above because
+    // Whether the network is inactive or not. Must be maintained separately from the above because
     // it depends on the state of other networks and requests, which only ConnectivityService knows.
     // (Example: we don't linger a network if it would become the best for a NetworkRequest if it
     // validated).
-    private boolean mLingering;
+    private boolean mInactive;
 
     // This represents the quality of the network with no clear scale.
     private int mScore;
@@ -895,20 +904,25 @@
 
     /**
      * 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.
+     * ConnectivityService when the request is moved to another network with a higher score, or
+     * when a network is newly created.
+     *
+     * @param requestId The requestId of the request that no longer need to be served by this
+     *                  network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
+     *                  {@code LingerTimer} for a newly created network.
      */
     public void lingerRequest(int requestId, long now, long duration) {
-        if (mLingerTimerForRequest.get(requestId) != null) {
+        if (mInactivityTimerForRequest.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 " + requestId + " already lingered");
         }
         final long expiryMs = now + duration;
-        LingerTimer timer = new LingerTimer(requestId, expiryMs);
-        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
-        mLingerTimers.add(timer);
-        mLingerTimerForRequest.put(requestId, timer);
+        InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+        if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+        mInactivityTimers.add(timer);
+        mInactivityTimerForRequest.put(requestId, timer);
     }
 
     /**
@@ -916,23 +930,25 @@
      * Returns true if the given requestId was lingering on this network, false otherwise.
      */
     public boolean unlingerRequest(int requestId) {
-        LingerTimer timer = mLingerTimerForRequest.get(requestId);
+        InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
         if (timer != null) {
-            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
-            mLingerTimers.remove(timer);
-            mLingerTimerForRequest.remove(requestId);
+            if (VDBG) {
+                Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+            }
+            mInactivityTimers.remove(timer);
+            mInactivityTimerForRequest.remove(requestId);
             return true;
         }
         return false;
     }
 
-    public long getLingerExpiry() {
-        return mLingerExpiryMs;
+    public long getInactivityExpiry() {
+        return mInactivityExpiryMs;
     }
 
-    public void updateLingerTimer() {
-        long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
-        if (newExpiry == mLingerExpiryMs) return;
+    public void updateInactivityTimer() {
+        long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+        if (newExpiry == mInactivityExpiryMs) return;
 
         // Even if we're going to reschedule the timer, cancel it first. This is because the
         // semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -940,49 +956,65 @@
         // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
         // has already been dispatched, rescheduling to some time in the future won't stop it
         // from calling its callback immediately.
-        if (mLingerMessage != null) {
-            mLingerMessage.cancel();
-            mLingerMessage = null;
+        if (mInactivityMessage != null) {
+            mInactivityMessage.cancel();
+            mInactivityMessage = null;
         }
 
         if (newExpiry > 0) {
-            mLingerMessage = new WakeupMessage(
+            mInactivityMessage = new WakeupMessage(
                     mContext, mHandler,
                     "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
                     EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
                     0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
                     this /* obj (NetworkAgentInfo) */);
-            mLingerMessage.schedule(newExpiry);
+            mInactivityMessage.schedule(newExpiry);
         }
 
-        mLingerExpiryMs = newExpiry;
+        mInactivityExpiryMs = newExpiry;
     }
 
-    public void linger() {
-        mLingering = true;
+    public void setInactive() {
+        mInactive = true;
     }
 
-    public void unlinger() {
-        mLingering = false;
+    public void unsetInactive() {
+        mInactive = false;
+    }
+
+    public boolean isInactive() {
+        return mInactive;
     }
 
     public boolean isLingering() {
-        return mLingering;
+        return mInactive && !isNascent();
     }
 
-    public void clearLingerState() {
-        if (mLingerMessage != null) {
-            mLingerMessage.cancel();
-            mLingerMessage = null;
+    /**
+     * Return whether the network is just connected and about to be torn down because of not
+     * satisfying any request.
+     */
+    public boolean isNascent() {
+        return mInactive && mInactivityTimers.size() == 1
+                && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE;
+    }
+
+    public void clearInactivityState() {
+        if (mInactivityMessage != null) {
+            mInactivityMessage.cancel();
+            mInactivityMessage = null;
         }
-        mLingerTimers.clear();
-        mLingerTimerForRequest.clear();
-        updateLingerTimer();  // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
-        mLingering = false;
+        mInactivityTimers.clear();
+        mInactivityTimerForRequest.clear();
+        // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+        updateInactivityTimer();
+        mInactive = false;
     }
 
-    public void dumpLingerTimers(PrintWriter pw) {
-        for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+    public void dumpInactivityTimers(PrintWriter pw) {
+        for (InactivityTimer timer : mInactivityTimers) {
+            pw.println(timer);
+        }
     }
 
     /**
@@ -1016,7 +1048,7 @@
                 + "network{" + network + "}  handle{" + network.getNetworkHandle() + "}  ni{"
                 + networkInfo.toShortString() + "} "
                 + "  Score{" + getCurrentScore() + "} "
-                + (isLingering() ? " lingering" : "")
+                + (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
                 + (everValidated ? " everValidated" : "")
                 + (lastValidated ? " lastValidated" : "")
                 + (partialConnectivity ? " partialConnectivity" : "")
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d507b5f..8d21f6f 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -265,7 +265,10 @@
         for (Entry<Integer, Boolean> app : apps.entrySet()) {
             List<Integer> list = app.getValue() ? system : network;
             for (int user : users) {
-                list.add(UserHandle.getUid(user, app.getKey()));
+                final UserHandle handle = UserHandle.of(user);
+                if (handle == null) continue;
+
+                list.add(UserHandle.getUid(handle, app.getKey()));
             }
         }
         try {
@@ -550,7 +553,10 @@
         for (UidRange range : ranges) {
             for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
                 for (int appId : appIds) {
-                    final int uid = UserHandle.getUid(userId, appId);
+                    final UserHandle handle = UserHandle.of(userId);
+                    if (handle == null) continue;
+
+                    final int uid = UserHandle.getUid(handle, appId);
                     if (range.contains(uid)) {
                         result.add(uid);
                     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d956ba3..fc2c7e0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -51,6 +51,7 @@
 import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
 import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.IpSecManager;
 import android.net.IpSecManager.IpSecTunnelInterface;
@@ -111,6 +112,7 @@
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.net.BaseNetworkObserver;
@@ -203,6 +205,7 @@
     protected final NetworkCapabilities mNetworkCapabilities;
     private final SystemServices mSystemServices;
     private final Ikev2SessionCreator mIkev2SessionCreator;
+    private final UserManager mUserManager;
 
     /**
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +280,10 @@
             return LocalServices.getService(DeviceIdleInternal.class);
         }
 
+        public PendingIntent getIntentForStatusPanel(Context context) {
+            return VpnConfig.getIntentForStatusPanel(context);
+        }
+
         public void sendArgumentsToDaemon(
                 final String daemon, final LocalSocket socket, final String[] arguments,
                 final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -327,7 +334,7 @@
         public InetAddress resolve(final String endpoint)
                 throws ExecutionException, InterruptedException {
             try {
-                return InetAddress.parseNumericAddress(endpoint);
+                return InetAddresses.parseNumericAddress(endpoint);
             } catch (IllegalArgumentException e) {
                 // Endpoint is not numeric : fall through and resolve
             }
@@ -405,6 +412,7 @@
         mLooper = looper;
         mSystemServices = systemServices;
         mIkev2SessionCreator = ikev2SessionCreator;
+        mUserManager = mContext.getSystemService(UserManager.class);
 
         mPackage = VpnConfig.LEGACY_VPN;
         mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1119,7 +1127,7 @@
 
         if (mConfig.dnsServers != null) {
             for (String dnsServer : mConfig.dnsServers) {
-                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+                InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
                 lp.addDnsServer(address);
                 allowIPv4 |= address instanceof Inet4Address;
                 allowIPv6 |= address instanceof Inet6Address;
@@ -1129,10 +1137,12 @@
         lp.setHttpProxy(mConfig.proxyInfo);
 
         if (!allowIPv4) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
         }
         if (!allowIPv6) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
         }
 
         // Concatenate search domains into a string.
@@ -1431,7 +1441,7 @@
             final long token = Binder.clearCallingIdentity();
             List<UserInfo> users;
             try {
-                users = UserManager.get(mContext).getAliveUsers();
+                users = mUserManager.getAliveUsers();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1515,7 +1525,7 @@
      */
     public void onUserAdded(int userId) {
         // If the user is restricted tie them to the parent user's VPN
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1543,7 +1553,7 @@
      */
     public void onUserRemoved(int userId) {
         // clean up if restricted
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1768,7 +1778,7 @@
     private void prepareStatusIntent() {
         final long token = Binder.clearCallingIdentity();
         try {
-            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+            mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1968,8 +1978,7 @@
 
     private void enforceNotRestrictedUser() {
         Binder.withCleanCallingIdentity(() -> {
-            final UserManager mgr = UserManager.get(mContext);
-            final UserInfo user = mgr.getUserInfo(mUserId);
+            final UserInfo user = mUserManager.getUserInfo(mUserId);
 
             if (user.isRestricted()) {
                 throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2004,9 +2013,8 @@
      */
     public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
             @Nullable Network underlying, @NonNull LinkProperties egress) {
-        UserManager mgr = UserManager.get(mContext);
-        UserInfo user = mgr.getUserInfo(mUserId);
-        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+        UserInfo user = mUserManager.getUserInfo(mUserId);
+        if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
                     new UserHandle(mUserId))) {
             throw new SecurityException("Restricted users cannot establish VPNs");
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dc..39ed7e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
 
 package com.android.server.net;
 
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
 import android.annotation.NonNull;
 import android.net.Network;
 import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@
     public abstract void resetUserState(int userId);
 
     /**
-     * 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
-     * take any locks.
-     *
-     * @param uid The target uid.
-     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
-     * @param isNetworkMetered True if the network is metered.
-     * @param isBackgroundRestricted True if data saver is enabled.
-     *
-     * @return true if networking is blocked for the UID under the specified conditions.
-     */
-    public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
-            boolean isBackgroundRestricted) {
-        // Log of invoking internal function is disabled because it will be called very
-        // frequently. And metrics are unlikely needed on this method because the callers are
-        // external and this method doesn't take any locks or perform expensive operations.
-        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
-                isBackgroundRestricted, null);
-    }
-
-    /**
      * Informs that an appId has been added or removed from the temp-powersave-allowlist so that
      * that network rules for that appId can be updated.
      *
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0fd39a4..6c67cba 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -70,6 +70,7 @@
 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.SUBSCRIPTION_OVERRIDE_UNMETERED;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.net.NetworkPolicyManager.resolveNetworkId;
@@ -231,6 +232,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.ConcurrentUtils;
@@ -1253,7 +1255,7 @@
                 // identified carrier, which may want to manage their own notifications. This method
                 // should be called every time the carrier config changes anyways, and there's no
                 // reason to alert if there isn't a carrier.
-                return;
+                continue;
             }
 
             final boolean notifyWarning = getBooleanDefeatingNullable(config,
@@ -3488,13 +3490,27 @@
 
     @Override
     public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-            long timeoutMillis, String callingPackage) {
+            int[] networkTypes, long timeoutMillis, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
 
-        // We can only override when carrier told us about plans
+        final ArraySet<Integer> allNetworksSet = new ArraySet<>();
+        addAll(allNetworksSet, TelephonyManager.getAllNetworkTypes());
+        final IntArray applicableNetworks = new IntArray();
+
+        // ensure all network types are valid
+        for (int networkType : networkTypes) {
+            if (allNetworksSet.contains(networkType)) {
+                applicableNetworks.add(networkType);
+            } else {
+                Log.d(TAG, "setSubscriptionOverride removing invalid network type: " + networkType);
+            }
+        }
+
+        // We can only override when carrier told us about plans. For the unmetered case,
+        // allow override without having plans defined.
         synchronized (mNetworkPoliciesSecondLock) {
             final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
-            if (plan == null
+            if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && plan == null
                     || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
                 throw new IllegalStateException(
                         "Must provide valid SubscriptionPlan to enable overriding");
@@ -3506,11 +3522,16 @@
         final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                 NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
         if (overrideEnabled || overrideValue == 0) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
-                    overrideMask, overrideValue, subId));
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = subId;
+            args.arg2 = overrideMask;
+            args.arg3 = overrideValue;
+            args.arg4 = applicableNetworks.toArray();
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
             if (timeoutMillis > 0) {
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
-                        overrideMask, 0, subId), timeoutMillis);
+                args.arg3 = 0;
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
+                        timeoutMillis);
             }
         }
     }
@@ -4778,10 +4799,10 @@
     }
 
     private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
-            int overrideMask, int overrideValue) {
+            int overrideMask, int overrideValue, int[] networkTypes) {
         if (listener != null) {
             try {
-                listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+                listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
             } catch (RemoteException ignored) {
             }
         }
@@ -4913,13 +4934,16 @@
                     return true;
                 }
                 case MSG_SUBSCRIPTION_OVERRIDE: {
-                    final int overrideMask = msg.arg1;
-                    final int overrideValue = msg.arg2;
-                    final int subId = (int) msg.obj;
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    final int subId = (int) args.arg1;
+                    final int overrideMask = (int) args.arg2;
+                    final int overrideValue = (int) args.arg3;
+                    final int[] networkTypes = (int[]) args.arg4;
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
+                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
+                                networkTypes);
                     }
                     mListeners.finishBroadcast();
                     return true;
@@ -5380,6 +5404,17 @@
     }
 
     @Override
+    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+            boolean isNetworkMetered, boolean isBackgroundRestricted) {
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+        // Log of invoking this function is disabled because it will be called very frequently. And
+        // metrics are unlikely needed on this method because the callers are external and this
+        // method doesn't take any locks or perform expensive operations.
+        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, null);
+    }
+
+    @Override
     public boolean isUidRestrictedOnMeteredNetworks(int uid) {
         mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         final int uidRules;
@@ -5388,9 +5423,9 @@
             uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
             isBackgroundRestricted = mRestrictBackground;
         }
-        //TODO(b/177490332): The logic here might not be correct because it doesn't consider
-        // RULE_REJECT_METERED condition. And it could be replaced by
-        // isUidNetworkingBlockedInternal().
+        // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+        //  RULE_REJECT_METERED condition. And it could be replaced by
+        //  isUidNetworkingBlockedInternal().
         return isBackgroundRestricted
                 && !hasRule(uidRules, RULE_ALLOW_METERED)
                 && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index c3cb42f..45419fe 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -22,8 +22,8 @@
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5417275..2067fd0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,8 +21,8 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
 import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 
 import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 0000000..a83edb7
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * 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.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+    private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+    private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final TombstoneWatcher mWatcher;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final SparseArray<TombstoneFile> mTombstones;
+
+    NativeTombstoneManager(Context context) {
+        mTombstones = new SparseArray<TombstoneFile>();
+        mContext = context;
+
+        final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+                THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+        thread.start();
+        mHandler = thread.getThreadHandler();
+
+        mWatcher = new TombstoneWatcher();
+        mWatcher.startWatching();
+    }
+
+    void onSystemReady() {
+        // Scan existing tombstones.
+        mHandler.post(() -> {
+            final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+            for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+                if (tombstoneFiles[i].isFile()) {
+                    handleTombstone(tombstoneFiles[i]);
+                }
+            }
+        });
+    }
+
+    private void handleTombstone(File path) {
+        final String filename = path.getName();
+        if (!filename.startsWith("tombstone_")) {
+            return;
+        }
+
+        if (filename.endsWith(".pb")) {
+            handleProtoTombstone(path);
+        } else {
+            BootReceiver.addTombstoneToDropBox(mContext, path);
+        }
+    }
+
+    private void handleProtoTombstone(File path) {
+        final String filename = path.getName();
+        if (!filename.endsWith(".pb")) {
+            Slog.w(TAG, "unexpected tombstone name: " + path);
+            return;
+        }
+
+        final String suffix = filename.substring("tombstone_".length());
+        final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+        int number;
+        try {
+            number = Integer.parseInt(numberStr);
+            if (number < 0 || number > 99) {
+                Slog.w(TAG, "unexpected tombstone name: " + path);
+                return;
+            }
+        } catch (NumberFormatException ex) {
+            Slog.w(TAG, "unexpected tombstone name: " + path);
+            return;
+        }
+
+        ParcelFileDescriptor pfd;
+        try {
+            pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+        } catch (FileNotFoundException ex) {
+            Slog.w(TAG, "failed to open " + path, ex);
+            return;
+        }
+
+        final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+        if (!parsedTombstone.isPresent()) {
+            IoUtils.closeQuietly(pfd);
+            return;
+        }
+
+        synchronized (mLock) {
+            TombstoneFile previous = mTombstones.get(number);
+            if (previous != null) {
+                previous.dispose();
+            }
+
+            mTombstones.put(number, parsedTombstone.get());
+        }
+    }
+
+    static class TombstoneFile {
+        final ParcelFileDescriptor mPfd;
+
+        final @UserIdInt int mUserId;
+        final @AppIdInt int mAppId;
+
+        boolean mPurged = false;
+
+        TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+            mPfd = pfd;
+            mUserId = userId;
+            mAppId = appId;
+        }
+
+        public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+            if (mPurged) {
+                return false;
+            }
+
+            if (userId.isPresent() && userId.get() != mUserId) {
+                return false;
+            }
+
+            if (appId.isPresent() && appId.get() != mAppId) {
+                return false;
+            }
+
+            return true;
+        }
+
+        public void dispose() {
+            IoUtils.closeQuietly(mPfd);
+        }
+
+        static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+            final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+            final ProtoInputStream stream = new ProtoInputStream(is);
+
+            int uid = 0;
+            String selinuxLabel = "";
+
+            try {
+                while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                    switch (stream.getFieldNumber()) {
+                        case (int) Tombstone.UID:
+                            uid = stream.readInt(Tombstone.UID);
+                            break;
+
+                        case (int) Tombstone.SELINUX_LABEL:
+                            selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+                            break;
+
+                        default:
+                            break;
+                    }
+                }
+            } catch (IOException ex) {
+                Slog.e(TAG, "Failed to parse tombstone", ex);
+                return Optional.empty();
+            }
+
+            if (!UserHandle.isApp(uid)) {
+                Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+                return Optional.empty();
+            }
+
+            final int userId = UserHandle.getUserId(uid);
+            final int appId = UserHandle.getAppId(uid);
+
+            if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+                Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+                return Optional.empty();
+            }
+
+            return Optional.of(new TombstoneFile(pfd, userId, appId));
+        }
+    }
+
+    class TombstoneWatcher extends FileObserver {
+        TombstoneWatcher() {
+            // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+            // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+            // isn't supported (MOVED_TO).
+            super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+        }
+
+        @Override
+        public void onEvent(int event, @Nullable String path) {
+            mHandler.post(() -> {
+                handleTombstone(new File(TOMBSTONE_DIR, path));
+            });
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 0000000..cb3c7ff0
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+    private static final String TAG = "NativeTombstoneManagerService";
+
+    private NativeTombstoneManager mManager;
+
+    public NativeTombstoneManagerService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mManager = new NativeTombstoneManager(getContext());
+        LocalServices.addService(NativeTombstoneManager.class, mManager);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            mManager.onSystemReady();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 330f995..9f0efa5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -299,6 +299,10 @@
         final ArraySet<File> unclaimedStages = newArraySet(
                 stagingDir.listFiles(sStageFilter));
 
+        // We also need to clean up orphaned staging directory for staged sessions
+        final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
+        unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
+
         // Ignore stages claimed by active sessions
         for (int i = 0; i < mSessions.size(); i++) {
             final PackageInstallerSession session = mSessions.valueAt(i);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6594a90..ae2e58f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1598,6 +1598,10 @@
         destroyInternal();
         // Dispatch message to remove session from PackageInstallerService.
         dispatchSessionFinished(error, detailMessage, null);
+        // TODO(b/173194203): clean up staged session in destroyInternal() call instead
+        if (isStaged() && stageDir != null) {
+            cleanStageDir();
+        }
     }
 
     private void onStorageUnhealthy() {
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index fd12c2d2..b6ddd93 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -28,16 +28,15 @@
 import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
 import android.os.ParcelUuid;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
-import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -55,19 +54,18 @@
 
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
+    @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
     @NonNull private final UnderlyingNetworkTrackerCallback mCb;
     @NonNull private final Dependencies mDeps;
     @NonNull private final Handler mHandler;
     @NonNull private final ConnectivityManager mConnectivityManager;
-    @NonNull private final SubscriptionManager mSubscriptionManager;
 
-    @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>();
+    @NonNull private final Map<Integer, NetworkCallback> mCellBringupCallbacks = new ArrayMap<>();
     @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
     @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
 
-    @NonNull private final Set<Integer> mSubIds = new ArraySet<>();
-
-    @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
+    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+    private boolean mIsRunning = true;
 
     @Nullable private UnderlyingNetworkRecord mCurrentRecord;
     @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
@@ -75,11 +73,13 @@
     public UnderlyingNetworkTracker(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
             @NonNull UnderlyingNetworkTrackerCallback cb) {
         this(
                 vcnContext,
                 subscriptionGroup,
+                snapshot,
                 requiredUnderlyingNetworkCapabilities,
                 cb,
                 new Dependencies());
@@ -88,11 +88,13 @@
     private UnderlyingNetworkTracker(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
             @NonNull UnderlyingNetworkTrackerCallback cb,
             @NonNull Dependencies deps) {
         mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
         mRequiredUnderlyingNetworkCapabilities =
                 Objects.requireNonNull(
                         requiredUnderlyingNetworkCapabilities,
@@ -103,7 +105,6 @@
         mHandler = new Handler(mVcnContext.getLooper());
 
         mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
-        mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
 
         registerNetworkRequests();
     }
@@ -149,34 +150,47 @@
     private void updateSubIdsAndCellularRequests() {
         mVcnContext.ensureRunningOnLooperThread();
 
-        Set<Integer> prevSubIds = new ArraySet<>(mSubIds);
-        mSubIds.clear();
-
-        // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup
-        // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
-        List<SubscriptionInfo> subInfos =
-                mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
-
-        for (SubscriptionInfo subInfo : subInfos) {
-            final int subId = subInfo.getSubscriptionId();
-            mSubIds.add(subId);
-
-            if (!mCellBringupCallbacks.contains(subId)) {
-                final NetworkBringupCallback cb = new NetworkBringupCallback();
-                mCellBringupCallbacks.put(subId, cb);
-
-                mConnectivityManager.requestBackgroundNetwork(
-                        getCellNetworkRequestForSubId(subId), mHandler, cb);
-            }
+        // Don't bother re-filing NetworkRequests if this Tracker has been torn down.
+        if (!mIsRunning) {
+            return;
         }
 
-        // unregister all NetworkCallbacks for outdated subIds
-        for (final int subId : prevSubIds) {
-            if (!mSubIds.contains(subId)) {
-                final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
-                mConnectivityManager.unregisterNetworkCallback(cb);
-            }
+        final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+
+        // new subIds to track = (updated list of subIds) - (currently tracked subIds)
+        final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup);
+        subIdsToRegister.removeAll(mCellBringupCallbacks.keySet());
+
+        // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds)
+        final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet());
+        subIdsToUnregister.removeAll(subIdsInSubGroup);
+
+        for (final int subId : subIdsToRegister) {
+            final NetworkBringupCallback cb = new NetworkBringupCallback();
+            mCellBringupCallbacks.put(subId, cb);
+
+            mConnectivityManager.requestBackgroundNetwork(
+                    getCellNetworkRequestForSubId(subId), mHandler, cb);
         }
+
+        for (final int subId : subIdsToUnregister) {
+            final NetworkCallback cb = mCellBringupCallbacks.remove(subId);
+            mConnectivityManager.unregisterNetworkCallback(cb);
+        }
+    }
+
+    /**
+     * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
+     *
+     * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
+     * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
+     * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
+     */
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        Objects.requireNonNull(snapshot, "Missing snapshot");
+
+        mLastSnapshot = snapshot;
+        updateSubIdsAndCellularRequests();
     }
 
     /** Tears down this Tracker, and releases all underlying network requests. */
@@ -186,11 +200,12 @@
         mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
         mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
 
-        for (final int subId : mSubIds) {
-            final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+        for (final NetworkCallback cb : mCellBringupCallbacks.values()) {
             mConnectivityManager.unregisterNetworkCallback(cb);
         }
-        mSubIds.clear();
+        mCellBringupCallbacks.clear();
+
+        mIsRunning = false;
     }
 
     /** Returns whether the currently selected Network matches the given network. */
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 132883e..5ec527a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -27,9 +27,18 @@
 import android.os.ParcelUuid;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Represents an single instance of a VCN.
@@ -63,41 +72,86 @@
      */
     private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
 
+    /**
+     * The TelephonySubscriptionSnapshot tracked by VcnManagementService has changed.
+     *
+     * <p>This updated snapshot should be cached locally and passed to all VcnGatewayConnections.
+     *
+     * @param obj TelephonySubscriptionSnapshot
+     */
+    private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
+
     /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
     private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
 
+    /**
+     * Causes this VCN to immediately enter Safemode.
+     *
+     * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+     * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+     */
+    private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final Dependencies mDeps;
     @NonNull private final VcnNetworkRequestListener mRequestListener;
+    @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
 
     @NonNull
     private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
             new HashMap<>();
 
     @NonNull private VcnConfig mConfig;
+    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
 
-    private boolean mIsRunning = true;
+    /**
+     * Whether this Vcn instance is active and running.
+     *
+     * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+     * shut down or has entered safe mode.
+     *
+     * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+     * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+     * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+     * Looper.
+     */
+    // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+    private final AtomicBoolean mIsActive = new AtomicBoolean(true);
 
     public Vcn(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
-            @NonNull VcnConfig config) {
-        this(vcnContext, subscriptionGroup, config, new Dependencies());
+            @NonNull VcnConfig config,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                config,
+                snapshot,
+                vcnSafemodeCallback,
+                new Dependencies());
     }
 
-    private Vcn(
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public Vcn(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnConfig config,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback,
             @NonNull Dependencies deps) {
         super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mVcnSafemodeCallback =
+                Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
         mRequestListener = new VcnNetworkRequestListener();
 
         mConfig = Objects.requireNonNull(config, "Missing config");
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
 
         // Register to receive cached and future NetworkRequests
         mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -110,11 +164,29 @@
         sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
     }
 
+    /** Asynchronously updates the Subscription snapshot for this VCN. */
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        Objects.requireNonNull(snapshot, "Missing snapshot");
+
+        sendMessage(obtainMessage(MSG_EVENT_SUBSCRIPTIONS_CHANGED, snapshot));
+    }
+
     /** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
     public void teardownAsynchronously() {
         sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
     }
 
+    /** Synchronously checks whether this Vcn is active. */
+    public boolean isActive() {
+        return mIsActive.get();
+    }
+
+    /** Get current Gateways for testing purposes */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public Set<VcnGatewayConnection> getVcnGatewayConnections() {
+        return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
+    }
+
     private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
         @Override
         public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -126,7 +198,7 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
-        if (!mIsRunning) {
+        if (!isActive()) {
             return;
         }
 
@@ -137,9 +209,15 @@
             case MSG_EVENT_NETWORK_REQUESTED:
                 handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
                 break;
+            case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
+                handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
+                break;
             case MSG_CMD_TEARDOWN:
                 handleTeardown();
                 break;
+            case MSG_CMD_ENTER_SAFEMODE:
+                handleEnterSafemode();
+                break;
             default:
                 Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
         }
@@ -161,7 +239,13 @@
             gatewayConnection.teardownAsynchronously();
         }
 
-        mIsRunning = false;
+        mIsActive.set(false);
+    }
+
+    private void handleEnterSafemode() {
+        handleTeardown();
+
+        mVcnSafemodeCallback.onEnteredSafemode();
     }
 
     private void handleNetworkRequested(
@@ -192,13 +276,27 @@
                         "Bringing up new VcnGatewayConnection for request " + request.requestId);
 
                 final VcnGatewayConnection vcnGatewayConnection =
-                        new VcnGatewayConnection(
-                                mVcnContext, mSubscriptionGroup, gatewayConnectionConfig);
+                        mDeps.newVcnGatewayConnection(
+                                mVcnContext,
+                                mSubscriptionGroup,
+                                mLastSnapshot,
+                                gatewayConnectionConfig,
+                                new VcnGatewayStatusCallbackImpl());
                 mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
             }
         }
     }
 
+    private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        mLastSnapshot = snapshot;
+
+        if (isActive()) {
+            for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+                gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
+            }
+        }
+    }
+
     private boolean requestSatisfiedByGatewayConnectionConfig(
             @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
@@ -214,11 +312,43 @@
     }
 
     /** Retrieves the network score for a VCN Network */
-    private int getNetworkScore() {
+    // Package visibility for use in VcnGatewayConnection
+    static int getNetworkScore() {
         // TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
         //                               subGrp" value
         return 52;
     }
 
-    private static class Dependencies {}
+    /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public interface VcnGatewayStatusCallback {
+        /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+        @Override
+        public void onEnteredSafemode() {
+            sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+        }
+    }
+
+    /** External dependencies used by Vcn, for injection in tests */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class Dependencies {
+        /** Builds a new VcnGatewayConnection */
+        public VcnGatewayConnection newVcnGatewayConnection(
+                VcnContext vcnContext,
+                ParcelUuid subscriptionGroup,
+                TelephonySubscriptionSnapshot snapshot,
+                VcnGatewayConnectionConfig connectionConfig,
+                VcnGatewayStatusCallback gatewayStatusCallback) {
+            return new VcnGatewayConnection(
+                    vcnContext,
+                    subscriptionGroup,
+                    snapshot,
+                    connectionConfig,
+                    gatewayStatusCallback);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 39c9606..9ecdf1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -17,8 +17,11 @@
 package com.android.server.vcn;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import static com.android.server.VcnManagementService.VDBG;
 
@@ -34,8 +37,10 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.annotations.PolicyDirection;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -47,23 +52,29 @@
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.ParcelUuid;
+import android.util.ArraySet;
 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.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import java.io.IOException;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.util.Arrays;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -113,6 +124,9 @@
 public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
+    private static final int[] MERGED_CAPABILITIES =
+            new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
+
     private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
     private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
 
@@ -359,6 +373,16 @@
      */
     private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
 
+    /**
+     * Sent when this VcnGatewayConnection is notified of a change in TelephonySubscriptions.
+     *
+     * <p>Relevant in all states.
+     *
+     * @param arg1 The "all" token; this signal is always honored.
+     */
+    // TODO(b/178426520): implement handling of this event
+    private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
     final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -379,10 +403,13 @@
     @NonNull
     final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
 
+    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+    @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull private final Dependencies mDeps;
 
     @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -457,28 +484,43 @@
     public VcnGatewayConnection(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
-            @NonNull VcnGatewayConnectionConfig connectionConfig) {
-        this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                snapshot,
+                connectionConfig,
+                gatewayStatusCallback,
+                new Dependencies());
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     VcnGatewayConnection(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
             @NonNull Dependencies deps) {
         super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+        mGatewayStatusCallback =
+                Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
 
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
+
         mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
 
         mUnderlyingNetworkTracker =
                 mDeps.newUnderlyingNetworkTracker(
                         mVcnContext,
                         subscriptionGroup,
+                        mLastSnapshot,
                         mConnectionConfig.getAllUnderlyingCapabilities(),
                         mUnderlyingNetworkTrackerCallback);
         mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
@@ -533,10 +575,28 @@
         mUnderlyingNetworkTracker.teardown();
     }
 
+    /**
+     * Notify this Gateway that subscriptions have changed.
+     *
+     * <p>This snapshot should be used to update any keepalive requests necessary for potential
+     * underlying Networks in this Gateway's subscription group.
+     */
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        Objects.requireNonNull(snapshot, "Missing snapshot");
+        mVcnContext.ensureRunningOnLooperThread();
+
+        mLastSnapshot = snapshot;
+        mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
+
+        sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
+    }
+
     private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
         @Override
         public void onSelectedUnderlyingNetworkChanged(
                 @Nullable UnderlyingNetworkRecord underlying) {
+            // TODO(b/179091925): Move the delayed-message handling to BaseState
+
             // If underlying is null, all underlying networks have been lost. Disconnect VCN after a
             // timeout.
             if (underlying == null) {
@@ -668,7 +728,8 @@
                 case EVENT_TRANSFORM_CREATED: // Fallthrough
                 case EVENT_SETUP_COMPLETED: // Fallthrough
                 case EVENT_DISCONNECT_REQUESTED: // Fallthrough
-                case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+                case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
+                case EVENT_SUBSCRIPTIONS_CHANGED:
                     logUnexpectedEvent(msg.what);
                     break;
                 default:
@@ -921,6 +982,8 @@
                     transitionTo(mDisconnectingState);
                     break;
                 case EVENT_SESSION_CLOSED:
+                    // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+                    // message may not be posted again. Defer to ensure immediate shutdown.
                     deferMessage(msg);
 
                     transitionTo(mDisconnectingState);
@@ -941,7 +1004,108 @@
         }
     }
 
-    private abstract class ConnectedStateBase extends ActiveBaseState {}
+    private abstract class ConnectedStateBase extends ActiveBaseState {
+        protected void updateNetworkAgent(
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull NetworkAgent agent,
+                @NonNull ChildSessionConfiguration childConfig) {
+            final NetworkCapabilities caps =
+                    buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+            final LinkProperties lp =
+                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+            agent.sendNetworkCapabilities(caps);
+            agent.sendLinkProperties(lp);
+        }
+
+        protected NetworkAgent buildNetworkAgent(
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig) {
+            final NetworkCapabilities caps =
+                    buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+            final LinkProperties lp =
+                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+            final NetworkAgent agent =
+                    new NetworkAgent(
+                            mVcnContext.getContext(),
+                            mVcnContext.getLooper(),
+                            TAG,
+                            caps,
+                            lp,
+                            Vcn.getNetworkScore(),
+                            new NetworkAgentConfig(),
+                            mVcnContext.getVcnNetworkProvider()) {
+                        @Override
+                        public void unwanted() {
+                            teardownAsynchronously();
+                        }
+                    };
+
+            agent.register();
+            agent.markConnected();
+
+            return agent;
+        }
+
+        protected void applyTransform(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull Network underlyingNetwork,
+                @NonNull IpSecTransform transform,
+                int direction) {
+            try {
+                // TODO: Set underlying network of tunnel interface
+
+                // Transforms do not need to be persisted; the IkeSession will keep them alive
+                mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
+            } catch (IOException e) {
+                Slog.d(TAG, "Transform application failed for network " + token, e);
+                sessionLost(token, e);
+            }
+        }
+
+        protected void setupInterface(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig) {
+            setupInterface(token, tunnelIface, childConfig, null);
+        }
+
+        protected void setupInterface(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig,
+                @Nullable ChildSessionConfiguration oldChildConfig) {
+            try {
+                final Set<LinkAddress> newAddrs =
+                        new ArraySet<>(childConfig.getInternalAddresses());
+                final Set<LinkAddress> existingAddrs = new ArraySet<>();
+                if (oldChildConfig != null) {
+                    existingAddrs.addAll(oldChildConfig.getInternalAddresses());
+                }
+
+                final Set<LinkAddress> toAdd = new ArraySet<>();
+                toAdd.addAll(newAddrs);
+                toAdd.removeAll(existingAddrs);
+
+                final Set<LinkAddress> toRemove = new ArraySet<>();
+                toRemove.addAll(existingAddrs);
+                toRemove.removeAll(newAddrs);
+
+                for (LinkAddress address : toAdd) {
+                    tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+                }
+
+                for (LinkAddress address : toRemove) {
+                    tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
+                }
+            } catch (IOException e) {
+                Slog.d(TAG, "Adding address to tunnel failed for token " + token, e);
+                sessionLost(token, e);
+            }
+        }
+    }
 
     /**
      * Stable state representing a VCN that has a functioning connection to the mobility anchor.
@@ -951,7 +1115,89 @@
      */
     class ConnectedState extends ConnectedStateBase {
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() throws Exception {
+            // Successful connection, clear failed attempt counter
+            mFailedAttempts = 0;
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED:
+                    handleUnderlyingNetworkChanged(msg);
+                    break;
+                case EVENT_SESSION_CLOSED:
+                    // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+                    // message may not be posted again. Defer to ensure immediate shutdown.
+                    deferMessage(msg);
+                    transitionTo(mDisconnectingState);
+                    break;
+                case EVENT_SESSION_LOST:
+                    transitionTo(mDisconnectingState);
+                    break;
+                case EVENT_TRANSFORM_CREATED:
+                    final EventTransformCreatedInfo transformCreatedInfo =
+                            (EventTransformCreatedInfo) msg.obj;
+
+                    applyTransform(
+                            mCurrentToken,
+                            mTunnelIface,
+                            mUnderlying.network,
+                            transformCreatedInfo.transform,
+                            transformCreatedInfo.direction);
+                    break;
+                case EVENT_SETUP_COMPLETED:
+                    mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
+
+                    setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
+
+        private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
+            final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+            mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+            if (mUnderlying == null) {
+                // Ignored for now; a new network may be coming up. If none does, the delayed
+                // NETWORK_LOST disconnect will be fired, and tear down the session + network.
+                return;
+            }
+
+            // mUnderlying assumed non-null, given check above.
+            // If network changed, migrate. Otherwise, update any existing networkAgent.
+            if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
+                mIkeSession.setNetwork(mUnderlying.network);
+            } else {
+                // oldUnderlying is non-null & underlying network itself has not changed
+                // (only network properties were changed).
+
+                // Network not yet set up, or child not yet connected.
+                if (mNetworkAgent != null && mChildConfig != null) {
+                    // If only network properties changed and agent is active, update properties
+                    updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+                }
+            }
+        }
+
+        protected void setupInterfaceAndNetworkAgent(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig) {
+            setupInterface(token, tunnelIface, childConfig);
+
+            if (mNetworkAgent == null) {
+                mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+            } else {
+                updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+            }
+        }
     }
 
     /**
@@ -966,7 +1212,8 @@
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static NetworkCapabilities buildNetworkCapabilities(
-            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
+            @Nullable UnderlyingNetworkRecord underlying) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
 
         builder.addTransportType(TRANSPORT_CELLULAR);
@@ -978,6 +1225,52 @@
             builder.addCapability(cap);
         }
 
+        if (underlying != null) {
+            final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+
+            // Mirror merged capabilities.
+            for (int cap : MERGED_CAPABILITIES) {
+                if (underlyingCaps.hasCapability(cap)) {
+                    builder.addCapability(cap);
+                }
+            }
+
+            // Set admin UIDs for ConnectivityDiagnostics use.
+            final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
+            Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
+
+            final int[] adminUids;
+            if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
+                    && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
+                            underlyingAdminUids, underlyingCaps.getOwnerUid())) {
+                adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
+                adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
+                Arrays.sort(adminUids);
+            } else {
+                adminUids = underlyingAdminUids;
+            }
+            builder.setAdministratorUids(adminUids);
+
+            // Set TransportInfo for SysUI use (never parcelled out of SystemServer).
+            if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
+                    && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
+                final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
+                builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
+            } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
+                    && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+                final TelephonyNetworkSpecifier telNetSpecifier =
+                        (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
+                builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
+            } else {
+                Slog.wtf(
+                        TAG,
+                        "Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+                                + " non-null underlying network");
+            }
+        }
+
+        // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
+
         return builder.build();
     }
 
@@ -1138,10 +1431,15 @@
         public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
                 VcnContext vcnContext,
                 ParcelUuid subscriptionGroup,
+                TelephonySubscriptionSnapshot snapshot,
                 Set<Integer> requiredUnderlyingNetworkCapabilities,
                 UnderlyingNetworkTrackerCallback callback) {
             return new UnderlyingNetworkTracker(
-                    vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
+                    vcnContext,
+                    subscriptionGroup,
+                    snapshot,
+                    requiredUnderlyingNetworkCapabilities,
+                    callback);
         }
 
         /** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index b9babae..fe4ea30 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -25,6 +25,9 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
 import java.util.Objects;
 import java.util.Set;
 
@@ -52,8 +55,13 @@
         super(context, looper, VcnNetworkProvider.class.getSimpleName());
     }
 
-    // Package-private
-    void registerListener(@NonNull NetworkRequestListener listener) {
+    /**
+     * Registers a NetworkRequestListener with this NetworkProvider.
+     *
+     * <p>Upon registering, the provided listener will receive all cached requests.
+     */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public void registerListener(@NonNull NetworkRequestListener listener) {
         mListeners.add(listener);
 
         // Send listener all cached requests
@@ -62,8 +70,9 @@
         }
     }
 
-    // Package-private
-    void unregisterListener(@NonNull NetworkRequestListener listener) {
+    /** Unregisters the specified listener from receiving future NetworkRequests. */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public void unregisterListener(@NonNull NetworkRequestListener listener) {
         mListeners.remove(listener);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7af237b..fd8fa82 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2023,13 +2023,6 @@
             final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
                     mLaunchFlags);
 
-            // The above code can remove {@code reusedActivity} from the task, leading to the
-            // {@code ActivityRecord} removing its reference to the {@code Task}. The task
-            // reference is needed in the call below to {@link setTargetStackAndMoveToFrontIfNeeded}
-            if (targetTaskTop.getTask() == null) {
-                targetTask.addChild(targetTaskTop);
-            }
-
             if (top != null) {
                 if (top.isRootOfTask()) {
                     // Activity aliases may mean we use different intents for the top activity,
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 389d07a..bbcc2c1 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -11,6 +11,9 @@
 per-file com_android_server_HardwarePropertiesManagerService.cpp = michaelwr@google.com, santoscordon@google.com
 per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, santoscordon@google.com
 
+# BatteryStats
+per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
+
 per-file Android.bp = file:platform/build/soong:/OWNERS
 per-file com_android_server_Usb* = file:/services/usb/OWNERS
 per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 203de9d..c355b2f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -139,6 +139,7 @@
 import com.android.server.om.OverlayManagerService;
 import com.android.server.os.BugreportManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.people.PeopleService;
 import com.android.server.pm.BackgroundDexOptService;
@@ -1072,6 +1073,11 @@
         mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        // Tracks native tombstones.
+        t.traceBegin("StartNativeTombstoneManagerService");
+        mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+        t.traceEnd();
+
         // Service to capture bugreports.
         t.traceBegin("StartBugreportManagerService");
         mSystemServiceManager.startService(BugreportManagerService.class);
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 45bca68..1328b91 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -16,16 +16,18 @@
 
 package com.android.server.apphibernation;
 
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalAnswers.returnsArgAt;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
 
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
@@ -48,6 +50,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -76,18 +79,21 @@
     private IActivityManager mIActivityManager;
     @Mock
     private UserManager mUserManager;
+    @Mock
+    private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
 
     @Before
     public void setUp() throws RemoteException {
+        // Share class loader to allow access to package-private classes
+        System.setProperty("dexmaker.share_classloader", "true");
         MockitoAnnotations.initMocks(this);
         doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
 
-        mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
-                mIActivityManager, mUserManager);
+        mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
 
-        verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+        verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
         mBroadcastReceiver = mReceiverCaptor.getValue();
 
         doReturn(mUserInfos).when(mUserManager).getUsers();
@@ -95,12 +101,19 @@
         doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
                 anyInt(), anyBoolean(), anyBoolean(), any(), any());
 
-        addUser(USER_ID_1);
+        List<PackageInfo> packages = new ArrayList<>();
+        packages.add(makePackageInfo(PACKAGE_NAME_1));
+        doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
+                intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
         mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        UserInfo userInfo = addUser(USER_ID_1);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
     }
 
     @Test
-    public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException {
+    public void testSetHibernatingForUser_packageIsHibernating() {
         // WHEN we hibernate a package for a user
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
 
@@ -109,8 +122,7 @@
     }
 
     @Test
-    public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating()
-            throws RemoteException {
+    public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() {
         // WHEN a new package is added and it is hibernated
         Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
                 Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
@@ -124,17 +136,12 @@
     }
 
     @Test
-    public void testSetHibernatingForUser_newUserAdded_packageIsHibernating()
+    public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
             throws RemoteException {
         // WHEN a new user is added and a package from the user is hibernated
-        List<PackageInfo> userPackages = new ArrayList<>();
-        userPackages.add(makePackageInfo(PACKAGE_NAME_1));
-        doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
-                .getInstalledPackages(anyInt(), eq(USER_ID_2));
-        Intent intent = new Intent(Intent.ACTION_USER_ADDED);
-        intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
-        mBroadcastReceiver.onReceive(mContext, intent);
-
+        UserInfo user2 = addUser(USER_ID_2);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
 
         // THEN the new user's package is hibernated
@@ -142,8 +149,7 @@
     }
 
     @Test
-    public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating()
-            throws RemoteException {
+    public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
         // GIVEN a package is currently hibernated
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
 
@@ -168,25 +174,25 @@
     }
 
     /**
-     * Add a mock user with one package. Must be called before
-     * {@link AppHibernationService#onBootPhase(int)} to work properly.
+     * Add a mock user with one package.
      */
-    private void addUser(int userId) throws RemoteException {
-        addUser(userId, new String[]{PACKAGE_NAME_1});
+    private UserInfo addUser(int userId) throws RemoteException {
+        return addUser(userId, new String[]{PACKAGE_NAME_1});
     }
 
     /**
-     * Add a mock user with the packages specified. Must be called before
-     * {@link AppHibernationService#onBootPhase(int)} to work properly
+     * Add a mock user with the packages specified.
      */
-    private void addUser(int userId, String[] packageNames) throws RemoteException {
-        mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */));
+    private UserInfo addUser(int userId, String[] packageNames) throws RemoteException {
+        UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
+        mUserInfos.add(userInfo);
         List<PackageInfo> userPackages = new ArrayList<>();
         for (String pkgName : packageNames) {
             userPackages.add(makePackageInfo(pkgName));
         }
         doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
-                .getInstalledPackages(anyInt(), eq(userId));
+                .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
+        return userInfo;
     }
 
     private static PackageInfo makePackageInfo(String packageName) {
@@ -194,4 +200,42 @@
         pkg.packageName = packageName;
         return pkg;
     }
+
+    private class MockInjector implements AppHibernationService.Injector {
+        private final Context mContext;
+
+        MockInjector(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public IActivityManager getActivityManager() {
+            return mIActivityManager;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        public IPackageManager getPackageManager() {
+            return mIPackageManager;
+        }
+
+        @Override
+        public UserManager getUserManager() {
+            return mUserManager;
+        }
+
+        @Override
+        public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+            return Mockito.mock(HibernationStateDiskStore.class);
+        }
+
+        @Override
+        public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+            return Mockito.mock(HibernationStateDiskStore.class);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
new file mode 100644
index 0000000..59f3c35
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.FileUtils;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+@SmallTest
+public class HibernationStateDiskStoreTest {
+    private static final String STATES_FILE_NAME = "states";
+    private final MockScheduledExecutorService mMockScheduledExecutorService =
+            new MockScheduledExecutorService();
+
+    private File mFile;
+    private HibernationStateDiskStore<String> mHibernationStateDiskStore;
+
+
+    @Before
+    public void setUp() {
+        mFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+        mHibernationStateDiskStore = new HibernationStateDiskStore<>(mFile,
+                new MockProtoReadWriter(), mMockScheduledExecutorService, STATES_FILE_NAME);
+    }
+
+    @After
+    public void tearDown() {
+        FileUtils.deleteContentsAndDir(mFile);
+    }
+
+    @Test
+    public void testScheduleWriteHibernationStates_writesDataThatCanBeRead() {
+        // GIVEN some data to be written
+        List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+
+        // WHEN the data is written
+        mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+        mMockScheduledExecutorService.executeScheduledTask();
+
+        // THEN the read data is equal to what was written
+        List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+        for (int i = 0; i < toWrite.size(); i++) {
+            assertEquals(toWrite.get(i), storedStrings.get(i));
+        }
+    }
+
+    @Test
+    public void testScheduleWriteHibernationStates_laterWritesOverwritePrevious() {
+        // GIVEN store has some data it is scheduled to write
+        mHibernationStateDiskStore.scheduleWriteHibernationStates(
+                new ArrayList<>(Arrays.asList("C", "D")));
+
+        // WHEN a write is scheduled with new data
+        List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+        mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+        mMockScheduledExecutorService.executeScheduledTask();
+
+        // THEN the written data is the last scheduled data
+        List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+        for (int i = 0; i < toWrite.size(); i++) {
+            assertEquals(toWrite.get(i), storedStrings.get(i));
+        }
+    }
+
+    /**
+     * Mock proto read / writer that just writes and reads a list of String data.
+     */
+    private final class MockProtoReadWriter implements ProtoReadWriter<List<String>> {
+        private static final long FIELD_ID = 1;
+
+        @Override
+        public void writeToProto(@NonNull ProtoOutputStream stream,
+                @NonNull List<String> data) {
+            for (int i = 0, size = data.size(); i < size; i++) {
+                stream.write(FIELD_ID, data.get(i));
+            }
+        }
+
+        @Nullable
+        @Override
+        public List<String> readFromProto(@NonNull ProtoInputStream stream)
+                throws IOException {
+            ArrayList<String> list = new ArrayList<>();
+            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                list.add(stream.readString(FIELD_ID));
+            }
+            return list;
+        }
+    }
+
+    /**
+     * Mock scheduled executor service that has minimum implementation and can synchronously
+     * execute scheduled tasks.
+     */
+    private final class MockScheduledExecutorService implements ScheduledExecutorService {
+
+        Runnable mScheduledRunnable = null;
+
+        @Override
+        public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+            mScheduledRunnable = command;
+            return Mockito.mock(ScheduledFuture.class);
+        }
+
+        @Override
+        public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
+                long period, TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+                long delay, TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void shutdown() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public List<Runnable> shutdownNow() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isShutdown() {
+            return false;
+        }
+
+        @Override
+        public boolean isTerminated() {
+            return false;
+        }
+
+        @Override
+        public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> Future<T> submit(Callable<T> task) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> Future<T> submit(Runnable task, T result) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Future<?> submit(Runnable task) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+                throws InterruptedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+                TimeUnit unit) throws InterruptedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+                throws InterruptedException, ExecutionException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+                throws InterruptedException, ExecutionException, TimeoutException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void execute(Runnable command) {
+            throw new UnsupportedOperationException();
+        }
+
+        void executeScheduledTask() {
+            mScheduledRunnable.run();
+        }
+    }
+}
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 df19aeb..58ba907 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@
     }
 
     /**
-     * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+     * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
      * conditions.
      */
     @Test
-    public void testIsUidNetworkingBlocked() {
+    public void testCheckUidNetworkingBlocked() {
         final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
 
         // Metered network. Data saver on.
@@ -1877,17 +1877,16 @@
 
     private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
             ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
 
         for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
             final boolean expectedResult = pair.first;
             final int rule = pair.second;
             assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
-                    expectedResult,
-                    npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+                    expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+                            metered, backgroundRestricted));
             assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
-                    npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+                    mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+                            backgroundRestricted));
         }
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index a118e0d..bbb25cd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -28,8 +28,8 @@
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3c7206f..69e4190 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,8 +33,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
 import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 7cc233b..cac7b82 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -84,7 +84,6 @@
     static_libs: [
         "libviewcompiler",
     ],
-    test_suites: ["general-tests"],
 }
 
 cc_binary_host {
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5f7d3f9..791e471 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -10,10 +10,6 @@
           "include-filter": "android.view.cts.PrecompiledLayoutTest"
         }
       ]
-    },
-    {
-      "name": "view-compiler-tests",
-      "host": true
     }
   ]
 }
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b73ef9b..be5fae4 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.net.Uri;
@@ -67,7 +68,8 @@
          * Sets the participants for the resulting {@link ConnectionRequest}
          * @param participants The participants to which the {@link Connection} is to connect.
          */
-        public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+        public @NonNull Builder setParticipants(
+                @SuppressLint("NullableCollection") @Nullable List<Uri> participants) {
             this.mParticipants = participants;
             return this;
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a42e364..b81c4f2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3937,6 +3937,20 @@
         public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
                 KEY_PREFIX + "enable_presence_group_subscribe_bool";
 
+        /**
+         * An integer key associated with the period of time in seconds the non-rcs capability
+         * information of each contact is cached on the device.
+         * <p>
+         * The rcs capability cache expiration sec is managed by
+         * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+         * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+         * config.
+         * <p>
+         * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+         */
+        public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+                KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
@@ -3947,6 +3961,7 @@
             defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
+            defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 904232b..4e481b3 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2589,14 +2589,45 @@
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
      * @throws SecurityException if the caller doesn't meet the requirements
-     *             outlined above.
+     *            outlined above.
      */
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
             @DurationMillisLong long timeoutMillis) {
+        setSubscriptionOverrideUnmetered(subId, overrideUnmetered,
+                TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+    }
 
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered unmetered. This will be reflected
+     * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideUnmetered set if the billing relationship should be
+     *            considered unmetered.
+     * @param networkTypes the network types this override applies to.
+     *            {@see TelephonyManager#getAllNetworkTypes()}
+     * @param timeoutMillis the timeout after which the requested override will
+     *            be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
+            @NonNull @Annotation.NetworkType int[] networkTypes,
+            @DurationMillisLong long timeoutMillis) {
         final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
         getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
-                overrideValue, timeoutMillis, mContext.getOpPackageName());
+                overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
     }
 
     /**
@@ -2621,13 +2652,46 @@
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
      * @throws SecurityException if the caller doesn't meet the requirements
-     *             outlined above.
+     *            outlined above.
      */
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
             @DurationMillisLong long timeoutMillis) {
+        setSubscriptionOverrideCongested(subId, overrideCongested,
+                TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+    }
+
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered congested. This will cause the
+     * device to delay certain network requests when possible, such as developer
+     * jobs that are willing to run in a flexible time window.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideCongested set if the subscription should be considered
+     *            congested.
+     * @param networkTypes the network types this override applies to.
+     *            {@see TelephonyManager#getAllNetworkTypes()}
+     * @param timeoutMillis the timeout after which the requested override will
+     *            be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
+            @NonNull @Annotation.NetworkType int[] networkTypes,
+            @DurationMillisLong long timeoutMillis) {
         final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
         getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
-                overrideValue, timeoutMillis, mContext.getOpPackageName());
+                overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index fb65949..6bf992e 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 import android.telephony.ims.stub.SipTransportImplBase;
@@ -52,7 +53,9 @@
      *    implementing this feature elsewhere. If all features of this {@link SipDelegate} are
      *    denied, this method should still be called.
      */
-    void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
+    void onCreated(@NonNull SipDelegate delegate,
+            @SuppressLint("NullableCollection")  // TODO(b/154763999): Mark deniedTags @Nonnull
+            @Nullable Set<FeatureTagState> deniedTags);
 
     /**
      * This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index f3c38bc..08eec29d 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,6 +25,7 @@
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -1325,7 +1326,7 @@
      * the RCS VoLTE single registration feature. Only default messaging application may receive
      * the intent.
      *
-     * <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
+     * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
      * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
      * status.
      */
@@ -1371,7 +1372,7 @@
      * provisioning is done using autoconfiguration, then these parameters shall be
      * sent in the HTTP get request to fetch the RCS provisioning. RCS client
      * configuration must be provided by the application before registering for the
-     * provisioning status events {@link #registerRcsProvisioningChangedCallback()}
+     * provisioning status events {@link #registerRcsProvisioningChangedCallback}
      * @param rcc RCS client configuration {@link RcsClientConfiguration}
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1387,13 +1388,15 @@
     }
 
     /**
-     * Returns a flag to indicate if the device software and the carrier
-     * have the capability to support RCS Volte single IMS registration.
-     * @return true if this single registration is capable, false otherwise
+     * Returns a flag to indicate whether or not the device supports IMS single registration for
+     * MMTEL and RCS features as well as if the carrier has provisioned the feature.
+     * @return true if IMS single registration is capable at this time, or false otherwise
      * @throws ImsException If the remote ImsService is not available for
      * any reason or the subscription associated with this instance is no
      * longer active. See {@link ImsException#getCode()} for more
      * information.
+     * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
+     * device supports IMS single registration.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
@@ -1430,7 +1433,7 @@
      * available. This can happen if the service crashed, for example.
      * It shall also throw this exception when the RCS client parameters for the
      * application are not valid. In that case application must set the client
-     * params (See {@link #setRcsClientConfiguration()}) and re register the
+     * params (See {@link #setRcsClientConfiguration}) and re register the
      * callback.
      * See {@link ImsException#getCode()} for a more detailed reason.
      */
@@ -1458,9 +1461,9 @@
      * will result in a no-op.
      * @param callback The existing {@link RcsProvisioningCallback} to be
      * removed.
-     * @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
-     * Executor, RcsProvisioningCallback) @throws IllegalArgumentException
-     * if the subscription associated with this callback is invalid.
+     * @see #registerRcsProvisioningChangedCallback
+     * @throws IllegalArgumentException if the subscription associated with this callback is
+     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterRcsProvisioningChangedCallback(
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 2e9eb94..04421c9 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -24,6 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.telephony.BinderCacheManager;
@@ -47,6 +48,9 @@
  * This allows multiple IMS applications to forward SIP messages to/from their application for the
  * purposes of providing a single IMS registration to the carrier's IMS network from potentially
  * many IMS stacks implementing a subset of the supported MMTEL/RCS features.
+ * <p>
+ * This API is only supported if the device supports the
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature.
  * @hide
  */
 @SystemApi
@@ -269,6 +273,7 @@
      * {@link ImsException#getCode()} for more information.
      *
      * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+     * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isSupported() throws ImsException {
diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
index 481e7f8..b99d8a7d 100644
--- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
@@ -26,4 +26,5 @@
 oneway interface IPublishResponseCallback {
     void onCommandError(int code);
     void onNetworkResponse(int code, String reason);
+    void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
 }
diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
index a141993..8cc8020 100644
--- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
@@ -30,6 +30,7 @@
 oneway interface ISubscribeResponseCallback {
     void onCommandError(int code);
     void onNetworkResponse(int code, in String reason);
+    void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
     void onNotifyCapabilitiesUpdate(in List<String> pidfXmls);
     void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason);
     void onTerminated(in String reason, long retryAfterMilliseconds);
diff --git a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
index 22985d0..65415ea 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
@@ -34,10 +34,11 @@
     }
 
     @Override
-    public void onCommandError(int code) {
+    public void onCommandError(int code) throws ImsException {
         try {
             mResponseBinder.onCommandError(code);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -46,6 +47,18 @@
         try {
             mResponseBinder.onNetworkResponse(code, reason);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText) throws ImsException {
+        try {
+            mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+                    reasonHeaderText);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
index 1fb339c..11118c0 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
@@ -40,10 +40,11 @@
     }
 
     @Override
-    public void onCommandError(int code) {
+    public void onCommandError(int code) throws ImsException {
         try {
             mResponseBinder.onCommandError(code);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -52,6 +53,18 @@
         try {
             mResponseBinder.onNetworkResponse(code, reason);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText) throws ImsException {
+        try {
+            mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+                    reasonHeaderText);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -60,6 +73,7 @@
         try {
             mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -69,6 +83,7 @@
         try {
             mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason));
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -90,6 +105,7 @@
         try {
             mResponseBinder.onTerminated(reason, retryAfterMilliseconds);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 2e35d27..5f8e93d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -44,8 +44,12 @@
         @Override
         public void setListener(IImsEcbmListener listener) {
             synchronized (mLock) {
-                if (mImsEcbm != null && listener != null && Objects.equals(
-                        mImsEcbm.asBinder(), listener.asBinder())) {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
+                if (mListener != null && listener != null && Objects.equals(
+                        mListener.asBinder(), listener.asBinder())) {
                     return;
                 }
                 if (listener == null) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 555a47e..8e961ac 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -48,6 +48,10 @@
         @Override
         public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
             synchronized (mLock) {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
                 if (mListener != null && listener != null && Objects.equals(
                         mListener.asBinder(), listener.asBinder())) {
                     return;
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eef4fca..83b89aa 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.ims.ImsUtListener;
+import android.util.Log;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
@@ -41,6 +42,7 @@
 // will break other implementations of ImsUt maintained by other ImsServices.
 @SystemApi
 public class ImsUtImplBase {
+    private static final String TAG = "ImsUtImplBase";
     /**
      * Bar all incoming calls. (See 3GPP TS 24.611)
      * @hide
@@ -207,6 +209,11 @@
         @Override
         public void setListener(IImsUtListener listener) throws RemoteException {
             synchronized (mLock) {
+                if (mUtListener != null
+                        && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mUtListener = null;
+                }
                 if (mUtListener != null && listener != null && Objects.equals(
                         mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
                     return;
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 7eba709..ec98be6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -140,6 +140,9 @@
          * Provide the framework with a subsequent network response update to
          * {@link #publishCapabilities(String, PublishResponseCallback)}.
          *
+         * If this network response also contains a “Reason” header, then the
+         * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+         *
          * @param sipCode The SIP response code sent from the network for the operation
          * token specified.
          * @param reason The optional reason response from the network. If there is a reason header
@@ -154,6 +157,31 @@
          */
         void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
                 @NonNull String reason) throws ImsException;
+
+        /**
+         * Provide the framework with a subsequent network response update to
+         * {@link #publishCapabilities(RcsContactUceCapability, int)} that also
+         * includes a reason provided in the “reason” header. See RFC3326 for more
+         * information.
+         *
+         * @param sipCode The SIP response code sent from the network.
+         * @param reasonPhrase The optional reason response from the network. If the
+         * network provided no reason with the sip code, the string should be empty.
+         * @param reasonHeaderCause The “cause” parameter of the “reason” header
+         * included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason” header
+         * included in the SIP message.
+         * @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 onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reasonPhrase,
+                @IntRange(from = 100, to = 699) int reasonHeaderCause,
+                @NonNull String reasonHeaderText) throws ImsException;
     }
 
     /**
@@ -222,6 +250,9 @@
          * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
          * subsequent NOTIFY responses to the subscription.
          *
+         * If this network response also contains a “Reason” header, then the
+         * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+         *
          * @param sipCode 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
@@ -236,6 +267,31 @@
                 @NonNull String reason) throws ImsException;
 
         /**
+         * Notify the framework  of the response to the SUBSCRIBE request from
+         * {@link #subscribeForCapabilities(RcsContactUceCapability, int)} that also
+         * includes a reason provided in the “reason” header. See RFC3326 for more
+         * information.
+         *
+         * @param sipCode The SIP response code sent from the network,
+         * @param reasonPhrase The optional reason response from the network. If the
+         * network provided no reason with the sip code, the string should be empty.
+         * @param reasonHeaderCause The “cause” parameter of the “reason” header
+         * included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason” header
+         * included in the SIP message.
+         * @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 onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reasonPhrase,
+                @IntRange(from = 100, to = 699) int reasonHeaderCause,
+                @NonNull String reasonHeaderText) throws ImsException;
+
+        /**
          * Notify the framework of the latest XML PIDF documents included in the network response
          * for the requested contacts' capabilities requested by the Framework using
          * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 02597d5..e673549 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -96,6 +96,19 @@
         assertSessionReady(sessionId);
     }
 
+    @Test
+    public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception {
+        InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
+                Install.single(TestApp.AIncompleteSplit).setStaged());
+    }
+
+    @Test
+    public void testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage()
+            throws Exception {
+        InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
+                Install.multi(TestApp.AIncompleteSplit, TestApp.B1, TestApp.Apex1).setStaged());
+    }
+
     private static void assertSessionReady(int sessionId) {
         assertSessionState(sessionId,
                 (session) -> assertThat(session.isStagedSessionReady()).isTrue());
diff --git a/tests/UpdatableSystemFontTest/OWNERS b/tests/UpdatableSystemFontTest/OWNERS
new file mode 100644
index 0000000..34ac813
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index cade5ba..d232a507 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -20,22 +20,20 @@
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.os.Build;
-import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRunner;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
 
 @IgnoreUpTo(Build.VERSION_CODES.R)
 @RunWith(DevSdkIgnoreRunner.class)
@@ -45,51 +43,51 @@
     private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
     private static final String TEST_PACKAGE = "com.google.apps.contacts";
 
-    private final List<String> mPackages = new ArrayList<>();
     private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
 
-    @Before
-    public void beforeEachTestMethod() {
-        mPackages.add(TEST_PACKAGE);
-    }
-
     @Test
-    public void builderAddNetworkPreferenceRequiresNonNullPackages() {
+    public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() {
         assertThrows(NullPointerException.class,
-                () -> mBuilder.addNetworkPreference(TEST_PREF, null));
+                () -> mBuilder.addNetworkPreference(null, TEST_PREF));
     }
 
     @Test
-    public void getNetworkPreferencesReturnsCorrectValue() {
-        final int expectedNumberOfMappings = 1;
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+    public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
+        assertThrows(NullPointerException.class,
+                () -> mBuilder.removeNetworkPreference(null));
+    }
 
-        final SparseArray<List<String>> networkPreferences =
+    @Test
+    public void testGetNetworkPreferenceReturnsCorrectValue() {
+        final int expectedNumberOfMappings = 1;
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+
+        final Map<String, Integer> networkPreferences =
                 mBuilder.build().getNetworkPreferences();
 
         assertEquals(expectedNumberOfMappings, networkPreferences.size());
-        assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size());
-        assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0));
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
     }
 
     @Test
-    public void getNetworkPreferencesReturnsUnmodifiableValue() {
+    public void testGetNetworkPreferenceReturnsUnmodifiableValue() {
         final String newPackage = "new.com.google.apps.contacts";
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
 
-        final SparseArray<List<String>> networkPreferences =
+        final Map<String, Integer> networkPreferences =
                 mBuilder.build().getNetworkPreferences();
 
         assertThrows(UnsupportedOperationException.class,
-                () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage));
+                () -> networkPreferences.put(newPackage, TEST_PREF));
 
         assertThrows(UnsupportedOperationException.class,
-                () -> networkPreferences.get(TEST_PREF).add(newPackage));
+                () -> networkPreferences.remove(TEST_PACKAGE));
+
     }
 
     @Test
-    public void toStringReturnsCorrectValue() {
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+    public void testToStringReturnsCorrectValue() {
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
 
         final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString();
 
@@ -99,10 +97,56 @@
 
     @Test
     public void testOemNetworkPreferencesParcelable() {
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
 
         final OemNetworkPreferences prefs = mBuilder.build();
 
         assertParcelSane(prefs, 1 /* fieldCount */);
     }
+
+    @Test
+    public void testAddNetworkPreferenceOverwritesPriorPreference() {
+        final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+        Map<String, Integer> networkPreferences =
+                mBuilder.build().getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+        assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+
+        mBuilder.addNetworkPreference(TEST_PACKAGE, newPref);
+        networkPreferences = mBuilder.build().getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+        assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref);
+    }
+
+    @Test
+    public void testRemoveNetworkPreferenceRemovesValue() {
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+        Map<String, Integer> networkPreferences =
+                mBuilder.build().getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+
+        mBuilder.removeNetworkPreference(TEST_PACKAGE);
+        networkPreferences = mBuilder.build().getNetworkPreferences();
+
+        assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
+    }
+
+    @Test
+    public void testConstructorByOemNetworkPreferencesSetsValue() {
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+        OemNetworkPreferences networkPreference = mBuilder.build();
+
+        final Map<String, Integer> networkPreferences =
+                new OemNetworkPreferences
+                        .Builder(networkPreference)
+                        .build()
+                        .getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+        assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b0cc7f1..fe19f86 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -331,11 +331,12 @@
     private static final String TAG = "ConnectivityServiceTest";
 
     private static final int TIMEOUT_MS = 500;
-    private static final int TEST_LINGER_DELAY_MS = 300;
-    // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
-    // LOST callback that arrives immediately and a LOST callback that arrives after the linger
-    // timeout. For this, our assertions should run fast enough to leave less than
-    // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+    private static final int TEST_LINGER_DELAY_MS = 400;
+    private static final int TEST_NASCENT_DELAY_MS = 300;
+    // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
+    // between a LOST callback that arrives immediately and a LOST callback that arrives after
+    // the linger/nascent timeout. For this, our assertions should run fast enough to leave
+    // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
     // supposedly fired, and the time we call expectCallback.
     private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
     // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
@@ -1262,22 +1263,35 @@
         }
     }
 
-    private void updateUidNetworkingBlocked() {
-        doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
-                i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
-                mRestrictBackground)
+    private void processBroadcastForVpn(Intent intent) {
+        // The BroadcastReceiver for this broadcast checks it is being run on the handler thread.
+        final Handler handler = new Handler(mCsHandlerThread.getLooper());
+        handler.post(() -> mServiceContext.sendBroadcast(intent));
+        waitForIdle();
+    }
+
+    private void mockUidNetworkingBlocked() {
+        doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+                .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+                        i.getArgument(1) /* metered */, mRestrictBackground)
         ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+        doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+                .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+                        inv.getArgument(1) /* uidRules */,
+                        inv.getArgument(2) /* isNetworkMetered */,
+                        inv.getArgument(3) /* isBackgroundRestricted */)
+        ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+                anyInt(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     private void setUidRulesChanged(int uidRules) throws RemoteException {
         mUidRules = uidRules;
-        updateUidNetworkingBlocked();
         mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
     }
 
     private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
         mRestrictBackground = restrictBackground;
-        updateUidNetworkingBlocked();
         mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
     }
 
@@ -1389,6 +1403,7 @@
                 mMockNetd,
                 mDeps);
         mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+        mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
         verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
@@ -1731,6 +1746,108 @@
         verifyNoNetwork();
     }
 
+    /**
+     * Verify a newly created network will be inactive instead of torn down even if no one is
+     * requesting.
+     */
+    @Test
+    public void testNewNetworkInactive() throws Exception {
+        // Create a callback that monitoring the testing network.
+        final TestNetworkCallback listenCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback);
+
+        // 1. Create a network that is not requested by anyone, and does not satisfy any of the
+        // default requests. Verify that the network will be inactive instead of torn down.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithoutInternet();
+        listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        listenCallback.assertNoCallback();
+
+        // Verify that the network will be torn down after nascent expiry. A small period of time
+        // is added in case of flakiness.
+        final int nascentTimeoutMs =
+                mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
+        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+
+        // 2. Create a network that is satisfied by a request comes later.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithoutInternet();
+        listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+        mCm.requestNetwork(wifiRequest, wifiCallback);
+        wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        // Verify that the network will be kept since the request is still satisfied. And is able
+        // to get disconnected as usual if the request is released after the nascent timer expires.
+        listenCallback.assertNoCallback(nascentTimeoutMs);
+        mCm.unregisterNetworkCallback(wifiCallback);
+        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+        // 3. Create a network that is satisfied by a request comes later.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithoutInternet();
+        listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        mCm.requestNetwork(wifiRequest, wifiCallback);
+        wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        // Verify that the network will still be torn down after the request gets removed.
+        mCm.unregisterNetworkCallback(wifiCallback);
+        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+        // There is no need to ensure that LOSING is never sent in the common case that the
+        // network immediately satisfies a request that was already present, because it is already
+        // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called.
+
+        mCm.unregisterNetworkCallback(listenCallback);
+    }
+
+    /**
+     * Verify a newly created network will be inactive and switch to background if only background
+     * request is satisfied.
+     */
+    @Test
+    public void testNewNetworkInactive_bgNetwork() throws Exception {
+        // Create a callback that monitoring the wifi network.
+        final TestNetworkCallback wifiListenCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback);
+
+        // Create callbacks that can monitor background and foreground mobile networks.
+        // This is done by granting using background networks permission before registration. Thus,
+        // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default.
+        grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+        final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback();
+        final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback);
+        mCm.registerNetworkCallback(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback);
+
+        // Connect wifi, which satisfies default request.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Connect a cellular network, verify that satisfies only the background callback.
+        setAlwaysOnNetworks(true);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        fgMobileListenCallback.assertNoCallback();
+        assertFalse(isForegroundNetwork(mCellNetworkAgent));
+
+        mCellNetworkAgent.disconnect();
+        bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        fgMobileListenCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(wifiListenCallback);
+        mCm.unregisterNetworkCallback(bgMobileListenCallback);
+        mCm.unregisterNetworkCallback(fgMobileListenCallback);
+    }
+
     @Test
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
@@ -3888,8 +4005,9 @@
             setAlwaysOnNetworks(false);
             testFactory.expectRequestRemove();
 
-            // ...  and cell data to be torn down.
-            cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            // ...  and cell data to be torn down after nascent network timeout.
+            cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+                    mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
             assertLength(1, mCm.getAllNetworks());
         } finally {
             testFactory.terminate();
@@ -5294,20 +5412,20 @@
         // MOBILE_IFNAME even though the default network is wifi.
         // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
         // applies to the system server UID should not have any bearing on network stats.
-        mService.setUnderlyingNetworksForVpn(onlyCell);
+        mMockVpn.setUnderlyingNetworks(onlyCell);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME});
         reset(mStatsService);
 
-        mService.setUnderlyingNetworksForVpn(cellAndWifi);
+        mMockVpn.setUnderlyingNetworks(cellAndWifi);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
         reset(mStatsService);
 
         // Null underlying networks are ignored.
-        mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+        mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
@@ -5356,25 +5474,25 @@
         // is probably a performance improvement (though it's very unlikely that a VPN would declare
         // no underlying networks).
         // Also, for the same reason as above, the active interface passed in is null.
-        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
 
         // Specifying only a null underlying network is the same as no networks.
-        mService.setUnderlyingNetworksForVpn(onlyNull);
+        mMockVpn.setUnderlyingNetworks(onlyNull);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
 
         // Specifying networks that are all disconnected is the same as specifying no networks.
-        mService.setUnderlyingNetworksForVpn(onlyCell);
+        mMockVpn.setUnderlyingNetworks(onlyCell);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
 
         // Passing in null again means follow the default network again.
-        mService.setUnderlyingNetworksForVpn(null);
+        mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{WIFI_IFNAME});
@@ -5849,7 +5967,7 @@
         mMockVpn.establishForMyUid(false, true, false);
         assertUidRangesUpdatedForMyUid(true);
         final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
-        mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+        mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
         callback.expectAvailableCallbacksUnvalidated(mMockVpn);
         assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
                 .hasTransport(TRANSPORT_VPN));
@@ -6043,7 +6161,7 @@
 
         final Set<UidRange> ranges = uidRangesForUid(uid);
         mMockVpn.registerAgent(ranges);
-        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
 
         // VPN networks do not satisfy the default request and are automatically validated
         // by NetworkMonitor
@@ -6291,7 +6409,7 @@
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mCellNetworkAgent.connect(true);
 
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6306,7 +6424,7 @@
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mWiFiNetworkAgent.connect(true);
 
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6317,7 +6435,7 @@
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Don't disconnect, but note the VPN is not using wifi any more.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6348,7 +6466,7 @@
         vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
 
         // Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6359,7 +6477,7 @@
         assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
 
         // Use both again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6374,7 +6492,7 @@
         vpnNetworkCallback.assertNoCallback();
 
         // Stop using WiFi. The VPN is suspended again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
@@ -6385,7 +6503,7 @@
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6520,9 +6638,7 @@
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
 
         // Send a USER_ADDED broadcast for it.
-        // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
-        final Handler handler = new Handler(mCsHandlerThread.getLooper());
-        handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+        processBroadcastForVpn(addedIntent);
 
         // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
         // restricted user.
@@ -6546,7 +6662,7 @@
         // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+        processBroadcastForVpn(removedIntent);
 
         // Expect that the VPN gains the UID range for the restricted user, and that the capability
         // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6603,9 +6719,7 @@
         // TODO: check that VPN app within restricted profile still has access, etc.
         final Intent addedIntent = new Intent(ACTION_USER_ADDED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        final Handler handler = new Handler(mCsHandlerThread.getLooper());
-        handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
-        waitForIdle();
+        processBroadcastForVpn(addedIntent);
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNull(mCm.getActiveNetworkForUid(restrictedUid));
 
@@ -6615,8 +6729,7 @@
         // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
-        waitForIdle();
+        processBroadcastForVpn(removedIntent);
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
 
@@ -6718,7 +6831,7 @@
         // Ensure VPN is now the active network.
         assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
         // VPN is using Cell
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6726,7 +6839,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is now using WiFi
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6734,7 +6847,7 @@
         assertFalse(mCm.isActiveNetworkMetered());
 
         // VPN is using Cell | WiFi.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6742,7 +6855,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is using WiFi | Cell.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6750,7 +6863,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is not using any underlying networks.
-        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
 
         // VPN without underlying networks is treated as metered.
@@ -6777,7 +6890,7 @@
         assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
 
         // VPN is tracking current platform default (WiFi).
-        mService.setUnderlyingNetworksForVpn(null);
+        mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
 
         // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
@@ -6785,7 +6898,7 @@
 
 
         // VPN explicitly declares WiFi as its underlying network.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6809,6 +6922,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build();
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+        mockUidNetworkingBlocked();
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
@@ -6891,6 +7005,7 @@
     public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
+        mockUidNetworkingBlocked();
 
         // No Networkcallbacks invoked before any network is active.
         setUidRulesChanged(RULE_REJECT_ALL);
@@ -7160,6 +7275,13 @@
         when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
     }
 
+    private void establishLegacyLockdownVpn() throws Exception {
+        // The legacy lockdown VPN only supports userId 0.
+        final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        mMockVpn.registerAgent(ranges);
+        mMockVpn.connect(true);
+    }
+
     @Test
     public void testLegacyLockdownVpn() throws Exception {
         mServiceContext.setPermission(
@@ -7184,9 +7306,7 @@
         final int userId = UserHandle.getUserId(Process.myUid());
         final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-        final Handler handler = new Handler(mCsHandlerThread.getLooper());
-        handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
-        waitForIdle();
+        processBroadcastForVpn(addedIntent);
 
         // Lockdown VPN disables teardown and enables lockdown.
         assertFalse(mMockVpn.getEnableTeardown());
@@ -7254,22 +7374,30 @@
         mMockVpn.expectStartLegacyVpnRunner();
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
         ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
-        mMockVpn.establishForMyUid();
+        establishLegacyLockdownVpn();
         callback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         b1.expectBroadcast();
         b2.expectBroadcast();
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+        assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName("wlan0");
         wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
         wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+        final NetworkCapabilities wifiNc = new NetworkCapabilities();
+        wifiNc.addTransportType(TRANSPORT_WIFI);
+        wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
 
         b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
         // Wifi is CONNECTING because the VPN isn't up yet.
@@ -7302,16 +7430,20 @@
         // The VPN comes up again on wifi.
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
         b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
-        mMockVpn.establishForMyUid();
+        establishLegacyLockdownVpn();
         callback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         b1.expectBroadcast();
         b2.expectBroadcast();
-
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+        assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+        assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Disconnect cell. Nothing much happens since it's not the default network.
         // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
@@ -8355,13 +8487,14 @@
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
             throws Exception {
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        mMockVpn.setVpnType(vpnType);
         mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
-        mMockVpn.setVpnType(vpnType);
 
         final UnderlyingNetworkInfo underlyingNetworkInfo =
                 new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>());
         mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
+        when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42);
     }
 
     private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8410,8 +8543,7 @@
         final int myUid = Process.myUid();
         setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
 
-        // TODO: Test the returned UID
-        mService.getConnectionOwnerUid(getTestConnectionInfo());
+        assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     @Test
@@ -8421,8 +8553,7 @@
         mServiceContext.setPermission(
                 android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
 
-        // TODO: Test the returned UID
-        mService.getConnectionOwnerUid(getTestConnectionInfo());
+        assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     @Test
@@ -8433,8 +8564,7 @@
         mServiceContext.setPermission(
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
 
-        // TODO: Test the returned UID
-        mService.getConnectionOwnerUid(getTestConnectionInfo());
+        assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
@@ -8618,7 +8748,7 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
+        assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network}));
         waitForIdle();
         assertTrue(
                 "Active VPN permission not applied",
@@ -8626,7 +8756,7 @@
                         Process.myPid(), Process.myUid(), naiWithoutUid,
                         mContext.getOpPackageName()));
 
-        assertTrue(mService.setUnderlyingNetworksForVpn(null));
+        assertTrue(mMockVpn.setUnderlyingNetworks(null));
         waitForIdle();
         assertFalse(
                 "VPN shouldn't receive callback on non-underlying network",
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index e590fb7..a10a3c8 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -55,7 +55,7 @@
     private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
 
     private val mMockService = mock(ConnectivityService::class.java).apply {
-        doReturn(false).`when`(this).isFallbackNetwork(any())
+        doReturn(false).`when`(this).isDefaultNetwork(any())
     }
     private val mTracker = LegacyTypeTracker(mMockService).apply {
         supportedTypes.forEach {
@@ -126,11 +126,11 @@
     fun testBroadcastOnDisconnect() {
         val mobileNai1 = mock(NetworkAgentInfo::class.java)
         val mobileNai2 = mock(NetworkAgentInfo::class.java)
-        doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai1)
+        doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
         mTracker.add(TYPE_MOBILE, mobileNai1)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
-        doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai2)
+        doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
         mTracker.add(TYPE_MOBILE, mobileNai2)
         verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
         mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f478282..73cc9f1 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
@@ -213,6 +215,8 @@
 
         when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
         when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+        when(mContext.getSystemServiceName(UserManager.class))
+                .thenReturn(Context.USER_SERVICE);
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
         when(mContext.getSystemServiceName(NotificationManager.class))
@@ -253,12 +257,14 @@
 
     @Test
     public void testRestrictedProfilesAreAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
 
         final Vpn vpn = createVpn(primaryUser.id);
-        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
-                null, null);
+
+        // Assume the user can have restricted profiles.
+        doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+        final Set<UidRange> ranges =
+                vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
                 PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -267,7 +273,6 @@
 
     @Test
     public void testManagedProfilesAreNotAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, managedProfileA);
 
         final Vpn vpn = createVpn(primaryUser.id);
@@ -290,7 +295,6 @@
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -316,7 +320,6 @@
 
     @Test
     public void testGetAlwaysAndOnGetLockDown() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
 
         // Default state.
@@ -341,7 +344,6 @@
 
     @Test
     public void testLockdownChangingPackage() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -369,7 +371,6 @@
 
     @Test
     public void testLockdownAllowlist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -444,7 +445,6 @@
 
     @Test
     public void testLockdownRuleRepeatability() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
                 new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -477,7 +477,6 @@
 
     @Test
     public void testLockdownRuleReversibility() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] entireUser = {
             new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -997,14 +996,12 @@
         profile.ipsecIdentifier = "id";
         profile.ipsecSecret = "secret";
         profile.l2tpSecret = "l2tpsecret";
+
         when(mConnectivityManager.getAllNetworks())
             .thenReturn(new Network[] { new Network(101) });
+
         when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
-                anyInt(), any(), anyInt())).thenAnswer(invocation -> {
-                    // The runner has registered an agent and is now ready.
-                    legacyRunnerReady.open();
-                    return new Network(102);
-                });
+                anyInt(), any(), anyInt())).thenReturn(new Network(102));
         final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
@@ -1020,14 +1017,20 @@
                             "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
                             "idle", "1800", "mtu", "1270", "mru", "1270" },
                     deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
             // Now wait for the runner to be ready before testing for the route.
-            legacyRunnerReady.block(10_000);
-            // In this test the expected address is always v4 so /32
+            ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+            verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+                    lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+            // In this test the expected address is always v4 so /32.
+            // Note that the interface needs to be specified because RouteInfo objects stored in
+            // LinkProperties objects always acquire the LinkProperties' interface.
             final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
-                    RouteInfo.RTN_THROW);
-            assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
-                    + vpn.mConfig.routes,
-                    vpn.mConfig.routes.contains(expectedRoute));
+                    null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+            final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+            assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+                    actualRoutes.contains(expectedRoute));
         } finally {
             // Now interrupt the thread, unblock the runner and clean up.
             vpn.mVpnRunner.exitVpnRunner();
@@ -1083,6 +1086,11 @@
         }
 
         @Override
+        public PendingIntent getIntentForStatusPanel(Context context) {
+            return null;
+        }
+
+        @Override
         public void sendArgumentsToDaemon(
                 final String daemon, final LocalSocket socket, final String[] arguments,
                 final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1179,11 +1187,6 @@
             final int id = (int) invocation.getArguments()[0];
             return userMap.get(id);
         }).when(mUserManager).getUserInfo(anyInt());
-
-        doAnswer(invocation -> {
-            final int id = (int) invocation.getArguments()[0];
-            return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
-        }).when(mUserManager).canHaveRestrictedProfile();
     }
 
     /**
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
 
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
+        return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+    }
+
+    // Public for use in VcnGatewayConnectionTest
+    public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
         final VcnGatewayConnectionConfig.Builder builder =
                 new VcnGatewayConnectionConfig.Builder()
                         .setRetryInterval(RETRY_INTERVALS_MS)
                         .setMaxMtu(MAX_MTU);
 
-        for (int caps : EXPOSED_CAPS) {
+        for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
         }
 
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e7d334e..4859644 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
 import com.android.server.vcn.VcnContext;
@@ -142,6 +143,9 @@
     private final TelephonySubscriptionTracker mSubscriptionTracker =
             mock(TelephonySubscriptionTracker.class);
 
+    private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+            ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
     private final VcnManagementService mVcnMgmtSvc;
 
     private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +188,7 @@
         doAnswer((invocation) -> {
             // Mock-within a doAnswer is safe, because it doesn't actually run nested.
             return mock(Vcn.class);
-        }).when(mMockDeps).newVcn(any(), any(), any());
+        }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
 
         final PersistableBundle bundle =
                 PersistableBundleUtils.fromMap(
@@ -304,14 +308,17 @@
 
     @Test
     public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
-        triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
-        verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+        verify(mMockDeps)
+                .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
     }
 
     @Test
     public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception {
         final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
         final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
         triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
 
@@ -319,6 +326,7 @@
         mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
         mTestLooper.dispatchAll();
         verify(vcn).teardownAsynchronously();
+        verify(mMockPolicyListener).onPolicyChanged();
     }
 
     @Test
@@ -389,6 +397,7 @@
             mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
             fail("Expected security exception for non system user");
         } catch (SecurityException expected) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
         }
     }
 
@@ -400,6 +409,7 @@
             mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
             fail("Expected security exception for missing carrier privileges");
         } catch (SecurityException expected) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
         }
     }
 
@@ -409,6 +419,7 @@
             mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
             fail("Expected exception due to mismatched packages in config and method call");
         } catch (IllegalArgumentException expected) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
         }
     }
 
@@ -473,7 +484,13 @@
         verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
 
         // Verify Vcn is started
-        verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
+        verify(mMockDeps)
+                .newVcn(
+                        eq(mVcnContext),
+                        eq(TEST_UUID_2),
+                        eq(TEST_VCN_CONFIG),
+                        eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+                        any());
 
         // Verify Vcn is updated if it was previously started
         mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -520,7 +537,7 @@
                 Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
     }
 
-    private void verifyMergedNetworkCapabilities(
+    private void verifyMergedNetworkCapabilitiesIsVcnManaged(
             NetworkCapabilities mergedCapabilities, @Transport int transportType) {
         assertTrue(mergedCapabilities.hasTransport(transportType));
         assertFalse(
@@ -529,7 +546,7 @@
     }
 
     @Test
-    public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception {
+    public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
         setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
 
         NetworkCapabilities nc =
@@ -542,12 +559,12 @@
                 mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
 
         assertFalse(policy.isTeardownRequested());
-        verifyMergedNetworkCapabilities(
+        verifyMergedNetworkCapabilitiesIsVcnManaged(
                 policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
     }
 
     @Test
-    public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception {
+    public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
         setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
 
         WifiInfo wifiInfo = mock(WifiInfo.class);
@@ -563,7 +580,7 @@
                 mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
 
         assertFalse(policy.isTeardownRequested());
-        verifyMergedNetworkCapabilities(
+        verifyMergedNetworkCapabilitiesIsVcnManaged(
                 policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
     }
 
@@ -591,4 +608,56 @@
 
         mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties());
     }
+
+    @Test
+    public void testSubscriptionSnapshotUpdateNotifiesVcn() {
+        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+        final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
+        final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
+
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
+
+        verify(vcnInstance).updateSubscriptionSnapshot(eq(snapshot));
+    }
+
+    @Test
+    public void testAddNewVcnUpdatesPolicyListener() throws Exception {
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
+
+    @Test
+    public void testRemoveVcnUpdatesPolicyListener() throws Exception {
+        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
+
+    @Test
+    public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+        verify(mMockDeps)
+                .newVcn(
+                        eq(mVcnContext),
+                        eq(TEST_UUID_1),
+                        eq(TEST_VCN_CONFIG),
+                        eq(snapshot),
+                        mSafemodeCallbackCaptor.capture());
+
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+        safemodeCallback.onEnteredSafemode();
+
+        assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 48e068d..1d459a3 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -42,8 +42,9 @@
 import android.os.ParcelUuid;
 import android.os.test.TestLooper;
 import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
 
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
 import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
@@ -58,13 +59,19 @@
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 public class UnderlyingNetworkTrackerTest {
     private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
     private static final int INITIAL_SUB_ID_1 = 1;
     private static final int INITIAL_SUB_ID_2 = 2;
+    private static final int UPDATED_SUB_ID = 3;
+
+    private static final Set<Integer> INITIAL_SUB_IDS =
+            new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
+    private static final Set<Integer> UPDATED_SUB_IDS =
+            new ArraySet<>(Arrays.asList(UPDATED_SUB_ID));
 
     private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
             new NetworkCapabilities.Builder()
@@ -90,7 +97,7 @@
     @Mock private Context mContext;
     @Mock private VcnNetworkProvider mVcnNetworkProvider;
     @Mock private ConnectivityManager mConnectivityManager;
-    @Mock private SubscriptionManager mSubscriptionManager;
+    @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
     @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
     @Mock private Network mNetwork;
 
@@ -113,23 +120,14 @@
                 mConnectivityManager,
                 Context.CONNECTIVITY_SERVICE,
                 ConnectivityManager.class);
-        setupSystemService(
-                mContext,
-                mSubscriptionManager,
-                Context.TELEPHONY_SUBSCRIPTION_SERVICE,
-                SubscriptionManager.class);
 
-        List<SubscriptionInfo> initialSubInfos =
-                Arrays.asList(
-                        getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
-                        getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
-        when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
-                .thenReturn(initialSubInfos);
+        when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
 
         mUnderlyingNetworkTracker =
                 new UnderlyingNetworkTracker(
                         mVcnContext,
                         SUB_GROUP,
+                        mSubscriptionSnapshot,
                         Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
                         mNetworkTrackerCb);
     }
@@ -154,23 +152,45 @@
                         eq(getWifiRequest()),
                         any(),
                         any(NetworkBringupCallback.class));
-        verify(mConnectivityManager)
-                .requestBackgroundNetwork(
-                        eq(getCellRequestForSubId(INITIAL_SUB_ID_1)),
-                        any(),
-                        any(NetworkBringupCallback.class));
-        verify(mConnectivityManager)
-                .requestBackgroundNetwork(
-                        eq(getCellRequestForSubId(INITIAL_SUB_ID_2)),
-                        any(),
-                        any(NetworkBringupCallback.class));
+        verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
         verify(mConnectivityManager)
                 .requestBackgroundNetwork(
                         eq(getRouteSelectionRequest()),
                         any(),
                         any(RouteSelectionCallback.class));
+    }
 
-        verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP));
+    private void verifyBackgroundCellRequests(
+            TelephonySubscriptionSnapshot snapshot,
+            ParcelUuid subGroup,
+            Set<Integer> expectedSubIds) {
+        verify(snapshot).getAllSubIdsInGroup(eq(subGroup));
+
+        for (final int subId : expectedSubIds) {
+            verify(mConnectivityManager)
+                    .requestBackgroundNetwork(
+                            eq(getCellRequestForSubId(subId)),
+                            any(),
+                            any(NetworkBringupCallback.class));
+        }
+    }
+
+    @Test
+    public void testUpdateSubscriptionSnapshot() {
+        // Verify initial cell background requests filed
+        verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
+        TelephonySubscriptionSnapshot subscriptionUpdate =
+                mock(TelephonySubscriptionSnapshot.class);
+        when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
+
+        mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
+
+        // verify that initially-filed bringup requests are unregistered
+        verify(mConnectivityManager, times(INITIAL_SUB_IDS.size()))
+                .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+        verifyBackgroundCellRequests(subscriptionUpdate, SUB_GROUP, UPDATED_SUB_IDS);
     }
 
     private NetworkRequest getWifiRequest() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
new file mode 100644
index 0000000..e20070e
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static android.net.IpSecManager.DIRECTION_IN;
+import static android.net.IpSecManager.DIRECTION_OUT;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+    private VcnIkeSession mIkeSession;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+
+        mIkeSession = mGatewayConnection.buildIkeSession();
+        mGatewayConnection.setIkeSession(mIkeSession);
+
+        mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testEnterStateCreatesNewIkeSession() throws Exception {
+        verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+    }
+
+    @Test
+    public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession, never()).close();
+    }
+
+    @Test
+    public void testNewNetworkTriggersMigration() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession, never()).close();
+        verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network);
+    }
+
+    @Test
+    public void testSameNetworkDoesNotTriggerMigration() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testCreatedTransformsAreApplied() throws Exception {
+        for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
+            getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+            mTestLooper.dispatchAll();
+
+            verify(mIpSecSvc)
+                    .applyTunnelModeTransform(
+                            eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
+        }
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testChildSessionClosedTriggersDisconnect() throws Exception {
+        getChildSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+        getIkeSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).close();
+    }
+
+    // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 4ecd215..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -44,7 +44,13 @@
     @Test
     public void testEnterWhileNotRunningTriggersQuit() throws Exception {
         final VcnGatewayConnection vgc =
-                new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+                new VcnGatewayConnection(
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
 
         vgc.setIsRunning(false);
         vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index d741e5c..bc6bee2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,20 +16,36 @@
 
 package com.android.server.vcn;
 
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
-import android.annotation.NonNull;
-import android.content.Context;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
 import android.os.ParcelUuid;
-import android.os.test.TestLooper;
+import android.os.Process;
 import android.telephony.SubscriptionInfo;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,7 +57,9 @@
 /** Tests for TelephonySubscriptionTracker */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class VcnGatewayConnectionTest {
+public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
+    private static final int TEST_UID = Process.myUid();
+
     private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
     private static final int TEST_SIM_SLOT_INDEX = 1;
     private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -57,26 +75,67 @@
         TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
     }
 
-    @NonNull private final Context mContext;
-    @NonNull private final TestLooper mTestLooper;
-    @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
-    @NonNull private final VcnGatewayConnection.Dependencies mDeps;
+    private WifiInfo mWifiInfo;
 
-    public VcnGatewayConnectionTest() {
-        mContext = mock(Context.class);
-        mTestLooper = new TestLooper();
-        mVcnNetworkProvider = mock(VcnNetworkProvider.class);
-        mDeps = mock(VcnGatewayConnection.Dependencies.class);
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mWifiInfo = mock(WifiInfo.class);
+    }
+
+    private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
+        final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
+        underlyingCaps.addTransportType(transportType);
+        underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
+        underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
+
+        if (transportType == TRANSPORT_WIFI) {
+            underlyingCaps.setTransportInfo(mWifiInfo);
+            underlyingCaps.setOwnerUid(TEST_UID);
+        } else if (transportType == TRANSPORT_CELLULAR) {
+            underlyingCaps.setAdministratorUids(new int[] {TEST_UID});
+            underlyingCaps.setNetworkSpecifier(
+                    new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
+        }
+
+        UnderlyingNetworkRecord record =
+                new UnderlyingNetworkRecord(
+                        new Network(0), underlyingCaps, new LinkProperties(), false);
+        final NetworkCapabilities vcnCaps =
+                VcnGatewayConnection.buildNetworkCapabilities(
+                        VcnGatewayConnectionConfigTest.buildTestConfig(), record);
+
+        assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
+        assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
+
+        final VcnTransportInfo info = (VcnTransportInfo) vcnCaps.getTransportInfo();
+        if (transportType == TRANSPORT_WIFI) {
+            assertEquals(mWifiInfo, info.getWifiInfo());
+        } else if (transportType == TRANSPORT_CELLULAR) {
+            assertEquals(TEST_SUBSCRIPTION_ID_1, info.getSubId());
+        }
     }
 
     @Test
-    public void testBuildNetworkCapabilities() throws Exception {
-        final NetworkCapabilities caps =
-                VcnGatewayConnection.buildNetworkCapabilities(
-                        VcnGatewayConnectionConfigTest.buildTestConfig());
+    public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
+        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
+    }
 
-        for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
-            assertTrue(caps.hasCapability(exposedCapability));
-        }
+    @Test
+    public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
+        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
+    }
+
+    @Test
+    public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+        final TelephonySubscriptionSnapshot updatedSnapshot =
+                mock(TelephonySubscriptionSnapshot.class);
+        mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
+
+        verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
     }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 4d92fb9..333b5b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,7 +27,9 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.IpSecConfig;
 import android.net.IpSecManager;
+import android.net.IpSecTransform;
 import android.net.IpSecTunnelInterfaceResponse;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -40,15 +42,22 @@
 import android.os.test.TestLooper;
 
 import com.android.server.IpSecService;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
 
+import java.util.Collections;
 import java.util.UUID;
 
 public class VcnGatewayConnectionTestBase {
     protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
-    protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+    protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
+    protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
+    protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
+    protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
+    protected static final int TEST_SUB_ID = 5;
     protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
             new UnderlyingNetworkRecord(
@@ -63,11 +72,16 @@
                     new LinkProperties(),
                     false /* blocked */);
 
+    protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
+            new TelephonySubscriptionSnapshot(
+                    Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
+
     @NonNull protected final Context mContext;
     @NonNull protected final TestLooper mTestLooper;
     @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
     @NonNull protected final VcnContext mVcnContext;
     @NonNull protected final VcnGatewayConnectionConfig mConfig;
+    @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
     @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
 
@@ -82,6 +96,7 @@
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
         mVcnContext = mock(VcnContext.class);
         mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+        mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
         mDeps = mock(VcnGatewayConnection.Dependencies.class);
         mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
 
@@ -94,7 +109,7 @@
 
         doReturn(mUnderlyingNetworkTracker)
                 .when(mDeps)
-                .newUnderlyingNetworkTracker(any(), any(), any(), any());
+                .newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
     }
 
     @Before
@@ -109,7 +124,18 @@
         mMockIkeSession = mock(VcnIkeSession.class);
         doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
 
-        mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+        mGatewayConnection =
+                new VcnGatewayConnection(
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
+    }
+
+    protected IpSecTransform makeDummyIpSecTransform() throws Exception {
+        return new IpSecTransform(mContext, new IpSecConfig());
     }
 
     protected IkeSessionCallback getIkeSessionCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
new file mode 100644
index 0000000..66cbf84
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.NetworkRequest;
+import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Set;
+import java.util.UUID;
+
+public class VcnTest {
+    private static final String PKG_NAME = VcnTest.class.getPackage().getName();
+    private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+    private static final int NETWORK_SCORE = 0;
+    private static final int PROVIDER_ID = 5;
+
+    private Context mContext;
+    private VcnContext mVcnContext;
+    private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+    private VcnNetworkProvider mVcnNetworkProvider;
+    private VcnSafemodeCallback mVcnSafemodeCallback;
+    private Vcn.Dependencies mDeps;
+
+    private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
+    private TestLooper mTestLooper;
+    private VcnGatewayConnectionConfig mGatewayConnectionConfig;
+    private VcnConfig mConfig;
+    private Vcn mVcn;
+
+    @Before
+    public void setUp() {
+        mContext = mock(Context.class);
+        mVcnContext = mock(VcnContext.class);
+        mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
+        mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+        mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
+        mDeps = mock(Vcn.Dependencies.class);
+
+        mTestLooper = new TestLooper();
+
+        doReturn(PKG_NAME).when(mContext).getOpPackageName();
+        doReturn(mContext).when(mVcnContext).getContext();
+        doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+        doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+        // Setup VcnGatewayConnection instance generation
+        doAnswer((invocation) -> {
+            // Mock-within a doAnswer is safe, because it doesn't actually run nested.
+            return mock(VcnGatewayConnection.class);
+        }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
+
+        mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
+
+        final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            configBuilder.addGatewayConnectionConfig(
+                    VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+        }
+        configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+        mConfig = configBuilder.build();
+
+        mVcn =
+                new Vcn(
+                        mVcnContext,
+                        TEST_SUB_GROUP,
+                        mConfig,
+                        mSubscriptionSnapshot,
+                        mVcnSafemodeCallback,
+                        mDeps);
+    }
+
+    private NetworkRequestListener verifyAndGetRequestListener() {
+        ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
+                ArgumentCaptor.forClass(NetworkRequestListener.class);
+        verify(mVcnNetworkProvider).registerListener(mNetworkRequestListenerCaptor.capture());
+
+        return mNetworkRequestListenerCaptor.getValue();
+    }
+
+    private void startVcnGatewayWithCapabilities(
+            NetworkRequestListener requestListener, int... netCapabilities) {
+        final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+        for (final int netCapability : netCapabilities) {
+            requestBuilder.addCapability(netCapability);
+        }
+
+        requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        startVcnGatewayWithCapabilities(
+                requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
+
+        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+        assertFalse(gatewayConnections.isEmpty());
+
+        final TelephonySubscriptionSnapshot updatedSnapshot =
+                mock(TelephonySubscriptionSnapshot.class);
+
+        mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+        mTestLooper.dispatchAll();
+
+        for (final VcnGatewayConnection gateway : gatewayConnections) {
+            verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
+        }
+    }
+
+    @Test
+    public void testGatewayEnteringSafemodeNotifiesVcn() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            startVcnGatewayWithCapabilities(requestListener, capability);
+        }
+
+        // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+        // Expect one VcnGatewayConnection per capability.
+        final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+        assertEquals(numExpectedGateways, gatewayConnections.size());
+        verify(mDeps, times(numExpectedGateways))
+                .newVcnGatewayConnection(
+                        eq(mVcnContext),
+                        eq(TEST_SUB_GROUP),
+                        eq(mSubscriptionSnapshot),
+                        any(),
+                        mGatewayStatusCallbackCaptor.capture());
+
+        // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+        // all Gateways
+        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+        statusCallback.onEnteredSafemode();
+        mTestLooper.dispatchAll();
+
+        for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+            verify(gatewayConnection).teardownAsynchronously();
+        }
+        verify(mVcnNetworkProvider).unregisterListener(requestListener);
+        verify(mVcnSafemodeCallback).onEnteredSafemode();
+    }
+}