Merge "Make getKeyStoreForUid convert uid to namespace."
diff --git a/Android.bp b/Android.bp
index 6a0bdc3..82915b5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -484,7 +484,7 @@
         "android.hardware.vibrator-V1.3-java",
         "android.security.apc-java",
         "android.security.authorization-java",
-        "android.system.keystore2-java",
+        "android.system.keystore2-V1-java",
         "android.system.suspend.control.internal-java",
         "devicepolicyprotosnano",
 
@@ -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/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 0000000..4f168ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_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/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/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index e21a6b2..50f4001 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -132,9 +132,6 @@
         "tests/XmlParserTests.cpp",
         "tests/ZipFileTests.cpp",
     ],
-    required: [
-        "idmap2",
-    ],
     static_libs: ["libgmock"],
     target: {
         android: {
@@ -163,9 +160,19 @@
             shared_libs: [
                 "libz",
             ],
+            data: [
+                ":libz",
+                ":idmap2",
+            ],
         },
     },
-    data: ["tests/data/**/*.apk"],
+    data: [
+        "tests/data/**/*.apk",
+    ],
+    compile_multilib: "first",
+    test_options: {
+        unit_test: true,
+    },
 }
 
 cc_binary {
diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml
deleted file mode 100644
index 5147f4e..0000000
--- a/cmds/idmap2/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     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.
--->
-<configuration description="Config for idmap2_tests">
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" />
-    </target_preparer>
-    <option name="test-suite-tag" value="idmap2_tests" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="idmap2_tests" />
-    </test>
-</configuration>
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 8e70bc5..976ec56 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -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..d66a96b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1613,6 +1613,7 @@
     field public static final int UUID_BYTES_128_BIT = 16; // 0x10
     field public static final int UUID_BYTES_16_BIT = 2; // 0x2
     field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+    field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
   }
 
   public final class BufferConstraint implements android.os.Parcelable {
@@ -2131,6 +2132,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 +7594,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 +8292,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 +8437,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 {
@@ -10730,7 +10756,6 @@
     method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int);
     method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long);
     method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int);
-    field public static final long NO_THROTTLE_EXPIRY_TIME = -1L; // 0xffffffffffffffffL
   }
 
   public final class DataCallResponse implements android.os.Parcelable {
@@ -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.bp b/core/java/Android.bp
index fb27f74..af5df76 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,3 +7,8 @@
     name: "IDropBoxManagerService.aidl",
     srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
 }
+
+filegroup {
+    name: "ITracingServiceProxy.aidl",
+    srcs: ["android/tracing/ITracingServiceProxy.aidl"],
+}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e6aa7a7..1ff64db 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -59,7 +59,7 @@
 per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
 
 # ResourcesManager
-per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = file:RESOURCES_OWNERS
 
 # VoiceInteraction
 per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
new file mode 100644
index 0000000..21c39a8
--- /dev/null
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -0,0 +1,2 @@
+rtmitchell@google.com
+toddke@google.com
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index c0736a6..d82cf19 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -167,6 +167,11 @@
     /** @hide */
     @NonNull
     @SystemApi
+    public static final ParcelUuid VOLUME_CONTROL =
+            ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
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/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..32b19a4 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;
@@ -31,7 +32,7 @@
 
 /**
  * Network definition that includes strong identity. Analogous to combining
- * {@link NetworkInfo} and an IMSI.
+ * {@link NetworkCapabilities} and an IMSI.
  *
  * @hide
  */
@@ -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
@@ -159,7 +160,7 @@
      */
     public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
             boolean defaultNetwork, @NetworkType int subType) {
-        final int type = state.networkInfo.getType();
+        final int legacyType = state.legacyNetworkType;
 
         String subscriberId = null;
         String networkId = null;
@@ -170,7 +171,7 @@
 
         subscriberId = state.subscriberId;
 
-        if (type == TYPE_WIFI) {
+        if (legacyType == TYPE_WIFI) {
             if (state.networkCapabilities.getSsid() != null) {
                 networkId = state.networkCapabilities.getSsid();
                 if (networkId == null) {
@@ -183,7 +184,7 @@
             }
         }
 
-        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+        return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
                 defaultNetwork);
     }
 
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/NetworkState.java b/core/java/android/net/NetworkState.java
index e1ef8b5..e466d2e 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -41,6 +42,7 @@
     public final Network network;
     public final String subscriberId;
     public final String networkId;
+    public final int legacyNetworkType;
 
     private NetworkState() {
         networkInfo = null;
@@ -49,17 +51,35 @@
         network = null;
         subscriberId = null;
         networkId = null;
+        legacyNetworkType = 0;
     }
 
+    public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties,
+            @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+            @Nullable String subscriberId, @Nullable String networkId) {
+        this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties,
+                networkCapabilities, network, subscriberId, networkId);
+    }
+
+    // Constructor that used internally in ConnectivityService mainline module.
     public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
             @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
             String subscriberId, String networkId) {
+        this(networkInfo.getType(), networkInfo, linkProperties,
+                networkCapabilities, network, subscriberId, networkId);
+    }
+
+    public NetworkState(int legacyNetworkType, @NonNull NetworkInfo networkInfo,
+            @NonNull LinkProperties linkProperties,
+            @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+            String subscriberId, String networkId) {
         this.networkInfo = networkInfo;
         this.linkProperties = linkProperties;
         this.networkCapabilities = networkCapabilities;
         this.network = network;
         this.subscriberId = subscriberId;
         this.networkId = networkId;
+        this.legacyNetworkType = legacyNetworkType;
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
@@ -80,6 +100,7 @@
         network = in.readParcelable(null);
         subscriberId = in.readString();
         networkId = in.readString();
+        legacyNetworkType = in.readInt();
     }
 
     @Override
@@ -95,6 +116,7 @@
         out.writeParcelable(network, flags);
         out.writeString(subscriberId);
         out.writeString(networkId);
+        out.writeInt(legacyNetworkType);
     }
 
     @UnsupportedAppUsage
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/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index c39b8c5..a79b197 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -130,6 +130,15 @@
     public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
     public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
+    public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
+            Tag.RESET_SINCE_ID_ROTATION;     // KM_BOOL | 1004
+    public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005;
+    public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006;
+    public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007;
+    public static final int KM_TAG_CERTIFICATE_NOT_BEFORE =
+            Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008;
+    public static final int KM_TAG_CERTIFICATE_NOT_AFTER =
+            Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009;
 
     // Algorithm values.
     public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
@@ -317,6 +326,10 @@
             ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
     public static final int KM_ERROR_DEVICE_LOCKED =
             ErrorCode.DEVICE_LOCKED; // -72;
+    public static final int KM_ERROR_MISSING_NOT_BEFORE =
+            ErrorCode.MISSING_NOT_BEFORE; // -80;
+    public static final int KM_ERROR_MISSING_NOT_AFTER =
+            ErrorCode.MISSING_NOT_AFTER; // -80;
     public static final int KM_ERROR_UNIMPLEMENTED =
             ErrorCode.UNIMPLEMENTED; // -100;
     public static final int KM_ERROR_VERSION_MISMATCH =
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/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
new file mode 100644
index 0000000..4520db3
--- /dev/null
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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.tracing;
+
+/**
+ * Binder interface for the TracingServiceProxy running in system_server.
+ *
+ * {@hide}
+ */
+interface ITracingServiceProxy
+{
+    /**
+     * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+     * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+     * there is no buffer available to dump.
+     */
+    oneway void notifyTraceSessionEnded(boolean sessionStolen);
+}
diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/core/java/android/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5ac95d4..c0d8187 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -94,7 +94,8 @@
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
                 Log.w(TAG,
-                        "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+                        "onRangingOpenedFailed - received unexpected SessionHandle: "
+                                + sessionHandle);
                 return;
             }
 
@@ -124,7 +125,7 @@
             @RangingChangeReason int reason, PersistableBundle params) {
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
-                Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+                Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
                         + sessionHandle);
                 return;
             }
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
index 928fcbdc..b23f5ad 100644
--- a/core/java/android/uwb/SessionHandle.java
+++ b/core/java/android/uwb/SessionHandle.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * @hide
  */
@@ -73,6 +75,11 @@
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hashCode(mId);
+    }
+
+    @Override
     public String toString() {
         return "SessionHandle [id=" + mId + "]";
     }
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/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/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS
new file mode 100644
index 0000000..5d3473c
--- /dev/null
+++ b/core/java/com/android/internal/os/CPU_OWNERS
@@ -0,0 +1,3 @@
+connoro@google.com
+dplotnikov@google.com
+rslawik@google.com  # Android Telemetry
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 1b07aa0..0aa54f5 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,5 +1,6 @@
 per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
 per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file *Cpu* = file:CPU_OWNERS
 
 # BatteryStats
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0381a75..a410180 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.
@@ -1103,4 +1096,11 @@
      * fully-feature Memory Tagging, rather than the static Tagged Pointers.
      */
     public static native boolean nativeSupportsTaggedPointers();
+
+    /**
+     * Returns the current native tagging level, as one of the
+     * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or
+     * we failed to determine the level.
+     */
+    public static native int nativeCurrentTaggingLevel();
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6335baa..7fde547 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -791,9 +791,19 @@
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
             if (Zygote.nativeSupportsMemoryTagging()) {
-                /* The system server is more privileged than regular app processes, so it has async
-                 * tag checks enabled on hardware that supports memory tagging. */
-                parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                /* The system server has ASYNC MTE by default, in order to allow
+                 * system services to specify their own MTE level later, as you
+                 * can't re-enable MTE once it's disabled. */
+                String mode = SystemProperties.get("arm64.memtag.process.system_server", "async");
+                if (mode.equals("async")) {
+                    parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                } else if (mode.equals("sync")) {
+                    parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC;
+                } else if (!mode.equals("off")) {
+                    /* When we have an invalid memory tag level, keep the current level. */
+                    parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel();
+                    Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\"");
+                }
             } else if (Zygote.nativeSupportsTaggedPointers()) {
                 /* Enable pointer tagging in the system server. Hardware support for this is present
                  * in all ARMv8 CPUs. */
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/jni/OWNERS b/core/jni/OWNERS
index 2279d57..35d1d7b 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -5,6 +5,9 @@
 # Connectivity
 per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
+
 # Display
 per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d7001d8..903ecae 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2526,6 +2526,36 @@
 #endif
 }
 
+static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) {
+#if defined(__aarch64__)
+  int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  if (level < 0) {
+    ALOGE("Failed to get memory tag level: %s", strerror(errno));
+    return 0;
+  } else if (!(level & PR_TAGGED_ADDR_ENABLE)) {
+    return 0;
+  }
+  // TBI is only possible on non-MTE hardware.
+  if (!mte_supported()) {
+    return MEMORY_TAG_LEVEL_TBI;
+  }
+
+  switch (level & PR_MTE_TCF_MASK) {
+    case PR_MTE_TCF_NONE:
+      return 0;
+    case PR_MTE_TCF_SYNC:
+      return MEMORY_TAG_LEVEL_SYNC;
+    case PR_MTE_TCF_ASYNC:
+      return MEMORY_TAG_LEVEL_ASYNC;
+    default:
+      ALOGE("Unknown memory tagging level: %i", level);
+      return 0;
+  }
+#else // defined(__aarch64__)
+  return 0;
+#endif // defined(__aarch64__)
+}
+
 static const JNINativeMethod gMethods[] = {
         {"nativeForkAndSpecialize",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
@@ -2565,6 +2595,8 @@
          (void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging},
         {"nativeSupportsTaggedPointers", "()Z",
          (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
+        {"nativeCurrentTaggingLevel", "()I",
+         (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel},
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
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/core/tests/coretests/src/com/android/internal/os/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS
index 4068e2b..3f8f9e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/os/OWNERS
@@ -1 +1,4 @@
 include /BATTERY_STATS_OWNERS
+
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a664ee3..5b32641 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">
@@ -459,6 +460,8 @@
         <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
         <!-- Permissions required for quick settings tile -->
         <permission name="android.permission.STATUS_BAR"/>
+        <!-- Permissions required to query Betterbug -->
+        <permission name="android.permission.QUERY_ALL_PACKAGES"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 372add9..d188b65 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -190,7 +190,7 @@
         keyDescriptor.blob = wrappedKey;
         keyDescriptor.domain = wrappedKeyDescriptor.domain;
 
-        return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+        return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor,
                 wrappingKeyDescriptor, maskingKey,
                 args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
     }
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/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 8475ad9..0f77749 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -164,6 +164,9 @@
 
         List<KeyParameter> parameters = new ArrayList<>();
         parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
                 KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
         ));
         parameters.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 32650ae..5619585 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -21,7 +21,6 @@
 import android.system.keystore2.Authorization;
 import android.system.keystore2.Domain;
 import android.system.keystore2.KeyDescriptor;
-import android.util.Log;
 
 import java.security.Key;
 
@@ -127,15 +126,6 @@
             return false;
         }
 
-        // If the key ids are equal and the class matches all the other fields cannot differ
-        // unless we have a bug.
-        if (!mAlgorithm.equals(other.mAlgorithm)
-                || !mAuthorizations.equals(other.mAuthorizations)
-                || !mDescriptor.equals(other.mDescriptor)) {
-            Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata"
-                    + "differs.");
-            return false;
-        }
         return true;
     }
 }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6a92980..70e30d2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -585,6 +585,30 @@
                     mSpec.getKeyValidityForConsumptionEnd()
             ));
         }
+        if (mSpec.getCertificateNotAfter() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER,
+                    mSpec.getCertificateNotAfter()
+            ));
+        }
+        if (mSpec.getCertificateNotBefore() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE,
+                    mSpec.getCertificateNotBefore()
+            ));
+        }
+        if (mSpec.getCertificateSerialNumber() != null) {
+            params.add(KeyStore2ParameterUtils.makeBignum(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL,
+                    mSpec.getCertificateSerialNumber()
+            ));
+        }
+        if (mSpec.getCertificateSubject() != null) {
+            params.add(KeyStore2ParameterUtils.makeBytes(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT,
+                    mSpec.getCertificateSubject().getEncoded()
+            ));
+        }
 
         if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
             params.add(KeyStore2ParameterUtils.makeInt(
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/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 8c8acc4..39607ae 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -866,7 +866,8 @@
         try {
             response = mKeyStore.getKeyEntry(wrappingkey);
         } catch (android.security.KeyStoreException e) {
-            throw new KeyStoreException("Failed to load wrapping key.", e);
+            throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
+                    + e.getErrorCode(), e);
         }
 
         KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 4c8ab8d..dcdd7de 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -28,6 +28,7 @@
 import android.security.keystore.UserAuthArgs;
 import android.system.keystore2.Authorization;
 
+import java.math.BigInteger;
 import java.security.ProviderException;
 import java.util.ArrayList;
 import java.util.Date;
@@ -154,6 +155,23 @@
     }
 
     /**
+     * This function constructs a {@link KeyParameter} expressing a Bignum.
+     * @param tag Must be KeyMint tag with the associated type BIGNUM.
+     * @param b A BitInteger to be stored in the new key parameter.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) {
+        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) {
+            throw new IllegalArgumentException("Not a bignum tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.value = KeyParameterValue.blob(b.toByteArray());
+        return p;
+    }
+
+    /**
      * This function constructs a {@link KeyParameter} expressing date.
      * @param tag Must be KeyMint tag with the associated type DATE.
      * @param date A date
@@ -167,10 +185,6 @@
         KeyParameter p = new KeyParameter();
         p.tag = tag;
         p.value = KeyParameterValue.dateTime(date.getTime());
-        if (p.value.getDateTime() < 0) {
-            throw new IllegalArgumentException("Date tag value out of range: "
-                    + p.value.getDateTime());
-        }
         return p;
     }
     /**
diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
new file mode 100644
index 0000000..67f407f
--- /dev/null
+++ b/libs/tracingproxy/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+    name: "libtracingproxy",
+
+    aidl: {
+        export_aidl_headers: true,
+        include_dirs: [
+            "frameworks/base/core/java",
+        ],
+    },
+
+    srcs: [
+        ":ITracingServiceProxy.aidl",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
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..d04a5be 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1368,7 +1368,7 @@
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
         try {
             return mService.getDefaultNetworkCapabilitiesForUser(
-                    userId, mContext.getOpPackageName());
+                    userId, mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1450,7 +1450,8 @@
     @Nullable
     public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
-            return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
+            return mService.getNetworkCapabilities(
+                    network, mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2142,7 +2143,7 @@
      */
     // TODO: Remove method and replace with direct call once R code is pushed to AOSP
     private @Nullable String getAttributionTag() {
-        return null;
+        return mContext.getAttributionTag();
     }
 
     /**
@@ -3231,32 +3232,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
@@ -3761,7 +3736,8 @@
                 Binder binder = new Binder();
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName);
+                            need, messenger, binder, callingPackageName,
+                            getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
                             need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
@@ -4206,7 +4182,8 @@
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingListenForNetwork(
-                    request.networkCapabilities, operation, mContext.getOpPackageName());
+                    request.networkCapabilities, operation, mContext.getOpPackageName(),
+                    getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4871,9 +4848,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..f909d13 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;
@@ -65,7 +66,7 @@
     Network getNetworkForType(int networkType);
     Network[] getAllNetworks();
     NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
-            int userId, String callingPackageName);
+            int userId, String callingPackageName, String callingAttributionTag);
 
     boolean isNetworkSupported(int networkType);
 
@@ -74,7 +75,8 @@
     LinkProperties getLinkPropertiesForType(int networkType);
     LinkProperties getLinkProperties(in Network network);
 
-    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
+    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName,
+            String callingAttributionTag);
 
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     NetworkState[] getAllNetworkState();
@@ -156,9 +158,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);
 
@@ -178,10 +177,12 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName);
+            in Messenger messenger, in IBinder binder, String callingPackageName,
+            String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation, String callingPackageName);
+            in PendingIntent operation, String callingPackageName,
+            String callingAttributionTag);
 
     void releaseNetworkRequest(in NetworkRequest networkRequest);
 
@@ -243,4 +244,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..6540397 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -16,6 +16,22 @@
 
 package android.net;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+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_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -30,6 +46,8 @@
 import android.text.TextUtils;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -156,8 +174,30 @@
      * needed in terms of {@link NetworkCapabilities} features
      */
     public static class Builder {
+        /**
+         * Capabilities that are currently compatible with VCN networks.
+         */
+        private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList(
+                NET_CAPABILITY_CAPTIVE_PORTAL,
+                NET_CAPABILITY_DUN,
+                NET_CAPABILITY_FOREGROUND,
+                NET_CAPABILITY_INTERNET,
+                NET_CAPABILITY_NOT_CONGESTED,
+                NET_CAPABILITY_NOT_METERED,
+                NET_CAPABILITY_NOT_RESTRICTED,
+                NET_CAPABILITY_NOT_ROAMING,
+                NET_CAPABILITY_NOT_SUSPENDED,
+                NET_CAPABILITY_NOT_VPN,
+                NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+                NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+                NET_CAPABILITY_TRUSTED,
+                NET_CAPABILITY_VALIDATED);
+
         private final NetworkCapabilities mNetworkCapabilities;
 
+        // A boolean that represents the user modified NOT_VCN_MANAGED capability.
+        private boolean mModifiedNotVcnManaged = false;
+
         /**
          * Default constructor for Builder.
          */
@@ -179,6 +219,7 @@
             // maybeMarkCapabilitiesRestricted() doesn't add back.
             final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
             nc.maybeMarkCapabilitiesRestricted();
+            deduceNotVcnManagedCapability(nc);
             return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
                     ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
         }
@@ -195,6 +236,9 @@
          */
         public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -206,6 +250,9 @@
          */
         public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.removeCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -263,6 +310,9 @@
         @NonNull
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
+            // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
+            // should not be add back later.
+            mModifiedNotVcnManaged = true;
             return this;
         }
 
@@ -382,6 +432,25 @@
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
         }
+
+        /**
+         * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities
+         * and user intention, which includes:
+         *   1. For the requests that don't have anything besides
+         *      {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to
+         *      allow the callers automatically utilize VCN networks if available.
+         *   2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED,
+         *      do not alter them to allow user fire request that suits their need.
+         *
+         * @hide
+         */
+        private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
+            if (mModifiedNotVcnManaged) return;
+            for (final int cap : nc.getCapabilities()) {
+                if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
+            }
+            nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+        }
     }
 
     // implement the Parcelable interface
@@ -435,25 +504,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 +618,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/Android.bp b/services/core/Android.bp
index 4bebe39..ba7f080 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -95,9 +95,9 @@
     libs: [
         "services.net",
         "android.hardware.light-V2.0-java",
-        "android.hardware.power-java",
+        "android.hardware.power-V1-java",
         "android.hardware.power-V1.0-java",
-        "android.hardware.vibrator-java",
+        "android.hardware.vibrator-V1-java",
         "android.net.ipsec.ike.stubs.module_lib",
         "app-compat-annotations",
         "framework-tethering.stubs.module_lib",
@@ -120,7 +120,7 @@
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
         "android.hardware.health-V2.1-java",
-        "android.hardware.light-java",
+        "android.hardware.light-V1-java",
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.0-java",
@@ -128,11 +128,11 @@
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
-        "android.hardware.rebootescrow-java",
+        "android.hardware.rebootescrow-V1-java",
         "android.hardware.soundtrigger-V2.3-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
-        "dnsresolver_aidl_interface-java",
+        "dnsresolver_aidl_interface-V7-java",
         "icu4j_calendar_astronomer",
         "netd-client",
         "overlayable_policy_aidl-java",
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..2f88351 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -45,6 +45,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -120,6 +121,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 +224,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 +283,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 +745,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 +768,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 +800,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 +816,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 +996,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 +1037,13 @@
 
         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(),
+                null /* attributionTag */);
+        mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
+        mDefaultNetworkRequests.add(mDefaultRequest);
+        mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
 
         mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1053,6 +1069,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,11 +1235,20 @@
 
         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) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
         netCap.setSingleUid(uid);
         return netCap;
@@ -1231,6 +1258,7 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
         if (transportType > -1) {
             netCap.addTransportType(transportType);
@@ -1284,7 +1312,7 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    null, networkRequest, new Binder()));
+                    null, networkRequest, new Binder(), null /* attributionTag */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
                     /* callOnUnavailable */ false);
@@ -1366,7 +1394,7 @@
     }
 
     private NetworkState getUnfilteredActiveNetworkState(int uid) {
-        NetworkAgentInfo nai = getFallbackNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
 
         final Network[] networks = getVpnUnderlyingNetworks(uid);
         if (networks != null) {
@@ -1499,7 +1527,7 @@
             }
         }
 
-        NetworkAgentInfo nai = getFallbackNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
         if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
                 ignoreBlocked)) {
             return null;
@@ -1619,7 +1647,7 @@
 
     @Override
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
-                int userId, String callingPackageName) {
+                int userId, String callingPackageName, @Nullable String callingAttributionTag) {
         // The basic principle is: if an app's traffic could possibly go over a
         // network, without the app doing anything multinetwork-specific,
         // (hence, by "default"), then include that network's capabilities in
@@ -1638,25 +1666,34 @@
 
         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,
+                                callingAttributionTag));
+            }
         }
 
         // 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(
-                                    nc, mDeps.getCallingUid(), callingPackageName));
+                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    callingAttributionTag));
                 }
             }
         }
@@ -1674,9 +1711,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
      */
@@ -1733,12 +1768,13 @@
     }
 
     @Override
-    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName,
+            @Nullable String callingAttributionTag) {
         mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
-                mDeps.getCallingUid(), callingPackageName);
+                mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
     @VisibleForTesting
@@ -1757,11 +1793,12 @@
         return newNc;
     }
 
-    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
+            @Nullable String callingAttributionTag) {
         final long token = Binder.clearCallingIdentity();
         try {
             return mLocationPermissionChecker.checkLocationPermission(
-                    callerPkgName, null /* featureId */, callerUid, null /* message */);
+                    callerPkgName, callingAttributionTag, callerUid, null /* message */);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1770,7 +1807,8 @@
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
+            @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
@@ -1779,7 +1817,8 @@
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
         if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
-            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            hasLocationPermission =
+                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
             newNc = new NetworkCapabilities(nc, hasLocationPermission);
         } else {
             newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
@@ -1796,7 +1835,8 @@
         }
         if (hasLocationPermission == null) {
             // Location permission not checked yet, check now for masking owner UID.
-            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            hasLocationPermission =
+                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
         }
         // Reset owner uid if the app has no location permission.
         if (!hasLocationPermission) {
@@ -1854,7 +1894,8 @@
         final ArrayList<NetworkState> result = new ArrayList<>();
         for (Network network : getAllNetworks()) {
             final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-            if (nai != null) {
+            // TODO: Consider include SUSPENDED networks.
+            if (nai != null && nai.networkInfo.isConnected()) {
                 // TODO (b/73321673) : NetworkState contains a copy of the
                 // NetworkCapabilities, which may contain UIDs of apps to which the
                 // network applies. Should the UIDs be cleared so as not to leak or
@@ -2007,7 +2048,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 +2066,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 +2160,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 +2623,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 +2744,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 +3011,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 +3020,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 +3341,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 +3375,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 +3395,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 +3440,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 +3476,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 +3499,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 +3511,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 +3598,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 +3646,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 +3739,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 +3783,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 +3838,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 +4299,7 @@
 
     @Override
     public NetworkRequest getDefaultRequest() {
-        return mFallbackRequest.mRequests.get(0);
+        return mDefaultRequest.mRequests.get(0);
     }
 
     private class InternalHandler extends Handler {
@@ -4506,7 +4545,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 +4564,7 @@
             Network network, int uid, boolean hasConnectivity) {
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getFallbackNetwork();
+            nai = getDefaultNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4891,7 +4930,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 +4982,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 +4998,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 +5457,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 +5482,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 +5530,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() {
@@ -5536,8 +5553,23 @@
         final int mPid;
         final int mUid;
         final Messenger messenger;
+        @Nullable
+        final String mCallingAttributionTag;
 
-        NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
+        /**
+         * 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,
+                @Nullable String callingAttributionTag) {
             mRequests = initializeRequests(r);
             ensureAllNetworkRequestsHaveType(mRequests);
             mPendingIntent = pi;
@@ -5546,9 +5578,11 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallingAttributionTag = callingAttributionTag;
         }
 
-        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
+        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder,
+                @Nullable String callingAttributionTag) {
             super();
             messenger = m;
             mRequests = initializeRequests(r);
@@ -5558,6 +5592,7 @@
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallingAttributionTag = callingAttributionTag;
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -5567,7 +5602,14 @@
         }
 
         NetworkRequestInfo(NetworkRequest r) {
-            this(r, null);
+            this(r, null /* pi */, null /* callingAttributionTag */);
+        }
+
+        // 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() {
@@ -5595,7 +5637,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);
         }
     }
@@ -5751,7 +5795,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
-        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
@@ -5840,7 +5885,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
-        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
         if (DBG) log("pendingRequest for " + nri);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
                 nri));
@@ -5884,7 +5930,8 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5904,7 +5951,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5913,7 +5961,8 @@
 
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation, @NonNull String callingPackageName) {
+            PendingIntent operation, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
@@ -5927,7 +5976,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
         if (VDBG) log("pendingListenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5951,15 +6001,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 +6014,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 +6033,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 +6083,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 +6119,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 +6166,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 +6236,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 +6312,7 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        if (isFallbackNetwork(networkAgent)) {
+        if (isDefaultNetwork(networkAgent)) {
             updateTcpBufferSizes(newLp.getTcpBufferSizes());
         }
 
@@ -6246,7 +6324,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 +6658,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 +7031,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 +7094,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.
@@ -7113,7 +7192,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName()));
+                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
                 // For this notification, arg1 contains the blocked status.
@@ -7132,7 +7212,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
+                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nri.mCallingAttributionTag));
                 break;
             }
             case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -7168,8 +7249,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 +7267,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 +7305,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 +7329,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 +7352,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 +7516,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 +7527,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 +7591,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 +7677,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 +7706,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 +7719,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 +7727,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 +7759,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 +7821,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 +7832,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 +7951,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 +8013,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 +8111,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 +8160,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 +8499,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 +9150,7 @@
             }
         }
     }
+
     /**
      * Registers {@link QosSocketFilter} with {@link IQosCallback}.
      *
@@ -9050,4 +9200,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/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index c56cef2..a83c981 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -16,12 +16,6 @@
 
 package com.android.server;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -33,10 +27,15 @@
 import android.os.SystemProperties;
 import android.util.Slog;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
 /**
  * A service designed to load and periodically save &quot;randomness&quot;
- * for the Linux kernel RNG and to mix in data from Hardware RNG (if present)
- * into the Linux RNG.
+ * for the Linux kernel RNG.
  *
  * <p>When a Linux system starts up, the entropy pool associated with
  * {@code /dev/random} may be in a fairly predictable state.  Applications which
@@ -45,15 +44,8 @@
  * this effect, it's helpful to carry the entropy pool information across
  * shutdowns and startups.
  *
- * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW
- * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever
- * EntropyMixer periodically runs to save a block of output from Linux RNG on
- * disk. This mixing is done in a way that does not increase the Linux RNG's
- * entropy estimate is not increased. This is to avoid having to trust/verify
- * the quality and authenticity of the &quot;randomness&quot; of the HW RNG.
- *
  * <p>This class was modeled after the script in the
- * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">
+ * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
  * random(4) manual page</a>.
  */
 public class EntropyMixer extends Binder {
@@ -64,7 +56,6 @@
     private static final long START_NANOTIME = System.nanoTime();
 
     private final String randomDevice;
-    private final String hwRandomDevice;
     private final String entropyFile;
 
     /**
@@ -80,7 +71,6 @@
                 Slog.e(TAG, "Will not process invalid message");
                 return;
             }
-            addHwRandomEntropy();
             writeEntropy();
             scheduleEntropyWriter();
         }
@@ -94,25 +84,21 @@
     };
 
     public EntropyMixer(Context context) {
-        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random");
+        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
     }
 
     /** Test only interface, not for public use */
     public EntropyMixer(
             Context context,
             String entropyFile,
-            String randomDevice,
-            String hwRandomDevice) {
+            String randomDevice) {
         if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
-        if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); }
         if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
 
         this.randomDevice = randomDevice;
-        this.hwRandomDevice = hwRandomDevice;
         this.entropyFile = entropyFile;
         loadInitialEntropy();
         addDeviceSpecificEntropy();
-        addHwRandomEntropy();
         writeEntropy();
         scheduleEntropyWriter();
         IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -192,23 +178,6 @@
         }
     }
 
-    /**
-     * Mixes in the output from HW RNG (if present) into the Linux RNG.
-     */
-    private void addHwRandomEntropy() {
-        if (!new File(hwRandomDevice).exists()) {
-            // HW RNG not present/exposed -- ignore
-            return;
-        }
-
-        try {
-            RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
-            Slog.i(TAG, "Added HW RNG output to entropy pool");
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
-        }
-    }
-
     private static String getSystemDir() {
         File dataDir = Environment.getDataDirectory();
         File systemDir = new File(dataDir, "system");
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/tools/hiddenapi/Android.bp b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
similarity index 67%
rename from tools/hiddenapi/Android.bp
rename to services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index e0eb06cb..4f75675 100644
--- a/tools/hiddenapi/Android.bp
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.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.
@@ -14,17 +14,12 @@
  * limitations under the License.
  */
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
+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/tools/hiddenapi/Android.bp b/services/core/java/com/android/server/apphibernation/UserLevelState.java
similarity index 66%
copy from tools/hiddenapi/Android.bp
copy to services/core/java/com/android/server/apphibernation/UserLevelState.java
index e0eb06cb..c66dad8 100644
--- a/tools/hiddenapi/Android.bp
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.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.
@@ -14,17 +14,12 @@
  * limitations under the License.
  */
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
+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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 5d1c4e6..16d4f94 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -555,7 +555,7 @@
     private void bootCompleted() {
         // on boot, if device is interactive, set HDMI CEC state as powered on as well
         if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
-            onWakeUp();
+            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
         }
     }
 
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..b5c0f28 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,
@@ -1960,14 +1962,13 @@
             if (state.network != null) {
                 mNetIdToSubId.put(state.network.netId, parseSubId(state));
             }
-            if (state.networkInfo != null && state.networkInfo.isConnected()) {
-                // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
-                // in the object created here is never used and its value doesn't matter, so use
-                // NETWORK_TYPE_UNKNOWN.
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
-                identified.put(state, ident);
-            }
+
+            // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+            // in the object created here is never used and its value doesn't matter, so use
+            // NETWORK_TYPE_UNKNOWN.
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                    true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+            identified.put(state, ident);
         }
 
         final ArraySet<String> newMeteredIfaces = new ArraySet<>();
@@ -2042,8 +2043,7 @@
         // One final pass to catch any metered ifaces that don't have explicitly
         // defined policies; typically Wi-Fi networks.
         for (NetworkState state : states) {
-            if (state.networkInfo != null && state.networkInfo.isConnected()
-                    && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+            if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
                 matchingIfaces.clear();
                 collectIfaces(matchingIfaces, state);
                 for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
@@ -3488,13 +3488,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 +3520,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 +4797,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 +4932,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 +5402,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 +5421,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/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0ab35a9..9706bce 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
-import android.net.NetworkInfo;
 import android.net.NetworkStack;
 import android.net.NetworkState;
 import android.net.NetworkStats;
@@ -1264,7 +1263,7 @@
 
     /**
      * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
-     * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
+     * NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
      * they are combined under a single {@link NetworkIdentitySet}.
      */
     @GuardedBy("mStatsLock")
@@ -1294,84 +1293,82 @@
         final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
         final ArraySet<String> mobileIfaces = new ArraySet<>();
         for (NetworkState state : states) {
-            if (state.networkInfo.isConnected()) {
-                final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
-                final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
-                final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
-                        : getSubTypeForState(state);
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        isDefault, subType);
+            final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType);
+            final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+            final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
+                    : getSubTypeForState(state);
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                    isDefault, subType);
 
-                // Traffic occurring on the base interface is always counted for
-                // both total usage and UID details.
-                final String baseIface = state.linkProperties.getInterfaceName();
-                if (baseIface != null) {
-                    findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
-                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+            // Traffic occurring on the base interface is always counted for
+            // both total usage and UID details.
+            final String baseIface = state.linkProperties.getInterfaceName();
+            if (baseIface != null) {
+                findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+                findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
 
-                    // Build a separate virtual interface for VT (Video Telephony) data usage.
-                    // Only do this when IMS is not metered, but VT is metered.
-                    // If IMS is metered, then the IMS network usage has already included VT usage.
-                    // VT is considered always metered in framework's layer. If VT is not metered
-                    // per carrier's policy, modem will report 0 usage for VT calls.
-                    if (state.networkCapabilities.hasCapability(
-                            NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+                // Build a separate virtual interface for VT (Video Telephony) data usage.
+                // Only do this when IMS is not metered, but VT is metered.
+                // If IMS is metered, then the IMS network usage has already included VT usage.
+                // VT is considered always metered in framework's layer. If VT is not metered
+                // per carrier's policy, modem will report 0 usage for VT calls.
+                if (state.networkCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
 
-                        // Copy the identify from IMS one but mark it as metered.
-                        NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
-                                ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
-                                ident.getRoaming(), true /* metered */,
-                                true /* onDefaultNetwork */);
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
-                    }
-
-                    if (isMobile) {
-                        mobileIfaces.add(baseIface);
-                    }
+                    // Copy the identify from IMS one but mark it as metered.
+                    NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
+                            ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
+                            ident.getRoaming(), true /* metered */,
+                            true /* onDefaultNetwork */);
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
                 }
 
-                // Traffic occurring on stacked interfaces is usually clatd.
-                //
-                // UID stats are always counted on the stacked interface and never on the base
-                // interface, because the packets on the base interface do not actually match
-                // application sockets (they're not IPv4) and thus the app uid is not known.
-                // For receive this is obvious: packets must be translated from IPv6 to IPv4
-                // before the application socket can be found.
-                // For transmit: either they go through the clat daemon which by virtue of going
-                // through userspace strips the original socket association during the IPv4 to
-                // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
-                // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
-                // which don't trigger again post ebpf translation.
-                // (as such stats accounted to the clat uid are ignored)
-                //
-                // Interface stats are more complicated.
-                //
-                // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
-                // *all* statistics are collected by iptables on the stacked v4-* interface.
-                //
-                // Additionally for ingress all packets bound for the clat IPv6 address are dropped
-                // in ip6tables raw prerouting and thus even non-offloaded packets are only
-                // accounted for on the stacked interface.
-                //
-                // For egress, packets subject to eBPF offload never appear on the base interface
-                // and only appear on the stacked interface. Thus to ensure packets increment
-                // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
-                // (or non eBPF offloaded) TX they would appear on both, however egress interface
-                // accounting is explicitly bypassed for traffic from the clat uid.
-                //
-                final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
-                for (LinkProperties stackedLink : stackedLinks) {
-                    final String stackedIface = stackedLink.getInterfaceName();
-                    if (stackedIface != null) {
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
-                        if (isMobile) {
-                            mobileIfaces.add(stackedIface);
-                        }
+                if (isMobile) {
+                    mobileIfaces.add(baseIface);
+                }
+            }
 
-                        mStatsFactory.noteStackedIface(stackedIface, baseIface);
+            // Traffic occurring on stacked interfaces is usually clatd.
+            //
+            // UID stats are always counted on the stacked interface and never on the base
+            // interface, because the packets on the base interface do not actually match
+            // application sockets (they're not IPv4) and thus the app uid is not known.
+            // For receive this is obvious: packets must be translated from IPv6 to IPv4
+            // before the application socket can be found.
+            // For transmit: either they go through the clat daemon which by virtue of going
+            // through userspace strips the original socket association during the IPv4 to
+            // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+            // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+            // which don't trigger again post ebpf translation.
+            // (as such stats accounted to the clat uid are ignored)
+            //
+            // Interface stats are more complicated.
+            //
+            // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+            // *all* statistics are collected by iptables on the stacked v4-* interface.
+            //
+            // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+            // in ip6tables raw prerouting and thus even non-offloaded packets are only
+            // accounted for on the stacked interface.
+            //
+            // For egress, packets subject to eBPF offload never appear on the base interface
+            // and only appear on the stacked interface. Thus to ensure packets increment
+            // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+            // (or non eBPF offloaded) TX they would appear on both, however egress interface
+            // accounting is explicitly bypassed for traffic from the clat uid.
+            //
+            final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+            for (LinkProperties stackedLink : stackedLinks) {
+                final String stackedIface = stackedLink.getInterfaceName();
+                if (stackedIface != null) {
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+                    if (isMobile) {
+                        mobileIfaces.add(stackedIface);
                     }
+
+                    mStatsFactory.noteStackedIface(stackedIface, baseIface);
                 }
             }
         }
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/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
new file mode 100644
index 0000000..8f22748
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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.tracing;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+import android.tracing.ITracingServiceProxy;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+/**
+ * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
+ * system tracing app Traceur.
+ *
+ * @hide
+ */
+public class TracingServiceProxy extends SystemService {
+    private static final String TAG = "TracingServiceProxy";
+
+    public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
+
+    private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
+    private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+
+    // Keep this in sync with the definitions in TraceService
+    private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
+            "com.android.traceur.NOTIFY_SESSION_STOPPED";
+    private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
+            "com.android.traceur.NOTIFY_SESSION_STOLEN";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+
+    private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
+        /**
+          * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+          * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+          * there is no buffer available to dump.
+          */
+        @Override
+        public void notifyTraceSessionEnded(boolean sessionStolen) {
+            notifyTraceur(sessionStolen);
+        }
+    };
+
+    public TracingServiceProxy(Context context) {
+        super(context);
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+    }
+
+    private void notifyTraceur(boolean sessionStolen) {
+        final Intent intent = new Intent();
+
+        try {
+            // Validate that Traceur is a system app.
+            PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME,
+                    PackageManager.MATCH_SYSTEM_ONLY);
+
+            intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
+            if (sessionStolen) {
+                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
+            } else {
+                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
+            }
+
+            try {
+                mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM);
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Failed to notifyTraceSessionEnded", e);
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Failed to locate Traceur", e);
+        }
+    }
+}
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..3726407 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vcn;
 
+import static com.android.server.VcnManagementService.VDBG;
 
 import android.annotation.NonNull;
 import android.net.NetworkCapabilities;
@@ -27,9 +28,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 +73,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 +165,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 +199,7 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
-        if (!mIsRunning) {
+        if (!isActive()) {
             return;
         }
 
@@ -137,9 +210,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);
         }
@@ -147,7 +226,7 @@
 
     private void handleConfigUpdated(@NonNull VcnConfig config) {
         // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
-        Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
+        Slog.v(getLogTag(), "Config updated: config = " + config.hashCode());
 
         mConfig = config;
 
@@ -161,23 +240,41 @@
             gatewayConnection.teardownAsynchronously();
         }
 
-        mIsRunning = false;
+        mIsActive.set(false);
+    }
+
+    private void handleEnterSafemode() {
+        handleTeardown();
+
+        mVcnSafemodeCallback.onEnteredSafemode();
     }
 
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
         if (score > getNetworkScore()) {
-            Slog.v(getLogTag(),
-                    "Request already satisfied by higher-scoring (" + score + ") network from "
-                            + "provider " + providerId + ": " + request);
+            if (VDBG) {
+                Slog.v(
+                        getLogTag(),
+                        "Request already satisfied by higher-scoring ("
+                                + score
+                                + ") network from "
+                                + "provider "
+                                + providerId
+                                + ": "
+                                + request);
+            }
             return;
         }
 
         // If preexisting VcnGatewayConnection(s) satisfy request, return
         for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
             if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
-                Slog.v(getLogTag(),
-                        "Request already satisfied by existing VcnGatewayConnection: " + request);
+                if (VDBG) {
+                    Slog.v(
+                            getLogTag(),
+                            "Request already satisfied by existing VcnGatewayConnection: "
+                                    + request);
+                }
                 return;
             }
         }
@@ -192,13 +289,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();
@@ -210,15 +321,47 @@
     }
 
     private String getLogTag() {
-        return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
+        return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
     }
 
     /** 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..2503e81 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,30 @@
 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.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -113,6 +125,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;
 
@@ -283,9 +298,9 @@
     private static final int EVENT_SETUP_COMPLETED = 6;
 
     private static class EventSetupCompletedInfo implements EventInfo {
-        @NonNull public final ChildSessionConfiguration childSessionConfig;
+        @NonNull public final VcnChildSessionConfiguration childSessionConfig;
 
-        EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) {
+        EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) {
             this.childSessionConfig = Objects.requireNonNull(childSessionConfig);
         }
 
@@ -359,6 +374,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 +404,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;
@@ -444,7 +472,7 @@
      * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
      * states, @Nullable otherwise.
      */
-    private ChildSessionConfiguration mChildConfig;
+    private VcnChildSessionConfiguration mChildConfig;
 
     /**
      * The active network agent.
@@ -457,28 +485,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 +576,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) {
@@ -599,7 +660,7 @@
                 new EventTransformCreatedInfo(direction, transform));
     }
 
-    private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
+    private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) {
         sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
     }
 
@@ -668,7 +729,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 +983,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 +1005,108 @@
         }
     }
 
-    private abstract class ConnectedStateBase extends ActiveBaseState {}
+    private abstract class ConnectedStateBase extends ActiveBaseState {
+        protected void updateNetworkAgent(
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull NetworkAgent agent,
+                @NonNull VcnChildSessionConfiguration 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 VcnChildSessionConfiguration 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 VcnChildSessionConfiguration childConfig) {
+            setupInterface(token, tunnelIface, childConfig, null);
+        }
+
+        protected void setupInterface(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull VcnChildSessionConfiguration childConfig,
+                @Nullable VcnChildSessionConfiguration 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 +1116,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 VcnChildSessionConfiguration childConfig) {
+            setupInterface(token, tunnelIface, childConfig);
+
+            if (mNetworkAgent == null) {
+                mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+            } else {
+                updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+            }
+        }
     }
 
     /**
@@ -961,12 +1208,70 @@
      */
     class RetryTimeoutState extends ActiveBaseState {
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() throws Exception {
+            // Reset upon entry to ConnectedState
+            mFailedAttempts++;
+
+            if (mUnderlying == null) {
+                Slog.wtf(TAG, "Underlying network was null in retry state");
+                transitionTo(mDisconnectedState);
+            } else {
+                sendMessageDelayed(
+                        EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs());
+            }
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED:
+                    final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+                    mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+                    // If new underlying is null, all networks were lost; go back to disconnected.
+                    if (mUnderlying == null) {
+                        removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+                        transitionTo(mDisconnectedState);
+                        return;
+                    } else if (oldUnderlying != null
+                            && mUnderlying.network.equals(oldUnderlying.network)) {
+                        // If the network has not changed, do nothing.
+                        return;
+                    }
+
+                    // Fallthrough
+                case EVENT_RETRY_TIMEOUT_EXPIRED:
+                    removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+                    transitionTo(mConnectingState);
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
+
+        private long getNextRetryIntervalsMs() {
+            final int retryDelayIndex = mFailedAttempts - 1;
+            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+
+            // Repeatedly use last item in retry timeout list.
+            if (retryDelayIndex >= retryIntervalsMs.length) {
+                return retryIntervalsMs[retryIntervalsMs.length - 1];
+            }
+
+            return retryIntervalsMs[retryDelayIndex];
+        }
     }
 
     @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,13 +1283,59 @@
             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();
     }
 
     private static LinkProperties buildConnectedLinkProperties(
             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
             @NonNull IpSecTunnelInterface tunnelIface,
-            @NonNull ChildSessionConfiguration childConfig) {
+            @NonNull VcnChildSessionConfiguration childConfig) {
         final LinkProperties lp = new LinkProperties();
 
         lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1035,17 +1386,25 @@
         }
     }
 
-    private class ChildSessionCallbackImpl implements ChildSessionCallback {
+    /** Implementation of ChildSessionCallback, exposed for testing. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public class VcnChildSessionCallback implements ChildSessionCallback {
         private final int mToken;
 
-        ChildSessionCallbackImpl(int token) {
+        VcnChildSessionCallback(int token) {
             mToken = token;
         }
 
+        /** Internal proxy method for injecting of mocked ChildSessionConfiguration */
+        @VisibleForTesting(visibility = Visibility.PRIVATE)
+        void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
+            Slog.v(TAG, "ChildOpened for token " + mToken);
+            childOpened(mToken, childConfig);
+        }
+
         @Override
         public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
-            Slog.v(TAG, "ChildOpened for token " + mToken);
-            childOpened(mToken, childConfig);
+            onOpened(new VcnChildSessionConfiguration(childConfig));
         }
 
         @Override
@@ -1128,7 +1487,7 @@
                 buildIkeParams(),
                 buildChildParams(),
                 new IkeSessionCallbackImpl(token),
-                new ChildSessionCallbackImpl(token));
+                new VcnChildSessionCallback(token));
     }
 
     /** External dependencies used by VcnGatewayConnection, for injection in tests */
@@ -1138,10 +1497,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. */
@@ -1160,6 +1524,35 @@
         }
     }
 
+    /**
+     * Proxy implementation of Child Session Configuration, used for testing.
+     *
+     * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for
+     * testing purposes. This is the unfortunate result of mockito-inline (for mocking final
+     * classes) not working properly with system services & associated classes.
+     *
+     * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual
+     * ChildSessionConfiguration.
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class VcnChildSessionConfiguration {
+        private final ChildSessionConfiguration mChildConfig;
+
+        public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) {
+            mChildConfig = childConfig;
+        }
+
+        /** Retrieves the addresses to be used inside the tunnel. */
+        public List<LinkAddress> getInternalAddresses() {
+            return mChildConfig.getInternalAddresses();
+        }
+
+        /** Retrieves the DNS servers to be used inside the tunnel. */
+        public List<InetAddress> getInternalDnsServers() {
+            return mChildConfig.getInternalDnsServers();
+        }
+    }
+
     /** Proxy implementation of IKE session, used for testing. */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class VcnIkeSession {
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index b9babae..bfeec01 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vcn;
 
+import static com.android.server.VcnManagementService.VDBG;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.NetworkProvider;
@@ -25,6 +27,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 +57,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 +72,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);
     }
 
@@ -74,11 +85,16 @@
 
     @Override
     public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
-        Slog.v(
-                TAG,
-                String.format(
-                        "Network requested: Request = %s, score = %d, providerId = %d",
-                        request, score, providerId));
+        if (VDBG) {
+            Slog.v(
+                    TAG,
+                    "Network requested: Request = "
+                            + request
+                            + ", score = "
+                            + score
+                            + ", providerId = "
+                            + providerId);
+        }
 
         final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
 
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/Android.bp b/services/core/jni/Android.bp
index e0db93a..87c766d 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -147,11 +147,11 @@
         "android.hardware.light@2.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-cpp",
+        "android.hardware.power-V1-cpp",
         "android.hardware.power.stats@1.0",
         "android.hardware.thermal@1.0",
         "android.hardware.tv.input@1.0",
-        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator-V1-cpp",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
@@ -160,7 +160,7 @@
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
         "android.frameworks.stats@1.0",
-        "android.system.suspend.control-cpp",
+        "android.system.suspend.control-V1-cpp",
         "android.system.suspend.control.internal-cpp",
         "android.system.suspend@1.0",
         "service.incremental",
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/incremental/Android.bp b/services/incremental/Android.bp
index e978ed4..7534c7c 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -51,9 +51,9 @@
     static_libs: [
         "libbase",
         "libext2_uuid",
-        "libdataloader_aidl-unstable-cpp",
-        "libincremental_aidl-unstable-cpp",
-        "libincremental_manager_aidl-unstable-cpp",
+        "libdataloader_aidl-cpp",
+        "libincremental_aidl-cpp",
+        "libincremental_manager_aidl-cpp",
         "libprotobuf-cpp-lite",
         "service.incremental.proto",
         "libutils",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 203de9d..62c3f43 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;
@@ -174,6 +175,7 @@
 import com.android.server.testharness.TestHarnessModeService;
 import com.android.server.textclassifier.TextClassificationManagerService;
 import com.android.server.textservices.TextServicesManagerService;
+import com.android.server.tracing.TracingServiceProxy;
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
@@ -1072,6 +1074,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);
@@ -2201,6 +2208,11 @@
         mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
         t.traceEnd();
 
+        // Perfetto TracingServiceProxy
+        t.traceBegin("startTracingServiceProxy");
+        mSystemServiceManager.startService(TracingServiceProxy.class);
+        t.traceEnd();
+
         // It is now time to start up the app processes...
 
         t.traceBegin("MakeVibratorServiceReady");
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 979f4e1..0aec701 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -82,7 +82,7 @@
         "libui",
         "libunwindstack",
         "libutils",
-        "netd_aidl_interface-cpp",
+        "netd_aidl_interface-V5-cpp",
     ],
 
     dxflags: ["--multi-dex"],
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 50e7a03..58d6dae 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -34,7 +34,7 @@
         assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
 
         // The constructor has the side effect of writing to file
-        new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null");
+        new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
 
         assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
     }
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..3ebe4ef 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -19,11 +19,13 @@
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -112,8 +114,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkState;
@@ -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));
         }
     }
 
@@ -1986,13 +1985,6 @@
         return users;
     }
 
-    private NetworkInfo buildNetworkInfo() {
-        final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_LTE, null, null);
-        ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-        return ni;
-    }
-
     private LinkProperties buildLinkProperties(String iface) {
         final LinkProperties lp = new LinkProperties();
         lp.setInterfaceName(iface);
@@ -2046,13 +2038,12 @@
     }
 
     private static NetworkState buildWifi() {
-        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addTransportType(TRANSPORT_WIFI);
         networkCapabilities.setSSID(TEST_SSID);
-        return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
+        return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null, TEST_SSID);
     }
 
     private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
@@ -2073,7 +2064,7 @@
         when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
                 .thenReturn(mCarrierConfig);
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
-                new NetworkState(buildNetworkInfo(),
+                new NetworkState(TYPE_MOBILE,
                         buildLinkProperties(TEST_IFACE),
                         buildNetworkCapabilities(TEST_SUB_ID, roaming),
                         new Network(TEST_NET_ID), TEST_IMSI, null)
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 4439f99..4a1a6ad 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -57,6 +57,6 @@
         "libui",
         "libunwindstack",
         "libutils",
-        "netd_aidl_interface-cpp",
+        "netd_aidl_interface-V5-cpp",
     ],
 }
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/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index 0059ad6..ae7d209 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -402,29 +402,27 @@
          * @see #getThresholds() for more details on signal strength thresholds
          */
         public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
-            Objects.requireNonNull(thresholds, "thresholds must not be null");
-            if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
-                    || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
-                throw new IllegalArgumentException(
-                        "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
-                                + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
-            }
-            mThresholds = thresholds.clone();
-            Arrays.sort(mThresholds);
-            return this;
+            return setThresholds(thresholds, false /*isSystem*/);
         }
 
         /**
-         * Set the signal strength thresholds for the corresponding signal measurement type without
-         * the length limitation.
+         * Set the signal strength thresholds for the corresponding signal measurement type.
          *
          * @param thresholds array of integer as the signal threshold values
+         * @param isSystem true is the caller is system which does not have restrictions on
+         *        the length of thresholds array.
          * @return the builder to facilitate the chaining
          *
          * @hide
          */
-        public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+        public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
             Objects.requireNonNull(thresholds, "thresholds must not be null");
+            if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                    || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+                throw new IllegalArgumentException(
+                        "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                                + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+            }
             mThresholds = thresholds.clone();
             Arrays.sort(mThresholds);
             return this;
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/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ApnThrottleStatus.java
index 51461d1..eec1408 100644
--- a/telephony/java/android/telephony/data/ApnThrottleStatus.java
+++ b/telephony/java/android/telephony/data/ApnThrottleStatus.java
@@ -261,6 +261,10 @@
         private long mThrottleExpiryTimeMillis;
         private @RetryType int mRetryType;
         private @ThrottleType int mThrottleType;
+
+        /**
+         * @hide
+         */
         public static final long NO_THROTTLE_EXPIRY_TIME =
                 DataCallResponse.RETRY_DURATION_UNDEFINED;
 
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/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS
new file mode 100644
index 0000000..d67285ed
--- /dev/null
+++ b/tests/ApkVerityTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+victorhsieh@google.com
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/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index dc9e587..e1da3d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -84,6 +85,7 @@
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
             case TRANSPORT_ETHERNET:
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index c2fddf3..fcfb4aa 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -345,15 +345,17 @@
     @Test
     public void testRequestType() throws Exception {
         final String testPkgName = "MyPackage";
+        final String testAttributionTag = "MyTag";
         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
         when(mCtx.getOpPackageName()).thenReturn(testPkgName);
+        when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
         final NetworkRequest request = makeRequest(1);
         final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
 
         manager.requestNetwork(request, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
                 eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
@@ -361,19 +363,19 @@
         verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
                 anyInt(), any(), any());
         verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
-                eq(testPkgName));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.registerDefaultNetworkCallback(callback);
         verify(mService).requestNetwork(eq(null),
                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.requestBackgroundNetwork(request, null, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
                 eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
 
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 91fcbc0..1f8f6f3 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -35,7 +35,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 import kotlin.test.assertFalse
@@ -60,16 +59,13 @@
         subscriberId: String? = null,
         ssid: String? = null
     ): NetworkState {
-        val info = mock(NetworkInfo::class.java)
-        doReturn(type).`when`(info).type
-        doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
         val lp = LinkProperties()
         val caps = NetworkCapabilities().apply {
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
             setSSID(ssid)
         }
-        return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+        return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId, ssid)
     }
 
     private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b0cc7f1..edc2c63 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
@@ -2590,6 +2707,10 @@
 
         NetworkCapabilities filter = new NetworkCapabilities();
         filter.addCapability(capability);
+        // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+        // NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
+        // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
+        filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
         handlerThread.start();
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
@@ -3841,6 +3962,7 @@
         handlerThread.start();
         NetworkCapabilities filter = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addCapability(NET_CAPABILITY_INTERNET);
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                 mServiceContext, "testFactory", filter);
@@ -3888,8 +4010,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 +5417,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 +5479,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});
@@ -5743,6 +5866,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkDownstreamBandwidthKbps(10);
         final NetworkCapabilities wifiNc = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
@@ -5751,6 +5875,7 @@
                 .addCapability(NET_CAPABILITY_NOT_ROAMING)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkUpstreamBandwidthKbps(20);
         mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
         mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
@@ -5849,7 +5974,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 +6168,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
@@ -6247,7 +6372,7 @@
 
     private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
         final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
-                userId, "com.android.calling.package");
+                userId, "com.android.calling.package", "com.test");
         final String defaultCapsString = Arrays.toString(defaultCaps);
         assertEquals(defaultCapsString, defaultCaps.length, networks.length);
         final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
@@ -6291,7 +6416,7 @@
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mCellNetworkAgent.connect(true);
 
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6306,7 +6431,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 +6442,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 +6473,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 +6484,7 @@
         assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
 
         // Use both again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6374,7 +6499,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 +6510,7 @@
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6520,9 +6645,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 +6669,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 +6726,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 +6736,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 +6838,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 +6846,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is now using WiFi
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6734,7 +6854,7 @@
         assertFalse(mCm.isActiveNetworkMetered());
 
         // VPN is using Cell | WiFi.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6742,7 +6862,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is using WiFi | Cell.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6750,7 +6870,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 +6897,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 +6905,7 @@
 
 
         // VPN explicitly declares WiFi as its underlying network.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6809,6 +6929,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build();
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+        mockUidNetworkingBlocked();
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
@@ -6891,6 +7012,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 +7282,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 +7313,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 +7381,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 +7437,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
@@ -7382,19 +7521,13 @@
             mWiFiNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
             callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
-            // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
-            //  it.
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-                reset(mMockNetd);
-            }
+            verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
             mCellNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
             callbackWithoutCap.assertNoCallback();
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkClearDefault();
-            }
+            verify(mMockNetd).networkClearDefault();
 
             mCm.unregisterNetworkCallback(callbackWithCap);
             mCm.unregisterNetworkCallback(callbackWithoutCap);
@@ -8245,7 +8378,8 @@
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
         if (op != null) {
-            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
+                    eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
         }
 
@@ -8258,7 +8392,7 @@
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName()).getOwnerUid();
+                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
     }
 
     private void verifyWifiInfoCopyNetCapsForCallerPermission(
@@ -8268,7 +8402,7 @@
         final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName());
+                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
         verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
     }
 
@@ -8355,13 +8489,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 +8545,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 +8555,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 +8566,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 +8750,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 +8758,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/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index dde78aa..214c82d 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,8 +80,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
@@ -1456,8 +1454,6 @@
     }
 
     private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) {
-        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1465,7 +1461,7 @@
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         capabilities.setSSID(TEST_SSID);
-        return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
+        return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
     }
 
     private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1473,17 +1469,14 @@
     }
 
     private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
-        final NetworkInfo info = new NetworkInfo(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
-        info.setRoaming(isRoaming);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
+        return new NetworkState(
+                TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
     }
 
     private NetworkStats buildEmptyStats() {
@@ -1491,11 +1484,9 @@
     }
 
     private static NetworkState buildVpnState() {
-        final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TUN_IFACE);
-        return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
+        return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
     }
 
     private long getElapsedRealtime() {
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..278d93a
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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 android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+
+/** 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 testChildOpenedRegistersNetwork() throws Exception {
+        final VcnChildSessionConfiguration mMockChildSessionConfig =
+                mock(VcnChildSessionConfiguration.class);
+        doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
+                .when(mMockChildSessionConfig)
+                .getInternalAddresses();
+        doReturn(Collections.singletonList(TEST_DNS_ADDR))
+                .when(mMockChildSessionConfig)
+                .getInternalDnsServers();
+
+        getChildSessionCallback().onOpened(mMockChildSessionConfig);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+        final ArgumentCaptor<LinkProperties> lpCaptor =
+                ArgumentCaptor.forClass(LinkProperties.class);
+        final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+                ArgumentCaptor.forClass(NetworkCapabilities.class);
+        verify(mConnMgr)
+                .registerNetworkAgent(
+                        any(),
+                        any(),
+                        lpCaptor.capture(),
+                        ncCaptor.capture(),
+                        anyInt(),
+                        any(),
+                        anyInt());
+        verify(mIpSecSvc)
+                .addAddressToTunnelInterface(
+                        eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+        final LinkProperties lp = lpCaptor.getValue();
+        assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses());
+        assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers());
+
+        final NetworkCapabilities nc = ncCaptor.getValue();
+        assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        for (int cap : mConfig.getAllExposedCapabilities()) {
+            assertTrue(nc.hasCapability(cap));
+        }
+    }
+
+    @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();
+    }
+}
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/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
new file mode 100644
index 0000000..3f2b47c
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+
+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.RetryTimeoutState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testNewNetworkTriggerRetry() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testSameNetworkDoesNotTriggerRetry() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testNullNetworkTriggersDisconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testTimeoutElapsingTriggersRetry() throws Exception {
+        mTestLooper.moveTimeForward(mConfig.getRetryIntervalsMs()[0]);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+}
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..d449eab 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,8 +27,13 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
+import android.net.IpSecConfig;
 import android.net.IpSecManager;
+import android.net.IpSecTransform;
 import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -40,15 +45,29 @@
 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 com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
 
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
 
+import java.net.InetAddress;
+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 InetAddress TEST_DNS_ADDR =
+            InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+    protected static final LinkAddress TEST_INTERNAL_ADDR =
+            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+
+    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,15 +82,21 @@
                     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;
 
     @NonNull protected final IpSecService mIpSecSvc;
+    @NonNull protected final ConnectivityManager mConnMgr;
 
     protected VcnIkeSession mMockIkeSession;
     protected VcnGatewayConnection mGatewayConnection;
@@ -82,19 +107,24 @@
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
         mVcnContext = mock(VcnContext.class);
         mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+        mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
         mDeps = mock(VcnGatewayConnection.Dependencies.class);
         mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
 
         mIpSecSvc = mock(IpSecService.class);
         setupIpSecManager(mContext, mIpSecSvc);
 
+        mConnMgr = mock(ConnectivityManager.class);
+        VcnTestUtils.setupSystemService(
+                mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
         doReturn(mContext).when(mVcnContext).getContext();
         doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
         doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
 
         doReturn(mUnderlyingNetworkTracker)
                 .when(mDeps)
-                .newUnderlyingNetworkTracker(any(), any(), any(), any());
+                .newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
     }
 
     @Before
@@ -109,7 +139,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() {
@@ -119,10 +160,10 @@
         return captor.getValue();
     }
 
-    protected ChildSessionCallback getChildSessionCallback() {
+    protected VcnChildSessionCallback getChildSessionCallback() {
         ArgumentCaptor<ChildSessionCallback> captor =
                 ArgumentCaptor.forClass(ChildSessionCallback.class);
         verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
-        return captor.getValue();
+        return (VcnChildSessionCallback) captor.getValue();
     }
 }
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();
+    }
+}
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
deleted file mode 100755
index 28ff606..0000000
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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.
-"""Generate API lists for non-SDK API enforcement."""
-import argparse
-from collections import defaultdict, namedtuple
-import functools
-import os
-import re
-import sys
-
-# Names of flags recognized by the `hiddenapi` tool.
-FLAG_SDK = 'sdk'
-FLAG_UNSUPPORTED = 'unsupported'
-FLAG_BLOCKED = 'blocked'
-FLAG_MAX_TARGET_O = 'max-target-o'
-FLAG_MAX_TARGET_P = 'max-target-p'
-FLAG_MAX_TARGET_Q = 'max-target-q'
-FLAG_MAX_TARGET_R = 'max-target-r'
-FLAG_CORE_PLATFORM_API = 'core-platform-api'
-FLAG_PUBLIC_API = 'public-api'
-FLAG_SYSTEM_API = 'system-api'
-FLAG_TEST_API = 'test-api'
-
-# List of all known flags.
-FLAGS_API_LIST = [
-    FLAG_SDK,
-    FLAG_UNSUPPORTED,
-    FLAG_BLOCKED,
-    FLAG_MAX_TARGET_O,
-    FLAG_MAX_TARGET_P,
-    FLAG_MAX_TARGET_Q,
-    FLAG_MAX_TARGET_R,
-]
-ALL_FLAGS = FLAGS_API_LIST + [
-    FLAG_CORE_PLATFORM_API,
-    FLAG_PUBLIC_API,
-    FLAG_SYSTEM_API,
-    FLAG_TEST_API,
-]
-
-FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
-ALL_FLAGS_SET = set(ALL_FLAGS)
-
-# Option specified after one of FLAGS_API_LIST to indicate that
-# only known and otherwise unassigned entries should be assign the
-# given flag.
-# For example, the max-target-P list is checked in as it was in P,
-# but signatures have changes since then. The flag instructs this
-# script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
-
-# Option specified after one of FLAGS_API_LIST to express that all
-# apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
-
-# Option specified after one of FLAGS_API_LIST to indicate an extra
-# tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
-
-# Regex patterns of fields/methods used in serialization. These are
-# considered public API despite being hidden.
-SERIALIZATION_PATTERNS = [
-    r'readObject\(Ljava/io/ObjectInputStream;\)V',
-    r'readObjectNoData\(\)V',
-    r'readResolve\(\)Ljava/lang/Object;',
-    r'serialVersionUID:J',
-    r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
-    r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
-    r'writeReplace\(\)Ljava/lang/Object;',
-]
-
-# Single regex used to match serialization API. It combines all the
-# SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
-
-# Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
-IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
-
-
-class StoreOrderedOptions(argparse.Action):
-    """An argparse action that stores a number of option arguments in the order that
-    they were specified.
-    """
-    def __call__(self, parser, args, values, option_string = None):
-        items = getattr(args, self.dest, None)
-        if items is None:
-            items = []
-        items.append([option_string.lstrip('-'), values])
-        setattr(args, self.dest, items)
-
-def get_args():
-    """Parses command line arguments.
-
-    Returns:
-        Namespace: dictionary of parsed arguments
-    """
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--output', required=True)
-    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
-        help='CSV files to be merged into output')
-
-    for flag in ALL_FLAGS:
-        parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
-            action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
-    parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
-        'entries should be assign the given flag. Must follow a list of entries and applies '
-        'to the preceding such list.')
-    parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that the previous list of entries '
-        'is a list of packages. All members in those packages will be given the flag. '
-        'Must follow a list of entries and applies to the preceding such list.')
-    parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
-        action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
-        'Must follow a list of entries and applies to the preceding such list.')
-
-    return parser.parse_args()
-
-
-def read_lines(filename):
-    """Reads entire file and return it as a list of lines.
-
-    Lines which begin with a hash are ignored.
-
-    Args:
-        filename (string): Path to the file to read from.
-
-    Returns:
-        Lines of the file as a list of string.
-    """
-    with open(filename, 'r') as f:
-        lines = f.readlines();
-    lines = filter(lambda line: not line.startswith('#'), lines)
-    lines = map(lambda line: line.strip(), lines)
-    return set(lines)
-
-
-def write_lines(filename, lines):
-    """Writes list of lines into a file, overwriting the file if it exists.
-
-    Args:
-        filename (string): Path to the file to be writting into.
-        lines (list): List of strings to write into the file.
-    """
-    lines = map(lambda line: line + '\n', lines)
-    with open(filename, 'w') as f:
-        f.writelines(lines)
-
-
-def extract_package(signature):
-    """Extracts the package from a signature.
-
-    Args:
-        signature (string): JNI signature of a method or field.
-
-    Returns:
-        The package name of the class containing the field/method.
-    """
-    full_class_name = signature.split(";->")[0]
-    # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
-    if (full_class_name[0] != "L"):
-        raise ValueError("Expected to start with 'L': %s" % full_class_name)
-    full_class_name = full_class_name[1:]
-    # If full_class_name doesn't contain '/', then package_name will be ''.
-    package_name = full_class_name.rpartition("/")[0]
-    return package_name.replace('/', '.')
-
-
-class FlagsDict:
-    def __init__(self):
-        self._dict_keyset = set()
-        self._dict = defaultdict(set)
-
-    def _check_entries_set(self, keys_subset, source):
-        assert isinstance(keys_subset, set)
-        assert keys_subset.issubset(self._dict_keyset), (
-            "Error: {} specifies signatures not present in code:\n"
-            "{}"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "".join(map(lambda x: "  " + str(x) + "\n", keys_subset - self._dict_keyset)))
-
-    def _check_flags_set(self, flags_subset, source):
-        assert isinstance(flags_subset, set)
-        assert flags_subset.issubset(ALL_FLAGS_SET), (
-            "Error processing: {}\n"
-            "The following flags were not recognized: \n"
-            "{}\n"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "\n".join(flags_subset - ALL_FLAGS_SET))
-
-    def filter_apis(self, filter_fn):
-        """Returns APIs which match a given predicate.
-
-        This is a helper function which allows to filter on both signatures (keys) and
-        flags (values). The built-in filter() invokes the lambda only with dict's keys.
-
-        Args:
-            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
-
-        Returns:
-            A set of APIs which match the predicate.
-        """
-        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
-
-    def get_valid_subset_of_unassigned_apis(self, api_subset):
-        """Sanitizes a key set input to only include keys which exist in the dictionary
-        and have not been assigned any API list flags.
-
-        Args:
-            entries_subset (set/list): Key set to be sanitized.
-
-        Returns:
-            Sanitized key set.
-        """
-        assert isinstance(api_subset, set)
-        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
-    def generate_csv(self):
-        """Constructs CSV entries from a dictionary.
-
-        Old versions of flags are used to generate the file.
-
-        Returns:
-            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
-        """
-        lines = []
-        for api in self._dict:
-          flags = sorted(self._dict[api])
-          lines.append(",".join([api] + flags))
-        return sorted(lines)
-
-    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
-        """Parses CSV entries and merges them into a given dictionary.
-
-        The expected CSV format is:
-            <api signature>,<flag1>,<flag2>,...,<flagN>
-
-        Args:
-            csv_lines (list of strings): Lines read from a CSV file.
-            source (string): Origin of `csv_lines`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed flags are invalid.
-        """
-        # Split CSV lines into arrays of values.
-        csv_values = [ line.split(',') for line in csv_lines ]
-
-        # Update the full set of API signatures.
-        self._dict_keyset.update([ csv[0] for csv in csv_values ])
-
-        # Check that all flags are known.
-        csv_flags = set()
-        for csv in csv_values:
-          csv_flags.update(csv[1:])
-        self._check_flags_set(csv_flags, source)
-
-        # Iterate over all CSV lines, find entry in dict and append flags to it.
-        for csv in csv_values:
-            flags = csv[1:]
-            if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
-                flags.append(FLAG_SDK)
-            self._dict[csv[0]].update(flags)
-
-    def assign_flag(self, flag, apis, source="<unknown>", tag = None):
-        """Assigns a flag to given subset of entries.
-
-        Args:
-            flag (string): One of ALL_FLAGS.
-            apis (set): Subset of APIs to receive the flag.
-            source (string): Origin of `entries_subset`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed API signatures of flags are invalid.
-        """
-        # Check that all APIs exist in the dict.
-        self._check_entries_set(apis, source)
-
-        # Check that the flag is known.
-        self._check_flags_set(set([ flag ]), source)
-
-        # Iterate over the API subset, find each entry in dict and assign the flag to it.
-        for api in apis:
-            self._dict[api].add(flag)
-            if tag:
-                self._dict[api].add(tag)
-
-
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
-
-def parse_ordered_flags(ordered_flags):
-    r = []
-    currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
-    for flag_value in ordered_flags:
-        flag, value = flag_value[0], flag_value[1]
-        if flag in ALL_FLAGS_SET:
-            if currentflag:
-                r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
-                ignore_conflicts, packages, tag = False, False, None
-            currentflag = flag
-            file = value
-        else:
-            if currentflag is None:
-                raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
-                    flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
-            if flag == FLAG_IGNORE_CONFLICTS:
-                ignore_conflicts = True
-            elif flag == FLAG_PACKAGES:
-                packages = True
-            elif flag == FLAG_TAG:
-                tag = value[0]
-
-
-    if currentflag:
-        r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
-    return r
-
-
-def main(argv):
-    # Parse arguments.
-    args = vars(get_args())
-    flagfiles = parse_ordered_flags(args['ordered_flags'])
-
-    # Initialize API->flags dictionary.
-    flags = FlagsDict()
-
-    # Merge input CSV files into the dictionary.
-    # Do this first because CSV files produced by parsing API stubs will
-    # contain the full set of APIs. Subsequent additions from text files
-    # will be able to detect invalid entries, and/or filter all as-yet
-    # unassigned entries.
-    for filename in args["csv"]:
-        flags.parse_and_merge_csv(read_lines(filename), filename)
-
-    # Combine inputs which do not require any particular order.
-    # (1) Assign serialization API to SDK.
-    flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
-
-    # (2) Merge text files with a known flag into the dictionary.
-    for info in flagfiles:
-        if (not info.ignore_conflicts) and (not info.packages):
-            flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
-
-    # Merge text files where conflicts should be ignored.
-    # This will only assign the given flag if:
-    # (a) the entry exists, and
-    # (b) it has not been assigned any other flag.
-    # Because of (b), this must run after all strict assignments have been performed.
-    for info in flagfiles:
-        if info.ignore_conflicts:
-            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
-            flags.assign_flag(info.flag, valid_entries, filename, info.tag)
-
-    # All members in the specified packages will be assigned the appropriate flag.
-    for info in flagfiles:
-        if info.packages:
-            packages_needing_list = set(read_lines(info.file))
-            should_add_signature_to_list = lambda sig,lists: extract_package(
-                sig) in packages_needing_list and not lists
-            valid_entries = flags.filter_apis(should_add_signature_to_list)
-            flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
-
-    # Mark all remaining entries as blocked.
-    flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
-    # Write output.
-    write_lines(args["output"], flags.generate_csv())
-
-if __name__ == "__main__":
-    main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
deleted file mode 100755
index 82d117f..0000000
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# 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.
-"""Unit tests for Hidden API list generation."""
-import unittest
-from generate_hiddenapi_lists import *
-
-class TestHiddenapiListGeneration(unittest.TestCase):
-
-    def test_filter_apis(self):
-        # Initialize flags so that A and B are put on the whitelist and
-        # C, D, E are left unassigned. Try filtering for the unassigned ones.
-        flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
-                        'C', 'D', 'E'])
-        filter_set = flags.filter_apis(lambda api, flags: not flags)
-        self.assertTrue(isinstance(filter_set, set))
-        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
-
-    def test_get_valid_subset_of_unassigned_keys(self):
-        # Create flags where only A is unassigned.
-        flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
-        flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
-
-        # Check three things:
-        # (1) B is selected as valid unassigned
-        # (2) A is not selected because it is assigned 'whitelist'
-        # (3) D is not selected because it is not a valid key
-        self.assertEqual(
-            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
-
-    def test_parse_and_merge_csv(self):
-        flags = FlagsDict()
-
-        # Test empty CSV entry.
-        self.assertEqual(flags.generate_csv(), [])
-
-        # Test new additions.
-        flags.parse_and_merge_csv([
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-        self.assertEqual(flags.generate_csv(), [
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-
-        # Test unknown flag.
-        with self.assertRaises(AssertionError):
-            flags.parse_and_merge_csv([ 'Z,foo' ])
-
-    def test_assign_flag(self):
-        flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
-
-        # Test new additions.
-        flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ])
-
-        # Test invalid API signature.
-        with self.assertRaises(AssertionError):
-            flags.assign_flag(FLAG_SDK, set([ 'C' ]))
-
-        # Test invalid flag.
-        with self.assertRaises(AssertionError):
-            flags.assign_flag('foo', set([ 'A' ]))
-
-    def test_extract_package(self):
-        signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
-        expected_package = 'com.foo.bar'
-        self.assertEqual(extract_package(signature), expected_package)
-
-        signature = 'Lcom/foo1/bar/MyClass;->method2()V'
-        expected_package = 'com.foo1.bar'
-        self.assertEqual(extract_package(signature), expected_package)
-
-        signature = 'Lcom/foo_bar/baz/MyClass;->method3()V'
-        expected_package = 'com.foo_bar.baz'
-        self.assertEqual(extract_package(signature), expected_package)
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
deleted file mode 100755
index 6a5b0e1..0000000
--- a/tools/hiddenapi/merge_csv.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# 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.
-"""
-Merge multiple CSV files, possibly with different columns.
-"""
-
-import argparse
-import csv
-import io
-
-from zipfile import ZipFile
-
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
-                                          'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
-                         default='-', type=argparse.FileType('w'))
-args_parser.add_argument('files', nargs=argparse.REMAINDER)
-args = args_parser.parse_args()
-
-
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|')
-
-
-if args.zip_input and len(args.files) > 0:
-    raise ValueError('Expecting either a single ZIP with CSV files'
-                     ' or a list of CSV files as input; not both.')
-
-csv_readers = []
-if len(args.files) > 0:
-    for file in args.files:
-        csv_readers.append(dict_reader(open(file, 'r')))
-elif args.zip_input:
-    with ZipFile(args.zip_input) as zip:
-        for entry in zip.namelist():
-            if entry.endswith('.uau'):
-                csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
-
-headers = set()
-if args.header:
-    fieldnames = args.header.split(',')
-else:
-    # Build union of all columns from source files:
-    for reader in csv_readers:
-        headers = headers.union(reader.fieldnames)
-    fieldnames = sorted(headers)
-
-# Concatenate all files to output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
-                        dialect='unix', fieldnames=fieldnames)
-writer.writeheader()
-for reader in csv_readers:
-    for row in reader:
-        writer.writerow(row)