Merge "Update mapping for the rat type in data stall proto"
diff --git a/apex/Android.bp b/apex/Android.bp
index c3b0014..362cf95 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -26,6 +26,19 @@
     "--hide Typo " +
     "--hide UnavailableSymbol "
 
+// TODO: remove this server classes are cleaned up.
+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," +
+    "\\) "
+
 stubs_defaults {
     name: "framework-module-stubs-defaults-publicapi",
     args: mainline_stubs_args,
@@ -34,36 +47,23 @@
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-systemapi",
-    args: mainline_stubs_args +
-    " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
-    "process=android.annotation.SystemApi.Process.ALL\\) ",
+    args: mainline_stubs_args + priv_apps,
     installable: false,
 }
 
+// The defaults for module_libs comes in two parts - defaults for API checks
+// and defaults for stub generation. This is because we want the API txt
+// files to *only* include the module_libs_api, but the stubs to include
+// module_libs_api as well as priv_apps.
+
 stubs_defaults {
-    name: "framework-module-stubs-defaults-module_apps_api",
-    args: mainline_stubs_args +
-    " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
-    "process=android.annotation.SystemApi.Process.ALL\\) " +
-    " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.MODULE_APPS," +
-    "process=android.annotation.SystemApi.Process.ALL\\) ",
+    name: "framework-module-api-defaults-module_libs_api",
+    args: mainline_stubs_args + module_libs,
     installable: false,
 }
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-module_libs_api",
-    args: mainline_stubs_args +
-    " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
-    "process=android.annotation.SystemApi.Process.ALL\\) " +
-    " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.MODULE_APPS," +
-    "process=android.annotation.SystemApi.Process.ALL\\) " +
-    " --show-annotation android.annotation.SystemApi\\(" +
-    "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
-    "process=android.annotation.SystemApi.Process.ALL\\) ",
+    args: mainline_stubs_args + module_libs + priv_apps,
     installable: false,
 }
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
index 4307e67..d802177 100644
--- a/api/module-app-current.txt
+++ b/api/module-app-current.txt
@@ -1,9 +1 @@
 // Signature format: 2.0
-package android.app {
-
-  public final class NotificationChannel implements android.os.Parcelable {
-    method public void setBlockableSystem(boolean);
-  }
-
-}
-
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index eeef074..278a186 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -6,7 +6,7 @@
     method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
     method public int describeContents();
     method @NonNull public java.util.List<java.lang.String> getDebugInfo();
-    method public int getPhoneId();
+    method public int getSlotIndex();
     method @Nullable public android.os.TimestampedValue<java.lang.Long> getUtcTime();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.timedetector.PhoneTimeSuggestion> CREATOR;
@@ -34,8 +34,8 @@
     method public int describeContents();
     method @NonNull public java.util.List<java.lang.String> getDebugInfo();
     method public int getMatchType();
-    method public int getPhoneId();
     method public int getQuality();
+    method public int getSlotIndex();
     method @Nullable public String getZoneId();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.timezonedetector.PhoneTimeZoneSuggestion> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index b3dea03..ef3ce2a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -553,6 +553,7 @@
     method public int getUserLockedFields();
     method public boolean isDeleted();
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
+    method public void setBlockableSystem(boolean);
     method public org.json.JSONObject toJson() throws org.json.JSONException;
     method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
   }
@@ -4601,6 +4602,12 @@
     field public final android.net.WifiKey wifiKey;
   }
 
+  public class NetworkPolicyManager {
+    method public void setSubscriptionOverride(int, int, int, long, @NonNull String);
+    field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2
+    field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1
+  }
+
   public class NetworkProvider {
     ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
@@ -6514,13 +6521,13 @@
 
   public class UpdateEngine {
     ctor public UpdateEngine();
-    method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
+    method @NonNull @WorkerThread public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
     method public void applyPayload(String, long, long, String[]);
     method public void applyPayload(@NonNull android.content.res.AssetFileDescriptor, @NonNull String[]);
     method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
     method public boolean bind(android.os.UpdateEngineCallback);
     method public void cancel();
-    method public int cleanupAppliedPayload();
+    method @WorkerThread public int cleanupAppliedPayload();
     method public void resetStatus();
     method public void resume();
     method public void suspend();
@@ -6529,8 +6536,8 @@
   }
 
   public static final class UpdateEngine.AllocateSpaceResult {
-    method public int errorCode();
-    method public long freeSpaceRequired();
+    method public int getErrorCode();
+    method public long getFreeSpaceRequired();
   }
 
   public static final class UpdateEngine.ErrorCodeConstants {
@@ -9612,6 +9619,7 @@
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 19d158d..28a21f7 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -18,7 +18,6 @@
 
 import android.compat.Compatibility;
 import android.os.Process;
-import android.util.StatsLog;
 
 import com.android.internal.compat.ChangeReporter;
 
@@ -46,20 +45,20 @@
         mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
         Arrays.sort(mDisabledChanges);
         mChangeReporter = new ChangeReporter(
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS);
+                ChangeReporter.SOURCE_APP_PROCESS);
     }
 
     protected void reportChange(long changeId) {
-        reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+        reportChange(changeId, ChangeReporter.STATE_LOGGED);
     }
 
     protected boolean isChangeEnabled(long changeId) {
         if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) {
             // Not present in the disabled array
-            reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
+            reportChange(changeId, ChangeReporter.STATE_ENABLED);
             return true;
         }
-        reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
+        reportChange(changeId, ChangeReporter.STATE_DISABLED);
         return false;
     }
 
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 8ad2b36..b08f035 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,8 +15,6 @@
  */
 package android.app;
 
-import static android.annotation.SystemApi.Client.MODULE_APPS;
-
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -325,7 +323,7 @@
      * @param blockableSystem if {@code true}, allows users to block notifications on this channel.
      * @hide
      */
-    @SystemApi(client = MODULE_APPS)
+    @SystemApi
     @TestApi
     public void setBlockableSystem(boolean blockableSystem) {
         mBlockableSystem = blockableSystem;
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 50de7385..da51ce2 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -29,10 +29,18 @@
 import java.util.Objects;
 
 /**
- * A time signal from a manual (user provided) source. The value consists of the number of
- * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
- * clock when that number was established. The elapsed realtime clock is considered accurate but
- * volatile, so time signals must not be persisted across device resets.
+ * A time signal from a manual (user provided) source.
+ *
+ * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds
+ * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the
+ * elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was entered. This information exists only to aid in
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection
+ * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
  *
  * @hide
  */
@@ -49,10 +57,8 @@
                 }
             };
 
-    @NonNull
-    private final TimestampedValue<Long> mUtcTime;
-    @Nullable
-    private ArrayList<String> mDebugInfo;
+    @NonNull private final TimestampedValue<Long> mUtcTime;
+    @Nullable private ArrayList<String> mDebugInfo;
 
     public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
         mUtcTime = Objects.requireNonNull(utcTime);
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index 17e9c5a..89fd6f31 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -29,10 +29,18 @@
 import java.util.Objects;
 
 /**
- * A time signal from a network time source like NTP. The value consists of the number of
- * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
- * clock when that number was established. The elapsed realtime clock is considered accurate but
- * volatile, so time signals must not be persisted across device resets.
+ * A time signal from a network time source like NTP.
+ *
+ * <p>{@code utcTime} contains the suggested time. The {@code utcTime.value} is the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the
+ * value of the elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was determined. This information exists only to aid
+ * in debugging and therefore is used by {@link #toString()}, but it is not for use in detection
+ * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
  *
  * @hide
  */
@@ -49,10 +57,8 @@
                 }
             };
 
-    @NonNull
-    private final TimestampedValue<Long> mUtcTime;
-    @Nullable
-    private ArrayList<String> mDebugInfo;
+    @NonNull private final TimestampedValue<Long> mUtcTime;
+    @Nullable private ArrayList<String> mDebugInfo;
 
     public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
         mUtcTime = Objects.requireNonNull(utcTime);
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index bd649f8..16288e8 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -32,12 +32,22 @@
  * A time suggestion from an identified telephony source. e.g. from NITZ information from a specific
  * radio.
  *
- * <p>The time value can be {@code null} to indicate that the telephony source has entered an
- * "un-opinionated" state and any previous suggestions from the source are being withdrawn. When not
- * {@code null}, the value consists of the number of milliseconds elapsed since 1/1/1970 00:00:00
- * UTC and the time according to the elapsed realtime clock when that number was established. The
- * elapsed realtime clock is considered accurate but volatile, so time suggestions must not be
- * persisted across device resets.
+ * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify
+ * suggestions from the same source when there are several in use.
+ *
+ * <p>{@code utcTime}. When not {@code null}, the {@code utcTime.value} is the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the
+ * value of the elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets. {@code utcTime} can be {@code null} to
+ * indicate that the telephony source has entered an "un-opinionated" state and any previous
+ * suggestion from the source is being withdrawn.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists, e.g. what triggered it to be made and what heuristic was used
+ * to determine the time or its absence. This information exists only to aid in debugging and
+ * therefore is used by {@link #toString()}, but it is not for use in detection logic and is not
+ * considered in {@link #hashCode()} or {@link #equals(Object)}.
  *
  * @hide
  */
@@ -57,19 +67,19 @@
                 }
             };
 
-    private final int mPhoneId;
+    private final int mSlotIndex;
     @Nullable private final TimestampedValue<Long> mUtcTime;
     @Nullable private ArrayList<String> mDebugInfo;
 
     private PhoneTimeSuggestion(Builder builder) {
-        mPhoneId = builder.mPhoneId;
+        mSlotIndex = builder.mSlotIndex;
         mUtcTime = builder.mUtcTime;
         mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
     }
 
     private static PhoneTimeSuggestion createFromParcel(Parcel in) {
-        int phoneId = in.readInt();
-        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(phoneId)
+        int slotIndex = in.readInt();
+        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(slotIndex)
                 .setUtcTime(in.readParcelable(null /* classLoader */))
                 .build();
         @SuppressWarnings("unchecked")
@@ -87,22 +97,24 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mPhoneId);
+        dest.writeInt(mSlotIndex);
         dest.writeParcelable(mUtcTime, 0);
         dest.writeList(mDebugInfo);
     }
 
     /**
-     * Returns an identifier for the source of this suggestion. When a device has several "phones",
-     * i.e. sim slots or equivalent, it is used to identify which one.
+     * Returns an identifier for the source of this suggestion.
+     *
+     * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}.
      */
-    public int getPhoneId() {
-        return mPhoneId;
+    public int getSlotIndex() {
+        return mSlotIndex;
     }
 
     /**
-     * Returns the suggestion. {@code null} means that the caller is no longer sure what time it
-     * is.
+     * Returns the suggested time or {@code null} if there isn't one.
+     *
+     * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}.
      */
     @Nullable
     public TimestampedValue<Long> getUtcTime() {
@@ -110,8 +122,9 @@
     }
 
     /**
-     * Returns debug metadata for the suggestion. The information is present in {@link #toString()}
-     * but is not considered for {@link #equals(Object)} and {@link #hashCode()}.
+     * Returns debug metadata for the suggestion.
+     *
+     * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
      */
     @NonNull
     public List<String> getDebugInfo() {
@@ -120,9 +133,9 @@
     }
 
     /**
-     * Associates information with the instance that can be useful for debugging / logging. The
-     * information is present in {@link #toString()} but is not considered for
-     * {@link #equals(Object)} and {@link #hashCode()}.
+     * Associates information with the instance that can be useful for debugging / logging.
+     *
+     * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull String debugInfo) {
         if (mDebugInfo == null) {
@@ -132,9 +145,9 @@
     }
 
     /**
-     * Associates information with the instance that can be useful for debugging / logging. The
-     * information is present in {@link #toString()} but is not considered for
-     * {@link #equals(Object)} and {@link #hashCode()}.
+     * Associates information with the instance that can be useful for debugging / logging.
+     *
+     * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull List<String> debugInfo) {
         if (mDebugInfo == null) {
@@ -152,19 +165,19 @@
             return false;
         }
         PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
-        return mPhoneId == that.mPhoneId
+        return mSlotIndex == that.mSlotIndex
                 && Objects.equals(mUtcTime, that.mUtcTime);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPhoneId, mUtcTime);
+        return Objects.hash(mSlotIndex, mUtcTime);
     }
 
     @Override
     public String toString() {
         return "PhoneTimeSuggestion{"
-                + "mPhoneId='" + mPhoneId + '\''
+                + "mSlotIndex='" + mSlotIndex + '\''
                 + ", mUtcTime=" + mUtcTime
                 + ", mDebugInfo=" + mDebugInfo
                 + '}';
@@ -177,16 +190,24 @@
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class Builder {
-        private final int mPhoneId;
+        private final int mSlotIndex;
         @Nullable private TimestampedValue<Long> mUtcTime;
         @Nullable private List<String> mDebugInfo;
 
-        /** Creates a builder with the specified {@code phoneId}. */
-        public Builder(int phoneId) {
-            mPhoneId = phoneId;
+        /**
+         * Creates a builder with the specified {@code slotIndex}.
+         *
+         * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}.
+         */
+        public Builder(int slotIndex) {
+            mSlotIndex = slotIndex;
         }
 
-        /** Returns the builder for call chaining. */
+        /**
+         * Returns the builder for call chaining.
+         *
+         * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}.
+         */
         @NonNull
         public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
             if (utcTime != null) {
@@ -198,7 +219,11 @@
             return this;
         }
 
-        /** Returns the builder for call chaining. */
+        /**
+         * Returns the builder for call chaining.
+         *
+         * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
+         */
         @NonNull
         public Builder addDebugInfo(@NonNull String debugInfo) {
             if (mDebugInfo == null) {
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
index a6b953b..3a9adc7 100644
--- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -28,10 +28,14 @@
 import java.util.Objects;
 
 /**
- * A time signal from a manual (user provided) source. The value consists of the number of
- * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
- * clock when that number was established. The elapsed realtime clock is considered accurate but
- * volatile, so time signals must not be persisted across device resets.
+ * A time signal from a manual (user provided) source.
+ *
+ * <p>{@code zoneId} contains the suggested time zone ID, e.g. "America/Los_Angeles".
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was entered. This information exists only to aid in
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection logic
+ * and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
  *
  * @hide
  */
@@ -48,10 +52,8 @@
                 }
             };
 
-    @NonNull
-    private final String mZoneId;
-    @Nullable
-    private ArrayList<String> mDebugInfo;
+    @NonNull private final String mZoneId;
+    @Nullable private ArrayList<String> mDebugInfo;
 
     public ManualTimeZoneSuggestion(@NonNull String zoneId) {
         mZoneId = Objects.requireNonNull(zoneId);
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
index d71ffcb..0544ccd 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -34,10 +34,14 @@
  * A time zone suggestion from an identified telephony source, e.g. from MCC and NITZ information
  * associated with a specific radio.
  *
- * <p>The time zone ID can be {@code null} to indicate that the telephony source has entered an
- * "un-opinionated" state and any previous suggestions from that source are being withdrawn.
- * When not {@code null}, the value consists of a suggested time zone ID and metadata that can be
- * used to judge quality / certainty of the suggestion.
+ * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify
+ * suggestions from the same source when there are several in use.
+ *
+ * <p>{@code zoneId}. When not {@code null}, {@code zoneId} contains the suggested time zone ID,
+ * e.g. "America/Los_Angeles". Suggestion metadata like {@code matchType} and {@code quality} can be
+ * used to judge quality / certainty of the suggestion. {@code zoneId} can be {@code null} to
+ * indicate that the telephony source has entered an "un-opinionated" state and any previous
+ * suggestion from the same source is being withdrawn.
  *
  * <p>{@code matchType} must be set to {@link #MATCH_TYPE_NA} when {@code zoneId} is {@code null},
  * and one of the other {@code MATCH_TYPE_} values when it is not {@code null}.
@@ -45,6 +49,12 @@
  * <p>{@code quality} must be set to {@link #QUALITY_NA} when {@code zoneId} is {@code null},
  * and one of the other {@code QUALITY_} values when it is not {@code null}.
  *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists, e.g. what triggered it to be made and what heuristic was used
+ * to determine the time zone or its absence. This information exists only to aid in debugging and
+ * therefore is used by {@link #toString()}, but it is not for use in detection logic and is not
+ * considered in {@link #hashCode()} or {@link #equals(Object)}.
+ *
  * @hide
  */
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -66,12 +76,12 @@
 
     /**
      * Creates an empty time zone suggestion, i.e. one that will cancel previous suggestions with
-     * the same {@code phoneId}.
+     * the same {@code slotIndex}.
      */
     @NonNull
     public static PhoneTimeZoneSuggestion createEmptySuggestion(
-            int phoneId, @NonNull String debugInfo) {
-        return new Builder(phoneId).addDebugInfo(debugInfo).build();
+            int slotIndex, @NonNull String debugInfo) {
+        return new Builder(slotIndex).addDebugInfo(debugInfo).build();
     }
 
     /** @hide */
@@ -131,41 +141,14 @@
      */
     public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3;
 
-    /**
-     * The ID of the phone this suggestion is associated with. For multiple-sim devices this
-     * helps to establish source so filtering / stickiness can be implemented.
-     */
-    private final int mPhoneId;
-
-    /**
-     * The suggestion. {@code null} means there is no current suggestion and any previous suggestion
-     * should be forgotten.
-     */
-    @Nullable
-    private final String mZoneId;
-
-    /**
-     * The type of "match" used to establish the time zone.
-     */
-    @MatchType
-    private final int mMatchType;
-
-    /**
-     * A measure of the quality of the time zone suggestion, i.e. how confident one could be in
-     * it.
-     */
-    @Quality
-    private final int mQuality;
-
-    /**
-     * Free-form debug information about how the suggestion was derived. Used for debug only,
-     * intentionally not used in equals(), etc.
-     */
-    @Nullable
-    private List<String> mDebugInfo;
+    private final int mSlotIndex;
+    @Nullable private final String mZoneId;
+    @MatchType private final int mMatchType;
+    @Quality private final int mQuality;
+    @Nullable private List<String> mDebugInfo;
 
     private PhoneTimeZoneSuggestion(Builder builder) {
-        mPhoneId = builder.mPhoneId;
+        mSlotIndex = builder.mSlotIndex;
         mZoneId = builder.mZoneId;
         mMatchType = builder.mMatchType;
         mQuality = builder.mQuality;
@@ -175,8 +158,8 @@
     @SuppressWarnings("unchecked")
     private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
         // Use the Builder so we get validation during build().
-        int phoneId = in.readInt();
-        PhoneTimeZoneSuggestion suggestion = new Builder(phoneId)
+        int slotIndex = in.readInt();
+        PhoneTimeZoneSuggestion suggestion = new Builder(slotIndex)
                 .setZoneId(in.readString())
                 .setMatchType(in.readInt())
                 .setQuality(in.readInt())
@@ -190,7 +173,7 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mPhoneId);
+        dest.writeInt(mSlotIndex);
         dest.writeString(mZoneId);
         dest.writeInt(mMatchType);
         dest.writeInt(mQuality);
@@ -203,17 +186,19 @@
     }
 
     /**
-     * Returns an identifier for the source of this suggestion. When a device has several "phones",
-     * i.e. sim slots or equivalent, it is used to identify which one.
+     * Returns an identifier for the source of this suggestion.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}.
      */
-    public int getPhoneId() {
-        return mPhoneId;
+    public int getSlotIndex() {
+        return mSlotIndex;
     }
 
     /**
      * Returns the suggested time zone Olson ID, e.g. "America/Los_Angeles". {@code null} means that
-     * the caller is no longer sure what the current time zone is. See
-     * {@link PhoneTimeZoneSuggestion} for the associated {@code matchType} / {@code quality} rules.
+     * the caller is no longer sure what the current time zone is.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}.
      */
     @Nullable
     public String getZoneId() {
@@ -222,8 +207,9 @@
 
     /**
      * Returns information about how the suggestion was determined which could be used to rank
-     * suggestions when several are available from different sources. See
-     * {@link PhoneTimeZoneSuggestion} for the associated rules.
+     * suggestions when several are available from different sources.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}.
      */
     @MatchType
     public int getMatchType() {
@@ -231,8 +217,9 @@
     }
 
     /**
-     * Returns information about the likelihood of the suggested zone being correct.  See
-     * {@link PhoneTimeZoneSuggestion} for the associated rules.
+     * Returns information about the likelihood of the suggested zone being correct.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}.
      */
     @Quality
     public int getQuality() {
@@ -240,8 +227,9 @@
     }
 
     /**
-     * Returns debug metadata for the suggestion. The information is present in {@link #toString()}
-     * but is not considered for {@link #equals(Object)} and {@link #hashCode()}.
+     * Returns debug metadata for the suggestion.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
      */
     @NonNull
     public List<String> getDebugInfo() {
@@ -250,9 +238,9 @@
     }
 
     /**
-     * Associates information with the instance that can be useful for debugging / logging. The
-     * information is present in {@link #toString()} but is not considered for
-     * {@link #equals(Object)} and {@link #hashCode()}.
+     * Associates information with the instance that can be useful for debugging / logging.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull String debugInfo) {
         if (mDebugInfo == null) {
@@ -262,9 +250,9 @@
     }
 
     /**
-     * Associates information with the instance that can be useful for debugging / logging. The
-     * information is present in {@link #toString()} but is not considered for
-     * {@link #equals(Object)} and {@link #hashCode()}.
+     * Associates information with the instance that can be useful for debugging / logging.
+     *
+     * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
      */
     public void addDebugInfo(@NonNull List<String> debugInfo) {
         if (mDebugInfo == null) {
@@ -282,7 +270,7 @@
             return false;
         }
         PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o;
-        return mPhoneId == that.mPhoneId
+        return mSlotIndex == that.mSlotIndex
                 && mMatchType == that.mMatchType
                 && mQuality == that.mQuality
                 && Objects.equals(mZoneId, that.mZoneId);
@@ -290,13 +278,13 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPhoneId, mZoneId, mMatchType, mQuality);
+        return Objects.hash(mSlotIndex, mZoneId, mMatchType, mQuality);
     }
 
     @Override
     public String toString() {
         return "PhoneTimeZoneSuggestion{"
-                + "mPhoneId=" + mPhoneId
+                + "mSlotIndex=" + mSlotIndex
                 + ", mZoneId='" + mZoneId + '\''
                 + ", mMatchType=" + mMatchType
                 + ", mQuality=" + mQuality
@@ -311,18 +299,25 @@
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class Builder {
-        private final int mPhoneId;
+        private final int mSlotIndex;
         @Nullable private String mZoneId;
         @MatchType private int mMatchType;
         @Quality private int mQuality;
         @Nullable private List<String> mDebugInfo;
 
-        public Builder(int phoneId) {
-            mPhoneId = phoneId;
+        /**
+         * Creates a builder with the specified {@code slotIndex}.
+         *
+         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}.
+         */
+        public Builder(int slotIndex) {
+            mSlotIndex = slotIndex;
         }
 
         /**
          * Returns the builder for call chaining.
+         *
+         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}.
          */
         @NonNull
         public Builder setZoneId(@Nullable String zoneId) {
@@ -330,21 +325,33 @@
             return this;
         }
 
-        /** Returns the builder for call chaining. */
+        /**
+         * Returns the builder for call chaining.
+         *
+         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}.
+         */
         @NonNull
         public Builder setMatchType(@MatchType int matchType) {
             mMatchType = matchType;
             return this;
         }
 
-        /** Returns the builder for call chaining. */
+        /**
+         * Returns the builder for call chaining.
+         *
+         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}.
+         */
         @NonNull
         public Builder setQuality(@Quality int quality) {
             mQuality = quality;
             return this;
         }
 
-        /** Returns the builder for call chaining. */
+        /**
+         * Returns the builder for call chaining.
+         *
+         * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
+         */
         @NonNull
         public Builder addDebugInfo(@NonNull String debugInfo) {
             if (mDebugInfo == null) {
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index b13e4b7..140363c 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -25,13 +25,16 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
 /**
@@ -57,6 +60,11 @@
  * </ul>
  */
 public class ConnectivityDiagnosticsManager {
+    /** @hide */
+    @VisibleForTesting
+    public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder>
+            sCallbacks = new ConcurrentHashMap<>();
+
     private final Context mContext;
     private final IConnectivityManager mService;
 
@@ -631,8 +639,9 @@
     /**
      * Registers a ConnectivityDiagnosticsCallback with the System.
      *
-     * <p>Only apps that offer network connectivity to the user are allowed to register callbacks.
-     * This includes:
+     * <p>Only apps that offer network connectivity to the user should be registering callbacks.
+     * These are the only apps whose callbacks will be invoked by the system. Apps considered to
+     * meet these conditions include:
      *
      * <ul>
      *   <li>Carrier apps with active subscriptions
@@ -640,15 +649,14 @@
      *   <li>WiFi Suggesters
      * </ul>
      *
-     * <p>Callbacks will be limited to receiving notifications for networks over which apps provide
-     * connectivity.
+     * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
      *
      * <p>If a registering app loses its relevant permissions, any callbacks it registered will
      * silently stop receiving callbacks.
      *
-     * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If
-     * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException
-     * will be thrown.
+     * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
+     * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
+     * multiple NetworkRequests, an IllegalArgumentException will be thrown.
      *
      * @param request The NetworkRequest that will be used to match with Networks for which
      *     callbacks will be fired
@@ -657,15 +665,21 @@
      *     System
      * @throws IllegalArgumentException if the same callback instance is registered with multiple
      *     NetworkRequests
-     * @throws SecurityException if the caller does not have appropriate permissions to register a
-     *     callback
      */
     public void registerConnectivityDiagnosticsCallback(
             @NonNull NetworkRequest request,
             @NonNull Executor e,
             @NonNull ConnectivityDiagnosticsCallback callback) {
-        // TODO(b/143187964): implement ConnectivityDiagnostics functionality
-        throw new UnsupportedOperationException("registerCallback() not supported yet");
+        final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e);
+        if (sCallbacks.putIfAbsent(callback, binder) != null) {
+            throw new IllegalArgumentException("Callback is currently registered");
+        }
+
+        try {
+            mService.registerConnectivityDiagnosticsCallback(binder, request);
+        } catch (RemoteException exception) {
+            exception.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -678,7 +692,15 @@
      */
     public void unregisterConnectivityDiagnosticsCallback(
             @NonNull ConnectivityDiagnosticsCallback callback) {
-        // TODO(b/143187964): implement ConnectivityDiagnostics functionality
-        throw new UnsupportedOperationException("registerCallback() not supported yet");
+        // unconditionally removing from sCallbacks prevents race conditions here, since remove() is
+        // atomic.
+        final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback);
+        if (binder == null) return;
+
+        try {
+            mService.unregisterConnectivityDiagnosticsCallback(binder);
+        } catch (RemoteException exception) {
+            exception.rethrowFromSystemServer();
+        }
     }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3e9e7fa..b050e47 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -120,6 +120,14 @@
 
     ParcelFileDescriptor establishVpn(in VpnConfig config);
 
+    boolean provisionVpnProfile(in VpnProfile profile, String packageName);
+
+    void deleteVpnProfile(String packageName);
+
+    void startVpnProfile(String packageName);
+
+    void stopVpnProfile(String packageName);
+
     VpnConfig getVpnConfig(int userId);
 
     @UnsupportedAppUsage
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index d1c1329..32e6a6d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,6 +18,9 @@
 
 import static android.content.pm.PackageManager.GET_SIGNATURES;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -38,6 +41,8 @@
 
 import com.google.android.collect.Sets;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.time.ZonedDateTime;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -48,14 +53,24 @@
  * {@hide}
  */
 @SystemService(Context.NETWORK_POLICY_SERVICE)
+@SystemApi
 public class NetworkPolicyManager {
 
     /* POLICY_* are masks and can be ORed, although currently they are not.*/
-    /** No specific network policy, use system default. */
+    /**
+     * No specific network policy, use system default.
+     * @hide
+     */
     public static final int POLICY_NONE = 0x0;
-    /** Reject network usage on metered networks when application in background. */
+    /**
+     * Reject network usage on metered networks when application in background.
+     * @hide
+     */
     public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
-    /** Allow metered network use in the background even when in data usage save mode. */
+    /**
+     * Allow metered network use in the background even when in data usage save mode.
+     * @hide
+     */
     public static final int POLICY_ALLOW_METERED_BACKGROUND = 0x4;
 
     /*
@@ -74,49 +89,98 @@
      *
      * See network-policy-restrictions.md for more info.
      */
-    /** No specific rule was set */
+    /**
+     * No specific rule was set
+     * @hide
+     */
     public static final int RULE_NONE = 0;
-    /** Allow traffic on metered networks. */
+    /**
+     * Allow traffic on metered networks.
+     * @hide
+     */
     public static final int RULE_ALLOW_METERED = 1 << 0;
-    /** Temporarily allow traffic on metered networks because app is on foreground. */
+    /**
+     * Temporarily allow traffic on metered networks because app is on foreground.
+     * @hide
+     */
     public static final int RULE_TEMPORARY_ALLOW_METERED = 1 << 1;
-    /** Reject traffic on metered networks. */
+    /**
+     * Reject traffic on metered networks.
+     * @hide
+     */
     public static final int RULE_REJECT_METERED = 1 << 2;
-    /** Network traffic should be allowed on all networks (metered or non-metered), although
-     * metered-network restrictions could still apply. */
+    /**
+     * Network traffic should be allowed on all networks (metered or non-metered), although
+     * metered-network restrictions could still apply.
+     * @hide
+     */
     public static final int RULE_ALLOW_ALL = 1 << 5;
-    /** Reject traffic on all networks. */
+    /**
+     * Reject traffic on all networks.
+     * @hide
+     */
     public static final int RULE_REJECT_ALL = 1 << 6;
-    /** Mask used to get the {@code RULE_xxx_METERED} rules */
+    /**
+     * Mask used to get the {@code RULE_xxx_METERED} rules
+     * @hide
+     */
     public static final int MASK_METERED_NETWORKS = 0b00001111;
-    /** Mask used to get the {@code RULE_xxx_ALL} rules */
+    /**
+     * Mask used to get the {@code RULE_xxx_ALL} rules
+     * @hide
+     */
     public static final int MASK_ALL_NETWORKS     = 0b11110000;
 
+    /** @hide */
     public static final int FIREWALL_RULE_DEFAULT = 0;
 
+    /** @hide */
     public static final String FIREWALL_CHAIN_NAME_NONE = "none";
+    /** @hide */
     public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
+    /** @hide */
     public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby";
+    /** @hide */
     public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave";
 
     private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
 
+    /** @hide */
     public static final int FOREGROUND_THRESHOLD_STATE =
             ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 
     /**
      * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it
      * applies to.
+     * @hide
      */
     public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
 
-    public static final int OVERRIDE_UNMETERED = 1 << 0;
-    public static final int OVERRIDE_CONGESTED = 1 << 1;
+    /**
+     * Mask used to check if an override value is marked as unmetered.
+     */
+    public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0;
+
+    /**
+     * Mask used to check if an override value is marked as congested.
+     */
+    public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "SUBSCRIPTION_OVERRIDE_" }, value = {
+        SUBSCRIPTION_OVERRIDE_UNMETERED,
+        SUBSCRIPTION_OVERRIDE_CONGESTED
+    })
+    public @interface SubscriptionOverrideMask {}
 
     private final Context mContext;
     @UnsupportedAppUsage
     private INetworkPolicyManager mService;
 
+    /** @hide */
     public NetworkPolicyManager(Context context, INetworkPolicyManager service) {
         if (service == null) {
             throw new IllegalArgumentException("missing INetworkPolicyManager");
@@ -125,6 +189,7 @@
         mService = service;
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public static NetworkPolicyManager from(Context context) {
         return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
@@ -135,6 +200,7 @@
      *
      * @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags,
      *     although it is not validated.
+     * @hide
      */
     @UnsupportedAppUsage
     public void setUidPolicy(int uid, int policy) {
@@ -152,6 +218,7 @@
      *
      * @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags,
      *     although it is not validated.
+     * @hide
      */
     public void addUidPolicy(int uid, int policy) {
         try {
@@ -168,6 +235,7 @@
      *
      * @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags,
      *     although it is not validated.
+     * @hide
      */
     public void removeUidPolicy(int uid, int policy) {
         try {
@@ -177,6 +245,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public int getUidPolicy(int uid) {
         try {
@@ -186,6 +255,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public int[] getUidsWithPolicy(int policy) {
         try {
@@ -195,6 +265,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void registerListener(INetworkPolicyListener listener) {
         try {
@@ -204,6 +275,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void unregisterListener(INetworkPolicyListener listener) {
         try {
@@ -213,6 +285,7 @@
         }
     }
 
+    /** @hide */
     public void setNetworkPolicies(NetworkPolicy[] policies) {
         try {
             mService.setNetworkPolicies(policies);
@@ -221,6 +294,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public NetworkPolicy[] getNetworkPolicies() {
         try {
@@ -230,6 +304,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public void setRestrictBackground(boolean restrictBackground) {
         try {
@@ -239,6 +314,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public boolean getRestrictBackground() {
         try {
@@ -249,6 +325,32 @@
     }
 
     /**
+     * Override connections to be temporarily marked as either unmetered or congested,
+     * along with automatic timeouts if desired.
+     *
+     * @param subId the subscriber ID this override applies to.
+     * @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 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
+     * @param callingPackage the name of the package making the call.
+     *
+     */
+    public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
+            @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
+                    @NonNull String callingPackage) {
+        try {
+            mService.setSubscriptionOverride(subId, overrideMask, overrideValue, timeoutMillis,
+                    callingPackage);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Resets network policy settings back to factory defaults.
      *
      * @hide
@@ -286,6 +388,7 @@
     /**
      * Check if given UID can have a {@link #setUidPolicy(int, int)} defined,
      * usually to protect critical system services.
+     * @hide
      */
     @Deprecated
     public static boolean isUidValidForPolicy(Context context, int uid) {
@@ -353,6 +456,7 @@
     /**
      * Returns true if {@param procState} is considered foreground and as such will be allowed
      * to access network when the device is idle or in battery saver mode. Otherwise, false.
+     * @hide
      */
     public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
         return procState <= FOREGROUND_THRESHOLD_STATE;
@@ -361,16 +465,19 @@
     /**
      * Returns true if {@param procState} is considered foreground and as such will be allowed
      * to access network when the device is in data saver mode. Otherwise, false.
+     * @hide
      */
     public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
         return procState <= FOREGROUND_THRESHOLD_STATE;
     }
 
+    /** @hide */
     public static String resolveNetworkId(WifiConfiguration config) {
         return WifiInfo.sanitizeSsid(config.isPasspoint()
                 ? config.providerFriendlyName : config.SSID);
     }
 
+    /** @hide */
     public static String resolveNetworkId(String ssid) {
         return WifiInfo.sanitizeSsid(ssid);
     }
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f95807a..e60cc81 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -20,8 +20,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.os.RemoteException;
+
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 
 /**
  * This class provides an interface for apps to manage platform VPN profiles
@@ -41,6 +50,15 @@
     @NonNull private final Context mContext;
     @NonNull private final IConnectivityManager mService;
 
+    private static Intent getIntentForConfirmation() {
+        final Intent intent = new Intent();
+        final ComponentName componentName = ComponentName.unflattenFromString(
+                Resources.getSystem().getString(
+                        com.android.internal.R.string.config_customVpnConfirmDialogComponent));
+        intent.setComponent(componentName);
+        return intent;
+    }
+
     /**
      * Create an instance of the VpnManger with the given context.
      *
@@ -57,18 +75,49 @@
     /**
      * Install a VpnProfile configuration keyed on the calling app's package name.
      *
-     * @param profile the PlatformVpnProfile provided by this package. Will override any previous
-     *     PlatformVpnProfile stored for this package.
-     * @return an intent to request user consent if needed (null otherwise).
+     * <p>This method returns {@code null} if user consent has already been granted, or an {@link
+     * Intent} to a system activity. If an intent is returned, the application should launch the
+     * activity using {@link Activity#startActivityForResult} to request user consent. The activity
+     * may pop up a dialog to require user action, and the result will come back via its {@link
+     * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
+     * consented, and the VPN profile can be started.
+     *
+     * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
+     *     stored for this package.
+     * @return an Intent requesting user consent to start the VPN, or null if consent is not
+     *     required based on privileges or previous user consent.
      */
     @Nullable
     public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
-        throw new UnsupportedOperationException("Not yet implemented");
+        final VpnProfile internalProfile;
+
+        try {
+            internalProfile = profile.toVpnProfile();
+        } catch (GeneralSecurityException | IOException e) {
+            // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
+            // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
+            // string as required by the VpnProfile.
+            throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
+        }
+
+        try {
+            // Profile can never be null; it either gets set, or an exception is thrown.
+            if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
+                return null;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return getIntentForConfirmation();
     }
 
     /** Delete the VPN profile configuration that was provisioned by the calling app */
     public void deleteProvisionedVpnProfile() {
-        throw new UnsupportedOperationException("Not yet implemented");
+        try {
+            mService.deleteVpnProfile(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -78,11 +127,19 @@
      *     setup, or if user consent has not been granted
      */
     public void startProvisionedVpnProfile() {
-        throw new UnsupportedOperationException("Not yet implemented");
+        try {
+            mService.startVpnProfile(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /** Tear down the VPN provided by the calling app (if any) */
     public void stopProvisionedVpnProfile() {
-        throw new UnsupportedOperationException("Not yet implemented");
+        try {
+            mService.stopVpnProfile(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 }
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 73e1adf..223f920 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
 import android.content.res.AssetFileDescriptor;
 import android.os.IUpdateEngine;
 import android.os.IUpdateEngineCallback;
@@ -478,7 +479,7 @@
          * </ul>
          */
         @ErrorCode
-        public int errorCode() {
+        public int getErrorCode() {
             return mErrorCode;
         }
 
@@ -492,14 +493,15 @@
          *
          * @return The following values:
          * <ul>
-         * <li>zero if {@link #errorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
-         * <li>non-zero if {@link #errorCode} returns {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
+         * <li>zero if {@link #getErrorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
+         * <li>non-zero if {@link #getErrorCode} returns
+         * {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
          * Value is the estimated total space required on userdata partition.</li>
          * </ul>
-         * @throws IllegalStateException if {@link #errorCode} is not one of the above.
+         * @throws IllegalStateException if {@link #getErrorCode} is not one of the above.
          *
          */
-        public long freeSpaceRequired() {
+        public long getFreeSpaceRequired() {
             if (mErrorCode == ErrorCodeConstants.SUCCESS) {
                 return 0;
             }
@@ -507,7 +509,7 @@
                 return mFreeSpaceRequired;
             }
             throw new IllegalStateException(String.format(
-                    "freeSpaceRequired() is not available when error code is %d", mErrorCode));
+                    "getFreeSpaceRequired() is not available when error code is %d", mErrorCode));
         }
     }
 
@@ -531,8 +533,10 @@
      *
      * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}.
      * @param headerKeyValuePairs See {@link #applyPayload}.
-     * @return See {@link AllocateSpaceResult}.
+     * @return See {@link AllocateSpaceResult#getErrorCode} and
+     *             {@link AllocateSpaceResult#getFreeSpaceRequired}.
      */
+    @WorkerThread
     @NonNull
     public AllocateSpaceResult allocateSpace(
                 @NonNull String payloadMetadataFilename,
@@ -583,6 +587,7 @@
      * @throws ServiceSpecificException if other transient errors has occurred.
      * A reboot may or may not help resolving the issue.
      */
+    @WorkerThread
     @ErrorCode
     public int cleanupAppliedPayload() {
         try {
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index 2bb3f1f..a7a90f6 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -78,6 +78,10 @@
     private boolean mChatbotSupported = false;
     /** Chatbot role support. */
     private boolean mChatbotRoleSupported = false;
+    /** Standalone Chatbot communication support. */
+    private boolean mSmChatbotSupported = false;
+    /** MMtel based call composer support. */
+    private boolean mMmtelCallComposerSupported = false;
     /** List of supported extensions. */
     private String[] mExts = new String[10];
     /** Time used to compute when to query again. */
@@ -498,6 +502,34 @@
         this.mChatbotRoleSupported = chatbotRoleSupported;
     }
 
+    /**
+     * Checks whether standalone chatbot communication is supported.
+     */
+    public boolean isSmChatbotSupported() {
+        return mSmChatbotSupported;
+    }
+
+    /**
+     * Sets standalone chatbot communication as supported or not supported.
+     */
+    public void setSmChatbotSupported(boolean smChatbotSupported) {
+        this.mSmChatbotSupported = smChatbotSupported;
+    }
+
+    /**
+     * Checks whether Mmtel based call composer is supported.
+     */
+    public boolean isMmtelCallComposerSupported() {
+        return mMmtelCallComposerSupported;
+    }
+
+    /**
+     * Sets Mmtel based call composer as supported or not supported.
+     */
+    public void setMmtelCallComposerSupported(boolean mmtelCallComposerSupported) {
+        this.mMmtelCallComposerSupported = mmtelCallComposerSupported;
+    }
+
     /** Gets the list of supported extensions. */
     public String[] getExts() {
         return mExts;
@@ -553,6 +585,8 @@
         dest.writeInt(mSharedSketchSupported ? 1 : 0);
         dest.writeInt(mChatbotSupported ? 1 : 0);
         dest.writeInt(mChatbotRoleSupported ? 1 : 0);
+        dest.writeInt(mSmChatbotSupported ? 1 : 0);
+        dest.writeInt(mMmtelCallComposerSupported ? 1 : 0);
 
         dest.writeInt(mRcsIpVoiceCallSupported ? 1 : 0);
         dest.writeInt(mRcsIpVideoCallSupported ? 1 : 0);
@@ -602,6 +636,8 @@
         mSharedSketchSupported = (source.readInt() == 0) ? false : true;
         mChatbotSupported = (source.readInt() == 0) ? false : true;
         mChatbotRoleSupported = (source.readInt() == 0) ? false : true;
+        mSmChatbotSupported = (source.readInt() == 0) ? false : true;
+        mMmtelCallComposerSupported = (source.readInt() == 0) ? false : true;
 
         mRcsIpVoiceCallSupported = (source.readInt() == 0) ? false : true;
         mRcsIpVideoCallSupported = (source.readInt() == 0) ? false : true;
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index e0eb9af..e978cf5 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.compat;
 
+import android.annotation.IntDef;
 import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
@@ -23,6 +24,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -42,7 +45,7 @@
         long mChangeId;
         int mState;
 
-        ChangeReport(long changeId, int state) {
+        ChangeReport(long changeId, @State int state) {
             mChangeId = changeId;
             mState = state;
         }
@@ -69,7 +72,7 @@
     // When true will of every time to debug (logcat).
     private boolean mDebugLogAll;
 
-    public ChangeReporter(int source) {
+    public ChangeReporter(@Source int source) {
         mSource = source;
         mReportedChanges =  new HashMap<>();
         mDebugLogAll = false;
@@ -174,7 +177,7 @@
     private void debugLog(int uid, long changeId, int state) {
         String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
                 uid, stateToString(state));
-        if (mSource == StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER) {
+        if (mSource == SOURCE_SYSTEM_SERVER) {
             Slog.d(TAG, message);
         } else {
             Log.d(TAG, message);
@@ -183,21 +186,56 @@
     }
 
     /**
-     * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+     * Transforms {@link #ChangeReporter.State} enum to a string.
      *
      * @param state to transform
      * @return a string representing the state
      */
-    private static String stateToString(int state) {
+    private static String stateToString(@State int state) {
         switch (state) {
-            case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
+            case STATE_LOGGED:
                 return "LOGGED";
-            case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
+            case STATE_ENABLED:
                 return "ENABLED";
-            case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
+            case STATE_DISABLED:
                 return "DISABLED";
             default:
                 return "UNKNOWN";
         }
     }
+
+    /** These values should be kept in sync with those in atoms.proto */
+    public static final int STATE_UNKNOWN_STATE =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__UNKNOWN_STATE;
+    public static final int STATE_ENABLED =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED;
+    public static final int STATE_DISABLED =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED;
+    public static final int STATE_LOGGED =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED;
+    public static final int SOURCE_UNKNOWN_SOURCE =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__UNKNOWN_SOURCE;
+    public static final int SOURCE_APP_PROCESS =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS;
+    public static final int SOURCE_SYSTEM_SERVER =
+                            StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "STATE_" }, value = {
+            STATE_UNKNOWN_STATE,
+            STATE_ENABLED,
+            STATE_DISABLED,
+            STATE_LOGGED
+    })
+    public @interface State {
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "SOURCE_" }, value = {
+            SOURCE_UNKNOWN_SOURCE,
+            SOURCE_APP_PROCESS,
+            SOURCE_SYSTEM_SERVER
+    })
+    public @interface Source {
+    }
 }
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
index 09bbe37..a052543 100644
--- a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -24,10 +24,10 @@
 public class ChangeReporterTest {
     @Test
     public void testStatsLogOnce() {
-        ChangeReporter reporter = new ChangeReporter(0);
+        ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
         int myUid = 1022, otherUid = 1023;
         long myChangeId = 500L, otherChangeId = 600L;
-        int myState = 1, otherState = 2;
+        int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
 
         assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
@@ -42,10 +42,10 @@
 
     @Test
     public void testStatsLogAfterReset() {
-        ChangeReporter reporter = new ChangeReporter(0);
+        ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
         int myUid = 1022;
         long myChangeId = 500L;
-        int myState = 1;
+        int myState = ChangeReporter.STATE_ENABLED;
 
         assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
@@ -60,10 +60,10 @@
 
     @Test
     public void testDebugLogOnce() {
-        ChangeReporter reporter = new ChangeReporter(0);
+        ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
         int myUid = 1022, otherUid = 1023;
         long myChangeId = 500L, otherChangeId = 600L;
-        int myState = 1, otherState = 2;
+        int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
 
         assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
@@ -78,10 +78,10 @@
 
     @Test
     public void testDebugLogAfterReset() {
-        ChangeReporter reporter = new ChangeReporter(0);
+        ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
         int myUid = 1022;
         long myChangeId = 500L;
-        int myState = 1;
+        int myState = ChangeReporter.STATE_ENABLED;
 
         assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
@@ -96,10 +96,10 @@
 
     @Test
     public void testDebugLogWithLogAll() {
-        ChangeReporter reporter = new ChangeReporter(0);
+        ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
         int myUid = 1022;
         long myChangeId = 500L;
-        int myState = 1;
+        int myState = ChangeReporter.STATE_ENABLED;
 
         assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
index ba29a97..d17b635 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -27,17 +27,17 @@
 import org.junit.Test;
 
 public class PhoneTimeSuggestionTest {
-    private static final int PHONE_ID = 99999;
+    private static final int SLOT_INDEX = 99999;
 
     @Test
     public void testEquals() {
-        PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+        PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
         {
             PhoneTimeSuggestion one = builder1.build();
             assertEquals(one, one);
         }
 
-        PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+        PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
         {
             PhoneTimeSuggestion one = builder1.build();
             PhoneTimeSuggestion two = builder2.build();
@@ -59,7 +59,7 @@
             assertEquals(two, one);
         }
 
-        PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(PHONE_ID + 1);
+        PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(SLOT_INDEX + 1);
         builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L));
         {
             PhoneTimeSuggestion one = builder1.build();
@@ -80,7 +80,7 @@
 
     @Test
     public void testParcelable() {
-        PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(PHONE_ID);
+        PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
         assertRoundTripParcelable(builder.build());
 
         builder.setUtcTime(new TimestampedValue<>(1111L, 2222L));
diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
index 0108a0b..384dbf9 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
@@ -26,17 +26,17 @@
 import org.junit.Test;
 
 public class PhoneTimeZoneSuggestionTest {
-    private static final int PHONE_ID = 99999;
+    private static final int SLOT_INDEX = 99999;
 
     @Test
     public void testEquals() {
-        PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+        PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
         {
             PhoneTimeZoneSuggestion one = builder1.build();
             assertEquals(one, one);
         }
 
-        PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+        PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
         {
             PhoneTimeZoneSuggestion one = builder1.build();
             PhoneTimeZoneSuggestion two = builder2.build();
@@ -45,7 +45,7 @@
         }
 
         PhoneTimeZoneSuggestion.Builder builder3 =
-                new PhoneTimeZoneSuggestion.Builder(PHONE_ID + 1);
+                new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX + 1);
         {
             PhoneTimeZoneSuggestion one = builder1.build();
             PhoneTimeZoneSuggestion three = builder3.build();
@@ -120,7 +120,7 @@
 
     @Test(expected = RuntimeException.class)
     public void testBuilderValidates_emptyZone_badMatchType() {
-        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
         // No zone ID, so match type should be left unset.
         builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET);
         builder.build();
@@ -128,7 +128,7 @@
 
     @Test(expected = RuntimeException.class)
     public void testBuilderValidates_zoneSet_badMatchType() {
-        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
         builder.setZoneId("Europe/London");
         builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
         builder.build();
@@ -136,7 +136,7 @@
 
     @Test
     public void testParcelable() {
-        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+        PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
         assertRoundTripParcelable(builder.build());
 
         builder.setZoneId("Europe/London");
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 9db4a35..b4c95e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -35,8 +35,10 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.zip.GZIPInputStream;
 
 /**
@@ -84,7 +86,7 @@
      * "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
      * of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
      */
-    private final Map<String, String> mFileNameToContentIdMap = new HashMap();
+    private final Map<String, Set<String>> mFileNameToContentIdMap = new HashMap();
 
     /*
      * A map from a content id (MD5 sum of file content) to a license file content.
@@ -186,10 +188,10 @@
      *     </licenses>
      */
     @VisibleForTesting
-    static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
+    static void parse(InputStreamReader in, Map<String, Set<String>> outFileNameToContentIdMap,
             Map<String, String> outContentIdToFileContentMap)
                     throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<String, Set<String>>();
         Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
 
         XmlPullParser parser = Xml.newPullParser();
@@ -206,7 +208,10 @@
                     if (!TextUtils.isEmpty(contentId)) {
                         String fileName = readText(parser).trim();
                         if (!TextUtils.isEmpty(fileName)) {
-                            fileNameToContentIdMap.put(fileName, contentId);
+                            Set<String> contentIds =
+                                    fileNameToContentIdMap.computeIfAbsent(
+                                            fileName, k -> new HashSet<>());
+                            contentIds.add(contentId);
                         }
                     }
                 } else if (TAG_FILE_CONTENT.equals(parser.getName())) {
@@ -224,7 +229,13 @@
 
             state = parser.next();
         }
-        outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
+        for (Map.Entry<String, Set<String>> entry : fileNameToContentIdMap.entrySet()) {
+            outFileNameToContentIdMap.merge(
+                    entry.getKey(), entry.getValue(), (s1, s2) -> {
+                        s1.addAll(s2);
+                        return s1;
+                    });
+        }
         outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
     }
 
@@ -240,7 +251,7 @@
     }
 
     @VisibleForTesting
-    static void generateHtml(Map<String, String> fileNameToContentIdMap,
+    static void generateHtml(Map<String, Set<String>> fileNameToContentIdMap,
             Map<String, String> contentIdToFileContentMap, PrintWriter writer,
             String noticeHeader) {
         List<String> fileNameList = new ArrayList();
@@ -259,19 +270,20 @@
 
         // Prints all the file list with a link to its license file content.
         for (String fileName : fileNameList) {
-            String contentId = fileNameToContentIdMap.get(fileName);
-            // Assigns an id to a newly referred license file content.
-            if (!contentIdToOrderMap.containsKey(contentId)) {
-                contentIdToOrderMap.put(contentId, count);
+            for (String contentId : fileNameToContentIdMap.get(fileName)) {
+                // Assigns an id to a newly referred license file content.
+                if (!contentIdToOrderMap.containsKey(contentId)) {
+                    contentIdToOrderMap.put(contentId, count);
 
-                // An index in contentIdAndFileNamesList is the order of each element.
-                contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
-                count++;
+                    // An index in contentIdAndFileNamesList is the order of each element.
+                    contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+                    count++;
+                }
+
+                int id = contentIdToOrderMap.get(contentId);
+                contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
+                writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
             }
-
-            int id = contentIdToOrderMap.get(contentId);
-            contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
-            writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
         }
 
         writer.println(HTML_MIDDLE_STRING);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 4b5e909..e87461f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -28,8 +28,11 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 @RunWith(RobolectricTestRunner.class)
 public class LicenseHtmlGeneratorFromXmlTest {
@@ -68,6 +71,7 @@
 
     private static final String HTML_BODY_STRING =
             "<li><a href=\"#id0\">/file0</a></li>\n"
+            + "<li><a href=\"#id1\">/file0</a></li>\n"
             + "<li><a href=\"#id0\">/file1</a></li>\n"
             + "</ul>\n"
             + "</div><!-- table of contents -->\n"
@@ -82,6 +86,15 @@
             + "license content #0\n"
             + "</pre><!-- license-text -->\n"
             + "</td></tr><!-- same-license -->\n"
+            + "<tr id=\"id1\"><td class=\"same-license\">\n"
+            + "<div class=\"label\">Notices for file(s):</div>\n"
+            + "<div class=\"file-list\">\n"
+            + "/file0 <br/>\n"
+            + "</div><!-- file-list -->\n"
+            + "<pre class=\"license-text\">\n"
+            + "license content #1\n"
+            + "</pre><!-- license-text -->\n"
+            + "</td></tr><!-- same-license -->\n"
             + "</table></body></html>\n";
 
     private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
@@ -91,22 +104,22 @@
 
     @Test
     public void testParseValidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
                 new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
                 fileNameToContentIdMap, contentIdToFileContentMap);
         assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
-        assertThat(fileNameToContentIdMap.get("/file0")).isEqualTo("0");
-        assertThat(fileNameToContentIdMap.get("/file1")).isEqualTo("0");
+        assertThat(fileNameToContentIdMap.get("/file0")).containsExactly("0");
+        assertThat(fileNameToContentIdMap.get("/file1")).containsExactly("0");
         assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
         assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
     }
 
     @Test(expected = XmlPullParserException.class)
     public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
@@ -116,12 +129,13 @@
 
     @Test
     public void testGenerateHtml() {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
-        fileNameToContentIdMap.put("/file0", "0");
-        fileNameToContentIdMap.put("/file1", "0");
+        fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
+        fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
         contentIdToFileContentMap.put("0", "license content #0");
+        contentIdToFileContentMap.put("1", "license content #1");
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
@@ -131,12 +145,13 @@
 
     @Test
     public void testGenerateHtmlWithCustomHeading() {
-        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
-        fileNameToContentIdMap.put("/file0", "0");
-        fileNameToContentIdMap.put("/file1", "0");
+        fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
+        fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
         contentIdToFileContentMap.put("0", "license content #0");
+        contentIdToFileContentMap.put("1", "license content #1");
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 09eece8..08d89ba 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -227,6 +227,9 @@
     <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
 
+    <!-- Permission needed to test mainline permission module rollback -->
+    <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e98c370..4b9925f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -559,13 +559,17 @@
                 .asInterface(ServiceManager.getService("dnsresolver"));
     }
 
-    /** Handler thread used for both of the handlers below. */
+    /** Handler thread used for all of the handlers below. */
     @VisibleForTesting
     protected final HandlerThread mHandlerThread;
     /** Handler used for internal events. */
     final private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
     final private NetworkStateTrackerHandler mTrackerHandler;
+    /** Handler used for processing {@link android.net.ConnectivityDiagnosticsManager} events */
+    @VisibleForTesting
+    final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
+
     private final DnsManager mDnsManager;
 
     private boolean mSystemReady;
@@ -632,6 +636,10 @@
     @VisibleForTesting
     final MultipathPolicyTracker mMultipathPolicyTracker;
 
+    @VisibleForTesting
+    final Map<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo>
+            mConnectivityDiagnosticsCallbacks = new HashMap<>();
+
     /**
      * Implements support for the legacy "one network per network type" model.
      *
@@ -964,6 +972,8 @@
         mHandlerThread.start();
         mHandler = new InternalHandler(mHandlerThread.getLooper());
         mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
+        mConnectivityDiagnosticsHandler =
+                new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper());
 
         mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
                 Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
@@ -3384,18 +3394,7 @@
         nri.unlinkDeathRecipient();
         mNetworkRequests.remove(nri.request);
 
-        synchronized (mUidToNetworkRequestCount) {
-            int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
-            if (requests < 1) {
-                Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
-                        nri.mUid);
-            } else if (requests == 1) {
-                mUidToNetworkRequestCount.removeAt(
-                        mUidToNetworkRequestCount.indexOfKey(nri.mUid));
-            } else {
-                mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
-            }
-        }
+        decrementNetworkRequestPerUidCount(nri);
 
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
@@ -3466,6 +3465,19 @@
         }
     }
 
+    private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) {
+        synchronized (mUidToNetworkRequestCount) {
+            final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
+            if (requests < 1) {
+                Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
+            } else if (requests == 1) {
+                mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
+            } else {
+                mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
+            }
+        }
+    }
+
     @Override
     public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
         enforceNetworkStackSettingsOrSetup();
@@ -4298,7 +4310,7 @@
             throwIfLockdownEnabled();
             Vpn vpn = mVpns.get(userId);
             if (vpn != null) {
-                return vpn.prepare(oldPackage, newPackage);
+                return vpn.prepare(oldPackage, newPackage, false);
             } else {
                 return false;
             }
@@ -4347,6 +4359,78 @@
     }
 
     /**
+     * Stores the given VPN profile based on the provisioning package name.
+     *
+     * <p>If there is already a VPN profile stored for the provisioning package, this call will
+     * overwrite the profile.
+     *
+     * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+     * exclusively by the Settings app, and passed into the platform at startup time.
+     *
+     * @return {@code true} if user consent has already been granted, {@code false} otherwise.
+     * @hide
+     */
+    @Override
+    public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
+        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized (mVpns) {
+            return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
+        }
+    }
+
+    /**
+     * Deletes the stored VPN profile for the provisioning package
+     *
+     * <p>If there are no profiles for the given package, this method will silently succeed.
+     *
+     * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+     * exclusively by the Settings app, and passed into the platform at startup time.
+     *
+     * @hide
+     */
+    @Override
+    public void deleteVpnProfile(@NonNull String packageName) {
+        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized (mVpns) {
+            mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
+        }
+    }
+
+    /**
+     * Starts the VPN based on the stored profile for the given package
+     *
+     * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+     * exclusively by the Settings app, and passed into the platform at startup time.
+     *
+     * @throws IllegalArgumentException if no profile was found for the given package name.
+     * @hide
+     */
+    @Override
+    public void startVpnProfile(@NonNull String packageName) {
+        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized (mVpns) {
+            throwIfLockdownEnabled();
+            mVpns.get(user).startVpnProfile(packageName, mKeyStore);
+        }
+    }
+
+    /**
+     * Stops the Platform VPN if the provided package is running one.
+     *
+     * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+     * exclusively by the Settings app, and passed into the platform at startup time.
+     *
+     * @hide
+     */
+    @Override
+    public void stopVpnProfile(@NonNull String packageName) {
+        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized (mVpns) {
+            mVpns.get(user).stopVpnProfile(packageName);
+        }
+    }
+
+    /**
      * Start legacy VPN, controlling native daemons as needed. Creates a
      * secondary thread to perform connection work, returning quickly.
      */
@@ -4549,6 +4633,13 @@
         }
     }
 
+    /**
+     * Throws if there is any currently running, always-on Legacy VPN.
+     *
+     * <p>The LockdownVpnTracker and mLockdownEnabled both track whether an always-on Legacy VPN is
+     * running across the entire system. Tracking for app-based VPNs is done on a per-user,
+     * per-package basis in Vpn.java
+     */
     @GuardedBy("mVpns")
     private void throwIfLockdownEnabled() {
         if (mLockdownEnabled) {
@@ -5084,6 +5175,10 @@
             }
         }
 
+        NetworkRequestInfo(NetworkRequest r) {
+            this(r, null);
+        }
+
         private void enforceRequestCountLimit() {
             synchronized (mUidToNetworkRequestCount) {
                 int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
@@ -6174,7 +6269,10 @@
     private void callCallbackForRequest(NetworkRequestInfo nri,
             NetworkAgentInfo networkAgent, int notificationType, int arg1) {
         if (nri.messenger == null) {
-            return;  // Default request has no msgr
+            // Default request has no msgr. Also prevents callbacks from being invoked for
+            // NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
+            // are Type.LISTEN, but should not have NetworkCallbacks invoked.
+            return;
         }
         Bundle bundle = new Bundle();
         // TODO: check if defensive copies of data is needed.
@@ -6328,12 +6426,34 @@
             }
         }
 
+        static class RequestReassignment {
+            @NonNull public final NetworkRequestInfo mRequest;
+            @Nullable public final NetworkAgentInfo mOldNetwork;
+            @Nullable public final NetworkAgentInfo mNewNetwork;
+            RequestReassignment(@NonNull final NetworkRequestInfo request,
+                    @Nullable final NetworkAgentInfo oldNetwork,
+                    @Nullable final NetworkAgentInfo newNetwork) {
+                mRequest = request;
+                mOldNetwork = oldNetwork;
+                mNewNetwork = newNetwork;
+            }
+        }
+
         @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
+        @NonNull private final List<RequestReassignment> mReassignments = new ArrayList<>();
 
         @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
             return mRematchedNetworks;
         }
 
+        @NonNull Iterable<RequestReassignment> getRequestReassignments() {
+            return mReassignments;
+        }
+
+        void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
+            mReassignments.add(reassignment);
+        }
+
         void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
             mRematchedNetworks.add(network);
         }
@@ -6409,20 +6529,13 @@
         changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
                 newNetwork.isBackgroundNetwork()));
 
-        final int score = newNetwork.getCurrentScore();
-
         if (VDBG || DDBG) log("rematching " + newNetwork.name());
 
         final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
                 computeRequestReassignmentForNetwork(newNetwork);
 
-        NetworkCapabilities nc = newNetwork.networkCapabilities;
-        if (VDBG) log(" network has: " + nc);
-
         // Find and migrate to this Network any NetworkRequests for
         // which this network is now the best.
-        final ArrayList<NetworkAgentInfo> removedRequests = new ArrayList<>();
-        final ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<>();
         for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
                 reassignedRequests.entrySet()) {
             final NetworkRequestInfo nri = entry.getKey();
@@ -6436,7 +6549,6 @@
                     }
                     previousSatisfier.removeRequest(nri.request.requestId);
                     previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
-                    removedRequests.add(previousSatisfier);
                 } else {
                     if (VDBG || DDBG) log("   accepting network in place of null");
                 }
@@ -6445,7 +6557,8 @@
                 if (!newSatisfier.addRequest(nri.request)) {
                     Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
                 }
-                addedRequests.add(nri);
+                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                        nri, previousSatisfier, newSatisfier));
                 // Tell NetworkProviders about the new score, so they can stop
                 // trying to connect if they know they cannot match it.
                 // TODO - this could get expensive if we have a lot of requests for this
@@ -6502,21 +6615,6 @@
             // Have a new default network, release the transition wakelock in
             scheduleReleaseNetworkTransitionWakelock();
         }
-
-        if (!newNetwork.networkCapabilities.equalRequestableCapabilities(nc)) {
-            Slog.wtf(TAG, String.format(
-                    "BUG: %s changed requestable capabilities during rematch: %s -> %s",
-                    newNetwork.name(), nc, newNetwork.networkCapabilities));
-        }
-        if (newNetwork.getCurrentScore() != score) {
-            Slog.wtf(TAG, String.format(
-                    "BUG: %s changed score during rematch: %d -> %d",
-                    newNetwork.name(), score, newNetwork.getCurrentScore()));
-        }
-
-        // Notify requested networks are available after the default net is switched, but
-        // before LegacyTypeTracker sends legacy broadcasts
-        for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
     }
 
     /**
@@ -6545,6 +6643,15 @@
 
         final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
 
+        // Notify requested networks are available after the default net is switched, but
+        // before LegacyTypeTracker sends legacy broadcasts
+        for (final NetworkReassignment.RequestReassignment event :
+                changes.getRequestReassignments()) {
+            if (null != event.mNewNetwork) {
+                notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+            }
+        }
+
         for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
             // Process listen requests and update capabilities if the background state has
             // changed for this network. For consistency with previous behavior, send onLost
@@ -7330,19 +7437,161 @@
         }
     }
 
+    /**
+     * Handler used for managing all Connectivity Diagnostics related functions.
+     *
+     * @see android.net.ConnectivityDiagnosticsManager
+     *
+     * TODO(b/147816404): Explore moving ConnectivityDiagnosticsHandler to a separate file
+     */
+    @VisibleForTesting
+    class ConnectivityDiagnosticsHandler extends Handler {
+        /**
+         * Used to handle ConnectivityDiagnosticsCallback registration events from {@link
+         * android.net.ConnectivityDiagnosticsManager}.
+         * obj = ConnectivityDiagnosticsCallbackInfo with IConnectivityDiagnosticsCallback and
+         * NetworkRequestInfo to be registered
+         */
+        private static final int EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 1;
+
+        /**
+         * Used to handle ConnectivityDiagnosticsCallback unregister events from {@link
+         * android.net.ConnectivityDiagnosticsManager}.
+         * obj = the IConnectivityDiagnosticsCallback to be unregistered
+         * arg1 = the uid of the caller
+         */
+        private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;
+
+        private ConnectivityDiagnosticsHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
+                    handleRegisterConnectivityDiagnosticsCallback(
+                            (ConnectivityDiagnosticsCallbackInfo) msg.obj);
+                    break;
+                }
+                case EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
+                    handleUnregisterConnectivityDiagnosticsCallback(
+                            (IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
+                    break;
+                }
+            }
+        }
+    }
+
+    /** Class used for cleaning up IConnectivityDiagnosticsCallback instances after their death. */
+    @VisibleForTesting
+    class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
+        @NonNull private final IConnectivityDiagnosticsCallback mCb;
+        @NonNull private final NetworkRequestInfo mRequestInfo;
+
+        @VisibleForTesting
+        ConnectivityDiagnosticsCallbackInfo(
+                @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
+            mCb = cb;
+            mRequestInfo = nri;
+        }
+
+        @Override
+        public void binderDied() {
+            log("ConnectivityDiagnosticsCallback IBinder died.");
+            unregisterConnectivityDiagnosticsCallback(mCb);
+        }
+    }
+
+    private void handleRegisterConnectivityDiagnosticsCallback(
+            @NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
+        ensureRunningOnConnectivityServiceThread();
+
+        final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
+        final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+
+        // This means that the client registered the same callback multiple times. Do
+        // not override the previous entry, and exit silently.
+        if (mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+            if (VDBG) log("Diagnostics callback is already registered");
+
+            // Decrement the reference count for this NetworkRequestInfo. The reference count is
+            // incremented when the NetworkRequestInfo is created as part of
+            // enforceRequestCountLimit().
+            decrementNetworkRequestPerUidCount(nri);
+            return;
+        }
+
+        mConnectivityDiagnosticsCallbacks.put(cb, cbInfo);
+
+        try {
+            cb.asBinder().linkToDeath(cbInfo, 0);
+        } catch (RemoteException e) {
+            cbInfo.binderDied();
+        }
+    }
+
+    private void handleUnregisterConnectivityDiagnosticsCallback(
+            @NonNull IConnectivityDiagnosticsCallback cb, int uid) {
+        ensureRunningOnConnectivityServiceThread();
+
+        if (!mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+            if (VDBG) log("Removing diagnostics callback that is not currently registered");
+            return;
+        }
+
+        final NetworkRequestInfo nri = mConnectivityDiagnosticsCallbacks.get(cb).mRequestInfo;
+
+        if (uid != nri.mUid) {
+            if (VDBG) loge("Different uid than registrant attempting to unregister cb");
+            return;
+        }
+
+        cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
+    }
+
     @Override
     public void registerConnectivityDiagnosticsCallback(
             @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
-        // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
-        throw new UnsupportedOperationException(
-                "registerConnectivityDiagnosticsCallback not yet implemented");
+        if (request.legacyType != TYPE_NONE) {
+            throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+                    + " Please use NetworkCapabilities instead.");
+        }
+
+        // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
+        // and administrator uids to be safe.
+        final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
+        restrictRequestUidsForCaller(nc);
+
+        final NetworkRequest requestWithId =
+                new NetworkRequest(
+                        nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN);
+
+        // NetworkRequestInfos created here count towards MAX_NETWORK_REQUESTS_PER_UID limit.
+        //
+        // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in
+        // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the
+        // callback's binder death.
+        final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
+        final ConnectivityDiagnosticsCallbackInfo cbInfo =
+                new ConnectivityDiagnosticsCallbackInfo(callback, nri);
+
+        mConnectivityDiagnosticsHandler.sendMessage(
+                mConnectivityDiagnosticsHandler.obtainMessage(
+                        ConnectivityDiagnosticsHandler
+                                .EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
+                        cbInfo));
     }
 
     @Override
     public void unregisterConnectivityDiagnosticsCallback(
             @NonNull IConnectivityDiagnosticsCallback callback) {
-        // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
-        throw new UnsupportedOperationException(
-                "unregisterConnectivityDiagnosticsCallback not yet implemented");
+        mConnectivityDiagnosticsHandler.sendMessage(
+                mConnectivityDiagnosticsHandler.obtainMessage(
+                        ConnectivityDiagnosticsHandler
+                                .EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
+                        Binder.getCallingUid(),
+                        0,
+                        callback));
     }
 }
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 5d31dbe..5c50962 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -86,6 +86,12 @@
             return mIsActive && mConfig != null;
         }
 
+        void release() {
+            if (mDeathHandler != null) {
+                mDeathHandler.release();
+            }
+        }
+
         // returns true if status of an active recording has changed
         boolean setActive(boolean active) {
             if (mIsActive == active) return false;
@@ -417,6 +423,7 @@
                     break;
                 case AudioManager.RECORD_CONFIG_EVENT_RELEASE:
                     configChanged = state.isActiveConfiguration();
+                    state.release();
                     mRecordStates.remove(stateIndex);
                     break;
                 default:
@@ -519,6 +526,10 @@
                 return false;
             }
         }
+
+        void release() {
+            mRecorderToken.unlinkToDeath(this, 0);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 029b7bc..3c6ae9d 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -25,7 +25,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.AndroidBuildClassifier;
@@ -53,7 +52,7 @@
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+                ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
     }
 
@@ -61,14 +60,14 @@
     PlatformCompat(Context context, CompatConfig compatConfig) {
         mContext = context;
         mChangeReporter = new ChangeReporter(
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+                ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = compatConfig;
     }
 
     @Override
     public void reportChange(long changeId, ApplicationInfo appInfo) {
         reportChange(changeId, appInfo.uid,
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+                ChangeReporter.STATE_LOGGED);
     }
 
     @Override
@@ -82,18 +81,18 @@
 
     @Override
     public void reportChangeByUid(long changeId, int uid) {
-        reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+        reportChange(changeId, uid, ChangeReporter.STATE_LOGGED);
     }
 
     @Override
     public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
         if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo.uid,
-                    StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
+                    ChangeReporter.STATE_ENABLED);
             return true;
         }
         reportChange(changeId, appInfo.uid,
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
+                ChangeReporter.STATE_DISABLED);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2933fab..eabc083 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -24,6 +24,8 @@
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -157,6 +159,16 @@
     // is actually O(n²)+O(n²).
     private static final int MAX_ROUTES_TO_EVALUATE = 150;
 
+    /**
+     * Largest profile size allowable for Platform VPNs.
+     *
+     * <p>The largest platform VPN profiles use IKEv2 RSA Certificate Authentication and have two
+     * X509Certificates, and one RSAPrivateKey. This should lead to a max size of 2x 12kB for the
+     * certificates, plus a reasonable upper bound on the private key of 32kB. The rest of the
+     * profile is expected to be negligible in size.
+     */
+    @VisibleForTesting static final int MAX_VPN_PROFILE_SIZE_BYTES = 1 << 17; // 128kB
+
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
 
@@ -656,6 +668,11 @@
      * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
      * it can be revoked by itself.
      *
+     * The permission checks to verify that the VPN has already been granted
+     * user consent are dependent on the type of the VPN being prepared. See
+     * {@link AppOpsManager#OP_ACTIVATE_VPN} and {@link
+     * AppOpsManager#OP_ACTIVATE_PLATFORM_VPN} for more information.
+     *
      * Note: when we added VPN pre-consent in
      * https://android.googlesource.com/platform/frameworks/base/+/0554260
      * the names oldPackage and newPackage became misleading, because when
@@ -674,10 +691,13 @@
      *
      * @param oldPackage The package name of the old VPN application
      * @param newPackage The package name of the new VPN application
-     *
+     * @param isPlatformVpn Whether the package being prepared is using a platform VPN profile.
+     *     Preparing a platform VPN profile requires only the lesser ACTIVATE_PLATFORM_VPN appop.
      * @return true if the operation succeeded.
      */
-    public synchronized boolean prepare(String oldPackage, String newPackage) {
+    // TODO: Use an Intdef'd type to represent what kind of VPN the caller is preparing.
+    public synchronized boolean prepare(
+            String oldPackage, String newPackage, boolean isPlatformVpn) {
         if (oldPackage != null) {
             // Stop an existing always-on VPN from being dethroned by other apps.
             if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
@@ -688,13 +708,14 @@
             if (!isCurrentPreparedPackage(oldPackage)) {
                 // The package doesn't match. We return false (to obtain user consent) unless the
                 // user has already consented to that VPN package.
-                if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
+                if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
+                        && isVpnPreConsented(mContext, oldPackage, isPlatformVpn)) {
                     prepareInternal(oldPackage);
                     return true;
                 }
                 return false;
             } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
-                    && !isVpnUserPreConsented(oldPackage)) {
+                    && !isVpnPreConsented(mContext, oldPackage, isPlatformVpn)) {
                 // Currently prepared VPN is revoked, so unprepare it and return false.
                 prepareInternal(VpnConfig.LEGACY_VPN);
                 return false;
@@ -805,13 +826,29 @@
         return false;
     }
 
-    private boolean isVpnUserPreConsented(String packageName) {
-        AppOpsManager appOps =
-                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+    private static boolean isVpnPreConsented(
+            Context context, String packageName, boolean isPlatformVpn) {
+        return isPlatformVpn
+                ? isVpnProfilePreConsented(context, packageName)
+                : isVpnServicePreConsented(context, packageName);
+    }
 
-        // Verify that the caller matches the given package and has permission to activate VPNs.
-        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
-                packageName) == AppOpsManager.MODE_ALLOWED;
+    private static boolean doesPackageHaveAppop(Context context, String packageName, int appop) {
+        final AppOpsManager appOps =
+                (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+        // Verify that the caller matches the given package and has the required permission.
+        return appOps.noteOpNoThrow(appop, Binder.getCallingUid(), packageName)
+                == AppOpsManager.MODE_ALLOWED;
+    }
+
+    private static boolean isVpnServicePreConsented(Context context, String packageName) {
+        return doesPackageHaveAppop(context, packageName, AppOpsManager.OP_ACTIVATE_VPN);
+    }
+
+    private static boolean isVpnProfilePreConsented(Context context, String packageName) {
+        return doesPackageHaveAppop(context, packageName, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN)
+                || isVpnServicePreConsented(context, packageName);
     }
 
     private int getAppUid(String app, int userHandle) {
@@ -1001,6 +1038,9 @@
      * Establish a VPN network and return the file descriptor of the VPN interface. This methods
      * returns {@code null} if the application is revoked or not prepared.
      *
+     * <p>This method supports ONLY VpnService-based VPNs. For Platform VPNs, see {@link
+     * provisionVpnProfile} and {@link startVpnProfile}
+     *
      * @param config The parameters to configure the network.
      * @return The file descriptor of the VPN interface.
      */
@@ -1011,7 +1051,7 @@
             return null;
         }
         // Check to ensure consent hasn't been revoked since we were prepared.
-        if (!isVpnUserPreConsented(mPackage)) {
+        if (!isVpnServicePreConsented(mContext, mPackage)) {
             return null;
         }
         // Check if the service is properly declared.
@@ -1676,6 +1716,10 @@
         public int settingsSecureGetIntForUser(String key, int def, int userId) {
             return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
         }
+
+        public boolean isCallerSystem() {
+            return Binder.getCallingUid() == Process.SYSTEM_UID;
+        }
     }
 
     private native int jniCreate(int mtu);
@@ -2224,4 +2268,148 @@
             }
         }
     }
+
+    private void verifyCallingUidAndPackage(String packageName) {
+        if (getAppUid(packageName, mUserHandle) != Binder.getCallingUid()) {
+            throw new SecurityException("Mismatched package and UID");
+        }
+    }
+
+    @VisibleForTesting
+    String getProfileNameForPackage(String packageName) {
+        return Credentials.PLATFORM_VPN + mUserHandle + "_" + packageName;
+    }
+
+    /**
+     * Stores an app-provisioned VPN profile and returns whether the app is already prepared.
+     *
+     * @param packageName the package name of the app provisioning this profile
+     * @param profile the profile to be stored and provisioned
+     * @param keyStore the System keystore instance to save VPN profiles
+     * @returns whether or not the app has already been granted user consent
+     */
+    public synchronized boolean provisionVpnProfile(
+            @NonNull String packageName, @NonNull VpnProfile profile, @NonNull KeyStore keyStore) {
+        checkNotNull(packageName, "No package name provided");
+        checkNotNull(profile, "No profile provided");
+        checkNotNull(keyStore, "KeyStore missing");
+
+        verifyCallingUidAndPackage(packageName);
+
+        final byte[] encodedProfile = profile.encode();
+        if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
+            throw new IllegalArgumentException("Profile too big");
+        }
+
+        // Permissions checked during startVpnProfile()
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    keyStore.put(
+                            getProfileNameForPackage(packageName),
+                            encodedProfile,
+                            Process.SYSTEM_UID,
+                            0 /* flags */);
+                });
+
+        // TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
+        // This mirrors the prepareAndAuthorize that is used by VpnService.
+
+        // Return whether the app is already pre-consented
+        return isVpnProfilePreConsented(mContext, packageName);
+    }
+
+    /**
+     * Deletes an app-provisioned VPN profile.
+     *
+     * @param packageName the package name of the app provisioning this profile
+     * @param keyStore the System keystore instance to save VPN profiles
+     */
+    public synchronized void deleteVpnProfile(
+            @NonNull String packageName, @NonNull KeyStore keyStore) {
+        checkNotNull(packageName, "No package name provided");
+        checkNotNull(keyStore, "KeyStore missing");
+
+        verifyCallingUidAndPackage(packageName);
+
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    keyStore.delete(getProfileNameForPackage(packageName), Process.SYSTEM_UID);
+                });
+    }
+
+    /**
+     * Retrieves the VpnProfile.
+     *
+     * <p>Must be used only as SYSTEM_UID, otherwise the key/UID pair will not match anything in the
+     * keystore.
+     */
+    @VisibleForTesting
+    @Nullable
+    VpnProfile getVpnProfilePrivileged(@NonNull String packageName, @NonNull KeyStore keyStore) {
+        if (!mSystemServices.isCallerSystem()) {
+            Log.wtf(TAG, "getVpnProfilePrivileged called as non-System UID ");
+            return null;
+        }
+
+        final byte[] encoded = keyStore.get(getProfileNameForPackage(packageName));
+        if (encoded == null) return null;
+
+        return VpnProfile.decode("" /* Key unused */, encoded);
+    }
+
+    /**
+     * Starts an already provisioned VPN Profile, keyed by package name.
+     *
+     * <p>This method is meant to be called by apps (via VpnManager and ConnectivityService).
+     * Privileged (system) callers should use startVpnProfilePrivileged instead. Otherwise the UIDs
+     * will not match during appop checks.
+     *
+     * @param packageName the package name of the app provisioning this profile
+     * @param keyStore the System keystore instance to retrieve VPN profiles
+     */
+    public synchronized void startVpnProfile(
+            @NonNull String packageName, @NonNull KeyStore keyStore) {
+        checkNotNull(packageName, "No package name provided");
+        checkNotNull(keyStore, "KeyStore missing");
+
+        // Prepare VPN for startup
+        if (!prepare(packageName, null /* newPackage */, true /* isPlatformVpn */)) {
+            throw new SecurityException("User consent not granted for package " + packageName);
+        }
+
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    final VpnProfile profile = getVpnProfilePrivileged(packageName, keyStore);
+                    if (profile == null) {
+                        throw new IllegalArgumentException("No profile found for " + packageName);
+                    }
+
+                    startVpnProfilePrivileged(profile);
+                });
+    }
+
+    private void startVpnProfilePrivileged(@NonNull VpnProfile profile) {
+        // TODO: Start PlatformVpnRunner
+    }
+
+    /**
+     * Stops an already running VPN Profile for the given package.
+     *
+     * <p>This method is meant to be called by apps (via VpnManager and ConnectivityService).
+     * Privileged (system) callers should (re-)prepare the LEGACY_VPN instead.
+     *
+     * @param packageName the package name of the app provisioning this profile
+     */
+    public synchronized void stopVpnProfile(@NonNull String packageName) {
+        checkNotNull(packageName, "No package name provided");
+
+        // To stop the VPN profile, the caller must be the current prepared package. Otherwise,
+        // the app is not prepared, and we can just return.
+        if (!isCurrentPreparedPackage(packageName)) {
+            // TODO: Also check to make sure that the running VPN is a VPN profile.
+            return;
+        }
+
+        prepareInternal(VpnConfig.LEGACY_VPN);
+    }
 }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index e95fc4a..a1e643f 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -101,11 +101,12 @@
     private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
 
     /**
-     * A mapping from phoneId to a time suggestion. We typically expect one or two mappings: devices
-     * will have a small number of telephony devices and phoneIds are assumed to be stable.
+     * A mapping from slotIndex to a time suggestion. We typically expect one or two mappings:
+     * devices will have a small number of telephony devices and slotIndexs are assumed to be
+     * stable.
      */
     @GuardedBy("this")
-    private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
+    private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionBySlotIndex =
             new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     @GuardedBy("this")
@@ -155,7 +156,7 @@
         }
 
         // Perform validation / input filtering and record the validated suggestion against the
-        // phoneId.
+        // slotIndex.
         if (!validateAndStorePhoneSuggestion(timeSuggestion)) {
             return;
         }
@@ -202,7 +203,7 @@
 
         ipw.println("Phone suggestion history:");
         ipw.increaseIndent(); // level 2
-        mSuggestionByPhoneId.dump(ipw);
+        mSuggestionBySlotIndex.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
         ipw.println("Network suggestion history:");
@@ -223,8 +224,8 @@
             return false;
         }
 
-        int phoneId = suggestion.getPhoneId();
-        PhoneTimeSuggestion previousSuggestion = mSuggestionByPhoneId.get(phoneId);
+        int slotIndex = suggestion.getSlotIndex();
+        PhoneTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex);
         if (previousSuggestion != null) {
             // We can log / discard suggestions with obvious issues with the reference time clock.
             if (previousSuggestion.getUtcTime() == null
@@ -249,7 +250,7 @@
         }
 
         // Store the latest suggestion.
-        mSuggestionByPhoneId.put(phoneId, suggestion);
+        mSuggestionBySlotIndex.put(slotIndex, suggestion);
         return true;
     }
 
@@ -323,15 +324,15 @@
         //
         // [1] https://en.wikipedia.org/wiki/NITZ
         //
-        // Generally, when there are suggestions from multiple phoneIds they should usually
+        // Generally, when there are suggestions from multiple slotIndexs they should usually
         // approximately agree. In cases where signals *are* inaccurate we don't want to vacillate
-        // between signals from two phoneIds. However, it is known for NITZ signals to be incorrect
-        // occasionally, which means we also don't want to stick forever with one phoneId. Without
-        // cross-referencing across sources (e.g. the current device time, NTP), or doing some kind
-        // of statistical analysis of consistency within and across phoneIds, we can't know which
-        // suggestions are more correct.
+        // between signals from two slotIndexs. However, it is known for NITZ signals to be
+        // incorrect occasionally, which means we also don't want to stick forever with one
+        // slotIndex. Without cross-referencing across sources (e.g. the current device time, NTP),
+        // or doing some kind of statistical analysis of consistency within and across slotIndexs,
+        // we can't know which suggestions are more correct.
         //
-        // For simplicity, we try to value recency, then consistency of phoneId.
+        // For simplicity, we try to value recency, then consistency of slotIndex.
         //
         // The heuristic works as follows:
         // Recency: The most recent suggestion from each phone is scored. The score is based on a
@@ -339,20 +340,20 @@
         // bucket, thus applying a loose reference time ordering. The suggestion with the highest
         // score is used.
         // Consistency: If there a multiple suggestions with the same score, the suggestion with the
-        // lowest phoneId is always taken.
+        // lowest slotIndex is always taken.
         //
         // In the trivial case with a single ID this will just mean that the latest received
         // suggestion is used.
 
         PhoneTimeSuggestion bestSuggestion = null;
         int bestScore = PHONE_INVALID_SCORE;
-        for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
-            Integer phoneId = mSuggestionByPhoneId.keyAt(i);
-            PhoneTimeSuggestion candidateSuggestion = mSuggestionByPhoneId.valueAt(i);
+        for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+            Integer slotIndex = mSuggestionBySlotIndex.keyAt(i);
+            PhoneTimeSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected - null suggestions should never be stored.
-                Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for phoneId."
-                        + " phoneId=" + phoneId);
+                Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for slotIndex."
+                        + " slotIndex=" + slotIndex);
                 continue;
             } else if (candidateSuggestion.getUtcTime() == null) {
                 // Unexpected - we do not store empty suggestions.
@@ -372,10 +373,10 @@
                 bestSuggestion = candidateSuggestion;
                 bestScore = candidateScore;
             } else if (bestScore == candidateScore) {
-                // Tie! Use the suggestion with the lowest phoneId.
-                int candidatePhoneId = candidateSuggestion.getPhoneId();
-                int bestPhoneId = bestSuggestion.getPhoneId();
-                if (candidatePhoneId < bestPhoneId) {
+                // Tie! Use the suggestion with the lowest slotIndex.
+                int candidateSlotIndex = candidateSuggestion.getSlotIndex();
+                int bestSlotIndex = bestSuggestion.getSlotIndex();
+                if (candidateSlotIndex < bestSlotIndex) {
                     bestSuggestion = candidateSuggestion;
                 }
             }
@@ -396,7 +397,7 @@
         }
 
         // The score is based on the age since receipt. Suggestions are bucketed so two
-        // suggestions in the same bucket from different phoneIds are scored the same.
+        // suggestions in the same bucket from different slotIndexs are scored the same.
         long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis();
 
         // Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
@@ -560,8 +561,8 @@
      */
     @VisibleForTesting
     @Nullable
-    public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
-        return mSuggestionByPhoneId.get(phoneId);
+    public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int slotIndex) {
+        return mSuggestionBySlotIndex.get(slotIndex);
     }
 
     /**
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index b4a4399..b0e0069 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -172,12 +172,12 @@
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
 
     /**
-     * A mapping from phoneId to a phone time zone suggestion. We typically expect one or two
-     * mappings: devices will have a small number of telephony devices and phoneIds are assumed to
+     * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
+     * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
      * be stable.
      */
     @GuardedBy("this")
-    private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionByPhoneId =
+    private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
             new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
 
     /**
@@ -205,7 +205,7 @@
     /**
      * Suggests a time zone for the device, or withdraws a previous suggestion if
      * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
-     * specific {@link PhoneTimeZoneSuggestion#getPhoneId() phone}.
+     * specific {@link PhoneTimeZoneSuggestion#getSlotIndex() phone}.
      * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
      * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
      * setting and what to set it to.
@@ -221,8 +221,8 @@
         QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
                 new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
 
-        // Store the suggestion against the correct phoneId.
-        mSuggestionByPhoneId.put(suggestion.getPhoneId(), scoredSuggestion);
+        // Store the suggestion against the correct slotIndex.
+        mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
@@ -384,8 +384,9 @@
         // and find the best. Note that we deliberately do not look at age: the caller can
         // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
         // expected to withdraw suggestions they no longer have confidence in.
-        for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
-            QualifiedPhoneTimeZoneSuggestion candidateSuggestion = mSuggestionByPhoneId.valueAt(i);
+        for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+            QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
+                    mSuggestionBySlotIndex.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected
                 continue;
@@ -396,10 +397,10 @@
             } else if (candidateSuggestion.score > bestSuggestion.score) {
                 bestSuggestion = candidateSuggestion;
             } else if (candidateSuggestion.score == bestSuggestion.score) {
-                // Tie! Use the suggestion with the lowest phoneId.
-                int candidatePhoneId = candidateSuggestion.suggestion.getPhoneId();
-                int bestPhoneId = bestSuggestion.suggestion.getPhoneId();
-                if (candidatePhoneId < bestPhoneId) {
+                // Tie! Use the suggestion with the lowest slotIndex.
+                int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
+                int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
+                if (candidateSlotIndex < bestSlotIndex) {
                     bestSuggestion = candidateSuggestion;
                 }
             }
@@ -455,7 +456,7 @@
 
         ipw.println("Phone suggestion history:");
         ipw.increaseIndent(); // level 2
-        mSuggestionByPhoneId.dump(ipw);
+        mSuggestionBySlotIndex.dump(ipw);
         ipw.decreaseIndent(); // level 2
         ipw.decreaseIndent(); // level 1
         ipw.flush();
@@ -465,8 +466,8 @@
      * A method used to inspect strategy state during tests. Not intended for general use.
      */
     @VisibleForTesting
-    public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
-        return mSuggestionByPhoneId.get(phoneId);
+    public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
+        return mSuggestionBySlotIndex.get(slotIndex);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 519856c..ae034b5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -16,8 +16,8 @@
 
 package android.telephony;
 
-import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
-import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
@@ -56,7 +56,6 @@
 import android.provider.Telephony.SimInfo;
 import android.telephony.euicc.EuiccManager;
 import android.telephony.ims.ImsMmTelManager;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 
@@ -2374,23 +2373,24 @@
         final SubscriptionInfo subInfo =
                 SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
 
-        Configuration config = context.getResources().getConfiguration();
-        Configuration newConfig = new Configuration();
-        newConfig.setTo(config);
+        Configuration overrideConfig = new Configuration();
         if (subInfo != null) {
-            newConfig.mcc = subInfo.getMcc();
-            newConfig.mnc = subInfo.getMnc();
-            if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
+            overrideConfig.mcc = subInfo.getMcc();
+            overrideConfig.mnc = subInfo.getMnc();
+            if (overrideConfig.mnc == 0) overrideConfig.mnc = Configuration.MNC_ZERO;
         }
 
         if (useRootLocale) {
-            newConfig.setLocale(Locale.ROOT);
+            overrideConfig.setLocale(Locale.ROOT);
         }
 
-        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
-        DisplayMetrics newMetrics = new DisplayMetrics();
-        newMetrics.setTo(metrics);
-        Resources res = new Resources(context.getResources().getAssets(), newMetrics, newConfig);
+        // Create new context with new configuration so that we can avoid modifying the passed in
+        // context.
+        // Note that if the original context configuration changes, the resources here will also
+        // change for all values except those overridden by newConfig (e.g. if the device has an
+        // orientation change).
+        Context newContext = context.createConfigurationContext(overrideConfig);
+        Resources res = newContext.getResources();
 
         if (cacheKey != null) {
             // Save the newly created Resources in the resource cache.
@@ -2524,9 +2524,9 @@
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
             @DurationMillisLong long timeoutMillis) {
         try {
-            final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
-            getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
-                    timeoutMillis, mContext.getOpPackageName());
+            final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
+            getNetworkPolicy().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
+                    overrideValue, timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2559,9 +2559,9 @@
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
             @DurationMillisLong long timeoutMillis) {
         try {
-            final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
-            getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
-                    timeoutMillis, mContext.getOpPackageName());
+            final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
+            getNetworkPolicy().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
+                    overrideValue, timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3275,31 +3275,6 @@
         return subId;
     }
 
-    /**
-     * Set whether a subscription always allows MMS connection. If true, MMS network
-     * request will be accepted by telephony even if user turns "mobile data" off
-     * on this subscription.
-     *
-     * @param subId which subscription it's setting to.
-     * @param alwaysAllow whether Mms data is always allowed.
-     * @return whether operation is successful.
-     *
-     * @hide
-     */
-    public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
-        try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            if (iSub != null) {
-                return iSub.setAlwaysAllowMmsData(subId, alwaysAllow);
-            }
-        } catch (RemoteException ex) {
-            if (!isSystemProcess()) {
-                ex.rethrowAsRuntimeException();
-            }
-        }
-        return false;
-    }
-
     private interface CallISubMethodHelper {
         int callMethod(ISub iSub) throws RemoteException;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 811120b..ffd40d0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12128,7 +12128,7 @@
      *  1) User data is turned on, or
      *  2) APN is un-metered for this subscription, or
      *  3) APN type is whitelisted. E.g. MMS is whitelisted if
-     *  {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+     *  {@link #setAlwaysAllowMmsData(boolean)} is turned on.
      *
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
      * @return whether data is enabled for a apn type.
@@ -12435,4 +12435,30 @@
         }
         return false;
     }
+
+    /**
+     * Set whether the specific sim card always allows MMS connection. If true, MMS network
+     * request will be accepted by telephony even if user turns "mobile data" off
+     * on this specific sim card.
+     *
+     * @param alwaysAllow whether Mms data is always allowed.
+     * @return whether operation is successful.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow);
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index cc02a40..571efce 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -295,8 +295,6 @@
 
     boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId);
 
-    boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
-
     int getActiveDataSubscriptionId();
 
     boolean canDisablePhysicalSubscription();
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d7fbafb..3a6269b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2241,6 +2241,11 @@
     boolean isDataAllowedInVoiceCall(int subId);
 
     /**
+     * Set whether a subscription always allows MMS connection.
+     */
+    boolean setAlwaysAllowMmsData(int subId, boolean allow);
+
+    /**
      * Command line command to enable or disable handling of CEP data for test purposes.
      */
     oneway void setCepEnabled(boolean isCepEnabled);
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 7ab4b56..2d5df4f 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -27,12 +27,18 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.content.Context;
 import android.os.PersistableBundle;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,15 +58,27 @@
 
     private static final Executor INLINE_EXECUTOR = x -> x.run();
 
+    @Mock private Context mContext;
+    @Mock private IConnectivityManager mService;
     @Mock private ConnectivityDiagnosticsCallback mCb;
 
     private ConnectivityDiagnosticsBinder mBinder;
+    private ConnectivityDiagnosticsManager mManager;
 
     @Before
     public void setUp() {
+        mContext = mock(Context.class);
+        mService = mock(IConnectivityManager.class);
         mCb = mock(ConnectivityDiagnosticsCallback.class);
 
         mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
+        mManager = new ConnectivityDiagnosticsManager(mContext, mService);
+    }
+
+    @After
+    public void tearDown() {
+        // clear ConnectivityDiagnosticsManager callbacks map
+        ConnectivityDiagnosticsManager.sCallbacks.clear();
     }
 
     private ConnectivityReport createSampleConnectivityReport() {
@@ -245,4 +263,53 @@
         // latch without waiting.
         verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity));
     }
+
+    @Test
+    public void testRegisterConnectivityDiagnosticsCallback() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+
+        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+        verify(mService).registerConnectivityDiagnosticsCallback(
+                any(ConnectivityDiagnosticsBinder.class), eq(request));
+        assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+    }
+
+    @Test
+    public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+
+        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+        try {
+            mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+            fail("Duplicate callback registration should fail");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testUnregisterConnectivityDiagnosticsCallback() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+        mManager.unregisterConnectivityDiagnosticsCallback(mCb);
+
+        verify(mService).unregisterConnectivityDiagnosticsCallback(
+                any(ConnectivityDiagnosticsBinder.class));
+        assertFalse(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+
+        // verify that re-registering is successful
+        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+        verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
+                any(ConnectivityDiagnosticsBinder.class), eq(request));
+        assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+    }
+
+    @Test
+    public void testUnregisterUnknownConnectivityDiagnosticsCallback() throws Exception {
+        mManager.unregisterConnectivityDiagnosticsCallback(mCb);
+
+        verifyNoMoreInteractions(mService);
+    }
 }
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
index 655c4d1..97551c9 100644
--- a/tests/net/java/android/net/VpnManagerTest.java
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -16,13 +16,21 @@
 
 package android.net;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.test.mock.MockContext;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.net.VpnProfile;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,7 +39,12 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class VpnManagerTest {
-    private static final String VPN_PROFILE_KEY = "KEY";
+    private static final String PKG_NAME = "fooPackage";
+
+    private static final String SESSION_NAME_STRING = "testSession";
+    private static final String SERVER_ADDR_STRING = "1.2.3.4";
+    private static final String IDENTITY_STRING = "Identity";
+    private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
 
     private IConnectivityManager mMockCs;
     private VpnManager mVpnManager;
@@ -39,7 +52,7 @@
             new MockContext() {
                 @Override
                 public String getOpPackageName() {
-                    return "fooPackage";
+                    return PKG_NAME;
                 }
             };
 
@@ -50,34 +63,49 @@
     }
 
     @Test
-    public void testProvisionVpnProfile() throws Exception {
-        try {
-            mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class));
-        } catch (UnsupportedOperationException expected) {
-        }
+    public void testProvisionVpnProfilePreconsented() throws Exception {
+        final PlatformVpnProfile profile = getPlatformVpnProfile();
+        when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(true);
+
+        // Expect there to be no intent returned, as consent has already been granted.
+        assertNull(mVpnManager.provisionVpnProfile(profile));
+        verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+    }
+
+    @Test
+    public void testProvisionVpnProfileNeedsConsent() throws Exception {
+        final PlatformVpnProfile profile = getPlatformVpnProfile();
+        when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
+
+        // Expect intent to be returned, as consent has not already been granted.
+        assertNotNull(mVpnManager.provisionVpnProfile(profile));
+        verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
     }
 
     @Test
     public void testDeleteProvisionedVpnProfile() throws Exception {
-        try {
-            mVpnManager.deleteProvisionedVpnProfile();
-        } catch (UnsupportedOperationException expected) {
-        }
+        mVpnManager.deleteProvisionedVpnProfile();
+        verify(mMockCs).deleteVpnProfile(eq(PKG_NAME));
     }
 
     @Test
     public void testStartProvisionedVpnProfile() throws Exception {
-        try {
-            mVpnManager.startProvisionedVpnProfile();
-        } catch (UnsupportedOperationException expected) {
-        }
+        mVpnManager.startProvisionedVpnProfile();
+        verify(mMockCs).startVpnProfile(eq(PKG_NAME));
     }
 
     @Test
     public void testStopProvisionedVpnProfile() throws Exception {
-        try {
-            mVpnManager.stopProvisionedVpnProfile();
-        } catch (UnsupportedOperationException expected) {
-        }
+        mVpnManager.stopProvisionedVpnProfile();
+        verify(mMockCs).stopVpnProfile(eq(PKG_NAME));
+    }
+
+    private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
+        return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+                .setBypassable(true)
+                .setMaxMtu(1300)
+                .setMetered(true)
+                .setAuthPsk(PSK_BYTES)
+                .build();
     }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e80b7c9..50f1bbe 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -139,6 +139,7 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IDnsResolver;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
@@ -180,6 +181,7 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Parcel;
@@ -210,6 +212,7 @@
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
 import com.android.server.connectivity.ConnectivityConstants;
 import com.android.server.connectivity.DefaultNetworkMetrics;
 import com.android.server.connectivity.IpConnectivityMetrics;
@@ -322,6 +325,8 @@
     @Mock UserManager mUserManager;
     @Mock NotificationManager mNotificationManager;
     @Mock AlarmManager mAlarmManager;
+    @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
+    @Mock IBinder mIBinder;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -6355,4 +6360,70 @@
                 UserHandle.getAppId(uid));
         return packageInfo;
     }
+
+    @Test
+    public void testRegisterConnectivityDiagnosticsCallbackInvalidRequest() throws Exception {
+        final NetworkRequest request =
+                new NetworkRequest(
+                        new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
+        try {
+            mService.registerConnectivityDiagnosticsCallback(
+                    mConnectivityDiagnosticsCallback, request);
+            fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
+        final NetworkRequest wifiRequest =
+                new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
+
+        when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+        mService.registerConnectivityDiagnosticsCallback(
+                mConnectivityDiagnosticsCallback, wifiRequest);
+
+        verify(mIBinder, timeout(TIMEOUT_MS))
+                .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+        assertTrue(
+                mService.mConnectivityDiagnosticsCallbacks.containsKey(
+                        mConnectivityDiagnosticsCallback));
+
+        mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
+        verify(mIBinder, timeout(TIMEOUT_MS))
+                .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+        assertFalse(
+                mService.mConnectivityDiagnosticsCallbacks.containsKey(
+                        mConnectivityDiagnosticsCallback));
+        verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
+    }
+
+    @Test
+    public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
+        final NetworkRequest wifiRequest =
+                new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
+        when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+        mService.registerConnectivityDiagnosticsCallback(
+                mConnectivityDiagnosticsCallback, wifiRequest);
+
+        verify(mIBinder, timeout(TIMEOUT_MS))
+                .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+        verify(mConnectivityDiagnosticsCallback).asBinder();
+        assertTrue(
+                mService.mConnectivityDiagnosticsCallbacks.containsKey(
+                        mConnectivityDiagnosticsCallback));
+
+        // Register the same callback again
+        mService.registerConnectivityDiagnosticsCallback(
+                mConnectivityDiagnosticsCallback, wifiRequest);
+
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        assertTrue(
+                mService.mConnectivityDiagnosticsCallbacks.containsKey(
+                        mConnectivityDiagnosticsCallback));
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index ce50bef..084ec73 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -28,11 +28,11 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.RouteInfo.RTN_UNREACHABLE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -43,6 +43,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -58,21 +59,20 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
-import android.net.IpPrefix;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
-import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.VpnService;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.INetworkManagementService;
 import android.os.Looper;
-import android.os.SystemClock;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.security.Credentials;
+import android.security.KeyStore;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
@@ -81,6 +81,7 @@
 
 import com.android.internal.R;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -90,9 +91,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -124,6 +122,8 @@
         managedProfileA.profileGroupId = primaryUser.id;
     }
 
+    static final String TEST_VPN_PKG = "com.dummy.vpn";
+
     /**
      * Names and UIDs for some fake packages. Important points:
      *  - UID is ordered increasing.
@@ -148,6 +148,8 @@
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
     @Mock private ConnectivityManager mConnectivityManager;
+    @Mock private KeyStore mKeyStore;
+    private final VpnProfile mVpnProfile = new VpnProfile("key");
 
     @Before
     public void setUp() throws Exception {
@@ -166,6 +168,7 @@
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
+        when(mSystemServices.isCallerSystem()).thenReturn(true);
 
         // Used by {@link Notification.Builder}
         ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -175,6 +178,10 @@
                 .thenReturn(applicationInfo);
 
         doNothing().when(mNetService).registerObserver(any());
+
+        // Deny all appops by default.
+        when(mAppOps.noteOpNoThrow(anyInt(), anyInt(), anyString()))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
     }
 
     @Test
@@ -464,12 +471,12 @@
         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
 
         // When a new VPN package is set the rules should change to cover that package.
-        vpn.prepare(null, PKGS[0]);
+        vpn.prepare(null, PKGS[0], false /* isPlatformVpn */);
         order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
 
         // When that VPN package is unset, everything should be undone again in reverse.
-        vpn.prepare(null, VpnConfig.LEGACY_VPN);
+        vpn.prepare(null, VpnConfig.LEGACY_VPN, false /* isPlatformVpn */);
         order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
         order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
     }
@@ -632,6 +639,185 @@
     }
 
     /**
+     * The profile name should NOT change between releases for backwards compatibility
+     *
+     * <p>If this is changed between releases, the {@link Vpn#getVpnProfilePrivileged()} method MUST
+     * be updated to ensure backward compatibility.
+     */
+    @Test
+    public void testGetProfileNameForPackage() throws Exception {
+        final Vpn vpn = createVpn(primaryUser.id);
+        setMockedUsers(primaryUser);
+
+        final String expected = Credentials.PLATFORM_VPN + primaryUser.id + "_" + TEST_VPN_PKG;
+        assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG));
+    }
+
+    private Vpn createVpnAndSetupUidChecks(int... grantedOps) throws Exception {
+        final Vpn vpn = createVpn(primaryUser.id);
+        setMockedUsers(primaryUser);
+
+        when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+                .thenReturn(Process.myUid());
+
+        for (final int op : grantedOps) {
+            when(mAppOps.noteOpNoThrow(op, Process.myUid(), TEST_VPN_PKG))
+                    .thenReturn(AppOpsManager.MODE_ALLOWED);
+        }
+
+        return vpn;
+    }
+
+    private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, int... checkedOps) {
+        assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore));
+
+        // The profile should always be stored, whether or not consent has been previously granted.
+        verify(mKeyStore)
+                .put(
+                        eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)),
+                        eq(mVpnProfile.encode()),
+                        eq(Process.SYSTEM_UID),
+                        eq(0));
+
+        for (final int checkedOp : checkedOps) {
+            verify(mAppOps).noteOpNoThrow(checkedOp, Process.myUid(), TEST_VPN_PKG);
+        }
+    }
+
+    @Test
+    public void testProvisionVpnProfilePreconsented() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        checkProvisionVpnProfile(
+                vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+    }
+
+    @Test
+    public void testProvisionVpnProfileNotPreconsented() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks();
+
+        // Expect that both the ACTIVATE_VPN and ACTIVATE_PLATFORM_VPN were tried, but the caller
+        // had neither.
+        checkProvisionVpnProfile(vpn, false /* expectedResult */,
+                AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, AppOpsManager.OP_ACTIVATE_VPN);
+    }
+
+    @Test
+    public void testProvisionVpnProfileVpnServicePreconsented() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_VPN);
+
+        checkProvisionVpnProfile(vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_VPN);
+    }
+
+    @Test
+    public void testProvisionVpnProfileTooLarge() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        final VpnProfile bigProfile = new VpnProfile("");
+        bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]);
+
+        try {
+            vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile, mKeyStore);
+            fail("Expected IAE due to profile size");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testDeleteVpnProfile() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks();
+
+        vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+        verify(mKeyStore)
+                .delete(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)), eq(Process.SYSTEM_UID));
+    }
+
+    @Test
+    public void testGetVpnProfilePrivileged() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks();
+
+        when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+                .thenReturn(new VpnProfile("").encode());
+
+        vpn.getVpnProfilePrivileged(TEST_VPN_PKG, mKeyStore);
+
+        verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+    }
+
+    @Test
+    public void testStartVpnProfile() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+                .thenReturn(mVpnProfile.encode());
+
+        vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+        verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+        verify(mAppOps)
+                .noteOpNoThrow(
+                        eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+                        eq(Process.myUid()),
+                        eq(TEST_VPN_PKG));
+    }
+
+    @Test
+    public void testStartVpnProfileVpnServicePreconsented() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_VPN);
+
+        when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+                .thenReturn(mVpnProfile.encode());
+
+        vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+        // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown.
+        verify(mAppOps).noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG);
+    }
+
+    @Test
+    public void testStartVpnProfileNotConsented() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks();
+
+        try {
+            vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+            fail("Expected failure due to no user consent");
+        } catch (SecurityException expected) {
+        }
+
+        // Verify both appops were checked.
+        verify(mAppOps)
+                .noteOpNoThrow(
+                        eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+                        eq(Process.myUid()),
+                        eq(TEST_VPN_PKG));
+        verify(mAppOps).noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG);
+
+        // Keystore should never have been accessed.
+        verify(mKeyStore, never()).get(any());
+    }
+
+    @Test
+    public void testStartVpnProfileMissingProfile() throws Exception {
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null);
+
+        try {
+            vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+            fail("Expected failure due to missing profile");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        verify(mKeyStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+        verify(mAppOps)
+                .noteOpNoThrow(
+                        eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+                        eq(Process.myUid()),
+                        eq(TEST_VPN_PKG));
+    }
+
+    /**
      * Mock some methods of vpn object.
      */
     private Vpn createVpn(@UserIdInt int userId) {