Merge "Migrate apex/permission to packages/modules/Permission"
diff --git a/METADATA b/METADATA
index d97975c..95577d8 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,4 @@
 third_party {
-  license_type: NOTICE
+  # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
+  license_type: RESTRICTED
 }
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 64ee09c..6afed7a 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -113,7 +113,7 @@
         last_released: {
             api_file: ":android-non-updatable.api.public.latest",
             removed_api_file: ":android-non-updatable-removed.api.public.latest",
-            baseline_file: ":public-api-incompatibilities-with-last-released",
+            baseline_file: ":android-incompatibilities.api.public.latest",
         },
         api_lint: {
             enabled: true,
@@ -165,7 +165,7 @@
         last_released: {
             api_file: ":android-non-updatable.api.system.latest",
             removed_api_file: ":android-non-updatable-removed.api.system.latest",
-            baseline_file: ":system-api-incompatibilities-with-last-released"
+            baseline_file: ":android-incompatibilities.api.system.latest"
         },
         api_lint: {
             enabled: true,
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
new file mode 100644
index 0000000..a1719c9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -0,0 +1 @@
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
diff --git a/apex/Android.bp b/apex/Android.bp
index 0a535a8..c6edc8f 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -96,6 +96,10 @@
         sdk_version: "module_current",
     },
 
+    // installable implies we'll create a non-apex (platform) variant, which
+    // we shouldn't ordinarily need (and it can create issues), so disable that.
+    installable: false,
+
     // Configure framework module specific metalava options.
     droiddoc_options: [mainline_stubs_args],
 
diff --git a/boot/Android.bp b/boot/Android.bp
new file mode 100644
index 0000000..dd4066a
--- /dev/null
+++ b/boot/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+boot_image {
+    name: "framework-boot-image",
+    image_name: "boot",
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index b0534a1..717e3bd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29649,6 +29649,8 @@
     field @Deprecated public static final String RADIO;
     field @Deprecated public static final String SERIAL;
     field @NonNull public static final String SKU;
+    field @NonNull public static final String SOC_MANUFACTURER;
+    field @NonNull public static final String SOC_MODEL;
     field public static final String[] SUPPORTED_32_BIT_ABIS;
     field public static final String[] SUPPORTED_64_BIT_ABIS;
     field public static final String[] SUPPORTED_ABIS;
@@ -36183,9 +36185,10 @@
 package android.security.keystore {
 
   public class BackendBusyException extends java.security.ProviderException {
-    ctor public BackendBusyException();
-    ctor public BackendBusyException(@NonNull String);
-    ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
+    ctor public BackendBusyException(long);
+    ctor public BackendBusyException(long, @NonNull String);
+    ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable);
+    method public long getBackOffHintMillis();
   }
 
   public class KeyExpiredException extends java.security.InvalidKeyException {
@@ -36323,6 +36326,7 @@
     field public static final int ORIGIN_IMPORTED = 2; // 0x2
     field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
     field public static final int ORIGIN_UNKNOWN = 4; // 0x4
+    field public static final int PURPOSE_AGREE_KEY = 64; // 0x40
     field public static final int PURPOSE_DECRYPT = 2; // 0x2
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
@@ -40272,6 +40276,7 @@
     field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
     field public static final int SIGNAL_LOST = -3; // 0xfffffffd
     field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb
+    field public static final int SLICE_REJECTED = 2252; // 0x8cc
     field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888
     field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894
     field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
@@ -41712,6 +41717,14 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.DownloadableSubscription> CREATOR;
   }
 
+  public static final class DownloadableSubscription.Builder {
+    ctor public DownloadableSubscription.Builder(@NonNull android.telephony.euicc.DownloadableSubscription);
+    ctor public DownloadableSubscription.Builder(@NonNull String);
+    method @NonNull public android.telephony.euicc.DownloadableSubscription build();
+    method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(@NonNull String);
+    method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(@NonNull String);
+  }
+
   public final class EuiccInfo implements android.os.Parcelable {
     ctor public EuiccInfo(@Nullable String);
     method public int describeContents();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ea23709..a7c5b74 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -201,6 +201,7 @@
     field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+    field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
     field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
     field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
     field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -10713,6 +10714,7 @@
     method public int getPduSessionId();
     method public int getProtocolType();
     method public long getRetryDurationMillis();
+    method @Nullable public android.telephony.data.SliceInfo getSliceInfo();
     method @Deprecated public int getSuggestedRetryTime();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
@@ -10747,6 +10749,7 @@
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
+    method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
     method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
   }
 
@@ -10819,7 +10822,7 @@
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
-    method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @NonNull android.telephony.data.DataServiceCallback);
+    method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback);
     method public void startHandover(int, @NonNull android.telephony.data.DataServiceCallback);
   }
 
@@ -10867,6 +10870,32 @@
     method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
   }
 
+  public final class SliceInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator();
+    method public int getMappedHplmnSliceServiceType();
+    method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator();
+    method public int getSliceServiceType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SliceInfo> CREATOR;
+    field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe
+    field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff
+    field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
+    field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
+    field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
+    field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
+    field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
+  }
+
+  public static final class SliceInfo.Builder {
+    ctor public SliceInfo.Builder();
+    method @NonNull public android.telephony.data.SliceInfo build();
+    method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
+    method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceServiceType(int);
+    method @NonNull public android.telephony.data.SliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
+    method @NonNull public android.telephony.data.SliceInfo.Builder setSliceServiceType(int);
+  }
+
 }
 
 package android.telephony.euicc {
@@ -10878,12 +10907,8 @@
 
   public static final class DownloadableSubscription.Builder {
     ctor public DownloadableSubscription.Builder();
-    ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription);
-    method public android.telephony.euicc.DownloadableSubscription build();
-    method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>);
-    method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(String);
-    method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(String);
-    method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(String);
+    method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(@NonNull java.util.List<android.telephony.UiccAccessRule>);
+    method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(@NonNull String);
   }
 
   public class EuiccCardManager {
@@ -11753,11 +11778,100 @@
     field public static final String RCS_PROFILE_2_3 = "UP_2.3";
   }
 
+  public final class RcsContactPresenceTuple implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.net.Uri getContactUri();
+    method @Nullable public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities getServiceCapabilities();
+    method @Nullable public String getServiceDescription();
+    method @NonNull public String getServiceId();
+    method @NonNull public String getServiceVersion();
+    method @NonNull public String getStatus();
+    method @Nullable public String getTimestamp();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR;
+    field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+    field public static final String SERVICE_ID_CHATBOT = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+    field public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+    field public static final String SERVICE_ID_CHATBOT_STANDALONE = " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+    field public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+    field public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+    field public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+    field public static final String SERVICE_ID_FT_OVER_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+    field public static final String SERVICE_ID_GEO_PUSH = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+    field public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+    field public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+    field public static final String SERVICE_ID_POST_CALL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+    field public static final String SERVICE_ID_SHARED_MAP = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+    field public static final String SERVICE_ID_SHARED_SKETCH = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+    field public static final String TUPLE_BASIC_STATUS_CLOSED = "closed";
+    field public static final String TUPLE_BASIC_STATUS_OPEN = "open";
+  }
+
+  public static final class RcsContactPresenceTuple.Builder {
+    ctor public RcsContactPresenceTuple.Builder(@NonNull String, @NonNull String, @NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple build();
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String);
+  }
+
+  public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getSupportedDuplexModes();
+    method @NonNull public java.util.List<java.lang.String> getUnsupportedDuplexModes();
+    method public boolean isAudioCapable();
+    method public boolean isVideoCapable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities> CREATOR;
+    field public static final String DUPLEX_MODE_FULL = "full";
+    field public static final String DUPLEX_MODE_HALF = "half";
+    field public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only";
+    field public static final String DUPLEX_MODE_SEND_ONLY = "send-only";
+  }
+
+  public static final class RcsContactPresenceTuple.ServiceCapabilities.Builder {
+    ctor public RcsContactPresenceTuple.ServiceCapabilities.Builder(boolean, boolean);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addSupportedDuplexMode(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addUnsupportedDuplexMode(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities build();
+  }
+
+  public final class RcsContactUceCapability implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCapabilityMechanism();
+    method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String);
+    method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples();
+    method @NonNull public android.net.Uri getContactUri();
+    method public int getRequestResult();
+    method public int getSourceType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CAPABILITY_MECHANISM_OPTIONS = 2; // 0x2
+    field public static final int CAPABILITY_MECHANISM_PRESENCE = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
+    field public static final int REQUEST_RESULT_FOUND = 3; // 0x3
+    field public static final int REQUEST_RESULT_NOT_FOUND = 2; // 0x2
+    field public static final int REQUEST_RESULT_NOT_ONLINE = 1; // 0x1
+    field public static final int REQUEST_RESULT_UNKNOWN = 0; // 0x0
+    field public static final int SOURCE_TYPE_CACHED = 1; // 0x1
+    field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0
+  }
+
+  public static final class RcsContactUceCapability.PresenceBuilder {
+    ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuples(@NonNull java.util.List<android.telephony.ims.RcsContactPresenceTuple>);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+  }
+
   public class RcsUceAdapter {
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
     field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
     field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
     field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
@@ -11770,6 +11884,18 @@
     field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
     field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8
     field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0
+    field public static final int ERROR_FORBIDDEN = 6; // 0x6
+    field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+    field public static final int ERROR_INSUFFICIENT_MEMORY = 10; // 0xa
+    field public static final int ERROR_LOST_NETWORK = 11; // 0xb
+    field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+    field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+    field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+    field public static final int ERROR_NOT_FOUND = 7; // 0x7
+    field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+    field public static final int ERROR_REQUEST_TIMEOUT = 9; // 0x9
+    field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+    field public static final int ERROR_SERVER_UNAVAILABLE = 12; // 0xc
     field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
     field public static final int PUBLISH_STATE_OK = 1; // 0x1
     field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
@@ -11778,6 +11904,12 @@
     field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
   }
 
+  public static interface RcsUceAdapter.CapabilitiesCallback {
+    method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
+    method public void onComplete();
+    method public void onError(int, long);
+  }
+
   public static interface RcsUceAdapter.OnPublishStateChangedListener {
     method public void onPublishStateChange(int);
   }
@@ -12188,6 +12320,7 @@
   public class RcsCapabilityExchangeImplBase {
     ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
     method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
+    method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
     field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
     field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
     field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
@@ -12206,6 +12339,14 @@
     method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
   }
 
+  public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
+    method public void onCommandError(int) throws android.telephony.ims.ImsException;
+    method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+    method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
+    method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
+    method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
+  }
+
   public interface SipDelegate {
     method public void closeDialog(@NonNull String);
     method public void notifyMessageReceiveError(@NonNull String, int);
diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS
new file mode 100644
index 0000000..20c758a
--- /dev/null
+++ b/core/java/android/content/integrity/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 722021
+
+toddke@android.com
+toddke@google.com
+patb@google.com
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index fbca7f1..b07bd68 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -21,6 +21,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -380,7 +381,13 @@
         // Query a property of the underlying socket to ensure that the socket's file descriptor
         // exists, is available to bind to a network and is not closed.
         socket.getReuseAddress();
-        bindSocket(socket.getFileDescriptor$());
+        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+        bindSocket(pfd.getFileDescriptor());
+        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+        // dup share the underlying socket in the kernel. The socket is never truly closed until the
+        // last fd pointing to the socket being closed. So close the dup one after binding the
+        // socket to control the lifetime of the dup fd.
+        pfd.close();
     }
 
     /**
@@ -392,7 +399,13 @@
         // Query a property of the underlying socket to ensure that the socket's file descriptor
         // exists, is available to bind to a network and is not closed.
         socket.getReuseAddress();
-        bindSocket(socket.getFileDescriptor$());
+        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+        bindSocket(pfd.getFileDescriptor());
+        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+        // dup share the underlying socket in the kernel. The socket is never truly closed until the
+        // last fd pointing to the socket being closed. So close the dup one after binding the
+        // socket to control the lifetime of the dup fd.
+        pfd.close();
     }
 
     /**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0a895b9..3843b9a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -204,6 +204,7 @@
             NET_CAPABILITY_TEMPORARILY_NOT_METERED,
             NET_CAPABILITY_OEM_PRIVATE,
             NET_CAPABILITY_VEHICLE_INTERNAL,
+            NET_CAPABILITY_NOT_VCN_MANAGED,
     })
     public @interface NetCapability { }
 
@@ -399,8 +400,16 @@
     @SystemApi
     public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
 
+    /**
+     * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+     *
+     * TODO(b/177299683): Add additional clarifying javadoc.
+     * @hide
+     */
+    public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -417,7 +426,8 @@
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
             | (1 << NET_CAPABILITY_NOT_SUSPENDED)
             | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
-            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -426,16 +436,21 @@
      * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
      * get immediately torn down because they do not have the requested capability.
      */
+    // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+    // are mutable but requestable. Factories are responsible for not getting
+    // in an infinite loop about these.
     private static final long NON_REQUESTABLE_CAPABILITIES =
-            MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+            MUTABLE_CAPABILITIES
+            & ~(1 << NET_CAPABILITY_TRUSTED)
+            & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
 
     /**
      * Capabilities that are set by default when the object is constructed.
      */
     private static final long DEFAULT_CAPABILITIES =
-            (1 << NET_CAPABILITY_NOT_RESTRICTED) |
-            (1 << NET_CAPABILITY_TRUSTED) |
-            (1 << NET_CAPABILITY_NOT_VPN);
+            (1 << NET_CAPABILITY_NOT_RESTRICTED)
+            | (1 << NET_CAPABILITY_TRUSTED)
+            | (1 << NET_CAPABILITY_NOT_VPN);
 
     /**
      * Capabilities that suggest that a network is restricted.
@@ -495,7 +510,8 @@
             | (1 << NET_CAPABILITY_NOT_VPN)
             | (1 << NET_CAPABILITY_NOT_ROAMING)
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
-            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+            | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
 
     /**
      * Adds the given capability to this {@code NetworkCapability} instance.
@@ -1982,6 +1998,7 @@
             case NET_CAPABILITY_TEMPORARILY_NOT_METERED:    return "TEMPORARILY_NOT_METERED";
             case NET_CAPABILITY_OEM_PRIVATE:          return "OEM_PRIVATE";
             case NET_CAPABILITY_VEHICLE_INTERNAL:     return "NET_CAPABILITY_VEHICLE_INTERNAL";
+            case NET_CAPABILITY_NOT_VCN_MANAGED:      return "NOT_VCN_MANAGED";
             default:                                  return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index f0c637c..04011fc 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -353,7 +353,9 @@
          *                         NetworkSpecifier.
          */
         public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
-            MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
+            if (networkSpecifier instanceof MatchAllNetworkSpecifier) {
+                throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+            }
             mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
             return this;
         }
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index a202d77..c9bca28 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -355,7 +355,7 @@
                     port = in.readInt();
                 }
                 String exclList = in.readString();
-                String[] parsedExclList = in.readStringArray();
+                String[] parsedExclList = in.createStringArray();
                 ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
                 return proxyProperties;
             }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1076118..5ae53b5 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -26,6 +26,7 @@
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.sysprop.SocProperties;
 import android.sysprop.TelephonyProperties;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -87,6 +88,14 @@
     /** The end-user-visible name for the end product. */
     public static final String MODEL = getString("ro.product.model");
 
+    /** The manufacturer of the device's primary system-on-chip. */
+    @NonNull
+    public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN);
+
+    /** The model name of the device's primary system-on-chip. */
+    @NonNull
+    public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN);
+
     /** The system bootloader version number. */
     public static final String BOOTLOADER = getString("ro.bootloader");
 
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 017f405..f994d29 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -177,6 +177,7 @@
     public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN;
     public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
     public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
+    public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY;
 
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index e6a1661..beef982 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -129,7 +129,7 @@
     void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
     void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
     void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
-    void noteNetworkInterfaceType(String iface, int type);
+    void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes);
     void noteNetworkStatsEnabled();
     void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
     void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 8bfc28e..ae680e0 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -23,7 +23,6 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.Uid;
 import android.os.Build;
@@ -37,6 +36,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -148,12 +148,11 @@
     boolean mHasBluetoothPowerReporting = false;
 
     public static boolean checkWifiOnly(Context context) {
-        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        if (cm == null) {
+        final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+        if (tm == null) {
             return false;
         }
-        return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        return !tm.isDataCapable();
     }
 
     public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1071d9f..2d75b70 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
 import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
 
@@ -32,7 +34,6 @@
 import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
@@ -101,6 +102,7 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
 
 import libcore.util.EmptyArray;
 
@@ -6099,11 +6101,12 @@
     }
 
     /** @hide */
-    public void noteNetworkInterfaceType(String iface, int networkType) {
+    public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) {
         if (TextUtils.isEmpty(iface)) return;
+        final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes);
 
         synchronized (mModemNetworkLock) {
-            if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
+            if (displayTransport == TRANSPORT_CELLULAR) {
                 mModemIfaces = includeInStringArray(mModemIfaces, iface);
                 if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces);
             } else {
@@ -6113,7 +6116,7 @@
         }
 
         synchronized (mWifiNetworkLock) {
-            if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+            if (displayTransport == TRANSPORT_WIFI) {
                 mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
                 if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
             } else {
diff --git a/core/jni/android_media_AudioErrors.h b/core/jni/android_media_AudioErrors.h
index c17a020..13c9115 100644
--- a/core/jni/android_media_AudioErrors.h
+++ b/core/jni/android_media_AudioErrors.h
@@ -35,7 +35,7 @@
     AUDIO_JAVA_WOULD_BLOCK        = -7,
 };
 
-static inline jint nativeToJavaStatus(status_t status) {
+static constexpr inline jint nativeToJavaStatus(status_t status) {
     switch (status) {
     case NO_ERROR:
         return AUDIO_JAVA_SUCCESS;
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7a5c383..065c79b 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -263,18 +263,7 @@
         return (jint) AUDIO_JAVA_ERROR;
     }
 
-    // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
-    if (tunerConfiguration != nullptr) {
-        const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
-        ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d",
-              tunerHelper.getContentId(), tunerHelper.getSyncId());
-        return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
-    }
-    // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
-    if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) {
-        ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode);
-        return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
-    }
+    const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
 
     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
     if (nSession == NULL) {
@@ -369,6 +358,18 @@
             offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
         }
 
+        if (encapsulationMode != 0) {
+            offloadInfo = AUDIO_INFO_INITIALIZER;
+            offloadInfo.format = format;
+            offloadInfo.sample_rate = sampleRateInHertz;
+            offloadInfo.channel_mask = nativeChannelMask;
+            offloadInfo.stream_type = AUDIO_STREAM_MUSIC;
+            offloadInfo.encapsulation_mode =
+                    static_cast<audio_encapsulation_mode_t>(encapsulationMode);
+            offloadInfo.content_id = tunerHelper.getContentId();
+            offloadInfo.sync_id = tunerHelper.getSyncId();
+        }
+
         // initialize the native AudioTrack object
         status_t status = NO_ERROR;
         switch (memoryMode) {
@@ -389,7 +390,8 @@
                                   sessionId, // audio session ID
                                   offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
                                           : AudioTrack::TRANSFER_SYNC,
-                                  offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values
+                                  (offload || encapsulationMode) ? &offloadInfo : NULL, -1,
+                                  -1, // default uid, pid values
                                   paa.get());
             break;
 
@@ -1364,8 +1366,7 @@
         return (jint)AUDIO_JAVA_ERROR;
     }
 
-    // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
-    return (jint)AUDIO_JAVA_ERROR;
+    return nativeToJavaStatus(lpTrack->setAudioDescriptionMixLevel(level));
 }
 
 static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz,
@@ -1381,12 +1382,10 @@
         return (jint)AUDIO_JAVA_ERROR;
     }
 
-    // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
-    // By contract we can return -infinity if unsupported.
-    *nativeLevel = -std::numeric_limits<float>::infinity();
+    status_t status = lpTrack->getAudioDescriptionMixLevel(reinterpret_cast<float *>(nativeLevel));
     env->ReleasePrimitiveArrayCritical(level, nativeLevel, 0 /* mode */);
-    nativeLevel = nullptr;
-    return (jint)AUDIO_JAVA_SUCCESS;
+
+    return nativeToJavaStatus(status);
 }
 
 static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, jint dualMonoMode) {
@@ -1396,8 +1395,8 @@
         return (jint)AUDIO_JAVA_ERROR;
     }
 
-    // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
-    return (jint)AUDIO_JAVA_ERROR;
+    return nativeToJavaStatus(
+            lpTrack->setDualMonoMode(static_cast<audio_dual_mono_mode_t>(dualMonoMode)));
 }
 
 static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz,
@@ -1407,18 +1406,17 @@
         ALOGE("%s: AudioTrack not initialized", __func__);
         return (jint)AUDIO_JAVA_ERROR;
     }
-    jfloat *nativeDualMonoMode = (jfloat *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL);
+    jint *nativeDualMonoMode = (jint *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL);
     if (nativeDualMonoMode == nullptr) {
         ALOGE("%s: Cannot retrieve dualMonoMode pointer", __func__);
         return (jint)AUDIO_JAVA_ERROR;
     }
 
-    // TODO: replace in r-dev or r-tv-dev with code if HW is able to select dual mono mode.
-    // By contract we can return DUAL_MONO_MODE_OFF if unsupported.
-    *nativeDualMonoMode = 0; // DUAL_MONO_MODE_OFF for now.
+    status_t status = lpTrack->getDualMonoMode(
+            reinterpret_cast<audio_dual_mono_mode_t *>(nativeDualMonoMode));
     env->ReleasePrimitiveArrayCritical(dualMonoMode, nativeDualMonoMode, 0 /* mode */);
-    nativeDualMonoMode = nullptr;
-    return (jint)AUDIO_JAVA_SUCCESS;
+
+    return nativeToJavaStatus(status);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3183ed3..24dabfc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1669,6 +1669,12 @@
     <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
         android:protectionLevel="signature|setup" />
 
+    <!-- Allows applications to restart the Wi-Fi subsystem.
+     @SystemApi
+     <p>Not for use by third-party applications. @hide -->
+    <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+                android:protectionLevel="signature|setup" />
+
     <!-- @SystemApi @hide Allows applications to toggle airplane mode.
          <p>Not for use by third-party or privileged applications.
     -->
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
index 1bcc773..93e8b78 100644
--- a/core/sysprop/WatchdogProperties.sysprop
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -16,7 +16,7 @@
 owner: Platform
 
 # To escape the watchdog timeout loop, fatal reboot the system when
-# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# watchdog timed out 'fatal_count' times in 'fatal_window_seconds'
 # seconds, if both values are not 0. Default value of both is 0.
 prop {
     api_name: "fatal_count"
@@ -26,8 +26,9 @@
     access: Readonly
 }
 
+# See 'fatal_count' for documentation.
 prop {
-    api_name: "fatal_window_second"
+    api_name: "fatal_window_seconds"
     type: Integer
     prop_name: "framework_watchdog.fatal_window.second"
     scope: Internal
@@ -35,9 +36,9 @@
 }
 
 # The fatal counting can be disabled by setting property
-# 'is_fatal_ignore' to true.
+# 'should_ignore_fatal_count' to true.
 prop {
-    api_name: "is_fatal_ignore"
+    api_name: "should_ignore_fatal_count"
     type: Boolean
     prop_name: "persist.debug.framework_watchdog.fatal_ignore"
     scope: Internal
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
index d901aef..c846211 100644
--- a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -7,13 +7,13 @@
     prop_name: "framework_watchdog.fatal_count"
   }
   prop {
-    api_name: "fatal_window_second"
+    api_name: "fatal_window_seconds"
     type: Integer
     scope: Internal
     prop_name: "framework_watchdog.fatal_window.second"
   }
   prop {
-    api_name: "is_fatal_ignore"
+    api_name: "should_ignore_fatal_count"
     scope: Internal
     prop_name: "persist.debug.framework_watchdog.fatal_ignore"
   }
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 912db1e..c61a4b5 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1,3 +1,4 @@
+per-file AssetTest.java = file:/core/java/android/content/res/OWNERS
 per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
 per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
diff --git a/core/tests/coretests/src/android/content/integrity/OWNERS b/core/tests/coretests/src/android/content/integrity/OWNERS
new file mode 100644
index 0000000..4ffc704
--- /dev/null
+++ b/core/tests/coretests/src/android/content/integrity/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/integrity/OWNERS
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 7b76706..8673365 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,3 +1,5 @@
+include /core/java/android/content/pm/OWNERS
+
 per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-per-file SigningDetailsTest.java = mpgroover@google.com
 per-file SigningDetailsTest.java = cbrubaker@google.com
+per-file SigningDetailsTest.java = mpgroover@google.com
diff --git a/core/tests/coretests/src/android/content/res/OWNERS b/core/tests/coretests/src/android/content/res/OWNERS
new file mode 100644
index 0000000..3e79d8f
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/res/OWNERS
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a185da1..30501fb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -344,6 +344,7 @@
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.MOVE_PACKAGE"/>
         <!-- Needed for test only -->
+        <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
         <permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
         <permission name="android.permission.OBSERVE_APP_USAGE"/>
         <permission name="android.permission.NETWORK_SCAN"/>
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 92d87aa..f7477bf 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.security.keymaster.KeymasterDefs;
 import android.system.keystore2.IKeystoreService;
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyEntryResponse;
@@ -107,7 +108,7 @@
                 return request.execute(service);
             } catch (ServiceSpecificException e) {
                 Log.e(TAG, "KeyStore exception", e);
-                throw new KeyStoreException(e.errorCode, "");
+                throw getKeyStoreException(e.errorCode);
             } catch (RemoteException e) {
                 if (firstTry) {
                     Log.w(TAG, "Looks like we may have lost connection to the Keystore "
@@ -274,4 +275,40 @@
         }
     }
 
+    static KeyStoreException getKeyStoreException(int errorCode) {
+        if (errorCode > 0) {
+            // KeyStore layer error
+            switch (errorCode) {
+                case ResponseCode.LOCKED:
+                    return new KeyStoreException(errorCode, "User authentication required");
+                case ResponseCode.UNINITIALIZED:
+                    return new KeyStoreException(errorCode, "Keystore not initialized");
+                case ResponseCode.SYSTEM_ERROR:
+                    return new KeyStoreException(errorCode, "System error");
+                case ResponseCode.PERMISSION_DENIED:
+                    return new KeyStoreException(errorCode, "Permission denied");
+                case ResponseCode.KEY_NOT_FOUND:
+                    return new KeyStoreException(errorCode, "Key not found");
+                case ResponseCode.VALUE_CORRUPTED:
+                    return new KeyStoreException(errorCode, "Key blob corrupted");
+                case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+                    return new KeyStoreException(errorCode, "Key permanently invalidated");
+                default:
+                    return new KeyStoreException(errorCode, String.valueOf(errorCode));
+            }
+        } else {
+            // Keymaster layer error
+            switch (errorCode) {
+                case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
+                    // The name of this parameter significantly differs between Keymaster and
+                    // framework APIs. Use the framework wording to make life easier for developers.
+                    return new KeyStoreException(errorCode,
+                            "Invalid user authentication validity duration");
+                default:
+                    return new KeyStoreException(errorCode,
+                            KeymasterDefs.getErrorMessage(errorCode));
+            }
+        }
+    }
+
 }
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 7ea9e14..a6552dd 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -73,8 +73,7 @@
                     );
                 }
                 default:
-                    // TODO Human readable string. Use something like KeyStore.getKeyStoreException
-                    throw new KeyStoreException(e.errorCode, "");
+                    throw KeyStore2.getKeyStoreException(e.errorCode);
             }
         } catch (RemoteException e) {
             // Log exception and report invalid operation handle.
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 3ef4aa5..372add9 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -52,7 +52,7 @@
         try {
             return request.execute();
         } catch (ServiceSpecificException e) {
-            throw new KeyStoreException(e.errorCode, "");
+            throw KeyStore2.getKeyStoreException(e.errorCode);
         } catch (RemoteException e) {
             // Log exception and report invalid operation handle.
             // This should prompt the caller drop the reference to this operation and retry.
@@ -96,25 +96,26 @@
             } catch (ServiceSpecificException e) {
                 switch (e.errorCode) {
                     case ResponseCode.BACKEND_BUSY: {
+                        long backOffHint = (long) (Math.random() * 80 + 20);
                         if (CompatChanges.isChangeEnabled(
                                 KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
                             // Starting with Android S we inform the caller about the
                             // backend being busy.
-                            throw new BackendBusyException();
+                            throw new BackendBusyException(backOffHint);
                         } else {
                             // Before Android S operation creation must always succeed. So we
                             // just have to retry. We do so with a randomized back-off between
-                            // 50 and 250ms.
+                            // 20 and 100ms.
                             // It is a little awkward that we cannot break out of this loop
                             // by interrupting this thread. But that is the expected behavior.
                             // There is some comfort in the fact that interrupting a thread
                             // also does not unblock a thread waiting for a binder transaction.
-                            interruptedPreservingSleep((long) (Math.random() * 200 + 50));
+                            interruptedPreservingSleep(backOffHint);
                         }
                         break;
                     }
                     default:
-                        throw new KeyStoreException(e.errorCode, "");
+                        throw KeyStore2.getKeyStoreException(e.errorCode);
                 }
             } catch (RemoteException e) {
                 Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java
index 1a88469..a813e93 100644
--- a/keystore/java/android/security/keystore/BackendBusyException.java
+++ b/keystore/java/android/security/keystore/BackendBusyException.java
@@ -16,37 +16,66 @@
 
 package android.security.keystore;
 
+import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 
 import java.security.ProviderException;
 
 /**
  * Indicates a transient error that prevented a key operation from being created.
- * Callers should try again with a back-off period of 10-30 milliseconds.
+ * Callers should try again with a back-off period of {@link #getBackOffHintMillis()}
+ * milliseconds.
  */
 public class BackendBusyException extends ProviderException {
 
+    private final long mBackOffHintMillis;
+
     /**
      * Constructs a new {@code BackendBusyException} without detail message and cause.
+     *
      */
-    public BackendBusyException() {
+    public BackendBusyException(@DurationMillisLong long backOffHintMillis) {
         super("The keystore backend has no operation slots available. Retry later.");
+        if (backOffHintMillis < 0) {
+            throw new IllegalArgumentException("Back-off hint cannot be negative.");
+        }
+        mBackOffHintMillis = backOffHintMillis;
     }
 
     /**
      * Constructs a new {@code BackendBusyException} with the provided detail message and
      * no cause.
      */
-    public BackendBusyException(@NonNull String message) {
+    public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+            @NonNull String message) {
         super(message);
+        if (backOffHintMillis < 0) {
+            throw new IllegalArgumentException("Back-off hint cannot be negative.");
+        }
+        mBackOffHintMillis = backOffHintMillis;
     }
 
     /**
      * Constructs a new {@code BackendBusyException} with the provided detail message and
      * cause.
      */
-    public BackendBusyException(@NonNull String message, @NonNull Throwable cause) {
+    public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+            @NonNull String message, @NonNull Throwable cause) {
         super(message, cause);
+        if (backOffHintMillis < 0) {
+            throw new IllegalArgumentException("Back-off hint cannot be negative.");
+        }
+        mBackOffHintMillis = backOffHintMillis;
     }
 
+    /**
+     * When retrying to start a Keystore operation after receiving this exception, this can be
+     * used to determine how long to wait before retrying. It is not guaranteed that the operation
+     * will succeeds after this time. Multiple retries may be necessary if the system is congested.
+     *
+     * @return Number of milliseconds to back off before retrying.
+     */
+    public @DurationMillisLong long getBackOffHintMillis() {
+        return mBackOffHintMillis;
+    }
 }
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 5928540..014d688 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -67,6 +67,7 @@
             PURPOSE_SIGN,
             PURPOSE_VERIFY,
             PURPOSE_WRAP_KEY,
+            PURPOSE_AGREE_KEY,
     })
     public @interface PurposeEnum {}
 
@@ -96,6 +97,11 @@
     public static final int PURPOSE_WRAP_KEY = 1 << 5;
 
     /**
+     * Purpose of key: creating a shared ECDH secret through key agreement.
+     */
+    public static final int PURPOSE_AGREE_KEY = 1 << 6;
+
+    /**
      * @hide
      */
     public static abstract class Purpose {
@@ -113,6 +119,8 @@
                     return KeymasterDefs.KM_PURPOSE_VERIFY;
                 case PURPOSE_WRAP_KEY:
                     return KeymasterDefs.KM_PURPOSE_WRAP;
+                case PURPOSE_AGREE_KEY:
+                    return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
@@ -130,6 +138,8 @@
                     return PURPOSE_VERIFY;
                 case KeymasterDefs.KM_PURPOSE_WRAP:
                     return PURPOSE_WRAP_KEY;
+                case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
+                    return PURPOSE_AGREE_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
new file mode 100644
index 0000000..fc963a8
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.Tag;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keystore.KeyStoreCryptoOperation;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi
+        implements KeyStoreCryptoOperation {
+
+    private static final String TAG = "AndroidKeyStoreKeyAgreementSpi";
+
+    /**
+     * ECDH implementation.
+     *
+     * @hide
+     */
+    public static class ECDH extends AndroidKeyStoreKeyAgreementSpi {
+        public ECDH() {
+            super(Algorithm.EC);
+        }
+    }
+
+    private final int mKeymintAlgorithm;
+
+    // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+    private AndroidKeyStorePrivateKey mKey;
+    private PublicKey mOtherPartyKey;
+
+    // Fields below are reset when engineDoFinal succeeds.
+    private KeyStoreOperation mOperation;
+    private long mOperationHandle;
+
+    protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) {
+        resetAll();
+
+        mKeymintAlgorithm = keymintAlgorithm;
+    }
+
+    @Override
+    protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+        resetAll();
+
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof AndroidKeyStorePrivateKey)) {
+            throw new InvalidKeyException(
+                    "Only Android KeyStore private keys supported. Key: " + key);
+        }
+        // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in
+        // ensureKeystoreOperationInitialized() below.
+        mKey = (AndroidKeyStorePrivateKey) key;
+
+        boolean success = false;
+        try {
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported algorithm parameters: " + params);
+        }
+        engineInit(key, random);
+    }
+
+    @Override
+    protected Key engineDoPhase(Key key, boolean lastPhase)
+            throws InvalidKeyException, IllegalStateException {
+        ensureKeystoreOperationInitialized();
+
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof PublicKey)) {
+            throw new InvalidKeyException("Only public keys supported. Key: " + key);
+        } else if (!lastPhase) {
+            throw new IllegalStateException(
+                    "Only one other party supported. lastPhase must be set to true.");
+        } else if (mOtherPartyKey != null) {
+            throw new IllegalStateException(
+                    "Only one other party supported. doPhase() must only be called exactly once.");
+        }
+        // The other party key will be passed as part of the doFinal() call, to prevent an
+        // additional IPC.
+        mOtherPartyKey = (PublicKey) key;
+
+        return null; // No intermediate key
+    }
+
+    @Override
+    protected byte[] engineGenerateSecret() throws IllegalStateException {
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new IllegalStateException("Not initialized", e);
+        }
+
+        if (mOtherPartyKey == null) {
+            throw new IllegalStateException("Other party key not provided. Call doPhase() first.");
+        }
+        byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+
+        try {
+            return mOperation.finish(otherPartyKeyEncoded, null);
+        } catch (KeyStoreException e) {
+            throw new ProviderException("Keystore operation failed", e);
+        } finally {
+            resetWhilePreservingInitState();
+        }
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(String algorithm)
+            throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException {
+        byte[] generatedSecret = engineGenerateSecret();
+
+        return new SecretKeySpec(generatedSecret, algorithm);
+    }
+
+    @Override
+    protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+            throws IllegalStateException, ShortBufferException {
+        byte[] generatedSecret = engineGenerateSecret();
+
+        if (generatedSecret.length > sharedSecret.length - offset) {
+            throw new ShortBufferException("Needed: " + generatedSecret.length);
+        }
+        System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length);
+        return generatedSecret.length;
+    }
+
+    @Override
+    public long getOperationHandle() {
+        return mOperationHandle;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            resetAll();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void resetWhilePreservingInitState() {
+        KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+        mOperationHandle = 0;
+        mOperation = null;
+        mOtherPartyKey = null;
+    }
+
+    private void resetAll() {
+        resetWhilePreservingInitState();
+        mKey = null;
+    }
+
+    private void ensureKeystoreOperationInitialized()
+            throws InvalidKeyException, IllegalStateException {
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        if (mOperation != null) {
+            return;
+        }
+
+        // We don't need to explicitly pass in any other parameters here, as they're part of the
+        // private key that is available to Keymint.
+        List<KeyParameter> parameters = new ArrayList<>();
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                Tag.PURPOSE, KeyPurpose.AGREE_KEY
+        ));
+
+        try {
+            mOperation =
+                    mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
+        } catch (KeyStoreException keyStoreException) {
+            // If necessary, throw an exception due to KeyStore operation having failed.
+            InvalidKeyException e =
+                    KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException);
+            if (e != null) {
+                throw e;
+            }
+        }
+
+        // Set the operation handle. This will be a random number, or the operation challenge if
+        // user authentication is required. If we got a challenge we check if the authorization can
+        // possibly succeed.
+        mOperationHandle =
+                KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey);
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 403da18..164bc86 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -94,6 +94,9 @@
             put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
         }
 
+        // javax.crypto.KeyAgreement
+        put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
+
         // java.security.SecretKeyFactory
         putSecretKeyFactoryImpl("AES");
         if (supports3DES) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7dff0c2..d22e97c 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5434,8 +5434,12 @@
     public boolean setAdditionalOutputDeviceDelay(
             @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) {
         Objects.requireNonNull(device);
-        // Implement the setter in r-dev or r-tv-dev as needed.
-        return false;
+        try {
+            return getService().setAdditionalOutputDeviceDelay(
+                new AudioDeviceAttributes(device), delayMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -5450,8 +5454,11 @@
     @IntRange(from = 0)
     public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
         Objects.requireNonNull(device);
-        // Implement the getter in r-dev or r-tv-dev as needed.
-        return 0;
+        try {
+            return getService().getAdditionalOutputDeviceDelay(new AudioDeviceAttributes(device));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -5468,8 +5475,12 @@
     @IntRange(from = 0)
     public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
         Objects.requireNonNull(device);
-        // Implement the getter in r-dev or r-tv-dev as needed.
-        return 0;
+        try {
+            return getService().getMaxAdditionalOutputDeviceDelay(
+                    new AudioDeviceAttributes(device));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b2c2c4b..d7ef454 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1269,10 +1269,12 @@
             // native code figure out the minimum buffer size.
             if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) {
                 int bytesPerSample = 1;
-                try {
-                    bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding());
-                } catch (IllegalArgumentException e) {
-                    // do nothing
+                if (AudioFormat.isEncodingLinearFrames(mFormat.getEncoding())) {
+                    try {
+                        bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding());
+                    } catch (IllegalArgumentException e) {
+                        // do nothing
+                    }
                 }
                 mBufferSizeInBytes = mFormat.getChannelCount() * bytesPerSample;
             }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ebaa316..ed48b56 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -330,4 +330,10 @@
 
     oneway void unregisterCommunicationDeviceDispatcher(
             ICommunicationDeviceDispatcher dispatcher);
+
+    boolean setAdditionalOutputDeviceDelay(in AudioDeviceAttributes device, long delayMillis);
+
+    long getAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
+
+    long getMaxAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5daf8b0..694b939 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -169,8 +169,9 @@
 static int IP_V4_LENGTH = 4;
 static int IP_V6_LENGTH = 16;
 
-void DestroyCallback(const C2Buffer * /* buf */, void *arg) {
+void DestroyCallback(const C2Buffer * buf, void *arg) {
     android::sp<android::MediaEvent> event = (android::MediaEvent *)arg;
+    android::Mutex::Autolock autoLock(event->mLock);
     if (event->mLinearBlockObj != NULL) {
         JNIEnv *env = android::AndroidRuntime::getJNIEnv();
         env->DeleteWeakGlobalRef(event->mLinearBlockObj);
@@ -179,6 +180,7 @@
 
     event->mAvHandleRefCnt--;
     event->finalize();
+    event->decStrong(buf);
 }
 
 namespace android {
@@ -369,6 +371,7 @@
             pC2Buffer->setInfo(info);
         }
         pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+        incStrong(pC2Buffer.get());
         jobject linearBlock =
                 env->NewObject(
                         env->FindClass("android/media/MediaCodec$LinearBlock"),
@@ -3646,6 +3649,7 @@
         ALOGD("Failed get MediaEvent");
         return NULL;
     }
+    android::Mutex::Autolock autoLock(mediaEventSp->mLock);
 
     return mediaEventSp->getLinearBlock();
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4b6862c..dcbad30 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -292,6 +292,9 @@
     <!-- Permission needed to test mainline permission module rollback -->
     <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
 
+    <!-- Permission needed to restart WiFi Subsystem -->
+    <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" />
+
     <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
     <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
 
diff --git a/services/Android.bp b/services/Android.bp
index 785ca35..a13dbe6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -139,7 +139,7 @@
         last_released: {
             api_file: ":android.api.system-server.latest",
             removed_api_file: ":removed.api.system-server.latest",
-            baseline_file: ":system-server-api-incompatibilities-with-last-released"
+            baseline_file: ":android-incompatibilities.api.system-server.latest"
         },
         api_lint: {
             enabled: true,
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5ad5805..4bebe39 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -84,6 +84,7 @@
         ":storaged_aidl",
         ":vold_aidl",
         ":platform-compat-config",
+        ":platform-compat-overrides",
         ":display-device-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74a6e07..d129b9c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5658,7 +5658,9 @@
         if (ns == null) {
             return;
         }
-        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
+        if (ns instanceof MatchAllNetworkSpecifier) {
+            throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+        }
     }
 
     private void ensureValid(NetworkCapabilities nc) {
@@ -6194,7 +6196,7 @@
         nai.networkAgentPortalData = lp.getCaptivePortalData();
     }
 
-    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+    private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
             @NonNull LinkProperties oldLp) {
         int netId = networkAgent.network.getNetId();
 
@@ -6203,8 +6205,7 @@
         // the LinkProperties for the network are accurate.
         networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
 
-        updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
-                networkAgent.networkInfo.getType());
+        updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
 
         // update filtering rules, need to happen after the interface update so netd knows about the
         // new interface (the interface name -> index map becomes initialized)
@@ -6343,7 +6344,7 @@
 
     private void updateInterfaces(final @Nullable LinkProperties newLp,
             final @Nullable LinkProperties oldLp, final int netId,
-            final @Nullable NetworkCapabilities caps, final int legacyType) {
+            final @NonNull NetworkCapabilities caps) {
         final CompareResult<String> interfaceDiff = new CompareResult<>(
                 oldLp != null ? oldLp.getAllInterfaceNames() : null,
                 newLp != null ? newLp.getAllInterfaceNames() : null);
@@ -6354,7 +6355,7 @@
                     if (DBG) log("Adding iface " + iface + " to network " + netId);
                     mNetd.networkAddInterface(netId, iface);
                     wakeupModifyInterface(iface, caps, true);
-                    bs.noteNetworkInterfaceType(iface, legacyType);
+                    bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes());
                 } catch (Exception e) {
                     loge("Exception adding interface: " + e);
                 }
@@ -6626,6 +6627,7 @@
      * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
      * and foreground status).
      */
+    @NonNull
     private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
         // Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
          // Don't complain for VPNs since they're not driven by requests and there is no risk of
@@ -6682,6 +6684,25 @@
         return newNc;
     }
 
+    private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai,
+            NetworkCapabilities prevNc, NetworkCapabilities newNc) {
+        final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        if (prevSuspended != suspended) {
+            // TODO (b/73132094) : remove this call once the few users of onSuspended and
+            // onResumed have been removed.
+            notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+                    : ConnectivityManager.CALLBACK_RESUMED);
+        }
+        if (prevSuspended != suspended || prevRoaming != roaming) {
+            // updateNetworkInfo will mix in the suspended info from the capabilities and
+            // take appropriate action for the network having possibly changed state.
+            updateNetworkInfo(nai, nai.networkInfo);
+        }
+    }
+
     /**
      * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
      *
@@ -6713,25 +6734,13 @@
             // on this network. We might have been called by rematchNetworkAndRequests when a
             // network changed foreground state.
             processListenRequests(nai);
-            final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
-            final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
-            final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-            final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-            if (prevSuspended != suspended || prevRoaming != roaming) {
-                // TODO (b/73132094) : remove this call once the few users of onSuspended and
-                // onResumed have been removed.
-                notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
-                        : ConnectivityManager.CALLBACK_RESUMED);
-                // updateNetworkInfo will mix in the suspended info from the capabilities and
-                // take appropriate action for the network having possibly changed state.
-                updateNetworkInfo(nai, nai.networkInfo);
-            }
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
             rematchAllNetworksAndRequests();
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
+        updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
 
         final boolean oldMetered = prevNc.isMetered();
         final boolean newMetered = newNc.isMetered();
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e5..a08d066 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -242,6 +242,7 @@
         nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
         nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
         nc.setAdministratorUids(administratorUids);
         if (!isMetered) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 630548d..ab24015 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -704,7 +704,7 @@
                 WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
                 Slog.w(TAG, "*** GOODBYE!");
                 if (!Build.IS_USER && isCrashLoopFound()
-                        && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+                        && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {
                     breakCrashLoop();
                 }
                 Process.killProcess(Process.myPid());
@@ -783,7 +783,7 @@
     private boolean isCrashLoopFound() {
         int fatalCount = WatchdogProperties.fatal_count().orElse(0);
         long fatalWindowMs = TimeUnit.SECONDS.toMillis(
-                WatchdogProperties.fatal_window_second().orElse(0));
+                WatchdogProperties.fatal_window_seconds().orElse(0));
         if (fatalCount == 0 || fatalWindowMs == 0) {
             if (fatalCount != fatalWindowMs) {
                 Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3c53880..9986085 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1066,9 +1066,9 @@
     }
 
     @Override
-    public void noteNetworkInterfaceType(String iface, int networkType) {
+    public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) {
         enforceCallingPermission();
-        mStats.noteNetworkInterfaceType(iface, networkType);
+        mStats.noteNetworkInterfaceForTransports(iface, transportTypes);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 024dca7..4c69704 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -32,6 +32,7 @@
 
 import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -166,6 +167,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -173,6 +175,7 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
 import java.util.stream.Collectors;
 
 /**
@@ -563,6 +566,117 @@
 
     private boolean mDockAudioMediaEnabled = true;
 
+    /**
+     * RestorableParameters is a thread-safe class used to store a
+     * first-in first-out history of parameters for replay / restoration.
+     *
+     * The idealized implementation of restoration would have a list of setting methods and
+     * values to be called for restoration.  Explicitly managing such setters and
+     * values would be tedious - a simpler method is to store the values and the
+     * method implicitly by lambda capture (the values must be immutable or synchronization
+     * needs to be taken).
+     *
+     * We provide queueRestoreWithRemovalIfTrue() to allow
+     * the caller to provide a BooleanSupplier lambda, which conveniently packages
+     * the setter and its parameters needed for restoration.  If during restoration,
+     * the BooleanSupplier returns true, it is removed from the mMap.
+     *
+     * We provide a setParameters() method as an example helper method.
+     */
+    private static class RestorableParameters {
+        /**
+         * Sets a parameter and queues for restoration if successful.
+         *
+         * @param id a string handle associated with this parameter.
+         * @param parameter the actual parameter string.
+         * @return the result of AudioSystem.setParameters
+         */
+        public int setParameters(@NonNull String id, @NonNull String parameter) {
+            Objects.requireNonNull(id, "id must not be null");
+            Objects.requireNonNull(parameter, "parameter must not be null");
+            synchronized (mMap) {
+                final int status = AudioSystem.setParameters(parameter);
+                if (status == AudioSystem.AUDIO_STATUS_OK) { // Java uses recursive mutexes.
+                    queueRestoreWithRemovalIfTrue(id, () -> { // remove me if set fails.
+                        return AudioSystem.setParameters(parameter) != AudioSystem.AUDIO_STATUS_OK;
+                    });
+                }
+                // Implementation detail: We do not mMap.remove(id); on failure.
+                return status;
+            }
+        }
+
+        /**
+         * Queues a restore method which is executed on restoreAll().
+         *
+         * If the supplier null, the id is removed from the restore map.
+         *
+         * Note: When the BooleanSupplier restore method is executed
+         * during restoreAll, if it returns true, it is removed from the
+         * restore map.
+         *
+         * @param id a unique tag associated with the restore method.
+         * @param supplier is a BooleanSupplier lambda.
+         */
+        public void queueRestoreWithRemovalIfTrue(
+                @NonNull String id, @Nullable BooleanSupplier supplier) {
+            Objects.requireNonNull(id, "id must not be null");
+            synchronized (mMap) {
+                if (supplier != null) {
+                    mMap.put(id, supplier);
+                } else {
+                    mMap.remove(id);
+                }
+            }
+        }
+
+        /**
+         * Restore all parameters
+         *
+         * During restoration after audioserver death, any BooleanSupplier that returns
+         * true will be removed from mMap.
+         */
+        public void restoreAll() {
+            synchronized (mMap) {
+                // Note: removing from values() also removes from the backing map.
+                // TODO: Consider catching exceptions?
+                mMap.values().removeIf(v -> {
+                    return v.getAsBoolean();
+                });
+            }
+        }
+
+        /**
+         * mMap is a LinkedHashMap<Key, Value> of parameters restored by restore().
+         * The Key is a unique id tag for identification.
+         * The Value is a lambda expression which returns true if the entry is to
+         *     be removed.
+         *
+         * 1) For memory limitation purposes, mMap keeps the latest MAX_ENTRIES
+         *    accessed in the map.
+         * 2) Parameters are restored in order of queuing, first in first out,
+         *    from earliest to latest.
+         */
+        @GuardedBy("mMap")
+        private Map</* @NonNull */ String, /* @NonNull */ BooleanSupplier> mMap =
+                new LinkedHashMap<>() {
+            // TODO: do we need this memory limitation?
+            private static final int MAX_ENTRIES = 1000;  // limit our memory for now.
+            @Override
+            protected boolean removeEldestEntry(Map.Entry eldest) {
+                if (size() <= MAX_ENTRIES) return false;
+                Log.w(TAG, "Parameter map exceeds "
+                        + MAX_ENTRIES + " removing " + eldest.getKey()); // don't silently remove.
+                return true;
+            }
+        };
+    }
+
+    // We currently have one instance for mRestorableParameters used for
+    // setAdditionalOutputDeviceDelay().  Other methods requiring restoration could share this
+    // or use their own instance.
+    private RestorableParameters mRestorableParameters = new RestorableParameters();
+
     private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
     // Used when safe volume warning message display is requested by setStreamVolume(). In this
@@ -1095,6 +1209,9 @@
             RotationHelper.updateOrientation();
         }
 
+        // Restore setParameters and other queued setters.
+        mRestorableParameters.restoreAll();
+
         synchronized (mSettingsLock) {
             final int forDock = mDockAudioMediaEnabled ?
                     AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE;
@@ -9303,6 +9420,95 @@
         }
     }
 
+    /**
+     * @hide
+     * Sets an additional audio output device delay in milliseconds.
+     *
+     * The additional output delay is a request to the output device to
+     * delay audio presentation (generally with respect to video presentation for better
+     * synchronization).
+     * It may not be supported by all output devices,
+     * and typically increases the audio latency by the amount of additional
+     * audio delay requested.
+     *
+     * If additional audio delay is supported by an audio output device,
+     * it is expected to be supported for all output streams (and configurations)
+     * opened on that device.
+     *
+     * @param deviceType
+     * @param address
+     * @param delayMillis delay in milliseconds desired.  This should be in range of {@code 0}
+     *     to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}.
+     * @return true if successful, false if the device does not support output device delay
+     *     or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
+     */
+    @Override
+    //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean setAdditionalOutputDeviceDelay(
+            @NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
+        Objects.requireNonNull(device, "device must not be null");
+        enforceModifyAudioRoutingPermission();
+        final String getterKey = "additional_output_device_delay="
+                 + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType())
+                 + "," + device.getAddress(); // "getter" key as an id.
+        final String setterKey = getterKey + "," + delayMillis;     // append the delay for setter
+        return mRestorableParameters.setParameters(getterKey, setterKey)
+                == AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    /**
+     * @hide
+     * Returns the current additional audio output device delay in milliseconds.
+     *
+     * @param deviceType
+     * @param address
+     * @return the additional output device delay. This is a non-negative number.
+     *     {@code 0} is returned if unsupported.
+     */
+    @Override
+    @IntRange(from = 0)
+    public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
+        Objects.requireNonNull(device, "device must not be null");
+        final String key = "additional_output_device_delay";
+        final String reply = AudioSystem.getParameters(
+                key + "=" + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType())
+                    + "," + device.getAddress());
+        long delayMillis;
+        try {
+            delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+        } catch (NullPointerException e) {
+            delayMillis = 0;
+        }
+        return delayMillis;
+    }
+
+    /**
+     * @hide
+     * Returns the maximum additional audio output device delay in milliseconds.
+     *
+     * @param deviceType
+     * @param address
+     * @return the maximum output device delay in milliseconds that can be set.
+     *     This is a non-negative number
+     *     representing the additional audio delay supported for the device.
+     *     {@code 0} is returned if unsupported.
+     */
+    @Override
+    @IntRange(from = 0)
+    public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
+        Objects.requireNonNull(device, "device must not be null");
+        final String key = "max_additional_output_device_delay";
+        final String reply = AudioSystem.getParameters(
+                key + "=" + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType())
+                    + "," + device.getAddress());
+        long delayMillis;
+        try {
+            delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+        } catch (NullPointerException e) {
+            delayMillis = 0;
+        }
+        return delayMillis;
+    }
 
     //======================
     // misc
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 9ba957e..e3757df 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,8 +23,11 @@
 
 import com.android.internal.compat.CompatibilityChangeInfo;
 import com.android.server.compat.config.Change;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.OverrideValue;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -253,6 +256,71 @@
         return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
     }
 
+    /**
+     * Checks whether a change has any package overrides.
+     * @return true if the change has at least one deferred override
+     */
+    boolean hasAnyPackageOverride() {
+        return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
+    }
+
+    /**
+     * Checks whether a change has any deferred overrides.
+     * @return true if the change has at least one deferred override
+     */
+    boolean hasAnyDeferredOverride() {
+        return mPackageOverrides != null && !mPackageOverrides.isEmpty();
+    }
+
+    void loadOverrides(ChangeOverrides changeOverrides) {
+        if (mDeferredOverrides == null) {
+            mDeferredOverrides = new HashMap<>();
+        }
+        mDeferredOverrides.clear();
+        for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
+            mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
+        }
+
+        if (mPackageOverrides == null) {
+            mPackageOverrides = new HashMap<>();
+        }
+        mPackageOverrides.clear();
+        for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
+            mPackageOverrides.put(override.getPackageName(), override.getEnabled());
+        }
+    }
+
+    ChangeOverrides saveOverrides() {
+        if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
+            return null;
+        }
+        ChangeOverrides changeOverrides = new ChangeOverrides();
+        changeOverrides.setChangeId(getId());
+        ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
+        List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
+        if (mDeferredOverrides != null) {
+            for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
+                OverrideValue override = new OverrideValue();
+                override.setPackageName(entry.getKey());
+                override.setEnabled(entry.getValue());
+                deferredList.add(override);
+            }
+        }
+        changeOverrides.setDeferred(deferredOverrides);
+        ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
+        List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
+        if (mPackageOverrides != null) {
+            for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) {
+                OverrideValue override = new OverrideValue();
+                override.setPackageName(entry.getKey());
+                override.setEnabled(entry.getValue());
+                validatedList.add(override);
+            }
+        }
+        changeOverrides.setValidated(validatedOverrides);
+        return changeOverrides;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 69686a2..6b77b9d 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -34,7 +34,10 @@
 import com.android.internal.compat.IOverrideValidator;
 import com.android.internal.compat.OverrideAllowedState;
 import com.android.server.compat.config.Change;
-import com.android.server.compat.config.XmlParser;
+import com.android.server.compat.config.Config;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.Overrides;
+import com.android.server.compat.overrides.XmlWriter;
 import com.android.server.pm.ApexManager;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +63,14 @@
 final class CompatConfig {
 
     private static final String TAG = "CompatConfig";
+    private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+    private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
 
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
     private final OverrideValidatorImpl mOverrideValidator;
+    private File mOverridesFile;
 
     @VisibleForTesting
     CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -83,6 +89,8 @@
             config.initConfigFromLib(Environment.buildPath(
                     apex.apexDirectory, "etc", "compatconfig"));
         }
+        File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
+        config.initOverrides(overridesFile);
         config.invalidateCache();
         return config;
     }
@@ -202,6 +210,17 @@
      * @throws IllegalStateException if overriding is not allowed
      */
     boolean addOverride(long changeId, String packageName, boolean enabled) {
+        boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
+        saveOverrides();
+        invalidateCache();
+        return alreadyKnown;
+    }
+
+    /**
+     * Unsafe version of {@link #addOverride(long, String, boolean)}.
+     * It does not invalidate the cache nor save the overrides.
+     */
+    private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
         boolean alreadyKnown = true;
         OverrideAllowedState allowedState =
                 mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -224,7 +243,6 @@
                     throw new IllegalStateException("Should only be able to override changes that "
                             + "are allowed or can be deferred.");
             }
-            invalidateCache();
         }
         return alreadyKnown;
     }
@@ -282,6 +300,17 @@
      * @return {@code true} if an override existed;
      */
     boolean removeOverride(long changeId, String packageName) {
+        boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
+        saveOverrides();
+        invalidateCache();
+        return overrideExists;
+    }
+
+    /**
+     * Unsafe version of {@link #removeOverride(long, String)}.
+     * It does not invalidate the cache nor save the overrides.
+     */
+    private boolean removeOverrideUnsafe(long changeId, String packageName) {
         boolean overrideExists = false;
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
@@ -300,7 +329,6 @@
                 }
             }
         }
-        invalidateCache();
         return overrideExists;
     }
 
@@ -315,12 +343,13 @@
     void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
         synchronized (mChanges) {
             for (Long changeId : overrides.enabledChanges()) {
-                addOverride(changeId, packageName, true);
+                addOverrideUnsafe(changeId, packageName, true);
             }
             for (Long changeId : overrides.disabledChanges()) {
-                addOverride(changeId, packageName, false);
+                addOverrideUnsafe(changeId, packageName, false);
 
             }
+            saveOverrides();
             invalidateCache();
         }
     }
@@ -337,8 +366,9 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange change = mChanges.valueAt(i);
-                removeOverride(change.getId(), packageName);
+                removeOverrideUnsafe(change.getId(), packageName);
             }
+            saveOverrides();
             invalidateCache();
         }
     }
@@ -372,8 +402,10 @@
     int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
         long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
-            addOverride(changeId, packageName, true);
+            addOverrideUnsafe(changeId, packageName, true);
         }
+        saveOverrides();
+        invalidateCache();
         return changes.length;
     }
 
@@ -386,8 +418,10 @@
     int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
         long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
         for (long changeId : changes) {
-            addOverride(changeId, packageName, false);
+            addOverrideUnsafe(changeId, packageName, false);
         }
+        saveOverrides();
+        invalidateCache();
         return changes.length;
     }
 
@@ -494,7 +528,8 @@
 
     private void readConfig(File configFile) {
         try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
-            for (Change change : XmlParser.read(in).getCompatChange()) {
+            Config config = com.android.server.compat.config.XmlParser.read(in);
+            for (Change change : config.getCompatChange()) {
                 Slog.d(TAG, "Adding: " + change.toString());
                 addChange(new CompatChange(change));
             }
@@ -503,6 +538,65 @@
         }
     }
 
+    void initOverrides(File overridesFile) {
+        if (!overridesFile.exists()) {
+            mOverridesFile = overridesFile;
+            // There have not been any overrides added yet.
+            return;
+        }
+
+        try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) {
+            Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in);
+            for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) {
+                long changeId = changeOverrides.getChangeId();
+                CompatChange compatChange = mChanges.get(changeId);
+                if (compatChange == null) {
+                    Slog.w(TAG, "Change ID " + changeId + " not found. "
+                            + "Skipping overrides for it.");
+                    continue;
+                }
+                compatChange.loadOverrides(changeOverrides);
+            }
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
+            return;
+        }
+        mOverridesFile = overridesFile;
+    }
+
+    /**
+     * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml
+     */
+    void saveOverrides() {
+        if (mOverridesFile == null) {
+            return;
+        }
+        synchronized (mChanges) {
+            // Create the file if it doesn't already exist
+            try {
+                mOverridesFile.createNewFile();
+            } catch (IOException e) {
+                Slog.e(TAG, "Could not create override config file: " + e.toString());
+                return;
+            }
+            try (PrintWriter out = new PrintWriter(mOverridesFile)) {
+                XmlWriter writer = new XmlWriter(out);
+                Overrides overrides = new Overrides();
+                List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+                for (int idx = 0; idx < mChanges.size(); ++idx) {
+                    CompatChange c = mChanges.valueAt(idx);
+                    ChangeOverrides changeOverrides = c.saveOverrides();
+                    if (changeOverrides != null) {
+                        changeOverridesList.add(changeOverrides);
+                    }
+                }
+                XmlWriter.write(writer, overrides);
+            } catch (IOException e) {
+                Slog.e(TAG, e.toString());
+            }
+        }
+    }
+
     IOverrideValidator getOverrideValidator() {
         return mOverrideValidator;
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ab0360b..b282484 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -329,7 +329,7 @@
     private final QosCallbackTracker mQosCallbackTracker;
 
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
-            LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
             int creatorUid, QosCallbackTracker qosCallbackTracker) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index a7be657..5e6b9f3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -686,7 +686,7 @@
 
             mHostname = hostname;
             mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{"
-                    + TextUtils.emptyIfNull(mHostname) + "}";
+                    + (mHostname == null ? "" : mHostname) + "}";
         }
 
         private SSLSocket setupSSLSocket() throws IOException {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8e50bb4..5d1c4e6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -621,7 +621,14 @@
         mWakeUpMessageReceived = false;
 
         if (isTvDeviceEnabled()) {
-            mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+            boolean autoWakeupEnabled =
+                readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
+            boolean autoDeviceOffEnabled =
+                readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+
+            mCecController.setOption(OptionKey.WAKEUP, autoWakeupEnabled);
+            tv().setAutoWakeup(autoWakeupEnabled);
+            tv().setAutoDeviceOff(autoDeviceOffEnabled);
         }
         int reason = -1;
         switch (initiatedBy) {
diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/core/java/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 81d07cc..c4225ed 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -88,6 +88,7 @@
     private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
 
     private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+    private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key";
 
     private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
 
@@ -317,6 +318,22 @@
         deleteFile(getRebootEscrowFile(userId));
     }
 
+    public void writeRebootEscrowServerBlob(byte[] serverBlob) {
+        writeFile(getRebootEscrowServerBlob(), serverBlob);
+    }
+
+    public byte[] readRebootEscrowServerBlob() {
+        return readFile(getRebootEscrowServerBlob());
+    }
+
+    public boolean hasRebootEscrowServerBlob() {
+        return hasFile(getRebootEscrowServerBlob());
+    }
+
+    public void removeRebootEscrowServerBlob() {
+        deleteFile(getRebootEscrowServerBlob());
+    }
+
     public boolean hasPassword(int userId) {
         return hasFile(getLockPasswordFilename(userId));
     }
@@ -445,6 +462,12 @@
         return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
     }
 
+    @VisibleForTesting
+    String getRebootEscrowServerBlob() {
+        // There is a single copy of server blob for all users.
+        return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB);
+    }
+
     private String getLockCredentialFilePathForUser(int userId, String basename) {
         String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
                         SYSTEM_DIRECTORY;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index fbec915..06962d4 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -124,26 +124,28 @@
     static class Injector {
         protected Context mContext;
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
-        private final RebootEscrowProviderInterface mRebootEscrowProvider;
+        private final LockSettingsStorage mStorage;
+        private RebootEscrowProviderInterface mRebootEscrowProvider;
 
-        Injector(Context context) {
+        Injector(Context context, LockSettingsStorage storage) {
             mContext = context;
+            mStorage = storage;
             mKeyStoreManager = new RebootEscrowKeyStoreManager();
+        }
 
-            RebootEscrowProviderInterface rebootEscrowProvider = null;
-            // TODO(xunchang) add implementation for server based ror.
+        private RebootEscrowProviderInterface createRebootEscrowProvider() {
+            RebootEscrowProviderInterface rebootEscrowProvider;
             if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
                     "server_based_ror_enabled", false)) {
-                Slog.e(TAG, "Server based ror isn't implemented yet.");
+                rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
             } else {
                 rebootEscrowProvider = new RebootEscrowProviderHalImpl();
             }
 
-            if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
-                mRebootEscrowProvider = rebootEscrowProvider;
-            } else {
-                mRebootEscrowProvider = null;
+            if (rebootEscrowProvider.hasRebootEscrowSupport()) {
+                return rebootEscrowProvider;
             }
+            return null;
         }
 
         public Context getContext() {
@@ -159,6 +161,12 @@
         }
 
         public RebootEscrowProviderInterface getRebootEscrowProvider() {
+            // Initialize for the provider lazily. Because the device_config and service
+            // implementation apps may change when system server is running.
+            if (mRebootEscrowProvider == null) {
+                mRebootEscrowProvider = createRebootEscrowProvider();
+            }
+
             return mRebootEscrowProvider;
         }
 
@@ -177,7 +185,7 @@
     }
 
     RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
-        this(new Injector(context), callbacks, storage);
+        this(new Injector(context, storage), callbacks, storage);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
new file mode 100644
index 0000000..ba1a680
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to
+ * encrypt & decrypt the blob.
+ */
+class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
+    private static final String TAG = "RebootEscrowProvider";
+
+    // Timeout for service binding
+    private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
+
+    /**
+     * Use the default lifetime of 10 minutes. The lifetime covers the following activities:
+     * Server wrap secret -> device reboot -> server unwrap blob.
+     */
+    private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
+
+    private final LockSettingsStorage mStorage;
+
+    private final Injector mInjector;
+
+    static class Injector {
+        private ResumeOnRebootServiceConnection mServiceConnection = null;
+
+        Injector(Context context) {
+            mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection();
+            if (mServiceConnection == null) {
+                Slog.e(TAG, "Failed to resolve resume on reboot server service.");
+            }
+        }
+
+        Injector(ResumeOnRebootServiceConnection serviceConnection) {
+            mServiceConnection = serviceConnection;
+        }
+
+        @Nullable
+        private ResumeOnRebootServiceConnection getServiceConnection() {
+            return mServiceConnection;
+        }
+
+        long getServiceTimeoutInSeconds() {
+            return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_service_timeout_in_seconds",
+                    DEFAULT_SERVICE_TIMEOUT_IN_SECONDS);
+        }
+
+        long getServerBlobLifetimeInMillis() {
+            return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_server_blob_lifetime_in_millis",
+                    DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS);
+        }
+    }
+
+    RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) {
+        this(storage, new Injector(context));
+    }
+
+    @VisibleForTesting
+    RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) {
+        mStorage = storage;
+        mInjector = injector;
+    }
+
+    @Override
+    public boolean hasRebootEscrowSupport() {
+        return mInjector.getServiceConnection() != null;
+    }
+
+    private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws
+            TimeoutException, RemoteException, IOException {
+        ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+        if (serviceConnection == null) {
+            Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server"
+                    + " service is unavailable");
+            return null;
+        }
+
+        // Decrypt with k_k from the key store first.
+        byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob);
+        if (decryptedBlob == null) {
+            Slog.w(TAG, "Decrypted server blob should not be null");
+            return null;
+        }
+
+        // Ask the server connection service to decrypt the inner layer, to get the reboot
+        // escrow key (k_s).
+        serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+        byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob,
+                mInjector.getServiceTimeoutInSeconds());
+        serviceConnection.unbindService();
+
+        return escrowKeyBytes;
+    }
+
+    @Override
+    public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+        byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+        // Delete the server blob in storage.
+        mStorage.removeRebootEscrowServerBlob();
+        if (serverBlob == null) {
+            Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
+            return null;
+        }
+
+        try {
+            byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+            if (escrowKeyBytes == null) {
+                Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
+                return null;
+            } else if (escrowKeyBytes.length != 32) {
+                Slog.e(TAG, "Decrypted reboot escrow key has incorrect size "
+                        + escrowKeyBytes.length);
+                return null;
+            }
+
+            return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+        } catch (TimeoutException | RemoteException | IOException e) {
+            Slog.w(TAG, "Failed to decrypt the server blob ", e);
+            return null;
+        }
+    }
+
+    @Override
+    public void clearRebootEscrowKey() {
+        mStorage.removeRebootEscrowServerBlob();
+    }
+
+    private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws
+            TimeoutException, RemoteException, IOException {
+        ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+        if (serviceConnection == null) {
+            Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server"
+                    + " service is unavailable");
+            return null;
+        }
+
+        serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+        // Ask the server connection service to encrypt the reboot escrow key.
+        byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes,
+                mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds());
+        serviceConnection.unbindService();
+
+        if (serverEncryptedBlob == null) {
+            Slog.w(TAG, "Server encrypted reboot escrow key cannot be null");
+            return null;
+        }
+
+        // Additionally wrap the server blob with a local key.
+        return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob);
+    }
+
+    @Override
+    public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+        mStorage.removeRebootEscrowServerBlob();
+        try {
+            byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey);
+            if (wrappedBlob == null) {
+                Slog.w(TAG, "Failed to encrypt the reboot escrow key");
+                return false;
+            }
+            mStorage.writeRebootEscrowServerBlob(wrappedBlob);
+
+            Slog.i(TAG, "Reboot escrow key encrypted and stored.");
+            return true;
+        } catch (TimeoutException | RemoteException | IOException e) {
+            Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e);
+        }
+
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 0fa97a2..703bfab 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -122,7 +122,9 @@
     private static final int TOKEN_ALL = Integer.MIN_VALUE;
 
     private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
-    private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int TEARDOWN_TIMEOUT_SECONDS = 5;
 
     private interface EventInfo {}
 
@@ -413,13 +415,6 @@
     private int mCurrentToken = -1;
 
     /**
-     * The next usable token.
-     *
-     * <p>A new token MUST be used for all new IKE sessions.
-     */
-    private int mNextToken = 0;
-
-    /**
      * The number of unsuccessful attempts since the last successful connection.
      *
      * <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -440,7 +435,7 @@
      * <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
      * Migrating states, null otherwise.
      */
-    private IkeSession mIkeSession;
+    private VcnIkeSession mIkeSession;
 
     /**
      * The last known child configuration.
@@ -643,6 +638,22 @@
 
         protected abstract void processStateMsg(Message msg) throws Exception;
 
+        @Override
+        public void exit() {
+            try {
+                exitState();
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Uncaught exception", e);
+                sendMessage(
+                        EVENT_DISCONNECT_REQUESTED,
+                        TOKEN_ALL,
+                        new EventDisconnectRequestedInfo(
+                                DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+            }
+        }
+
+        protected void exitState() throws Exception {}
+
         protected void logUnhandledMessage(Message msg) {
             // Log as unexpected all known messages, and log all else as unknown.
             switch (msg.what) {
@@ -669,18 +680,11 @@
             }
         }
 
-        protected void teardownIke() {
-            if (mIkeSession != null) {
-                mIkeSession.close();
-            }
-        }
-
         protected void handleDisconnectRequested(String msg) {
             Slog.v(TAG, "Tearing down. Cause: " + msg);
             mIsRunning = false;
 
             teardownNetwork();
-            teardownIke();
 
             if (mIkeSession == null) {
                 // Already disconnected, go straight to DisconnectedState
@@ -773,8 +777,91 @@
      * does not complete teardown in a timely fashion, it will be killed (forcibly closed).
      */
     private class DisconnectingState extends ActiveBaseState {
+        /**
+         * Whether to skip the RetryTimeoutState and go straight to the ConnectingState.
+         *
+         * <p>This is used when an underlying network change triggered a restart on a new network.
+         *
+         * <p>Reset (to false) upon exit of the DisconnectingState.
+         */
+        private boolean mSkipRetryTimeout = false;
+
+        // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change.
+        public void setSkipRetryTimeout(boolean shouldSkip) {
+            mSkipRetryTimeout = shouldSkip;
+        }
+
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() throws Exception {
+            if (mIkeSession == null) {
+                Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+                sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+                return;
+            }
+
+            // If underlying network has already been lost, save some time and just kill the session
+            if (mUnderlying == null) {
+                // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+                mIkeSession.kill();
+                return;
+            }
+
+            mIkeSession.close();
+            sendMessageDelayed(
+                    EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+                    mCurrentToken,
+                    TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+                    mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+                    // If we received a new underlying network, continue.
+                    if (mUnderlying != null) {
+                        break;
+                    }
+
+                    // Fallthrough; no network exists to send IKE close session requests.
+                case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+                    // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+                    mIkeSession.kill();
+
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    teardownNetwork();
+
+                    String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+                    if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+                        // Will trigger EVENT_SESSION_CLOSED immediately.
+                        mIkeSession.kill();
+                        break;
+                    }
+
+                    // Otherwise we are already in the process of shutting down.
+                    break;
+                case EVENT_SESSION_CLOSED:
+                    mIkeSession = null;
+
+                    if (mIsRunning && mUnderlying != null) {
+                        transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState);
+                    } else {
+                        teardownNetwork();
+                        transitionTo(mDisconnectedState);
+                    }
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
+
+        @Override
+        protected void exitState() throws Exception {
+            mSkipRetryTimeout = false;
+        }
     }
 
     /**
@@ -785,7 +872,69 @@
      */
     private class ConnectingState extends ActiveBaseState {
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() {
+            if (mIkeSession != null) {
+                Slog.wtf(TAG, "ConnectingState entered with active session");
+
+                // Attempt to recover.
+                mIkeSession.kill();
+                mIkeSession = null;
+            }
+
+            mIkeSession = buildIkeSession();
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED:
+                    final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+                    mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+                    if (oldUnderlying == null) {
+                        // This should never happen, but if it does, there's likely a nasty bug.
+                        Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?");
+                    }
+
+                    // If new underlying is null, all underlying networks have been lost; disconnect
+                    if (mUnderlying == null) {
+                        transitionTo(mDisconnectingState);
+                        break;
+                    }
+
+                    if (oldUnderlying != null
+                            && mUnderlying.network.equals(oldUnderlying.network)) {
+                        break; // Only network properties have changed; continue connecting.
+                    }
+                    // Else, retry on the new network.
+
+                    // Immediately come back to the ConnectingState (skip RetryTimeout, since this
+                    // isn't a failure)
+                    mDisconnectingState.setSkipRetryTimeout(true);
+
+                    // fallthrough - disconnect, and retry on new network.
+                case EVENT_SESSION_LOST:
+                    transitionTo(mDisconnectingState);
+                    break;
+                case EVENT_SESSION_CLOSED:
+                    deferMessage(msg);
+
+                    transitionTo(mDisconnectingState);
+                    break;
+                case EVENT_SETUP_COMPLETED: // fallthrough
+                case EVENT_TRANSFORM_CREATED:
+                    // Child setup complete; move to ConnectedState for NetworkAgent registration
+                    deferMessage(msg);
+                    transitionTo(mConnectedState);
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
     }
 
     private abstract class ConnectedStateBase extends ActiveBaseState {}
@@ -946,6 +1095,38 @@
         mIsRunning = isRunning;
     }
 
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    VcnIkeSession getIkeSession() {
+        return mIkeSession;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    void setIkeSession(@Nullable VcnIkeSession session) {
+        mIkeSession = session;
+    }
+
+    private IkeSessionParams buildIkeParams() {
+        // TODO: Implement this once IkeSessionParams is persisted
+        return null;
+    }
+
+    private ChildSessionParams buildChildParams() {
+        // TODO: Implement this once IkeSessionParams is persisted
+        return null;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    VcnIkeSession buildIkeSession() {
+        final int token = ++mCurrentToken;
+
+        return mDeps.newIkeSession(
+                mVcnContext,
+                buildIkeParams(),
+                buildChildParams(),
+                new IkeSessionCallbackImpl(token),
+                new ChildSessionCallbackImpl(token));
+    }
+
     /** External dependencies used by VcnGatewayConnection, for injection in tests */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class Dependencies {
@@ -958,19 +1139,67 @@
         }
 
         /** Builds a new IkeSession. */
-        public IkeSession newIkeSession(
+        public VcnIkeSession newIkeSession(
                 VcnContext vcnContext,
                 IkeSessionParams ikeSessionParams,
                 ChildSessionParams childSessionParams,
                 IkeSessionCallback ikeSessionCallback,
                 ChildSessionCallback childSessionCallback) {
-            return new IkeSession(
-                    vcnContext.getContext(),
+            return new VcnIkeSession(
+                    vcnContext,
                     ikeSessionParams,
                     childSessionParams,
-                    new HandlerExecutor(new Handler(vcnContext.getLooper())),
                     ikeSessionCallback,
                     childSessionCallback);
         }
     }
+
+    /** Proxy implementation of IKE session, used for testing. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class VcnIkeSession {
+        private final IkeSession mImpl;
+
+        public VcnIkeSession(
+                VcnContext vcnContext,
+                IkeSessionParams ikeSessionParams,
+                ChildSessionParams childSessionParams,
+                IkeSessionCallback ikeSessionCallback,
+                ChildSessionCallback childSessionCallback) {
+            mImpl =
+                    new IkeSession(
+                            vcnContext.getContext(),
+                            ikeSessionParams,
+                            childSessionParams,
+                            new HandlerExecutor(new Handler(vcnContext.getLooper())),
+                            ikeSessionCallback,
+                            childSessionCallback);
+        }
+
+        /** Creates a new IKE Child session. */
+        public void openChildSession(
+                @NonNull ChildSessionParams childSessionParams,
+                @NonNull ChildSessionCallback childSessionCallback) {
+            mImpl.openChildSession(childSessionParams, childSessionCallback);
+        }
+
+        /** Closes an IKE session as identified by the ChildSessionCallback. */
+        public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+            mImpl.closeChildSession(childSessionCallback);
+        }
+
+        /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+        public void close() {
+            mImpl.close();
+        }
+
+        /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+        public void kill() {
+            mImpl.kill();
+        }
+
+        /** Sets the underlying network used by the IkeSession. */
+        public void setNetwork(@NonNull Network network) {
+            mImpl.setNetwork(network);
+        }
+    }
 }
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index b7d6424..3690afc 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -8,11 +8,19 @@
 
 xsd_config {
     name: "platform-compat-config",
-    srcs: ["platform-compat-config.xsd"],
-    api_dir: "platform-compat-schema",
+    srcs: ["platform-compat/config/platform-compat-config.xsd"],
+    api_dir: "platform-compat/config/schema",
     package_name: "com.android.server.compat.config",
 }
 
+xsd_config {
+    name: "platform-compat-overrides",
+    srcs: ["platform-compat/overrides/platform-compat-overrides.xsd"],
+    api_dir: "platform-compat/overrides/schema",
+    package_name: "com.android.server.compat.overrides",
+    gen_writer: true,
+}
+
 
 xsd_config {
     name: "display-device-config",
diff --git a/services/core/xsd/platform-compat-schema/OWNERS b/services/core/xsd/platform-compat/OWNERS
similarity index 100%
rename from services/core/xsd/platform-compat-schema/OWNERS
rename to services/core/xsd/platform-compat/OWNERS
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat/config/platform-compat-config.xsd
similarity index 100%
rename from services/core/xsd/platform-compat-config.xsd
rename to services/core/xsd/platform-compat/config/platform-compat-config.xsd
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat/config/schema/current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/current.txt
rename to services/core/xsd/platform-compat/config/schema/current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/config/schema/last_current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_current.txt
rename to services/core/xsd/platform-compat/config/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/config/schema/last_removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_removed.txt
rename to services/core/xsd/platform-compat/config/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/config/schema/removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/removed.txt
rename to services/core/xsd/platform-compat/config/schema/removed.txt
diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
new file mode 100644
index 0000000..e27e1b8
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- This defines the format of the XML file used to store compat config overrides in
+  ~ /data/misc/appcompat/compat_framework_overrides.xml
+-->
+<xs:schema version="2.0" elementFormDefault="qualified"
+    xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+
+    <xs:complexType name="override-value">
+        <xs:attribute type="xs:string" name="packageName" use="required" />
+        <xs:attribute type="xs:boolean" name="enabled" use="required" />
+    </xs:complexType>
+
+    <xs:complexType name="change-overrides">
+        <xs:attribute type="xs:long" name="changeId" use="required"/>
+        <xs:element name="validated">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+                </xs:sequence>
+            </xs:complexType>
+        </xs:element>
+        <xs:element name="deferred">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+                </xs:sequence>
+            </xs:complexType>
+        </xs:element>
+    </xs:complexType>
+
+    <xs:element name="overrides">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="change-overrides" type="change-overrides" maxOccurs="unbounded" minOccurs="0" />
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt
new file mode 100644
index 0000000..08b8207
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/current.txt
@@ -0,0 +1,51 @@
+// Signature format: 2.0
+package com.android.server.compat.overrides {
+
+  public class ChangeOverrides {
+    ctor public ChangeOverrides();
+    method public long getChangeId();
+    method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
+    method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
+    method public void setChangeId(long);
+    method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
+    method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
+  }
+
+  public static class ChangeOverrides.Deferred {
+    ctor public ChangeOverrides.Deferred();
+    method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+  }
+
+  public static class ChangeOverrides.Validated {
+    ctor public ChangeOverrides.Validated();
+    method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+  }
+
+  public class OverrideValue {
+    ctor public OverrideValue();
+    method public boolean getEnabled();
+    method public String getPackageName();
+    method public void setEnabled(boolean);
+    method public void setPackageName(String);
+  }
+
+  public class Overrides {
+    ctor public Overrides();
+    method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public class XmlWriter implements java.io.Closeable {
+    ctor public XmlWriter(java.io.PrintWriter);
+    method public void close();
+    method public static void write(com.android.server.compat.overrides.XmlWriter, com.android.server.compat.overrides.Overrides) throws java.io.IOException;
+  }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/overrides/schema/last_current.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_current.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/overrides/schema/removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/removed.txt
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index ac8dc34..a53ff9b 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -44,6 +44,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.UUID;
 
 @RunWith(AndroidJUnit4.class)
@@ -69,6 +71,10 @@
         os.close();
     }
 
+    private String readFile(File file) throws IOException {
+        return new String(Files.readAllBytes(Paths.get(file.toURI())));
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -499,4 +505,86 @@
         assertThat(compatConfig.isChangeEnabled(1236L,
             ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
     }
+
+    @Test
+    public void testSaveOverrides() throws Exception {
+        File overridesFile = new File(createTempDir(), "overrides.xml");
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1L)
+                .addEnableSinceSdkChangeWithId(2, 2L)
+                .build();
+        compatConfig.forceNonDebuggableFinalForTest(true);
+        compatConfig.initOverrides(overridesFile);
+        when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                                .withPackageName("foo.bar")
+                                .debuggable()
+                                .build());
+        when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        compatConfig.addOverride(1L, "foo.bar", true);
+        compatConfig.addOverride(2L, "bar.baz", false);
+
+        assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                + "<overrides>\n"
+                + "    <change-overrides changeId=\"1\">\n"
+                + "        <validated>\n"
+                + "            <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+                + "            </override-value>\n"
+                + "        </validated>\n"
+                + "        <deferred>\n"
+                + "        </deferred>\n"
+                + "    </change-overrides>\n"
+                + "    <change-overrides changeId=\"2\">\n"
+                + "        <validated>\n"
+                + "        </validated>\n"
+                + "        <deferred>\n"
+                + "            <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
+                + "            </override-value>\n"
+                + "        </deferred>\n"
+                + "    </change-overrides>\n"
+                + "</overrides>\n");
+    }
+
+    @Test
+    public void testLoadOverrides() throws Exception {
+        File tempDir = createTempDir();
+        File overridesFile = new File(tempDir, "overrides.xml");
+        // Change 1 is enabled for foo.bar (validated)
+        // Change 2 is disabled for bar.baz (deferred)
+        String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                       + "<overrides>"
+                       +    "<change-overrides changeId=\"1\">"
+                       +        "<deferred/>"
+                       +        "<validated>"
+                       +            "<override-value packageName=\"foo.bar\" enabled=\"true\"/>"
+                       +        "</validated>"
+                       +    "</change-overrides>"
+                       +    "<change-overrides changeId=\"2\">"
+                       +        "<deferred>"
+                       +           "<override-value packageName=\"bar.baz\" enabled=\"false\"/>"
+                       +        "</deferred>"
+                       +        "<validated/>"
+                       +    "</change-overrides>"
+                       + "</overrides>";
+        writeToFile(tempDir, "overrides.xml", xmlData);
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1L)
+                .addEnableSinceSdkChangeWithId(2, 2L)
+                .build();
+        compatConfig.forceNonDebuggableFinalForTest(true);
+        compatConfig.initOverrides(overridesFile);
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("foo.bar")
+                .debuggable()
+                .build();
+        when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+                .thenReturn(applicationInfo);
+        when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+        assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 1581d9a..691d174 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -82,6 +82,11 @@
     }
 
     @Override
+    String getRebootEscrowServerBlob() {
+        return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
+    }
+
+    @Override
     protected File getSyntheticPasswordDirectoryForUser(int userId) {
         return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(
                 userId).getAbsolutePath());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index f74e45b..a4ba4c8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
@@ -52,6 +53,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -92,6 +94,7 @@
     private UserManager mUserManager;
     private RebootEscrowManager.Callbacks mCallbacks;
     private IRebootEscrow mRebootEscrow;
+    private ResumeOnRebootServiceConnection mServiceConnection;
     private RebootEscrowKeyStoreManager mKeyStoreManager;
 
     LockSettingsStorageTestable mStorage;
@@ -108,6 +111,7 @@
 
     static class MockInjector extends RebootEscrowManager.Injector {
         private final IRebootEscrow mRebootEscrow;
+        private final ResumeOnRebootServiceConnection mServiceConnection;
         private final RebootEscrowProviderInterface mRebootEscrowProvider;
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
@@ -116,10 +120,11 @@
         MockInjector(Context context, UserManager userManager,
                 IRebootEscrow rebootEscrow,
                 RebootEscrowKeyStoreManager keyStoreManager,
+                LockSettingsStorageTestable storage,
                 MockableRebootEscrowInjected injected) {
-            super(context);
+            super(context, storage);
             mRebootEscrow = rebootEscrow;
-
+            mServiceConnection = null;
             RebootEscrowProviderHalImpl.Injector halInjector =
                     new RebootEscrowProviderHalImpl.Injector() {
                         @Override
@@ -133,6 +138,22 @@
             mInjected = injected;
         }
 
+        MockInjector(Context context, UserManager userManager,
+                ResumeOnRebootServiceConnection serviceConnection,
+                RebootEscrowKeyStoreManager keyStoreManager,
+                LockSettingsStorageTestable storage,
+                MockableRebootEscrowInjected injected) {
+            super(context, storage);
+            mServiceConnection = serviceConnection;
+            mRebootEscrow = null;
+            RebootEscrowProviderServerBasedImpl.Injector injector =
+                    new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
+            mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
+            mUserManager = userManager;
+            mKeyStoreManager = keyStoreManager;
+            mInjected = injected;
+        }
+
         @Override
         public UserManager getUserManager() {
             return mUserManager;
@@ -165,6 +186,7 @@
         mUserManager = mock(UserManager.class);
         mCallbacks = mock(RebootEscrowManager.Callbacks.class);
         mRebootEscrow = mock(IRebootEscrow.class);
+        mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
         mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
         mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
 
@@ -186,7 +208,12 @@
         when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
         mInjected = mock(MockableRebootEscrowInjected.class);
         mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
-                mKeyStoreManager, mInjected), mCallbacks, mStorage);
+                mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+    }
+
+    private void setServerBasedRebootEscrowProvider() throws Exception {
+        mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
+                mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
     }
 
     @Test
@@ -202,6 +229,19 @@
     }
 
     @Test
+    public void prepareRebootEscrowServerBased_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
     public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
         mService.setRebootEscrowListener(mockListener);
@@ -246,6 +286,28 @@
     }
 
     @Test
+    public void armServiceServerBased_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+        assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+        assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
     public void armService_HalFailure_NonFatal() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
         mService.setRebootEscrowListener(mockListener);
@@ -346,6 +408,40 @@
     }
 
     @Test
+    public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        mService.loadRebootEscrowDataIfAvailable();
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+    }
+
+    @Test
     public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
         when(mInjected.getBootCount()).thenReturn(0);
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
new file mode 100644
index 0000000..bc1e025
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowProviderServerBasedImplTests {
+    private SecretKey mKeyStoreEncryptionKey;
+    private RebootEscrowKey mRebootEscrowKey;
+    private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection;
+    private LockSettingsStorageTestable mStorage;
+    private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider;
+    private Answer<byte[]> mFakeEncryption;
+
+    private static final byte[] TEST_AES_KEY = new byte[] {
+            0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+            0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+            0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+            0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
+        mRebootEscrowKey = RebootEscrowKey.generate();
+        mServiceConnection = mock(
+                ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class);
+
+        Context context = new ContextWrapper(InstrumentationRegistry.getContext());
+        mStorage = new LockSettingsStorageTestable(context,
+                new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+        mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage,
+                new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection));
+
+        mFakeEncryption = invocation -> {
+            byte[] secret = invocation.getArgument(0);
+            for (int i = 0; i < secret.length; i++) {
+                secret[i] = (byte) (secret[i] ^ 0xf);
+            }
+            return secret;
+        };
+    }
+
+    @Test
+    public void getAndClearRebootEscrowKey_loopback_success() throws Exception {
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+        when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption);
+
+        assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+        mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+
+        RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+                mKeyStoreEncryptionKey);
+        assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes()));
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
+    public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception {
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+        when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(
+                invocation -> {
+                    byte[] secret = invocation.getArgument(0);
+                    for (int i = 0; i < secret.length; i++) {
+                        secret[i] = (byte) (secret[i] ^ 0xe);
+                    }
+                    return secret;
+                }
+        );
+
+        assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+        mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // Expect to get wrong key bytes
+        RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+                mKeyStoreEncryptionKey);
+        assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes());
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
+    public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+        doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+
+        assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+        mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // Expect to get null key bytes when the server service fails to unwrap the blob.
+        RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+                mKeyStoreEncryptionKey);
+        assertNull(ks);
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index d502da9..99a77ae5 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -915,6 +915,8 @@
     public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA;
     /** System preference change back to SRAT during handoff */
     public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
+    /** Data call fail due to the slice not being allowed for the data call. */
+    public static final int SLICE_REJECTED = 0x8CC;
 
     //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
 
@@ -985,7 +987,7 @@
      * the authentication failed.
      */
     public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001;
-    /** IKE message timeout, tunnel setup failed due to no response from EPDG  */
+    /** IKE message timeout, tunnel setup failed due to no response from EPDG */
     public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002;
     /** IKE Certification validation failure  */
     public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003;
@@ -1419,6 +1421,7 @@
         sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
         sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
         sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
+        sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED");
         sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
         sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
         sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 8348502..46ec4a3 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,6 +136,7 @@
     private final int mPduSessionId;
     private final Qos mDefaultQos;
     private final List<QosSession> mQosSessions;
+    private final SliceInfo mSliceInfo;
 
     /**
      * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -186,6 +187,7 @@
         mPduSessionId = PDU_SESSION_ID_NOT_SET;
         mDefaultQos = null;
         mQosSessions = new ArrayList<>();
+        mSliceInfo = null;
     }
 
     private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -194,7 +196,8 @@
             @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
             @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
             @HandoverFailureMode int handoverFailureMode, int pduSessionId,
-            @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) {
+            @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+            @Nullable SliceInfo sliceInfo) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
         mId = id;
@@ -216,6 +219,7 @@
         mPduSessionId = pduSessionId;
         mDefaultQos = defaultQos;
         mQosSessions = qosSessions;
+        mSliceInfo = sliceInfo;
     }
 
     /** @hide */
@@ -243,6 +247,7 @@
         mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
         mQosSessions = new ArrayList<>();
         source.readList(mQosSessions, QosSession.class.getClassLoader());
+        mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
     }
 
     /**
@@ -368,7 +373,7 @@
     }
 
     /**
-     * @return default QOS of the data call received from the network
+     * @return default QOS of the data connection received from the network
      *
      * @hide
      */
@@ -379,16 +384,24 @@
     }
 
     /**
-     * @return All the dedicated bearer QOS sessions of the data call received from the network
+     * @return All the dedicated bearer QOS sessions of the data connection received from the
+     * network.
      *
      * @hide
      */
-
     @NonNull
     public List<QosSession> getQosSessions() {
         return mQosSessions;
     }
 
+    /**
+     * @return The slice info related to this data connection.
+     */
+    @Nullable
+    public SliceInfo getSliceInfo() {
+        return mSliceInfo;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -411,6 +424,7 @@
            .append(" pduSessionId=").append(getPduSessionId())
            .append(" defaultQos=").append(mDefaultQos)
            .append(" qosSessions=").append(mQosSessions)
+           .append(" sliceInfo=").append(mSliceInfo)
            .append("}");
         return sb.toString();
     }
@@ -454,7 +468,8 @@
                 && mHandoverFailureMode == other.mHandoverFailureMode
                 && mPduSessionId == other.mPduSessionId
                 && isQosSame
-                && isQosSessionsSame;
+                && isQosSessionsSame
+                && Objects.equals(mSliceInfo, other.mSliceInfo);
     }
 
     @Override
@@ -462,7 +477,7 @@
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
                 mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
-                mQosSessions);
+                mQosSessions, mSliceInfo);
     }
 
     @Override
@@ -493,6 +508,7 @@
             dest.writeParcelable((NrQos)mDefaultQos, flags);
         }
         dest.writeList(mQosSessions);
+        dest.writeParcelable(mSliceInfo, flags);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -576,6 +592,8 @@
 
         private List<QosSession> mQosSessions = new ArrayList<>();
 
+        private SliceInfo mSliceInfo;
+
         /**
          * Default constructor for Builder.
          */
@@ -799,6 +817,21 @@
         }
 
         /**
+         * The Slice used for this data connection.
+         * <p/>
+         * If a handover occurs from EPDG to 5G,
+         * this is the {@link SliceInfo} used in {@link DataService#setupDataCall}.
+         *
+         * @param sliceInfo the slice info for the data call
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setSliceInfo(@Nullable SliceInfo sliceInfo) {
+            mSliceInfo = sliceInfo;
+            return this;
+        }
+
+        /**
          * Build the DataCallResponse.
          *
          * @return the DataCallResponse object.
@@ -807,7 +840,7 @@
             return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
                     mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
                     mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
-                    mDefaultQos, mQosSessions);
+                    mDefaultQos, mQosSessions, mSliceInfo);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2ec9651..03c2ef9 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -194,13 +194,19 @@
          *                     The standard range of values are 1-15 while 0 means no pdu session id
          *                     was attached to this call.  Reference: 3GPP TS 24.007 section
          *                     11.2.3.1b.
+         * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G.
+         *        The value is null unless the access network is
+         *        {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a
+         *        handover is occurring from EPDG to 5G.  If the slice passed is rejected, then
+         *        {@link DataCallResponse#getCause()} is
+         *        {@link android.telephony.DataFailCause#SLICE_REJECTED}.
          * @param callback The result callback for this request.
          */
         public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming,
                 @SetupDataReason int reason,
                 @Nullable LinkProperties linkProperties,
-                @IntRange(from = 0, to = 15) int pduSessionId,
+                @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo,
                 @NonNull DataServiceCallback callback) {
             /* Call the old version since the new version isn't supported */
             setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason,
@@ -392,10 +398,11 @@
         public final int reason;
         public final LinkProperties linkProperties;
         public final int pduSessionId;
+        public final SliceInfo sliceInfo;
         public final IDataServiceCallback callback;
         SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                              boolean allowRoaming, int reason, LinkProperties linkProperties,
-                             int pduSessionId, IDataServiceCallback callback) {
+                             int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) {
             this.accessNetworkType = accessNetworkType;
             this.dataProfile = dataProfile;
             this.isRoaming = isRoaming;
@@ -403,6 +410,7 @@
             this.linkProperties = linkProperties;
             this.reason = reason;
             this.pduSessionId = pduSessionId;
+            this.sliceInfo = sliceInfo;
             this.callback = callback;
         }
     }
@@ -513,6 +521,7 @@
                             setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
                             setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
                             setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId,
+                            setupDataCallRequest.sliceInfo,
                             (setupDataCallRequest.callback != null)
                                     ? new DataServiceCallback(setupDataCallRequest.callback)
                                     : null);
@@ -676,10 +685,12 @@
         @Override
         public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming, int reason,
-                LinkProperties linkProperties, int pduSessionId, IDataServiceCallback callback) {
+                LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo,
+                IDataServiceCallback callback) {
             mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,
                     new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
-                            allowRoaming, reason, linkProperties, pduSessionId, callback))
+                            allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
+                            callback))
                     .sendToTarget();
         }
 
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 3f1f033..e0b9a1a 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -19,6 +19,7 @@
 import android.net.LinkProperties;
 import android.telephony.data.DataProfile;
 import android.telephony.data.IDataServiceCallback;
+import android.telephony.data.SliceInfo;
 
 /**
  * {@hide}
@@ -29,7 +30,7 @@
     void removeDataServiceProvider(int slotId);
     void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
                        boolean allowRoaming, int reason, in LinkProperties linkProperties,
-                       int pduSessionId, IDataServiceCallback callback);
+                       int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback);
     void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
     void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
                              IDataServiceCallback callback);
diff --git a/telephony/java/android/telephony/data/SliceInfo.aidl b/telephony/java/android/telephony/data/SliceInfo.aidl
new file mode 100644
index 0000000..286ea5e
--- /dev/null
+++ b/telephony/java/android/telephony/data/SliceInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable SliceInfo;
diff --git a/telephony/java/android/telephony/data/SliceInfo.java b/telephony/java/android/telephony/data/SliceInfo.java
new file mode 100644
index 0000000..51857a7
--- /dev/null
+++ b/telephony/java/android/telephony/data/SliceInfo.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents a S-NSSAI as defined in 3GPP TS 24.501.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SliceInfo implements Parcelable {
+    /**
+     * When set on a Slice Differentiator, this value indicates that there is no corresponding
+     * Slice.
+     */
+    public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1;
+
+    /**
+     *  Indicates that the service type is not present.
+     */
+    public static final int SLICE_SERVICE_TYPE_NONE = 0;
+
+    /**
+     *  Slice suitable for the handling of 5G enhanced Mobile Broadband.
+     */
+    public static final int SLICE_SERVICE_TYPE_EMBB = 1;
+
+    /**
+     * Slice suitable for the handling of ultra-reliable low latency communications.
+     */
+    public static final int SLICE_SERVICE_TYPE_URLLC = 2;
+
+    /**
+     * Slice suitable for the handling of massive IoT.
+     */
+    public static final int SLICE_SERVICE_TYPE_MIOT = 3;
+
+    /**
+     * The min acceptable value for a Slice Differentiator
+     */
+    @SuppressLint("MinMaxConstant")
+    public static final int MIN_SLICE_DIFFERENTIATOR = -1;
+
+    /**
+     * The max acceptable value for a Slice Differentiator
+     */
+    @SuppressLint("MinMaxConstant")
+    public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE;
+
+    /** @hide */
+    @IntDef(prefix = { "SLICE_SERVICE_TYPE_" }, value = {
+            SLICE_SERVICE_TYPE_NONE,
+            SLICE_SERVICE_TYPE_EMBB,
+            SLICE_SERVICE_TYPE_URLLC,
+            SLICE_SERVICE_TYPE_MIOT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceServiceType {}
+
+
+    @SliceServiceType
+    private final int mSliceServiceType;
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    private final int mSliceDifferentiator;
+    @SliceServiceType
+    private final int mMappedHplmnSliceServiceType;
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    private final int mMappedHplmnSliceDifferentiator;
+
+    private SliceInfo(@SliceServiceType int sliceServiceType,
+            int sliceDifferentiator, int mappedHplmnSliceServiceType,
+            int mappedHplmnSliceDifferentiator) {
+        mSliceServiceType = sliceServiceType;
+        mSliceDifferentiator = sliceDifferentiator;
+        mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+        mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+    }
+
+    /**
+     * The type of service provided by the slice.
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @SliceServiceType
+    public int getSliceServiceType() {
+        return mSliceServiceType;
+    }
+
+    /**
+     * Identifies the slice from others with the same Slice Service Type.
+     * <p/>
+     * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if {@link #getSliceServiceType} returns
+     * {@link #SLICE_SERVICE_TYPE_NONE}.
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    public int getSliceDifferentiator() {
+        return mSliceDifferentiator;
+    }
+
+    /**
+     * Corresponds to a Slice Info (S-NSSAI) of the HPLMN.
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @SliceServiceType
+    public int getMappedHplmnSliceServiceType() {
+        return mMappedHplmnSliceServiceType;
+    }
+
+    /**
+     * This Slice Differentiator corresponds to a {@link SliceInfo} (S-NSSAI) of the HPLMN;
+     * {@link #getSliceDifferentiator()} is mapped to this value.
+     * <p/>
+     * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true:
+     * <ul>
+     * <li>{@link #getSliceDifferentiator()} returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE}</li>
+     * <li>{@link #getMappedHplmnSliceServiceType()} returns {@link #SLICE_SERVICE_TYPE_NONE}</li>
+     * </ul>
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    public int getMappedHplmnSliceDifferentiator() {
+        return mMappedHplmnSliceDifferentiator;
+    }
+
+    private SliceInfo(@NonNull Parcel in) {
+        mSliceServiceType = in.readInt();
+        mSliceDifferentiator = in.readInt();
+        mMappedHplmnSliceServiceType = in.readInt();
+        mMappedHplmnSliceDifferentiator = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSliceServiceType);
+        dest.writeInt(mSliceDifferentiator);
+        dest.writeInt(mMappedHplmnSliceServiceType);
+        dest.writeInt(mMappedHplmnSliceDifferentiator);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<SliceInfo> CREATOR =
+            new Parcelable.Creator<SliceInfo>() {
+                @Override
+                @NonNull
+                public SliceInfo createFromParcel(@NonNull Parcel source) {
+                    return new SliceInfo(source);
+                }
+
+                @Override
+                @NonNull
+                public SliceInfo[] newArray(int size) {
+                    return new SliceInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "SliceInfo{"
+                + "mSliceServiceType=" + sliceServiceTypeToString(mSliceServiceType)
+                + ", mSliceDifferentiator=" + mSliceDifferentiator
+                + ", mMappedHplmnSliceServiceType="
+                + sliceServiceTypeToString(mMappedHplmnSliceServiceType)
+                + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator
+                + '}';
+    }
+
+    private static String sliceServiceTypeToString(@SliceServiceType int sliceServiceType) {
+        switch(sliceServiceType) {
+            case SLICE_SERVICE_TYPE_NONE:
+                return "NONE";
+            case SLICE_SERVICE_TYPE_EMBB:
+                return "EMBB";
+            case SLICE_SERVICE_TYPE_URLLC:
+                return "URLLC";
+            case SLICE_SERVICE_TYPE_MIOT:
+                return "MIOT";
+            default:
+                return Integer.toString(sliceServiceType);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SliceInfo sliceInfo = (SliceInfo) o;
+        return mSliceServiceType == sliceInfo.mSliceServiceType
+                && mSliceDifferentiator == sliceInfo.mSliceDifferentiator
+                && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType
+                && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType,
+                mMappedHplmnSliceDifferentiator);
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link SliceInfo} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code SliceInfo}:
+     *
+     * <pre><code>
+     *
+     * SliceInfo response = new SliceInfo.Builder()
+     *     .setSliceServiceType(SLICE_SERVICE_TYPE_URLLC)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        @SliceServiceType
+        private int mSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+        @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+        private int mSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+        @SliceServiceType
+        private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+        @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+        private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the Slice Service Type.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setSliceServiceType(@SliceServiceType int mSliceServiceType) {
+            this.mSliceServiceType = mSliceServiceType;
+            return this;
+        }
+
+        /**
+         * Set the Slice Differentiator.
+         * <p/>
+         * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+         * corresponding Slice.
+         *
+         * @throws IllegalArgumentException if the parameter is not between
+         * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setSliceDifferentiator(
+                @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+                        int sliceDifferentiator) {
+            if (sliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+                    || sliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+                throw new IllegalArgumentException("The slice diffentiator value is out of range");
+            }
+            this.mSliceDifferentiator = sliceDifferentiator;
+            return this;
+        }
+
+        /**
+         * Set the HPLMN Slice Service Type.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setMappedHplmnSliceServiceType(
+                @SliceServiceType int mappedHplmnSliceServiceType) {
+            this.mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+            return this;
+        }
+
+        /**
+         * Set the HPLMN Slice Differentiator.
+         * <p/>
+         * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+         * corresponding Slice of the HPLMN.
+         *
+         * @throws IllegalArgumentException if the parameter is not between
+         * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setMappedHplmnSliceDifferentiator(
+                @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+                        int mappedHplmnSliceDifferentiator) {
+            if (mappedHplmnSliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+                    || mappedHplmnSliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+                throw new IllegalArgumentException("The slice diffentiator value is out of range");
+            }
+            this.mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+            return this;
+        }
+
+        /**
+         * Build the {@link SliceInfo}.
+         *
+         * @return the {@link SliceInfo} object.
+         */
+        @NonNull
+        public SliceInfo build() {
+            return new SliceInfo(this.mSliceServiceType, this.mSliceDifferentiator,
+                    this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index 52b31d7..a5150b0 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -15,6 +15,7 @@
  */
 package android.telephony.euicc;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.PendingIntent;
@@ -102,44 +103,81 @@
         this.accessRules = accessRules;
     }
 
-    /** @hide */
-    @SystemApi
     public static final class Builder {
         @Nullable private String encodedActivationCode;
         @Nullable private String confirmationCode;
         @Nullable private String carrierName;
         List<UiccAccessRule> accessRules;
 
+        /** @hide */
+        @SystemApi
         public Builder() {}
 
-        public Builder(DownloadableSubscription baseSubscription) {
+        public Builder(@NonNull DownloadableSubscription baseSubscription) {
             encodedActivationCode = baseSubscription.getEncodedActivationCode();
             confirmationCode = baseSubscription.getConfirmationCode();
             carrierName = baseSubscription.getCarrierName();
             accessRules = baseSubscription.getAccessRules();
         }
 
+        public Builder(@NonNull String encodedActivationCode) {
+            this.encodedActivationCode = encodedActivationCode;
+        }
+
+        /**
+         * Builds a {@link DownloadableSubscription} object.
+         * @return a non-null {@link DownloadableSubscription} object.
+         */
+        @NonNull
         public DownloadableSubscription build() {
             return new DownloadableSubscription(encodedActivationCode, confirmationCode,
                     carrierName, accessRules);
         }
 
-        public Builder setEncodedActivationCode(String value) {
+        /**
+         * Sets the encoded activation code.
+         * @param value the activation code to use. An activation code can be parsed from a user
+         *              scanned QR code. The format of activation code is defined in SGP.22. For
+         *              example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For
+         *              detail, see {@code com.android.euicc.data.ActivationCode}. Must not be null.
+         */
+        @NonNull
+        public Builder setEncodedActivationCode(@NonNull String value) {
             encodedActivationCode = value;
             return this;
         }
 
-        public Builder setConfirmationCode(String value) {
+        /**
+         * Sets the confirmation code.
+         * @param value the confirmation code to use to authenticate the carrier server got
+         *              subscription download.
+         */
+        @NonNull
+        public Builder setConfirmationCode(@NonNull String value) {
             confirmationCode = value;
             return this;
         }
 
-        public Builder setCarrierName(String value) {
+        /**
+         * Sets the user-visible carrier name.
+         * @param value carrier name.
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public Builder setCarrierName(@NonNull String value) {
             carrierName = value;
             return this;
         }
 
-        public Builder setAccessRules(List<UiccAccessRule> value) {
+        /**
+         * Sets the {@link UiccAccessRule}s dictating access to this subscription.
+         * @param value A list of {@link UiccAccessRule}s.
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public Builder setAccessRules(@NonNull List<UiccAccessRule> value) {
             accessRules = value;
             return this;
         }
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index 519d016..5eb75e7 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,14 +35,135 @@
  * network during a SUBSCRIBE request. See RFC3863 for more information.
  * @hide
  */
+@SystemApi
 public final class RcsContactPresenceTuple implements Parcelable {
 
-    /** The service id of the MMTEL */
+    /**
+     * The service ID used to indicate that MMTEL service is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
     public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
 
-    /** The service id of the Call Composer */
+    /**
+     * The service ID used to indicate that the chat(v1.0) is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+
+    /**
+     * The service ID used to indicate that the chat(v2.0) is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+
+    /**
+     * The service ID used to indicate that the File Transfer is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+
+    /**
+     * The service ID used to indicate that the File Transfer over SMS is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_FT_OVER_SMS =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+
+    /**
+     * The service ID used to indicate that the Geolocation Push is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_GEO_PUSH =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+
+    /**
+     * The service ID used to indicate that the Geolocation Push via SMS is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_GEO_PUSH_VIA_SMS =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+
+    /**
+     * The service ID used to indicate that the Call Composer is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
     public static final String SERVICE_ID_CALL_COMPOSER =
-            "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer";
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+
+    /**
+     * The service ID used to indicate that the Post Call is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_POST_CALL =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+
+    /**
+     * The service ID used to indicate that the Shared Map is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_SHARED_MAP =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+
+    /**
+     * The service ID used to indicate that the Shared Sketch is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_SHARED_SKETCH =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+
+    /**
+     * The service ID used to indicate that the Chatbot using Session is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHATBOT =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+
+    /**
+     * The service ID used to indicate that the Standalone Messaging is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHATBOT_STANDALONE =
+            " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+
+    /**
+     * The service ID used to indicate that the Chatbot Role is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "SERVICE_ID_", value = {
+            SERVICE_ID_MMTEL,
+            SERVICE_ID_CHAT_V1,
+            SERVICE_ID_CHAT_V2,
+            SERVICE_ID_FT,
+            SERVICE_ID_FT_OVER_SMS,
+            SERVICE_ID_GEO_PUSH,
+            SERVICE_ID_GEO_PUSH_VIA_SMS,
+            SERVICE_ID_CALL_COMPOSER,
+            SERVICE_ID_POST_CALL,
+            SERVICE_ID_SHARED_MAP,
+            SERVICE_ID_SHARED_SKETCH,
+            SERVICE_ID_CHATBOT,
+            SERVICE_ID_CHATBOT_STANDALONE,
+            SERVICE_ID_CHATBOT_ROLE
+    })
+    public @interface ServiceId {}
 
     /** The service capabilities is available. */
     public static final String TUPLE_BASIC_STATUS_OPEN = "open";
@@ -149,6 +271,7 @@
             in.readStringList(mSupportedDuplexModeList);
             in.readStringList(mUnsupportedDuplexModeList);
         }
+
         @Override
         public void writeToParcel(@NonNull Parcel out, int flags) {
             out.writeBoolean(mIsAudioCapable);
@@ -217,12 +340,14 @@
 
         /**
          * Builds a RcsContactPresenceTuple instance.
+         * @param status The status associated with the service capability. See RFC3865 for more
+         * information.
          * @param serviceId The OMA Presence service-id associated with this capability. See the
          * OMA Presence SIMPLE specification v1.1, section 10.5.1.
          * @param serviceVersion The OMA Presence version associated with the service capability.
          * See the OMA Presence SIMPLE specification v1.1, section 10.5.1.
          */
-        public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId,
+        public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId,
                 @NonNull String serviceVersion) {
             mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion);
         }
@@ -230,16 +355,17 @@
         /**
          * The optional SIP Contact URI associated with the PIDF tuple element.
          */
-        public @NonNull Builder addContactUri(@NonNull Uri contactUri) {
+        public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
             mPresenceTuple.mContactUri = contactUri;
             return this;
         }
 
         /**
          * The optional timestamp indicating the data and time of the status change of this tuple.
-         * See RFC3863, section 4.1.7 for more information on the expected format.
+         * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
+         * string per RFC3339.
          */
-        public @NonNull Builder addTimeStamp(@NonNull String timestamp) {
+        public @NonNull Builder setTimestamp(@NonNull String timestamp) {
             mPresenceTuple.mTimestamp = timestamp;
             return this;
         }
@@ -248,7 +374,7 @@
          * An optional parameter containing the description element of the service-description. See
          * OMA Presence SIMPLE specification v1.1
          */
-        public @NonNull Builder addDescription(@NonNull String description) {
+        public @NonNull Builder setServiceDescription(@NonNull String description) {
             mPresenceTuple.mServiceDescription = description;
             return this;
         }
@@ -257,7 +383,7 @@
          * An optional parameter containing the service capabilities of the presence tuple if they
          * are present in the servcaps element.
          */
-        public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+        public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) {
             mPresenceTuple.mServiceCapabilities = caps;
             return this;
         }
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index d4715bf..fe85502 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -33,6 +34,7 @@
  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
  * @hide
  */
+@SystemApi
 public final class RcsContactUceCapability implements Parcelable {
 
     /** Contains presence information associated with the contact */
@@ -70,52 +72,46 @@
     public @interface SourceType {}
 
     /**
+     * Capability information for the requested contact has expired and can not be refreshed due to
+     * a temporary network error. This is a temporary error and the capabilities of the contact
+     * should be queried again at a later time.
+     */
+    public static final int REQUEST_RESULT_UNKNOWN = 0;
+
+    /**
      * The requested contact was found to be offline when queried. This is only applicable to
      * contact capabilities that were queried via OPTIONS requests and the network returned a
      * 408/480 response.
      */
-    public static final int REQUEST_RESULT_NOT_ONLINE = 0;
+    public static final int REQUEST_RESULT_NOT_ONLINE = 1;
 
     /**
      * Capability information for the requested contact was not found. The contact should not be
      * considered an RCS user.
      */
-    public static final int REQUEST_RESULT_NOT_FOUND = 1;
+    public static final int REQUEST_RESULT_NOT_FOUND = 2;
 
     /**
      * Capability information for the requested contact was found successfully.
      */
-    public static final int REQUEST_RESULT_FOUND = 2;
-
-    /**
-     * Capability information for the requested contact has expired and can not be refreshed due to
-     * a temporary network error. This is a temporary error and the capabilities of the contact
-     * should be queried again at a later time.
-     */
-    public static final int REQUEST_RESULT_UNKNOWN = 3;
+    public static final int REQUEST_RESULT_FOUND = 3;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "REQUEST_RESULT_", value = {
+        REQUEST_RESULT_UNKNOWN,
         REQUEST_RESULT_NOT_ONLINE,
         REQUEST_RESULT_NOT_FOUND,
-        REQUEST_RESULT_FOUND,
-        REQUEST_RESULT_UNKNOWN
+        REQUEST_RESULT_FOUND
     })
     public @interface RequestResult {}
 
     /**
-     * The base class of {@link OptionsBuilder} and {@link PresenceBuilder}
-     */
-    public static abstract class RcsUcsCapabilityBuilder {
-        public abstract @NonNull RcsContactUceCapability build();
-    }
-
-    /**
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through SIP OPTIONS.
+     * @hide
      */
-    public static class OptionsBuilder extends RcsUcsCapabilityBuilder {
+    public static final class OptionsBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -162,7 +158,6 @@
         /**
          * @return the constructed instance.
          */
-        @Override
         public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
@@ -172,7 +167,7 @@
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through a presence server.
      */
-    public static class PresenceBuilder extends RcsUcsCapabilityBuilder {
+    public static final class PresenceBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -214,7 +209,6 @@
         /**
          * @return the RcsContactUceCapability instance.
          */
-        @Override
         public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
@@ -284,6 +278,7 @@
      * <p>
      * Note: this is only populated if {@link #getCapabilityMechanism} is
      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
+     * @hide
      */
     public @NonNull List<String> getOptionsFeatureTags() {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
@@ -299,7 +294,7 @@
      * Note: this is only populated if {@link #getCapabilityMechanism} is
      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
      */
-    public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+    public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
             return Collections.emptyList();
         }
@@ -309,13 +304,14 @@
     /**
      * Get the RcsContactPresenceTuple associated with the given service id.
      * @param serviceId The service id to get the presence tuple.
-     * @return The RcsContactPresenceTuple which has the given service id.
+     * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the
+     * service id does not exist in the list of presence tuples returned from the network.
      *
      * <p>
      * Note: this is only populated if {@link #getCapabilityMechanism} is
      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
      */
-    public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) {
+    public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
             return null;
         }
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 6c31466..070fd799 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -63,6 +63,7 @@
      * RcsFeature should not publish capabilities or service capability requests.
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
 
     /**@hide*/
@@ -77,12 +78,14 @@
      * An unknown error has caused the request to fail.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_GENERIC_FAILURE = 1;
 
     /**
      * The carrier network does not have UCE support enabled for this subscriber.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_NOT_ENABLED = 2;
 
     /**
@@ -90,12 +93,14 @@
      * 1x only currently).
      * @hide
      */
+    @SystemApi
     public static final int ERROR_NOT_AVAILABLE = 3;
 
     /**
      * The network has responded with SIP 403 error and a reason "User not registered."
      * @hide
      */
+    @SystemApi
     public static final int ERROR_NOT_REGISTERED = 4;
 
     /**
@@ -103,12 +108,14 @@
      * presence" for this subscriber.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_NOT_AUTHORIZED = 5;
 
     /**
      * The network has responded to this request with a SIP 403 error and no reason.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_FORBIDDEN = 6;
 
     /**
@@ -116,6 +123,7 @@
      * subscriber to the carrier network.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_NOT_FOUND = 7;
 
     /**
@@ -123,6 +131,7 @@
      * with a lower number of contact numbers. The number varies per carrier.
      * @hide
      */
+    @SystemApi
     // TODO: Try to integrate this into the API so that the service will split based on carrier.
     public static final int ERROR_REQUEST_TOO_LARGE = 8;
 
@@ -130,18 +139,21 @@
      * The network did not respond to the capabilities request before the request timed out.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_REQUEST_TIMEOUT = 9;
 
     /**
      * The request failed due to the service having insufficient memory.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_INSUFFICIENT_MEMORY = 10;
 
     /**
      * The network was lost while trying to complete the request.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_LOST_NETWORK = 11;
 
     /**
@@ -149,6 +161,7 @@
      * time returned in {@link CapabilitiesCallback#onError} has elapsed.
      * @hide
      */
+    @SystemApi
     public static final int ERROR_SERVER_UNAVAILABLE = 12;
 
     /**@hide*/
@@ -405,6 +418,7 @@
      * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
      * @hide
      */
+    @SystemApi
     public interface CapabilitiesCallback {
 
         /**
@@ -424,10 +438,10 @@
          * The pending request has resulted in an error and may need to be retried, depending on the
          * error code.
          * @param errorCode The reason for the framework being unable to process the request.
-         * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+         * @param retryIntervalMillis The time in milliseconds the requesting application should
          * wait before retrying, if non-zero.
          */
-        void onError(@ErrorCode int errorCode, long retryAfterMilliseconds);
+        void onError(@ErrorCode int errorCode, long retryIntervalMillis);
     }
 
     private final Context mContext;
@@ -458,9 +472,9 @@
      * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
      * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
      *
+     * @param contactNumbers A list of numbers that the capabilities are being requested for.
      * @param executor The executor that will be used when the request is completed and the
      *         {@link CapabilitiesCallback} is called.
-     * @param contactNumbers A list of numbers that the capabilities are being requested for.
      * @param c A one-time callback for when the request for capabilities completes or there is an
      *         error processing the request.
      * @throws ImsException if the subscription associated with this instance of
@@ -469,9 +483,10 @@
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
-            @NonNull List<Uri> contactNumbers,
+    public void requestCapabilities(@NonNull List<Uri> contactNumbers,
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull CapabilitiesCallback c) throws ImsException {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
@@ -495,8 +510,7 @@
             public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
                 final long callingIdentity = Binder.clearCallingIdentity();
                 try {
-                    executor.execute(() ->
-                            c.onCapabilitiesReceived(contactCapabilities));
+                    executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
                 } finally {
                     restoreCallingIdentity(callingIdentity);
                 }
@@ -550,13 +564,17 @@
      * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
      *
      * @param contactNumber The contact of the capabilities is being requested for.
+     * @param executor The executor that will be used when the request is completed and the
+     * {@link CapabilitiesCallback} is called.
      * @param c A one-time callback for when the request for capabilities completes or there is
      * an error processing the request.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    public void requestNetworkAvailability(@NonNull @CallbackExecutor Executor executor,
-            @NonNull Uri contactNumber, @NonNull CapabilitiesCallback c) throws ImsException {
+    public void requestAvailability(@NonNull Uri contactNumber,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CapabilitiesCallback c) throws ImsException {
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
@@ -569,7 +587,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "requestNetworkAvailability: IImsRcsController is null");
+            Log.e(TAG, "requestAvailability: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -579,8 +597,7 @@
             public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
                 final long callingIdentity = Binder.clearCallingIdentity();
                 try {
-                    executor.execute(() ->
-                            c.onCapabilitiesReceived(contactCapabilities));
+                    executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
                 } finally {
                     restoreCallingIdentity(callingIdentity);
                 }
@@ -606,12 +623,12 @@
         };
 
         try {
-            imsRcsController.requestNetworkAvailability(mSubId, mContext.getOpPackageName(),
+            imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(),
                     mContext.getAttributionTag(), contactNumber, internalCallback);
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.toString(), e.errorCode);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#requestNetworkAvailability", e);
+            Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -683,7 +700,7 @@
         if (imsRcsController == null) {
             Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
-                ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
         PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
@@ -694,7 +711,7 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
             throw new ImsException("Remote IMS Service is not available",
-                ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 3634989..7a6c28b 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -52,7 +52,7 @@
     // ImsUceAdapter specific
     void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
             in List<Uri> contactNumbers, IRcsUceControllerCallback c);
-    void requestNetworkAvailability(int subId, String callingPackage,
+    void requestAvailability(int subId, String callingPackage,
             String callingFeatureId, in Uri contactNumber,
             IRcsUceControllerCallback c);
     int getUcePublishState(int subId);
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index c84e23c..7eba709 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
@@ -139,18 +140,19 @@
          * Provide the framework with a subsequent network response update to
          * {@link #publishCapabilities(String, PublishResponseCallback)}.
          *
-         * @param code The SIP response code sent from the network for the operation
+         * @param sipCode The SIP response code sent from the network for the operation
          * token specified.
          * @param reason The optional reason response from the network. If there is a reason header
          * included in the response, that should take precedence over the reason provided in the
-         * status line. If the network provided no reason with the code, the string should be empty.
+         * status line. If the network provided no reason with the sip code, the string should be
+         * empty.
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
          * not currently connected to the framework. This can happen if the {@link RcsFeature}
          * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
          * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
          * when the Telephony stack has crashed.
          */
-        void onNetworkResponse(@IntRange(from = 100, to = 699) int code,
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
                 @NonNull String reason) throws ImsException;
     }
 
@@ -173,7 +175,7 @@
 
         /**
          * Send the response of a SIP OPTIONS capability exchange to the framework.
-         * @param code The SIP response code that was sent by the network in response
+         * @param sipCode The SIP response code that was sent by the network in response
          * to the request sent by {@link #sendOptionsCapabilityRequest}.
          * @param reason The optional SIP response reason sent by the network.
          * If none was sent, this should be an empty string.
@@ -186,17 +188,20 @@
          * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
          * cases when the Telephony stack has crashed.
          */
-        void onNetworkResponse(int code, @NonNull String reason,
+        void onNetworkResponse(int sipCode, @NonNull String reason,
                 @Nullable List<String> theirCaps) throws ImsException;
     }
 
     /**
      * Interface used by the framework to receive the response of the subscribe request.
-     * @hide
      */
     public interface SubscribeResponseCallback {
         /**
          * Notify the framework that the command associated with this callback has failed.
+         * <p>
+         * Must only be called when there was an error generating a SUBSCRIBE request due to an
+         * IMS stack error. This is a terminating event, so no other callback event will be
+         * expected after this callback.
          *
          * @param code The reason why the associated command has failed.
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
@@ -211,27 +216,38 @@
         /**
          * Notify the framework of the response to the SUBSCRIBE request from
          * {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}.
+         * <p>
+         * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
+         * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
+         * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
+         * subsequent NOTIFY responses to the subscription.
          *
-         * @param code The SIP response code sent from the network for the operation
+         * @param sipCode The SIP response code sent from the network for the operation
          * token specified.
          * @param reason The optional reason response from the network. If the network
-         *  provided no reason with the code, the string should be empty.
+         *  provided no reason with the sip code, the string should be empty.
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
          * not currently connected to the framework. This can happen if the
          * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
          * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
          * This may also happen in rare cases when the Telephony stack has crashed.
          */
-        void onNetworkResponse(@IntRange(from = 100, to = 699) int code,
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
                 @NonNull String reason) throws ImsException;
 
         /**
-         * Provides the framework with latest XML PIDF documents included in the
-         * network response for the requested  contacts' capabilities requested by the
-         * Framework using {@link #requestCapabilities(List, int)}. This should be
-         * called every time a new NOTIFY event is received with new capability
-         * information.
+         * Notify the framework of the latest XML PIDF documents included in the network response
+         * for the requested contacts' capabilities requested by the Framework using
+         * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
+         * <p>
+         * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a
+         * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY
+         * responses that contain RLMI information and potentially multiple PIDF XMLs, each
+         * PIDF XML should be separated and added as a separate item in the List. This should be
+         * called every time a new NOTIFY event is received with new capability information.
          *
+         * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed
+         * for.
          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
          * not currently connected to the framework.
          * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
@@ -242,21 +258,42 @@
         void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
 
         /**
-         * A resource in the resource list for the presence subscribe event has been terminated.
+         * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response
+         * for the ongoing SUBSCRIBE dialog has been terminated.
          * <p>
-         * This allows the framework to know that there will not be any capability information for
-         * a specific contact URI that they subscribed for.
+         * This will be used to notify the framework that a contact URI that the IMS stack has
+         * subscribed to on the Resource List Server has been terminated as well as the reason why.
+         * Usually this means that there will not be any capability information for the contact URI
+         * that they subscribed for. See RFC 4662 for more information.
+         *
+         * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the
+         * list is the contact URI and its terminated reason.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework.
+         * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+         * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
          */
         void onResourceTerminated(
                 @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException;
 
         /**
-         * The subscription associated with a previous #requestCapabilities operation
-         * has been terminated. This will mostly be due to the subscription expiring,
-         * but may also happen due to an error.
-         * <p>
-         * This allows the framework to know that there will no longer be any
-         * capability updates for the requested operationToken.
+         * The subscription associated with a previous
+         * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}
+         * operation has been terminated. This will mostly be due to the network sending a final
+         * NOTIFY response due to the subscription expiring, but this may also happen due to a
+         * network error.
+         *
+         * @param reason The reason for the request being unable to process.
+         * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+         * wait before retrying, if non-zero.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework.
+         * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+         * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
          */
         void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
     }
@@ -278,18 +315,23 @@
     /**
      * The user capabilities of one or multiple contacts have been requested by the framework.
      * <p>
+     * The implementer must follow up this call with an
+     * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
      * The response from the network to the SUBSCRIBE request must be sent back to the framework
-     * using {@link #onSubscribeNetworkResponse(int, String, int)}. As NOTIFY requests come in from
-     * the network, the requested contact’s capabilities should be sent back to the framework using
-     * {@link #onSubscribeNotifyRequest} and {@link onSubscribeResourceTerminated}
+     * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
+     * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
+     * sent back to the framework using
+     * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
+     * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
      * should be called with the presence information for the contacts specified.
      * <p>
-     * Once the subscription is terminated, {@link #onSubscriptionTerminated} must be called for
-     * the framework to finish listening for NOTIFY responses.
+     * Once the subscription is terminated,
+     * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
+     * framework to finish listening for NOTIFY responses.
+     *
      * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
      * capabilities for.
      * @param cb The callback of the subscribe request.
-     * @hide
      */
     // executor used is defined in the constructor.
     @SuppressLint("ExecutorRegistration")
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e556664..c5ff47b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2382,6 +2382,11 @@
      */
     String getMobileProvisioningUrl();
 
+    /*
+     * Remove the EAB contacts from the EAB database.
+     */
+    int removeContactFromEab(int subId, String contacts);
+
     /**
      * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
      * specified thresholds.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index ca73689..81c1688 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -64,6 +64,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -5963,23 +5964,18 @@
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         && nc.hasTransport(TRANSPORT_WIFI));
-
-        // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
-        // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+        callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
         callback.assertNoCallback();
 
         assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
                 .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
-        assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);  // BUG: VPN caps have NOT_SUSPENDED.
+        assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
-        // BUG: the device has connectivity, so this should return true.
-        assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+        assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
 
-        // Unsuspend cellular and then switch back to it.
-        // The same bug happens in the opposite direction: the VPN's capabilities correctly have
-        // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+        // Unsuspend cellular and then switch back to it. The VPN remains not suspended.
         mCellNetworkAgent.resume();
         callback.assertNoCallback();
         mWiFiNetworkAgent.disconnect();
@@ -5996,12 +5992,11 @@
                 .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
-        assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);  // BUG: VPN caps have NOT_SUSPENDED.
+        assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
-        // BUG: the device has connectivity, so this should return true.
-        assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+        assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
 
-        // Re-suspending the current network fixes the problem.
+        // Suspend cellular and expect no connectivity.
         mCellNetworkAgent.suspend();
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6017,6 +6012,7 @@
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
         assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
 
+        // Resume cellular and expect that connectivity comes back.
         mCellNetworkAgent.resume();
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6407,10 +6403,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        // While the SUSPENDED callback should in theory be sent here, it is not. This is
-        // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
-        // been public and are deprecated and slated for removal, there is no sense in spending
-        // resources fixing this bug now.
+        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
@@ -6422,8 +6415,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
-        // worth anybody's time to fix.
+        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Disconnect cell. Receive update without even removing the dead network from the
@@ -7335,39 +7327,68 @@
         b2.expectBroadcast();
     }
 
+    /**
+     * Test mutable and requestable network capabilities such as
+     * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+     * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+     * {@code ConnectivityService} re-assign the networks accordingly.
+     */
     @Test
-    public final void testLoseTrusted() throws Exception {
-        final NetworkRequest trustedRequest = new NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_TRUSTED)
-                .build();
-        final TestNetworkCallback trustedCallback = new TestNetworkCallback();
-        mCm.requestNetwork(trustedRequest, trustedCallback);
+    public final void testLoseMutableAndRequestableCaps() throws Exception {
+        final int[] testCaps = new int [] {
+                NET_CAPABILITY_TRUSTED,
+                NET_CAPABILITY_NOT_VCN_MANAGED
+        };
+        for (final int testCap : testCaps) {
+            // Create requests with and without the testing capability.
+            final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+            final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+            mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+                    callbackWithCap);
+            mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+                    callbackWithoutCap);
 
-        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        mCellNetworkAgent.connect(true);
-        trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-        reset(mMockNetd);
+            // Setup networks with testing capability and verify the default network changes.
+            mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+            mCellNetworkAgent.addCapability(testCap);
+            mCellNetworkAgent.connect(true);
+            callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+            callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+            verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(true);
-        trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
-        verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
-        reset(mMockNetd);
+            mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+            mWiFiNetworkAgent.addCapability(testCap);
+            mWiFiNetworkAgent.connect(true);
+            callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+            callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+            verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
-        mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
-        trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
-        verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-        reset(mMockNetd);
+            // Remove the testing capability on wifi, verify the callback and default network
+            // changes back to cellular.
+            mWiFiNetworkAgent.removeCapability(testCap);
+            callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+            callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+            // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
+            //  it.
+            if (testCap == NET_CAPABILITY_TRUSTED) {
+                verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+                reset(mMockNetd);
+            }
 
-        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
-        trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        verify(mMockNetd).networkClearDefault();
+            mCellNetworkAgent.removeCapability(testCap);
+            callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            callbackWithoutCap.assertNoCallback();
+            if (testCap == NET_CAPABILITY_TRUSTED) {
+                verify(mMockNetd).networkClearDefault();
+            }
 
-        mCm.unregisterNetworkCallback(trustedCallback);
+            mCm.unregisterNetworkCallback(callbackWithCap);
+            mCm.unregisterNetworkCallback(callbackWithoutCap);
+        }
     }
 
-    @Ignore // 40%+ flakiness : figure out why and re-enable.
     @Test
     public final void testBatteryStatsNetworkType() throws Exception {
         final LinkProperties cellLp = new LinkProperties();
@@ -7375,8 +7396,8 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         mCellNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
-                TYPE_MOBILE);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+                new int[] { TRANSPORT_CELLULAR });
         reset(mBatteryStatsService);
 
         final LinkProperties wifiLp = new LinkProperties();
@@ -7384,18 +7405,20 @@
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
-                TYPE_WIFI);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(),
+                new int[] { TRANSPORT_WIFI });
         reset(mBatteryStatsService);
 
         mCellNetworkAgent.disconnect();
+        mWiFiNetworkAgent.disconnect();
 
         cellLp.setInterfaceName("wifi0");
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         mCellNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
-                TYPE_MOBILE);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+                new int[] { TRANSPORT_CELLULAR });
+        mCellNetworkAgent.disconnect();
     }
 
     /**
@@ -7468,8 +7491,8 @@
         assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
         verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
         verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
-        verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
-                TYPE_MOBILE);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+                new int[] { TRANSPORT_CELLULAR });
 
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -7489,7 +7512,8 @@
         // Make sure BatteryStats was not told about any v4- interfaces, as none should have
         // come online yet.
         waitForIdle();
-        verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+        verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"),
+                any());
 
         verifyNoMoreInteractions(mMockNetd);
         verifyNoMoreInteractions(mMockDnsResolver);
@@ -7542,8 +7566,8 @@
         assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
 
         for (final LinkProperties stackedLp : stackedLpsAfterChange) {
-            verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
-                    TYPE_MOBILE);
+            verify(mBatteryStatsService).noteNetworkInterfaceForTransports(
+                    stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR });
         }
         reset(mMockNetd);
         when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
new file mode 100644
index 0000000..d936183
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectingState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase {
+    private VcnIkeSession mIkeSession;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mGatewayConnection.transitionTo(mGatewayConnection.mConnectingState);
+        mTestLooper.dispatchAll();
+
+        mIkeSession = mGatewayConnection.getIkeSession();
+    }
+
+    @Test
+    public void testEnterStateCreatesNewIkeSession() throws Exception {
+        verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+    }
+
+    @Test
+    public void testNullNetworkTriggersDisconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).kill();
+    }
+
+    @Test
+    public void testNewNetworkTriggersReconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).close();
+        verify(mIkeSession, never()).kill();
+    }
+
+    @Test
+    public void testSameNetworkDoesNotTriggerReconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testChildSessionClosedTriggersDisconnect() throws Exception {
+        getChildSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).close();
+    }
+
+    @Test
+    public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+        getIkeSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).close();
+    }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 0000000..d0fec55
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+        mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testIkeSessionClosed() throws Exception {
+        getIkeSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testTimeoutExpired() throws Exception {
+        mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+        mTestLooper.dispatchAll();
+
+        verify(mMockIkeSession).kill();
+    }
+
+    @Test
+    public void testTeardown() throws Exception {
+        mGatewayConnection.teardownAsynchronously();
+        mTestLooper.dispatchAll();
+
+        // Should do nothing; already tearing down.
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+    }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 1725dd9..b4d39bf 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -17,11 +17,13 @@
 package com.android.server.vcn;
 
 import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
 import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -30,6 +32,8 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
 import android.os.ParcelUuid;
@@ -38,6 +42,7 @@
 import com.android.server.IpSecService;
 
 import org.junit.Before;
+import org.mockito.ArgumentCaptor;
 
 import java.util.UUID;
 
@@ -68,6 +73,7 @@
 
     @NonNull protected final IpSecService mIpSecSvc;
 
+    protected VcnIkeSession mMockIkeSession;
     protected VcnGatewayConnection mGatewayConnection;
 
     public VcnGatewayConnectionTestBase() {
@@ -100,6 +106,23 @@
                         TEST_IPSEC_TUNNEL_IFACE);
         doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
 
+        mMockIkeSession = mock(VcnIkeSession.class);
+        doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
         mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
     }
+
+    protected IkeSessionCallback getIkeSessionCallback() {
+        ArgumentCaptor<IkeSessionCallback> captor =
+                ArgumentCaptor.forClass(IkeSessionCallback.class);
+        verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+        return captor.getValue();
+    }
+
+    protected ChildSessionCallback getChildSessionCallback() {
+        ArgumentCaptor<ChildSessionCallback> captor =
+                ArgumentCaptor.forClass(ChildSessionCallback.class);
+        verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
+        return captor.getValue();
+    }
 }