Merge "Define and use default request in Ikev2VpnRunner"
diff --git a/Android.bp b/Android.bp
index eb1718e..570040f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -744,6 +744,7 @@
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
+        "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
         "telephony/java/android/telephony/Annotation.java",
     ],
 }
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 7060347..cb36e63 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -110,37 +110,9 @@
 }
 
 /////////////////////////////////////////////////////////////////////
-// *-api-stubs-docs modules providing source files for the stub libraries
+// These modules provide source files for the stub libraries
 /////////////////////////////////////////////////////////////////////
 
-// api-stubs-docs, system-api-stubs-docs, and test-api-stubs-docs have APIs
-// from the non-updatable part of the platform as well as from the updatable
-// modules
-droidstubs {
-    name: "api-stubs-docs",
-    defaults: ["metalava-full-api-stubs-default"],
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
-    args: metalava_framework_docs_args,
-    check_api: {
-        current: {
-            api_file: "api/current.txt",
-            removed_api_file: "api/removed.txt",
-        },
-        last_released: {
-            api_file: ":android.api.public.latest",
-            removed_api_file: ":removed.api.public.latest",
-            baseline_file: ":public-api-incompatibilities-with-last-released",
-        },
-        api_lint: {
-            enabled: true,
-            new_since: ":android.api.public.latest",
-            baseline_file: "api/lint-baseline.txt",
-        },
-    },
-}
-
 droidstubs {
     name: "api-stubs-docs-non-updatable",
     defaults: ["metalava-non-updatable-api-stubs-default"],
@@ -177,31 +149,6 @@
     "\\) "
 
 droidstubs {
-    name: "system-api-stubs-docs",
-    defaults: ["metalava-full-api-stubs-default"],
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
-    args: metalava_framework_docs_args + priv_apps,
-    check_api: {
-        current: {
-            api_file: "api/system-current.txt",
-            removed_api_file: "api/system-removed.txt",
-        },
-        last_released: {
-            api_file: ":android.api.system.latest",
-            removed_api_file: ":removed.api.system.latest",
-            baseline_file: ":system-api-incompatibilities-with-last-released"
-        },
-        api_lint: {
-            enabled: true,
-            new_since: ":android.api.system.latest",
-            baseline_file: "api/system-lint-baseline.txt",
-        },
-    },
-}
-
-droidstubs {
     name: "system-api-stubs-docs-non-updatable",
     defaults: ["metalava-non-updatable-api-stubs-default"],
     arg_files: ["core/res/AndroidManifest.xml"],
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 42725c5..e0467df 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -154,6 +154,7 @@
      * @param tag Debugging tag for dumps associated with this job (instead of the service class)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public abstract @Result int scheduleAsPackage(@NonNull JobInfo job, @NonNull String packageName,
@@ -196,6 +197,7 @@
      * Returns a list of all currently-executing jobs.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract List<JobInfo> getStartedJobs();
 
     /**
@@ -205,5 +207,6 @@
      * <p class="note">This is a slow operation, so it should be called sparingly.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract List<JobSnapshot> getAllJobSnapshots();
 }
\ No newline at end of file
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index 71a52bb..e30df05 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -21,7 +21,7 @@
 apex_defaults {
     name: "com.android.permission-defaults",
     updatable: true,
-    min_sdk_version: "R",
+    min_sdk_version: "30",
     key: "com.android.permission.key",
     certificate: ":com.android.permission.certificate",
     java_libs: [
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index ede8852..f13861e 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -35,7 +35,7 @@
     prebuilts: ["com.android.os.statsd.init.rc"],
     name: "com.android.os.statsd-defaults",
     updatable: true,
-    min_sdk_version: "R",
+    min_sdk_version: "30",
     key: "com.android.os.statsd.key",
     certificate: ":com.android.os.statsd.certificate",
 }
diff --git a/api/Android.bp b/api/Android.bp
index ae0d596..471b993 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -57,6 +57,25 @@
 }
 
 genrule {
+    name: "frameworks-base-api-current.srcjar",
+    srcs: [
+        ":api-stubs-docs-non-updatable",
+        ":conscrypt.module.public.api{.public.stubs.source}",
+        ":framework-media{.public.stubs.source}",
+        ":framework-mediaprovider{.public.stubs.source}",
+        ":framework-permission{.public.stubs.source}",
+        ":framework-sdkextensions{.public.stubs.source}",
+        ":framework-statsd{.public.stubs.source}",
+        ":framework-tethering{.public.stubs.source}",
+        ":framework-wifi{.public.stubs.source}",
+    ],
+    out: ["current.srcjar"],
+    tools: ["merge_zips"],
+    cmd: "$(location merge_zips) $(out) $(in)",
+    visibility: ["//visibility:private"], // Used by make module in //development, mind.
+}
+
+genrule {
     name: "frameworks-base-api-removed.txt",
     srcs: [
         ":conscrypt.module.public.api{.public.removed-api.txt}",
diff --git a/api/current.txt b/api/current.txt
index b5cd13b..6632db3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -42742,6 +42742,12 @@
 
 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);
+  }
+
   public class KeyExpiredException extends java.security.InvalidKeyException {
     ctor public KeyExpiredException();
     ctor public KeyExpiredException(String);
@@ -42821,10 +42827,11 @@
     method public String getKeystoreAlias();
     method public int getOrigin();
     method public int getPurposes();
+    method public int getSecurityLevel();
     method @NonNull public String[] getSignaturePaddings();
     method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
-    method public boolean isInsideSecureHardware();
+    method @Deprecated public boolean isInsideSecureHardware();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isTrustedUserPresenceRequired();
     method public boolean isUserAuthenticationRequired();
@@ -45850,6 +45857,8 @@
     field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
     field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
     field public static final String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC";
+    field public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ = "android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
+    field public static final String EXTRA_AUDIO_CODEC_BITRATE_KBPS = "android.telecom.extra.AUDIO_CODEC_BITRATE_KBPS";
     field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
@@ -46296,6 +46305,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
+    method public boolean hasCompanionInCallServiceAccess();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -46695,6 +46705,7 @@
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
     field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
     field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+    field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
     field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
     field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
     field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -46889,6 +46900,10 @@
     field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
     field public static final int SERVICE_CLASS_NONE = 0; // 0x0
     field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+    field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+    field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+    field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+    field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
   }
 
   public static final class CarrierConfigManager.Apn {
diff --git a/api/system-current.txt b/api/system-current.txt
index 042e2dc..dcffa06 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9377,8 +9377,13 @@
     ctor public DeviceIdAttestationException(@Nullable String, @Nullable Throwable);
   }
 
+  public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    method public int getNamespace();
+  }
+
   public static final class KeyGenParameterSpec.Builder {
-    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
+    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setNamespace(int);
+    method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
   }
 
 }
@@ -10587,6 +10592,17 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
   }
 
+  public final class CarrierBandwidth implements android.os.Parcelable {
+    ctor public CarrierBandwidth(int, int, int, int);
+    method public int describeContents();
+    method public int getPrimaryDownlinkCapacityKbps();
+    method public int getPrimaryUplinkCapacityKbps();
+    method public int getSecondaryDownlinkCapacityKbps();
+    method public int getSecondaryUplinkCapacityKbps();
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CarrierBandwidth> CREATOR;
+    field public static final int INVALID = -1; // 0xffffffff
+  }
+
   public class CarrierConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
     method @NonNull public static android.os.PersistableBundle getDefaultConfig();
@@ -11203,6 +11219,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -11802,7 +11819,6 @@
     method public int getEmergencyServiceCategories();
     method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes();
     method @NonNull public android.os.Bundle getProprietaryCallExtras();
     method public int getRestrictCause();
     method public int getServiceType();
@@ -11824,7 +11840,6 @@
     method public void setEmergencyServiceCategories(int);
     method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
     method public void setHasKnownUserIntentEmergency(boolean);
-    method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -12333,6 +12348,7 @@
   public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public MmTelFeature();
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
     method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
     method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
     method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
diff --git a/config/hiddenapi-temp-blocklist.txt b/config/hiddenapi-temp-blocklist.txt
new file mode 100644
index 0000000..246eeea
--- /dev/null
+++ b/config/hiddenapi-temp-blocklist.txt
@@ -0,0 +1,55 @@
+Landroid/app/IActivityManager$Stub$Proxy;->setActivityController(Landroid/app/IActivityController;Z)V
+Landroid/app/IActivityManager$Stub$Proxy;->updatePersistentConfiguration(Landroid/content/res/Configuration;)V
+Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
+Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
+Landroid/app/INotificationManager$Stub;->TRANSACTION_enqueueNotificationWithTag:I
+Landroid/bluetooth/IBluetooth$Stub$Proxy;->getConnectionState(Landroid/bluetooth/BluetoothDevice;)I
+Landroid/bluetooth/IBluetooth$Stub;->TRANSACTION_enable:I
+Landroid/bluetooth/IBluetoothManager$Stub;->TRANSACTION_enable:I
+Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
+Landroid/content/om/IOverlayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/om/IOverlayManager;
+Landroid/content/pm/IPackageManager$Stub;->TRANSACTION_getApplicationInfo:I
+Landroid/hardware/ICameraService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/ICameraService;
+Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
+Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
+Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ICountryDetector;
+Landroid/location/IGeofenceProvider$Stub;-><init>()V
+Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
+Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
+Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
+Landroid/Manifest$permission;->READ_FRAME_BUFFER:Ljava/lang/String;
+Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
+Landroid/net/INetworkPolicyListener$Stub;-><init>()V
+Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
+Landroid/net/sip/ISipSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/sip/ISipSession;
+Landroid/net/wifi/IWifiManager$Stub;->TRANSACTION_getScanResults:I
+Landroid/net/wifi/p2p/IWifiP2pManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/p2p/IWifiP2pManager;
+Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I
+Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
+Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
+Landroid/service/euicc/IEuiccService$Stub;-><init>()V
+Landroid/service/media/IMediaBrowserServiceCallbacks$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/media/IMediaBrowserServiceCallbacks;
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
+Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
+Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
+Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V
+Lcom/android/ims/internal/IImsService$Stub;-><init>()V
+Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService;
+Lcom/android/ims/internal/IImsUt$Stub;-><init>()V
+Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
+Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
+Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
+Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
+Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
+Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
+Lcom/android/internal/widget/IRemoteViewsFactory$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/IRemoteViewsFactory;
diff --git a/config/hiddenapi-unsupported.txt b/config/hiddenapi-unsupported.txt
index a3543dc..8a377ac 100644
--- a/config/hiddenapi-unsupported.txt
+++ b/config/hiddenapi-unsupported.txt
@@ -26,19 +26,14 @@
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J
 Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Landroid/app/IActivityManager$Stub$Proxy;->setActivityController(Landroid/app/IActivityController;Z)V
-Landroid/app/IActivityManager$Stub$Proxy;->updatePersistentConfiguration(Landroid/content/res/Configuration;)V
-Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
 Landroid/app/IAlarmManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/IAlarmManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IAlarmManager;
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
 Landroid/app/IAssistDataReceiver$Stub;-><init>()V
-Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
 Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/INotificationManager$Stub$Proxy;->areNotificationsEnabledForPackage(Ljava/lang/String;I)Z
 Landroid/app/INotificationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/INotificationManager;
-Landroid/app/INotificationManager$Stub;->TRANSACTION_enqueueNotificationWithTag:I
 Landroid/app/IProcessObserver$Stub;-><init>()V
 Landroid/app/ISearchManager$Stub$Proxy;->getGlobalSearchActivity()Landroid/content/ComponentName;
 Landroid/app/ISearchManager$Stub$Proxy;->getWebSearchActivity()Landroid/content/ComponentName;
@@ -68,9 +63,7 @@
 Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/usage/IUsageStatsManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/usage/IUsageStatsManager;
 Landroid/bluetooth/IBluetooth$Stub$Proxy;->getAddress()Ljava/lang/String;
-Landroid/bluetooth/IBluetooth$Stub$Proxy;->getConnectionState(Landroid/bluetooth/BluetoothDevice;)I
 Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
-Landroid/bluetooth/IBluetooth$Stub;->TRANSACTION_enable:I
 Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp;
 Landroid/bluetooth/IBluetoothCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothGattCallback$Stub;-><init>()V
@@ -79,11 +72,9 @@
 Landroid/bluetooth/IBluetoothHidDeviceCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/bluetooth/IBluetoothManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothManager;
-Landroid/bluetooth/IBluetoothManager$Stub;->TRANSACTION_enable:I
 Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
 Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
-Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
 Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
 Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -108,7 +99,6 @@
 Landroid/content/ISyncStatusObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/ISyncStatusObserver$Stub;-><init>()V
 Landroid/content/ISyncStatusObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/ISyncStatusObserver;
-Landroid/content/om/IOverlayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/om/IOverlayManager;
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
@@ -141,7 +131,6 @@
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
 Landroid/content/pm/IPackageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageManager;
-Landroid/content/pm/IPackageManager$Stub;->TRANSACTION_getApplicationInfo:I
 Landroid/content/pm/IPackageMoveObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageMoveObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageMoveObserver;
 Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -155,30 +144,20 @@
 Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
 Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/fingerprint/IFingerprintService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/fingerprint/IFingerprintService;
-Landroid/hardware/ICameraService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/ICameraService;
 Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
-Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
-Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
 Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
 Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
-Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ICountryDetector;
 Landroid/location/ICountryListener$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider;
-Landroid/location/IGeofenceProvider$Stub;-><init>()V
 Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/location/ILocationListener$Stub;-><init>()V
 Landroid/location/ILocationListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationListener;
 Landroid/location/ILocationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
-Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
 Landroid/location/INetInitiatedListener$Stub;-><init>()V
-Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
-Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
-Landroid/Manifest$permission;->READ_FRAME_BUFFER:Ljava/lang/String;
 Landroid/media/IAudioRoutesObserver$Stub;-><init>()V
 Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/media/IAudioService$Stub;-><init>()V
@@ -186,7 +165,6 @@
 Landroid/media/IMediaRouterService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaRouterService;
 Landroid/media/IMediaScannerListener$Stub;-><init>()V
 Landroid/media/IMediaScannerService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaScannerService;
-Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
 Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager;
 Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
 Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -200,23 +178,17 @@
 Landroid/net/IConnectivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager;
 Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
-Landroid/net/INetworkPolicyListener$Stub;-><init>()V
 Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
 Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService;
 Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
 Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
-Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
-Landroid/net/sip/ISipSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/sip/ISipSession;
 Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/wifi/IWifiManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiManager;
-Landroid/net/wifi/IWifiManager$Stub;->TRANSACTION_getScanResults:I
 Landroid/net/wifi/IWifiScanner$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/wifi/IWifiScanner$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/net/wifi/IWifiScanner$Stub;-><init>()V
 Landroid/net/wifi/IWifiScanner$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiScanner;
-Landroid/net/wifi/p2p/IWifiP2pManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/p2p/IWifiP2pManager;
-Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I
 Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/IDeviceIdentifiersPolicyService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdentifiersPolicyService;
 Landroid/os/IDeviceIdleController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdleController;
@@ -227,8 +199,6 @@
 Landroid/os/IPowerManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/IPowerManager$Stub$Proxy;->isLightDeviceIdleMode()Z
 Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
-Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
-Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
 Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem;
 Landroid/os/IRemoteCallback$Stub;-><init>()V
 Landroid/os/IUpdateEngine$Stub;-><init>()V
@@ -241,16 +211,11 @@
 Landroid/security/IKeyChainService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeyChainService;
 Landroid/security/keystore/IKeystoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/keystore/IKeystoreService;
 Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
-Landroid/service/euicc/IEuiccService$Stub;-><init>()V
-Landroid/service/media/IMediaBrowserServiceCallbacks$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/media/IMediaBrowserServiceCallbacks;
 Landroid/service/notification/INotificationListener$Stub;-><init>()V
 Landroid/service/persistentdata/IPersistentDataBlockService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/persistentdata/IPersistentDataBlockService;
 Landroid/service/vr/IVrManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/vr/IVrManager;
 Landroid/service/wallpaper/IWallpaperConnection$Stub;-><init>()V
 Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
-Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
-Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
-Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
 Landroid/view/accessibility/IAccessibilityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/accessibility/IAccessibilityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/accessibility/IAccessibilityManager;
 Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -274,19 +239,7 @@
 Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/webkit/IWebViewUpdateService$Stub$Proxy;->waitForAndGetProvider()Landroid/webkit/WebViewProviderResponse;
 Landroid/webkit/IWebViewUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/webkit/IWebViewUpdateService;
-Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
-Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
 Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession;
-Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
-Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V
-Lcom/android/ims/internal/IImsService$Stub;-><init>()V
-Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService;
-Lcom/android/ims/internal/IImsUt$Stub;-><init>()V
-Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
-Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
-Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
 Lcom/android/internal/app/IAppOpsCallback$Stub;-><init>()V
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lang/String;)I
@@ -313,15 +266,10 @@
 Lcom/android/internal/app/IBatteryStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats;
 Lcom/android/internal/app/IMediaContainerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IMediaContainerService;
-Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
 Lcom/android/internal/app/IVoiceInteractionManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IVoiceInteractionManagerService;
 Lcom/android/internal/appwidget/IAppWidgetService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/appwidget/IAppWidgetService;
-Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
 Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
-Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
 Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
-Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
 Lcom/android/internal/os/IDropBoxManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/os/IDropBoxManagerService;
 Lcom/android/internal/policy/IKeyguardService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardService;
 Lcom/android/internal/policy/IKeyguardStateCallback$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardStateCallback;
@@ -343,9 +291,7 @@
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
-Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
 Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
@@ -355,4 +301,3 @@
 Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
 Lcom/android/internal/view/IInputMethodSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodSession;
 Lcom/android/internal/widget/ILockSettings$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/ILockSettings;
-Lcom/android/internal/widget/IRemoteViewsFactory$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/IRemoteViewsFactory;
diff --git a/core/api/current.txt b/core/api/current.txt
index 61a1424..02652b2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40910,6 +40910,12 @@
 
 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);
+  }
+
   public class KeyExpiredException extends java.security.InvalidKeyException {
     ctor public KeyExpiredException();
     ctor public KeyExpiredException(String);
@@ -40989,10 +40995,11 @@
     method public String getKeystoreAlias();
     method public int getOrigin();
     method public int getPurposes();
+    method public int getSecurityLevel();
     method @NonNull public String[] getSignaturePaddings();
     method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
-    method public boolean isInsideSecureHardware();
+    method @Deprecated public boolean isInsideSecureHardware();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isTrustedUserPresenceRequired();
     method public boolean isUserAuthenticationRequired();
@@ -44018,6 +44025,8 @@
     field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
     field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
     field public static final String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC";
+    field public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ = "android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
+    field public static final String EXTRA_AUDIO_CODEC_BITRATE_KBPS = "android.telecom.extra.AUDIO_CODEC_BITRATE_KBPS";
     field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
@@ -44464,6 +44473,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
+    method public boolean hasCompanionInCallServiceAccess();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -44863,6 +44873,7 @@
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
     field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
     field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+    field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
     field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
     field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
     field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -45057,6 +45068,10 @@
     field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
     field public static final int SERVICE_CLASS_NONE = 0; // 0x0
     field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+    field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+    field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+    field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+    field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
   }
 
   public static final class CarrierConfigManager.Apn {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f36f4f3..18d1064 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8259,8 +8259,13 @@
     ctor public DeviceIdAttestationException(@Nullable String, @Nullable Throwable);
   }
 
+  public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    method public int getNamespace();
+  }
+
   public static final class KeyGenParameterSpec.Builder {
-    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
+    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setNamespace(int);
+    method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
   }
 
 }
@@ -9469,6 +9474,17 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
   }
 
+  public final class CarrierBandwidth implements android.os.Parcelable {
+    ctor public CarrierBandwidth(int, int, int, int);
+    method public int describeContents();
+    method public int getPrimaryDownlinkCapacityKbps();
+    method public int getPrimaryUplinkCapacityKbps();
+    method public int getSecondaryDownlinkCapacityKbps();
+    method public int getSecondaryUplinkCapacityKbps();
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CarrierBandwidth> CREATOR;
+    field public static final int INVALID = -1; // 0xffffffff
+  }
+
   public class CarrierConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
     method @NonNull public static android.os.PersistableBundle getDefaultConfig();
@@ -10085,6 +10101,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -10684,7 +10701,6 @@
     method public int getEmergencyServiceCategories();
     method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes();
     method @NonNull public android.os.Bundle getProprietaryCallExtras();
     method public int getRestrictCause();
     method public int getServiceType();
@@ -10706,7 +10722,6 @@
     method public void setEmergencyServiceCategories(int);
     method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
     method public void setHasKnownUserIntentEmergency(boolean);
-    method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -11215,6 +11230,7 @@
   public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public MmTelFeature();
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
     method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
     method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
     method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 9a06867..a21a156 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -103,7 +103,6 @@
 public final class LoadedApk {
     static final String TAG = "LoadedApk";
     static final boolean DEBUG = false;
-    private static final String PROPERTY_NAME_APPEND_NATIVE = "pi.append_native_lib_paths";
 
     @UnsupportedAppUsage
     private final ActivityThread mActivityThread;
@@ -909,7 +908,7 @@
             needToSetupJitProfiles = true;
         }
 
-        if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
+        if (!libPaths.isEmpty()) {
             // Temporarily disable logging of disk reads on the Looper thread as this is necessary
             StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
             try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6fa5a7b..6edc8ea 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6680,7 +6680,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public boolean isDeviceManaged() {
         try {
             return mService.hasDeviceOwner();
@@ -10392,7 +10392,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public @Nullable CharSequence getDeviceOwnerOrganizationName() {
         try {
             return mService.getDeviceOwnerOrganizationName();
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
index 7e7710b..0f88811 100644
--- a/core/java/android/app/backup/OWNERS
+++ b/core/java/android/app/backup/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 656484
 
-include platform/frameworks/base/services/backup:/OWNERS
+include platform/frameworks/base:/services/backup/OWNERS
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index a941756..a6089c3 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -835,6 +835,7 @@
     }
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     protected abstract IContentProvider acquireProvider(Context c, String name);
 
@@ -851,15 +852,19 @@
     }
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean releaseProvider(IContentProvider icp);
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean releaseUnstableProvider(IContentProvider icp);
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void unstableProviderDied(IContentProvider icp);
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a98bd6a..7ffcead 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -800,6 +800,7 @@
      * case {@link #getOpPackageName()} will be the name of the primary package in
      * that process (so that app ops uid verification will work with the name).
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract String getBasePackageName();
 
@@ -916,6 +917,7 @@
      * @see #MODE_PRIVATE
      * @removed
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract SharedPreferences getSharedPreferences(File file, @PreferencesMode int mode);
 
     /**
@@ -946,6 +948,7 @@
     public abstract boolean deleteSharedPreferences(String name);
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void reloadSharedPreferences();
 
     /**
@@ -1034,6 +1037,7 @@
      * @see #getSharedPreferences(String, int)
      * @removed
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract File getSharedPreferencesPath(String name);
 
     /**
@@ -1505,6 +1509,7 @@
      * There is no guarantee when these files will be deleted.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @SystemApi
     public abstract File getPreloadsFileCache();
@@ -2173,6 +2178,7 @@
      * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
             String[] receiverPermissions);
 
@@ -2203,6 +2209,7 @@
      * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract void sendBroadcast(Intent intent,
             @Nullable String receiverPermission,
@@ -2213,6 +2220,7 @@
      * of an associated app op as per {@link android.app.AppOpsManager}.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void sendBroadcast(Intent intent,
             String receiverPermission, int appOp);
@@ -2328,6 +2336,7 @@
      * @see android.app.Activity#RESULT_OK
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract void sendOrderedBroadcast(@NonNull Intent intent,
             @Nullable String receiverPermission, @Nullable Bundle options,
@@ -2340,6 +2349,7 @@
      * of an associated app op as per {@link android.app.AppOpsManager}.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void sendOrderedBroadcast(Intent intent,
             String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
@@ -2393,6 +2403,7 @@
      * @see #sendBroadcast(Intent, String, Bundle)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
@@ -2415,6 +2426,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent,
@@ -2461,6 +2473,7 @@
      *       BroadcastReceiver, Handler, int, String, Bundle)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
@@ -2474,6 +2487,7 @@
      *       BroadcastReceiver, Handler, int, String, Bundle)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage
     public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
@@ -2688,6 +2702,7 @@
      * @hide
      * This is just here for sending CONNECTIVITY_ACTION.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @RequiresPermission(allOf = {
             android.Manifest.permission.INTERACT_ACROSS_USERS,
@@ -2978,6 +2993,7 @@
      * @see #sendBroadcast
      * @see #unregisterReceiver
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
     @UnsupportedAppUsage
@@ -3088,6 +3104,7 @@
     /**
      * @hide like {@link #startForegroundService(Intent)} but for a specific user.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user);
@@ -3126,6 +3143,7 @@
     /**
      * @hide like {@link #startService(Intent)} but for a specific user.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage
@@ -3134,6 +3152,7 @@
     /**
      * @hide like {@link #stopService(Intent)} but for a specific user.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
     public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
 
@@ -5190,6 +5209,7 @@
     public abstract int checkPermission(@NonNull String permission, int pid, int uid);
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @PackageManager.PermissionResult
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public abstract int checkPermission(@NonNull String permission, int pid, int uid,
@@ -5417,6 +5437,7 @@
             @Intent.AccessUriMode int modeFlags);
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @PackageManager.PermissionResult
     public abstract int checkUriPermission(Uri uri, int pid, int uid,
             @Intent.AccessUriMode int modeFlags, IBinder callerToken);
@@ -5701,6 +5722,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract Context createApplicationContext(ApplicationInfo application,
             @CreatePackageOptions int flags) throws PackageManager.NameNotFoundException;
@@ -5920,6 +5942,7 @@
      * @see #isCredentialProtectedStorage()
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract Context createCredentialProtectedStorageContext();
 
@@ -5932,6 +5955,7 @@
      * @return The compatibility info holder, or null if not required by the application.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
 
     /**
@@ -5966,12 +5990,14 @@
      * @see #getDisplay()
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @TestApi
     public abstract int getDisplayId();
 
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void updateDisplay(int displayId);
 
     /**
@@ -6000,6 +6026,7 @@
      * @see #createCredentialProtectedStorageContext()
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract boolean isCredentialProtectedStorage();
 
@@ -6007,6 +6034,7 @@
      * Returns true if the context can load unsafe resources, e.g. fonts.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract boolean canLoadUnsafeResources();
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1cca53f..81d9b11 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2101,6 +2101,7 @@
     }
 
     /** @hide */
+    @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
     public boolean isOem() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
     }
@@ -2148,11 +2149,13 @@
     }
 
     /** @hide */
+    @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
     public boolean isVendor() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
     /** @hide */
+    @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
     public boolean isProduct() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dbddc22..74463fd 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -40,7 +40,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.StorageStatsManager;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -3682,7 +3682,7 @@
      * @hide
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.R)
     public static final long FILTER_APPLICATION_QUERY = 135549675L;
 
     /** {@hide} */
@@ -3796,6 +3796,7 @@
      *             found on the system.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage
     public abstract PackageInfo getPackageInfoAsUser(@NonNull String packageName,
@@ -3862,6 +3863,7 @@
      *         does not contain such an activity.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
 
     /**
@@ -3927,6 +3929,7 @@
      *             found on the system.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract int getPackageUidAsUser(@NonNull String packageName, @UserIdInt int userId)
             throws NameNotFoundException;
@@ -3945,6 +3948,7 @@
      *             found on the system.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract int getPackageUidAsUser(@NonNull String packageName,
             @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
@@ -3987,6 +3991,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract boolean arePermissionsIndividuallyControlled();
 
@@ -3995,6 +4000,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract boolean isWirelessConsentModeEnabled();
 
     /**
@@ -4047,6 +4053,7 @@
             @ApplicationInfoFlags int flags) throws NameNotFoundException;
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
@@ -4228,6 +4235,7 @@
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@@ -4276,6 +4284,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     @NonNull
     @TestApi
@@ -4389,6 +4398,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
     public abstract void grantRuntimePermission(@NonNull String packageName,
@@ -4415,6 +4425,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
     public abstract void revokeRuntimePermission(@NonNull String packageName,
@@ -4459,6 +4470,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
@@ -4481,6 +4493,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
@@ -4704,6 +4717,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
 
@@ -4819,6 +4833,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @TestApi
     public abstract @Nullable String[] getNamesForUids(int[] uids);
 
@@ -4835,6 +4850,7 @@
      *             found on the system.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract int getUidForSharedUser(@NonNull String sharedUserName)
             throws NameNotFoundException;
@@ -4878,6 +4894,7 @@
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @TestApi
     public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
@@ -4890,6 +4907,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
     public abstract @NonNull List<InstantAppInfo> getInstantApps();
@@ -4901,6 +4919,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
     public abstract @Nullable Drawable getInstantAppIcon(String packageName);
@@ -4949,6 +4968,7 @@
      * deprecated
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract int getInstantAppCookieMaxSize();
 
     /**
@@ -5006,6 +5026,7 @@
     /**
      * @removed
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract boolean setInstantAppCookie(@Nullable byte[] cookie);
 
     /**
@@ -5044,6 +5065,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
             @InstallFlags int flags, @UserIdInt int userId);
 
@@ -5056,6 +5078,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
     @SystemApi
@@ -5075,6 +5098,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     @TestApi
     public abstract @NonNull String getServicesSystemSharedLibraryPackageName();
@@ -5086,6 +5110,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     @TestApi
     public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
@@ -5192,6 +5217,7 @@
      *         containing something else, such as the activity resolver.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
@@ -5233,6 +5259,7 @@
      *         empty list is returned.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
@@ -5256,6 +5283,7 @@
      *         empty list is returned.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
@@ -5318,6 +5346,7 @@
      *         no matching receivers, an empty list or null is returned.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
@@ -5329,6 +5358,7 @@
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
@@ -5367,6 +5397,7 @@
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
             @ResolveInfoFlags int flags, @UserIdInt int userId);
@@ -5399,6 +5430,7 @@
      *         empty list or null is returned.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
@@ -5437,6 +5469,7 @@
      *         no matching services, an empty list or null is returned.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
@@ -5504,6 +5537,7 @@
      *         provider. If a provider was not found, returns null.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
@@ -5888,6 +5922,7 @@
      * @return the drawable or null if no drawable is required.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract Drawable getUserBadgeForDensity(@NonNull UserHandle user, int density);
@@ -5906,6 +5941,7 @@
      * @return the drawable or null if no drawable is required.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract Drawable getUserBadgeForDensityNoBackground(@NonNull UserHandle user,
@@ -6030,6 +6066,7 @@
             throws NameNotFoundException;
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract Resources getResourcesForApplicationAsUser(@NonNull String packageName,
@@ -6075,6 +6112,7 @@
      *
      * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @SystemApi
     public abstract int installExistingPackage(@NonNull String packageName)
@@ -6087,6 +6125,7 @@
      *
      * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @SystemApi
     public abstract int installExistingPackage(@NonNull String packageName,
@@ -6099,6 +6138,7 @@
      *
      * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @RequiresPermission(anyOf = {
             Manifest.permission.INSTALL_EXISTING_PACKAGES,
@@ -6179,6 +6219,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT)
     public abstract void verifyIntentFilter(int verificationId, int verificationCode,
@@ -6204,6 +6245,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
     public abstract int getIntentVerificationStatusAsUser(@NonNull String packageName,
@@ -6229,6 +6271,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
     public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String packageName,
@@ -6246,6 +6289,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
     public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
@@ -6262,6 +6306,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
     public abstract List<IntentFilter> getAllIntentFilters(@NonNull String packageName);
@@ -6276,6 +6321,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @SystemApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@@ -6293,6 +6339,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(allOf = {
             Manifest.permission.SET_PREFERRED_APPLICATIONS,
@@ -6319,6 +6366,7 @@
             @Nullable String installerPackageName);
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
     public abstract void setUpdateAvailable(@NonNull String packageName, boolean updateAvaialble);
@@ -6339,6 +6387,7 @@
      *            indicate that no callback is desired.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(Manifest.permission.DELETE_PACKAGES)
     @UnsupportedAppUsage
     public abstract void deletePackage(@NonNull String packageName,
@@ -6359,6 +6408,7 @@
      * @param userId The user Id
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
             Manifest.permission.INTERACT_ACROSS_USERS_FULL})
@@ -6376,6 +6426,7 @@
      *
      * @deprecated use {@link #getInstallSourceInfo(String)} instead
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @Nullable
     public abstract String getInstallerPackageName(@NonNull String packageName);
@@ -6415,6 +6466,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void clearApplicationUserData(@NonNull String packageName,
             @Nullable IPackageDataObserver observer);
@@ -6434,6 +6486,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void deleteApplicationCacheFiles(@NonNull String packageName,
             @Nullable IPackageDataObserver observer);
@@ -6456,6 +6509,7 @@
      *            callback is desired.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void deleteApplicationCacheFilesAsUser(@NonNull String packageName,
             @UserIdInt int userId, @Nullable IPackageDataObserver observer);
@@ -6489,6 +6543,7 @@
     }
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void freeStorageAndNotify(@Nullable String volumeUuid, long freeStorageSize,
             @Nullable IPackageDataObserver observer);
@@ -6522,6 +6577,7 @@
     }
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void freeStorage(@Nullable String volumeUuid, long freeStorageSize,
             @Nullable IntentSender pi);
@@ -6545,6 +6601,7 @@
      * @deprecated use {@link StorageStatsManager} instead.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @UnsupportedAppUsage
     public abstract void getPackageSizeInfoAsUser(@NonNull String packageName,
@@ -6676,6 +6733,7 @@
      * an app to be responsible for a particular role and to check current role
      * holders, see {@link android.app.role.RoleManager}.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Deprecated
     @UnsupportedAppUsage
     public abstract void replacePreferredActivity(@NonNull IntentFilter filter, int match,
@@ -6772,6 +6830,7 @@
      * default, if any.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract ComponentName getHomeActivities(@NonNull List<ResolveInfo> outActivities);
@@ -6873,6 +6932,7 @@
      * @param userId Ther userId of the user whose restrictions are to be flushed.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void flushPackageRestrictionsAsUser(@UserIdInt int userId);
 
@@ -6883,6 +6943,7 @@
      * or by installing it, such as with {@link #installExistingPackage(String)}
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean setApplicationHiddenSettingAsUser(@NonNull String packageName,
             boolean hidden, @NonNull UserHandle userHandle);
@@ -6892,6 +6953,7 @@
      * @see #setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean getApplicationHiddenSettingAsUser(@NonNull String packageName,
             @NonNull UserHandle userHandle);
@@ -6918,6 +6980,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
     public abstract void addOnPermissionsChangeListener(
@@ -6930,6 +6993,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
     public abstract void removeOnPermissionsChangeListener(
@@ -6943,6 +7007,7 @@
      *        application's AndroidManifest.xml.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias);
@@ -6950,6 +7015,7 @@
     /** Return the signing {@link KeySet} for this application.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract KeySet getSigningKeySet(@NonNull String packageName);
@@ -6961,6 +7027,7 @@
      * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean isSignedBy(@NonNull String packageName, @NonNull KeySet ks);
 
@@ -6970,6 +7037,7 @@
      * {@link #isSignedBy(String packageName, KeySet ks)}.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean isSignedByExactly(@NonNull String packageName, @NonNull KeySet ks);
 
@@ -7187,6 +7255,7 @@
      * @throws IllegalArgumentException if the package was not found.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean isPackageSuspendedForUser(@NonNull String packageName, int userId);
 
@@ -7262,6 +7331,7 @@
      * @param packageName the package to change the category hint for.
      * @param categoryHint the category hint to set.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void setApplicationCategoryHint(@NonNull String packageName,
             @ApplicationInfo.Category int categoryHint);
 
@@ -7277,34 +7347,43 @@
     }
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract int getMoveStatus(int moveId);
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void registerMoveCallback(@NonNull MoveCallback callback,
             @NonNull Handler handler);
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void unregisterMoveCallback(@NonNull MoveCallback callback);
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public abstract int movePackage(@NonNull String packageName, @NonNull VolumeInfo vol);
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public abstract @Nullable VolumeInfo getPackageCurrentVolume(@NonNull ApplicationInfo app);
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public abstract List<VolumeInfo> getPackageCandidateVolumes(
             @NonNull ApplicationInfo app);
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract int movePrimaryStorage(@NonNull VolumeInfo vol);
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume();
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes();
 
     /**
@@ -7314,6 +7393,7 @@
      * @return identity that uniquely identifies current device
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
 
@@ -7322,6 +7402,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean isUpgrade();
 
@@ -7351,6 +7432,7 @@
      *            {@link #ONLY_IF_NO_MATCH_FOUND}.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
             @UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
@@ -7362,12 +7444,14 @@
      * @param sourceUserId The source user id.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId);
 
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract Drawable loadItemIcon(@NonNull PackageItemInfo itemInfo,
@@ -7376,12 +7460,14 @@
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo,
             @Nullable ApplicationInfo appInfo);
 
     /** {@hide} */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean isPackageAvailable(@NonNull String packageName);
 
@@ -7599,6 +7685,7 @@
      *         user, {@code INSTALL_REASON_UNKNOWN} is returned.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @TestApi
     @InstallReason
     public abstract int getInstallReason(@NonNull String packageName, @NonNull UserHandle user);
@@ -7627,6 +7714,7 @@
      * @see {@link android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS}
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @SystemApi
     public abstract ComponentName getInstantAppResolverSettingsComponent();
@@ -7638,6 +7726,7 @@
      * @see {@link android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE}
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @SystemApi
     public abstract ComponentName getInstantAppInstallerComponent();
@@ -7648,6 +7737,7 @@
      * @see {@link android.provider.Settings.Secure#ANDROID_ID}
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     public abstract String getInstantAppAndroidId(@NonNull String packageName,
             @NonNull UserHandle user);
@@ -7692,6 +7782,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract void registerDexModule(@NonNull String dexModulePath,
             @Nullable DexModuleRegisterCallback callback);
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 2e48ce9..a33fb58 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -188,6 +188,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void prepare(int maxCount, @NonNull Surface surface)
             throws CameraAccessException;
 
@@ -227,6 +228,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void tearDown(@NonNull Surface surface) throws CameraAccessException;
 
     /**
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 4c96c54..f25b06b 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -407,7 +407,7 @@
      */
     @Nullable
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public HdmiClient getClient(int type) {
         if (mService == null) {
             return null;
@@ -440,7 +440,7 @@
      */
     @Nullable
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public HdmiPlaybackClient getPlaybackClient() {
         return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK);
     }
@@ -458,7 +458,7 @@
      */
     @Nullable
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public HdmiTvClient getTvClient() {
         return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV);
     }
@@ -476,7 +476,7 @@
      * @hide
      */
     @Nullable
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public HdmiAudioSystemClient getAudioSystemClient() {
         return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
     }
@@ -491,7 +491,7 @@
      * @return {@link HdmiSwitchClient} instance. {@code null} on failure.
      */
     @Nullable
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public HdmiSwitchClient getSwitchClient() {
         return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
     }
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1ed791d..d444807 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -654,7 +654,7 @@
      *             register a {@link android.hardware.location.ContextHubClientCallback}.
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public int registerCallback(@NonNull Callback callback) {
         return registerCallback(callback, null);
     }
@@ -688,7 +688,7 @@
      *             register a {@link android.hardware.location.ContextHubClientCallback}.
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public int registerCallback(Callback callback, Handler handler) {
         synchronized(this) {
             if (mCallback != null) {
@@ -892,7 +892,7 @@
      * @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister
      *             a {@link android.hardware.location.ContextHubClientCallback}.
      */
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     @Deprecated
     public int unregisterCallback(@NonNull Callback callback) {
       synchronized(this) {
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 0edd055..969db96 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -257,6 +257,7 @@
      * @throws IllegalArgumentException if id==0
      * @hide This API is not thoroughly elaborated yet
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract @Nullable Bitmap getMetadataImage(int id);
 
     /**
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 21634cc..1c35cb6 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -299,7 +299,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public boolean resetDevice() {
         return native_reset_device();
     }
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 0778943..6a8e3f9 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -36,12 +36,16 @@
     public static final int OEM_NETWORK_PREFERENCE_DEFAULT = 0;
 
     /**
-     * Prefer networks in order: NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_OEM_PAID, default.
+     * If an unmetered network is available, use it.
+     * Otherwise, if a network with the OEM_PAID capability is available, use it.
+     * Otherwise, use the general default network.
      */
     public static final int OEM_NETWORK_PREFERENCE_OEM_PAID = 1;
 
     /**
-     * Prefer networks in order: NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_OEM_PAID.
+     * If an unmetered network is available, use it.
+     * Otherwise, if a network with the OEM_PAID capability is available, use it.
+     * Otherwise, the app doesn't get a network.
      */
     public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK = 2;
 
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index a985e93..4e019cf 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -301,7 +301,7 @@
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
      */
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public static void setThreadStatsUid(int uid) {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
     }
@@ -339,7 +339,7 @@
      *
      * @see #setThreadStatsUid(int)
      */
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public static void clearThreadStatsUid() {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
     }
@@ -976,11 +976,17 @@
         }
     }
 
-    // NOTE: keep these in sync with android_net_TrafficStats.cpp
-    private static final int TYPE_RX_BYTES = 0;
-    private static final int TYPE_RX_PACKETS = 1;
-    private static final int TYPE_TX_BYTES = 2;
-    private static final int TYPE_TX_PACKETS = 3;
-    private static final int TYPE_TCP_RX_PACKETS = 4;
-    private static final int TYPE_TCP_TX_PACKETS = 5;
+    // NOTE: keep these in sync with {@code com_android_server_net_NetworkStatsService.cpp}.
+    /** {@hide} */
+    public static final int TYPE_RX_BYTES = 0;
+    /** {@hide} */
+    public static final int TYPE_RX_PACKETS = 1;
+    /** {@hide} */
+    public static final int TYPE_TX_BYTES = 2;
+    /** {@hide} */
+    public static final int TYPE_TX_PACKETS = 3;
+    /** {@hide} */
+    public static final int TYPE_TCP_RX_PACKETS = 4;
+    /** {@hide} */
+    public static final int TYPE_TCP_TX_PACKETS = 5;
 }
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index af06906..9dd0114 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -16,8 +16,13 @@
 
 package android.net.vcn;
 
+import android.net.vcn.VcnConfig;
+import android.os.ParcelUuid;
+
 /**
  * @hide
  */
 interface IVcnManagementService {
+    void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config);
+    void clearVcnConfig(in ParcelUuid subscriptionGroup);
 }
diff --git a/core/java/android/net/vcn/VcnConfig.aidl b/core/java/android/net/vcn/VcnConfig.aidl
new file mode 100644
index 0000000..67006a4
--- /dev/null
+++ b/core/java/android/net/vcn/VcnConfig.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.net.vcn;
+
+/** @hide */
+parcelable VcnConfig;
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
new file mode 100644
index 0000000..148acf1
--- /dev/null
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -0,0 +1,83 @@
+/*
+ * 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.net.vcn;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents a configuration for a Virtual Carrier Network.
+ *
+ * @hide
+ */
+public final class VcnConfig implements Parcelable {
+    @NonNull private static final String TAG = VcnConfig.class.getSimpleName();
+
+    private VcnConfig() {
+        validate();
+    }
+    // TODO: Implement getters, validators, etc
+
+    /**
+     * Validates this configuration.
+     *
+     * @hide
+     */
+    private void validate() {
+        // TODO: implement validation logic
+    }
+
+    // Parcelable methods
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {}
+
+    @NonNull
+    public static final Parcelable.Creator<VcnConfig> CREATOR =
+            new Parcelable.Creator<VcnConfig>() {
+                @NonNull
+                public VcnConfig createFromParcel(Parcel in) {
+                    // TODO: Ensure all methods are pulled from the parcels
+                    return new VcnConfig();
+                }
+
+                @NonNull
+                public VcnConfig[] newArray(int size) {
+                    return new VcnConfig[size];
+                }
+            };
+
+    /** This class is used to incrementally build {@link VcnConfig} objects. */
+    public static class Builder {
+        // TODO: Implement this builder
+
+        /**
+         * Builds and validates the VcnConfig.
+         *
+         * @return an immutable VcnConfig instance
+         */
+        @NonNull
+        public VcnConfig build() {
+            return new VcnConfig();
+        }
+    }
+}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index d563b03..6769b9e 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -18,11 +18,14 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
 
 /**
- * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks
+ * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
  *
  * @hide
  */
@@ -45,4 +48,56 @@
         mContext = requireNonNull(ctx, "missing context");
         mService = requireNonNull(service, "missing service");
     }
+
+    // TODO: Make setVcnConfig(), clearVcnConfig() Public API
+    /**
+     * Sets the VCN configuration for a given subscription group.
+     *
+     * <p>An app that has carrier privileges for any of the subscriptions in the given group may set
+     * a VCN configuration. If a configuration already exists for the given subscription group, it
+     * will be overridden. Any active VCN(s) may be forced to restart to use the new configuration.
+     *
+     * <p>This API is ONLY permitted for callers running as the primary user.
+     *
+     * @param subscriptionGroup the subscription group that the configuration should be applied to
+     * @param config the configuration parameters for the VCN
+     * @throws SecurityException if the caller does not have carrier privileges, or is not running
+     *     as the primary user
+     * @hide
+     */
+    @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
+    public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+        requireNonNull(subscriptionGroup, "subscriptionGroup was null");
+        requireNonNull(config, "config was null");
+
+        try {
+            mService.setVcnConfig(subscriptionGroup, config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    // TODO: Make setVcnConfig(), clearVcnConfig() Public API
+    /**
+     * Clears the VCN configuration for a given subscription group.
+     *
+     * <p>An app that has carrier privileges for any of the subscriptions in the given group may
+     * clear a VCN configuration. This API is ONLY permitted for callers running as the primary
+     * user. Any active VCN will be torn down.
+     *
+     * @param subscriptionGroup the subscription group that the configuration should be applied to
+     * @throws SecurityException if the caller does not have carrier privileges, or is not running
+     *     as the primary user
+     * @hide
+     */
+    @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
+    public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
+        requireNonNull(subscriptionGroup, "subscriptionGroup was null");
+
+        try {
+            mService.clearVcnConfig(subscriptionGroup);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index d91c458..010459d 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -170,6 +170,15 @@
     int FLAG_ONEWAY             = 0x00000001;
 
     /**
+     * Flag to {@link #transact}: request binder driver to clear transaction data.
+     *
+     * Be very careful when using this flag in Java, since Java objects read from a Java
+     * Parcel may be non-trivial to clear.
+     * @hide
+     */
+    int FLAG_CLEAR_BUF          = 0x00000020;
+
+    /**
      * @hide
      */
     int FLAG_COLLECT_NOTED_APP_OPS = 0x00000002;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9c7f8be..cf90174 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -277,6 +277,8 @@
     private static final int EX_TRANSACTION_FAILED = -129;
 
     @CriticalNative
+    private static native void nativeMarkSensitive(long nativePtr);
+    @CriticalNative
     private static native int nativeDataSize(long nativePtr);
     @CriticalNative
     private static native int nativeDataAvail(long nativePtr);
@@ -491,6 +493,14 @@
     public static native long getGlobalAllocCount();
 
     /**
+     * Parcel data should be zero'd before realloc'd or deleted.
+     * @hide
+     */
+    public final void markSensitive() {
+        nativeMarkSensitive(mNativePtr);
+    }
+
+    /**
      * Returns the total amount of data contained in the parcel.
      */
     public final int dataSize() {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8cdcd49..38e1704 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -422,7 +422,7 @@
      * {@hide}
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
         try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
             return verifyPackageCompatibility(inputStream);
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index ded9be5..ab74199 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -60,7 +60,7 @@
      * uses reflection to read this whenever text is selected (http://b/36095274).
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 172649311)
     public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
 
     /** @hide */
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 684ea08..648d934 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2221,7 +2221,7 @@
     /** @hide */
     @SystemApi
     @WorkerThread
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public long getAllocatableBytes(@NonNull UUID storageUuid,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
         try {
@@ -2270,7 +2270,7 @@
     /** @hide */
     @SystemApi
     @WorkerThread
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
         try {
@@ -2320,7 +2320,7 @@
     /** @hide */
     @SystemApi
     @WorkerThread
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
         final File file = ParcelFileDescriptor.getFile(fd);
diff --git a/core/java/android/service/autofill/InternalTransformation.java b/core/java/android/service/autofill/InternalTransformation.java
index 0dba2b9..d31ea99 100644
--- a/core/java/android/service/autofill/InternalTransformation.java
+++ b/core/java/android/service/autofill/InternalTransformation.java
@@ -45,6 +45,7 @@
      * @param template the {@link RemoteViews presentation template}.
      * @param childViewId resource id of the child view inside the template.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     abstract void apply(@NonNull ValueFinder finder, @NonNull RemoteViews template,
             int childViewId) throws Exception;
 
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index 2a809b1..4ffffc6 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -46,12 +46,13 @@
  * CarrierMessagingService.
  * @hide
  */
-public abstract class CarrierMessagingServiceWrapper {
+public final class CarrierMessagingServiceWrapper {
     // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
     // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
     private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
 
     private volatile ICarrierMessagingService mICarrierMessagingService;
+    private Runnable mOnServiceReadyCallback;
 
     /**
      * Binds to the carrier messaging service under package {@code carrierPackageName}. This method
@@ -63,12 +64,14 @@
      * @hide
      */
     public boolean bindToCarrierMessagingService(@NonNull Context context,
-            @NonNull String carrierPackageName) {
+            @NonNull String carrierPackageName,
+            @NonNull Runnable onServiceReadyCallback) {
         Preconditions.checkState(mCarrierMessagingServiceConnection == null);
 
         Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
         intent.setPackage(carrierPackageName);
         mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
+        mOnServiceReadyCallback = onServiceReadyCallback;
         return context.bindService(intent, mCarrierMessagingServiceConnection,
                 Context.BIND_AUTO_CREATE);
     }
@@ -83,22 +86,17 @@
         Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
         context.unbindService(mCarrierMessagingServiceConnection);
         mCarrierMessagingServiceConnection = null;
+        mOnServiceReadyCallback = null;
     }
 
     /**
-     * Implemented by subclasses to use the carrier messaging service once it is ready.
-     * @hide
-     */
-    public abstract void onServiceReady();
-
-    /**
      * Called when connection with service is established.
      *
      * @param carrierMessagingService the carrier messaing service interface
      */
     private void onServiceReady(ICarrierMessagingService carrierMessagingService) {
         mICarrierMessagingService = carrierMessagingService;
-        onServiceReady();
+        if (mOnServiceReadyCallback != null) mOnServiceReadyCallback.run();
     }
 
     /**
@@ -113,11 +111,11 @@
      * @hide
      */
     public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
-            int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
+            int subId, @NonNull final CarrierMessagingCallback callback) {
         if (mICarrierMessagingService != null) {
             try {
                 mICarrierMessagingService.filterSms(pdu, format, destPort, subId,
-                        new CarrierMessagingCallbackWrapperInternal(callback));
+                        new CarrierMessagingCallbackInternal(callback));
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -137,11 +135,11 @@
      * @hide
      */
     public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
-            int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
+            int sendSmsFlag, @NonNull final CarrierMessagingCallback callback) {
         if (mICarrierMessagingService != null) {
             try {
                 mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag,
-                        new CarrierMessagingCallbackWrapperInternal(callback));
+                        new CarrierMessagingCallbackInternal(callback));
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -163,11 +161,11 @@
      */
     public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
             int destPort, int sendSmsFlag,
-            @NonNull final CarrierMessagingCallbackWrapper callback) {
+            @NonNull final CarrierMessagingCallback callback) {
         if (mICarrierMessagingService != null) {
             try {
                 mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort,
-                        sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback));
+                        sendSmsFlag, new CarrierMessagingCallbackInternal(callback));
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -188,11 +186,11 @@
      */
     public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
             @NonNull String destAddress, int sendSmsFlag,
-            @NonNull final CarrierMessagingCallbackWrapper callback) {
+            @NonNull final CarrierMessagingCallback callback) {
         if (mICarrierMessagingService != null) {
             try {
                 mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress,
-                        sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback));
+                        sendSmsFlag, new CarrierMessagingCallbackInternal(callback));
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -212,11 +210,11 @@
      * @hide
      */
     public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
-            @NonNull final CarrierMessagingCallbackWrapper callback) {
+            @NonNull final CarrierMessagingCallback callback) {
         if (mICarrierMessagingService != null) {
             try {
                 mICarrierMessagingService.sendMms(pduUri, subId, location,
-                        new CarrierMessagingCallbackWrapperInternal(callback));
+                        new CarrierMessagingCallbackInternal(callback));
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -235,11 +233,11 @@
      * @hide
      */
     public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
-            @NonNull final CarrierMessagingCallbackWrapper callback) {
+            @NonNull final CarrierMessagingCallback callback) {
         if (mICarrierMessagingService != null) {
             try {
                 mICarrierMessagingService.downloadMms(pduUri, subId, location,
-                        new CarrierMessagingCallbackWrapperInternal(callback));
+                        new CarrierMessagingCallbackInternal(callback));
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -265,7 +263,7 @@
      * {@link CarrierMessagingServiceWrapper}.
      * @hide
      */
-    public abstract static class CarrierMessagingCallbackWrapper {
+    public interface CarrierMessagingCallback {
 
         /**
          * Response callback for {@link CarrierMessagingServiceWrapper#filterSms}.
@@ -277,7 +275,7 @@
          *               {@see CarrierMessagingService#onReceiveTextSms}.
          * @hide
          */
-        public void onFilterComplete(int result) {
+        default void onFilterComplete(int result) {
 
         }
 
@@ -291,7 +289,7 @@
          *                   only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
          * @hide
          */
-        public void onSendSmsComplete(int result, int messageRef) {
+        default void onSendSmsComplete(int result, int messageRef) {
 
         }
 
@@ -305,7 +303,7 @@
          *                    {@link CarrierMessagingService#SEND_STATUS_OK}.
          * @hide
          */
-        public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
+        default void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
 
         }
 
@@ -319,7 +317,7 @@
          *                    {@link CarrierMessagingService#SEND_STATUS_OK}.
          * @hide
          */
-        public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
+        default void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
 
         }
 
@@ -330,43 +328,43 @@
          *               and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
          * @hide
          */
-        public void onDownloadMmsComplete(int result) {
+        default void onDownloadMmsComplete(int result) {
 
         }
     }
 
-    private final class CarrierMessagingCallbackWrapperInternal
+    private final class CarrierMessagingCallbackInternal
             extends ICarrierMessagingCallback.Stub {
-        CarrierMessagingCallbackWrapper mCarrierMessagingCallbackWrapper;
+        CarrierMessagingCallback mCarrierMessagingCallback;
 
-        CarrierMessagingCallbackWrapperInternal(CarrierMessagingCallbackWrapper callback) {
-            mCarrierMessagingCallbackWrapper = callback;
+        CarrierMessagingCallbackInternal(CarrierMessagingCallback callback) {
+            mCarrierMessagingCallback = callback;
         }
 
         @Override
         public void onFilterComplete(int result) throws RemoteException {
-            mCarrierMessagingCallbackWrapper.onFilterComplete(result);
+            mCarrierMessagingCallback.onFilterComplete(result);
         }
 
         @Override
         public void onSendSmsComplete(int result, int messageRef) throws RemoteException {
-            mCarrierMessagingCallbackWrapper.onSendSmsComplete(result, messageRef);
+            mCarrierMessagingCallback.onSendSmsComplete(result, messageRef);
         }
 
         @Override
         public void onSendMultipartSmsComplete(int result, int[] messageRefs)
                 throws RemoteException {
-            mCarrierMessagingCallbackWrapper.onSendMultipartSmsComplete(result, messageRefs);
+            mCarrierMessagingCallback.onSendMultipartSmsComplete(result, messageRefs);
         }
 
         @Override
         public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException {
-            mCarrierMessagingCallbackWrapper.onSendMmsComplete(result, sendConfPdu);
+            mCarrierMessagingCallback.onSendMmsComplete(result, sendConfPdu);
         }
 
         @Override
         public void onDownloadMmsComplete(int result) throws RemoteException {
-            mCarrierMessagingCallbackWrapper.onDownloadMmsComplete(result);
+            mCarrierMessagingCallback.onDownloadMmsComplete(result);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 0bf68b7..8242f4e 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -90,7 +90,7 @@
      *
      * @param data the data to write
      */
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public int write(byte[] data) {
         try {
             return sService.write(data);
@@ -102,7 +102,7 @@
     /**
      * Returns the data block stored on the persistent partition.
      */
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public byte[] read() {
         try {
             return sService.read();
@@ -130,7 +130,7 @@
      *
      * Returns -1 on error.
      */
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public long getMaximumDataBlockSize() {
         try {
             return sService.getMaximumDataBlockSize();
diff --git a/core/java/android/uwb/RangingParams.java b/core/java/android/uwb/RangingParams.java
deleted file mode 100644
index f23d9ed..0000000
--- a/core/java/android/uwb/RangingParams.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.PersistableBundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * An object used when requesting to open a new {@link RangingSession}.
- * <p>Use {@link RangingParams.Builder} to create an instance of this class.
- *
- *  @hide
- */
-public final class RangingParams implements Parcelable {
-    private final boolean mIsInitiator;
-    private final boolean mIsController;
-    private final Duration mSamplePeriod;
-    private final UwbAddress mLocalDeviceAddress;
-    private final List<UwbAddress> mRemoteDeviceAddresses;
-    private final int mChannelNumber;
-    private final int mTransmitPreambleCodeIndex;
-    private final int mReceivePreambleCodeIndex;
-    private final int mStsPhyPacketType;
-    private final PersistableBundle mSpecificationParameters;
-
-    private RangingParams(boolean isInitiator, boolean isController,
-            @NonNull Duration samplingPeriod, @NonNull UwbAddress localDeviceAddress,
-            @NonNull List<UwbAddress> remoteDeviceAddresses, int channelNumber,
-            int transmitPreambleCodeIndex, int receivePreambleCodeIndex,
-            @StsPhyPacketType int stsPhyPacketType,
-            @NonNull PersistableBundle specificationParameters) {
-        mIsInitiator = isInitiator;
-        mIsController = isController;
-        mSamplePeriod = samplingPeriod;
-        mLocalDeviceAddress = localDeviceAddress;
-        mRemoteDeviceAddresses = remoteDeviceAddresses;
-        mChannelNumber = channelNumber;
-        mTransmitPreambleCodeIndex = transmitPreambleCodeIndex;
-        mReceivePreambleCodeIndex = receivePreambleCodeIndex;
-        mStsPhyPacketType = stsPhyPacketType;
-        mSpecificationParameters = specificationParameters;
-    }
-
-    /**
-     * Get if the local device is the initiator
-     *
-     * @return true if the device is the initiator
-     */
-    public boolean isInitiator() {
-        return mIsInitiator;
-    }
-
-    /**
-     * Get if the local device is the controller
-     *
-     * @return true if the device is the controller
-     */
-    public boolean isController() {
-        return mIsController;
-    }
-
-    /**
-     * The desired amount of time between two adjacent samples of measurement
-     *
-     * @return the ranging sample period
-     */
-    @NonNull
-    public Duration getSamplingPeriod() {
-        return mSamplePeriod;
-    }
-
-    /**
-     * Local device's {@link UwbAddress}
-     *
-     * <p>Simultaneous {@link RangingSession}s on the same device can have different results for
-     * {@link #getLocalDeviceAddress()}.
-     *
-     * @return the local device's {@link UwbAddress}
-     */
-    @NonNull
-    public UwbAddress getLocalDeviceAddress() {
-        return mLocalDeviceAddress;
-    }
-
-    /**
-     * Gets a list of all remote device's {@link UwbAddress}
-     *
-     * @return a {@link List} of {@link UwbAddress} representing the remote devices
-     */
-    @NonNull
-    public List<UwbAddress> getRemoteDeviceAddresses() {
-        return mRemoteDeviceAddresses;
-    }
-
-    /**
-     * Channel number used between this device pair as defined by 802.15.4z
-     *
-     * Range: -1, 0-15
-     *
-     * @return the channel to use
-     */
-    public int getChannelNumber() {
-        return mChannelNumber;
-    }
-
-    /**
-     * Preamble index used between this device pair as defined by 802.15.4z
-     *
-     * Range: 0, 0-32
-     *
-     * @return the preamble index to use for transmitting
-     */
-    public int getTxPreambleIndex() {
-        return mTransmitPreambleCodeIndex;
-    }
-
-    /**
-     * preamble index used between this device pair as defined by 802.15.4z
-     *
-     * Range: 0, 13-16, 21-32
-     *
-     * @return the preamble index to use for receiving
-     */
-    public int getRxPreambleIndex() {
-        return mReceivePreambleCodeIndex;
-    }
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            STS_PHY_PACKET_TYPE_SP0,
-            STS_PHY_PACKET_TYPE_SP1,
-            STS_PHY_PACKET_TYPE_SP2,
-            STS_PHY_PACKET_TYPE_SP3})
-    public @interface StsPhyPacketType {}
-
-    /**
-     * PHY packet type SP0 when STS is used as defined by 802.15.4z
-     */
-    public static final int STS_PHY_PACKET_TYPE_SP0 = 0;
-
-    /**
-     * PHY packet type SP1 when STS is used as defined by 802.15.4z
-     */
-    public static final int STS_PHY_PACKET_TYPE_SP1 = 1;
-
-    /**
-     * PHY packet type SP2 when STS is used as defined by 802.15.4z
-     */
-    public static final int STS_PHY_PACKET_TYPE_SP2 = 2;
-
-    /**
-     * PHY packet type SP3 when STS is used as defined by 802.15.4z
-     */
-    public static final int STS_PHY_PACKET_TYPE_SP3 = 3;
-
-    /**
-     * Get the type of PHY packet when STS is used as defined by 802.15.4z
-     *
-     * @return the {@link StsPhyPacketType} to use
-     */
-    @StsPhyPacketType
-    public int getStsPhyPacketType() {
-        return mStsPhyPacketType;
-    }
-
-    /**
-     * Parameters for a specific UWB protocol constructed using a support library.
-     *
-     * <p>Android reserves the '^android.*' namespace
-     *
-     * @return a {@link PersistableBundle} copy of protocol specific parameters
-     */
-    public @Nullable PersistableBundle getSpecificationParameters() {
-        return new PersistableBundle(mSpecificationParameters);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj instanceof RangingParams) {
-            RangingParams other = (RangingParams) obj;
-
-            return mIsInitiator == other.mIsInitiator
-                    && mIsController == other.mIsController
-                    && mSamplePeriod.equals(other.mSamplePeriod)
-                    && mLocalDeviceAddress.equals(other.mLocalDeviceAddress)
-                    && mRemoteDeviceAddresses.equals(other.mRemoteDeviceAddresses)
-                    && mChannelNumber == other.mChannelNumber
-                    && mTransmitPreambleCodeIndex == other.mTransmitPreambleCodeIndex
-                    && mReceivePreambleCodeIndex == other.mReceivePreambleCodeIndex
-                    && mStsPhyPacketType == other.mStsPhyPacketType
-                    && mSpecificationParameters.size() == other.mSpecificationParameters.size()
-                    && mSpecificationParameters.kindofEquals(other.mSpecificationParameters);
-        }
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mIsInitiator, mIsController, mSamplePeriod, mLocalDeviceAddress,
-                mRemoteDeviceAddresses, mChannelNumber, mTransmitPreambleCodeIndex,
-                mReceivePreambleCodeIndex, mStsPhyPacketType, mSpecificationParameters);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeBoolean(mIsInitiator);
-        dest.writeBoolean(mIsController);
-        dest.writeLong(mSamplePeriod.getSeconds());
-        dest.writeInt(mSamplePeriod.getNano());
-        dest.writeParcelable(mLocalDeviceAddress, flags);
-
-        UwbAddress[] remoteAddresses = new UwbAddress[mRemoteDeviceAddresses.size()];
-        mRemoteDeviceAddresses.toArray(remoteAddresses);
-        dest.writeParcelableArray(remoteAddresses, flags);
-
-        dest.writeInt(mChannelNumber);
-        dest.writeInt(mTransmitPreambleCodeIndex);
-        dest.writeInt(mReceivePreambleCodeIndex);
-        dest.writeInt(mStsPhyPacketType);
-        dest.writePersistableBundle(mSpecificationParameters);
-    }
-
-    public static final @android.annotation.NonNull Creator<RangingParams> CREATOR =
-            new Creator<RangingParams>() {
-                @Override
-                public RangingParams createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-                    builder.setIsInitiator(in.readBoolean());
-                    builder.setIsController(in.readBoolean());
-                    builder.setSamplePeriod(Duration.ofSeconds(in.readLong(), in.readInt()));
-                    builder.setLocalDeviceAddress(
-                            in.readParcelable(UwbAddress.class.getClassLoader()));
-
-                    UwbAddress[] remoteAddresses =
-                            in.readParcelableArray(null, UwbAddress.class);
-                    for (UwbAddress remoteAddress : remoteAddresses) {
-                        builder.addRemoteDeviceAddress(remoteAddress);
-                    }
-
-                    builder.setChannelNumber(in.readInt());
-                    builder.setTransmitPreambleCodeIndex(in.readInt());
-                    builder.setReceivePreambleCodeIndex(in.readInt());
-                    builder.setStsPhPacketType(in.readInt());
-                    builder.setSpecificationParameters(in.readPersistableBundle());
-
-                    return builder.build();
-                }
-
-                @Override
-                public RangingParams[] newArray(int size) {
-                    return new RangingParams[size];
-                }
-    };
-
-    /**
-     * Builder class for {@link RangingParams}.
-     */
-    public static final class Builder {
-        private boolean mIsInitiator = false;
-        private boolean mIsController = false;
-        private Duration mSamplePeriod = null;
-        private UwbAddress mLocalDeviceAddress = null;
-        private List<UwbAddress> mRemoteDeviceAddresses = new ArrayList<>();
-        private int mChannelNumber = 0;
-        private int mTransmitPreambleCodeIndex = 0;
-        private int mReceivePreambleCodeIndex = 0;
-        private int mStsPhyPacketType = STS_PHY_PACKET_TYPE_SP0;
-        private PersistableBundle mSpecificationParameters = new PersistableBundle();
-
-        /**
-         * Set whether the device is the initiator or responder as defined by IEEE 802.15.4z
-         *
-         * @param isInitiator whether the device is the initiator (true) or responder (false)
-         */
-        public Builder setIsInitiator(boolean isInitiator) {
-            mIsInitiator = isInitiator;
-            return this;
-        }
-
-        /**
-         * Set whether the local device is the controller or controlee as defined by IEEE 802.15.4z
-         *
-         * @param isController whether the device is the controller (true) or controlee (false)
-         */
-        public Builder setIsController(boolean isController) {
-            mIsController = isController;
-            return this;
-        }
-
-        /**
-         * Set the time between ranging samples
-         *
-         * @param samplePeriod the time between ranging samples
-         */
-        public Builder setSamplePeriod(@NonNull Duration samplePeriod) {
-            mSamplePeriod = samplePeriod;
-            return this;
-        }
-
-        /**
-         * Set the local device address
-         *
-         * @param localDeviceAddress the local device's address for the {@link RangingSession}
-         */
-        public Builder setLocalDeviceAddress(@NonNull UwbAddress localDeviceAddress) {
-            mLocalDeviceAddress = localDeviceAddress;
-            return this;
-        }
-
-        /**
-         * Add a remote device's address to the ranging session
-         *
-         * @param remoteDeviceAddress a remote device's address for the {@link RangingSession}
-         * @throws IllegalArgumentException if {@code remoteDeviceAddress} is already present.
-         */
-        public Builder addRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
-            if (mRemoteDeviceAddresses.contains(remoteDeviceAddress)) {
-                throw new IllegalArgumentException(
-                        "Remote device address already added: " + remoteDeviceAddress.toString());
-            }
-            mRemoteDeviceAddresses.add(remoteDeviceAddress);
-            return this;
-        }
-
-        /**
-         * Set the IEEE 802.15.4z channel to use for the {@link RangingSession}
-         * <p>Valid values are in the range [-1, 15]
-         *
-         * @param channelNumber the channel to use for the {@link RangingSession}
-         * @throws IllegalArgumentException if {@code channelNumber} is invalid.
-         */
-        public Builder setChannelNumber(int channelNumber) {
-            if (channelNumber < -1 || channelNumber > 15) {
-                throw new IllegalArgumentException("Invalid channel number");
-            }
-            mChannelNumber = channelNumber;
-            return this;
-        }
-
-        private static final Set<Integer> VALID_TX_PREAMBLE_CODES = new HashSet<Integer>(
-                Arrays.asList(0, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
-
-        /**
-         * Set the IEEE 802.15.4z preamble code index to use when transmitting
-         *
-         * <p>Valid values are in the ranges: [0], [13-16], [21-32]
-         *
-         * @param transmitPreambleCodeIndex preamble code index to use for transmitting
-         * @throws IllegalArgumentException if {@code transmitPreambleCodeIndex} is invalid.
-         */
-        public Builder setTransmitPreambleCodeIndex(int transmitPreambleCodeIndex) {
-            if (!VALID_TX_PREAMBLE_CODES.contains(transmitPreambleCodeIndex)) {
-                throw new IllegalArgumentException(
-                        "Invalid transmit preamble: " + transmitPreambleCodeIndex);
-            }
-            mTransmitPreambleCodeIndex = transmitPreambleCodeIndex;
-            return this;
-        }
-
-        private static final Set<Integer> VALID_RX_PREAMBLE_CODES = new HashSet<Integer>(
-                Arrays.asList(0, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
-
-        /**
-         * Set the IEEE 802.15.4z preamble code index to use when receiving
-         *
-         * Valid values are in the ranges: [0], [16-32]
-         *
-         * @param receivePreambleCodeIndex preamble code index to use for receiving
-         * @throws IllegalArgumentException if {@code receivePreambleCodeIndex} is invalid.
-         */
-        public Builder setReceivePreambleCodeIndex(int receivePreambleCodeIndex) {
-            if (!VALID_RX_PREAMBLE_CODES.contains(receivePreambleCodeIndex)) {
-                throw new IllegalArgumentException(
-                        "Invalid receive preamble: " + receivePreambleCodeIndex);
-            }
-            mReceivePreambleCodeIndex = receivePreambleCodeIndex;
-            return this;
-        }
-
-        /**
-         * Set the IEEE 802.15.4z PHY packet type when STS is used
-         *
-         * @param stsPhyPacketType PHY packet type when STS is used
-         * @throws IllegalArgumentException if {@code stsPhyPacketType} is invalid.
-         */
-        public Builder setStsPhPacketType(@StsPhyPacketType int stsPhyPacketType) {
-            if (stsPhyPacketType != STS_PHY_PACKET_TYPE_SP0
-                    && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP1
-                    && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP2
-                    && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP3) {
-                throw new IllegalArgumentException("unknown StsPhyPacketType: " + stsPhyPacketType);
-            }
-
-            mStsPhyPacketType = stsPhyPacketType;
-            return this;
-        }
-
-        /**
-         * Set the specification parameters
-         *
-         * <p>Creates a copy of the parameters
-         *
-         * @param parameters specification parameters built from support library
-         */
-        public Builder setSpecificationParameters(@NonNull PersistableBundle parameters) {
-            mSpecificationParameters = new PersistableBundle(parameters);
-            return this;
-        }
-
-        /**
-         * Build the {@link RangingParams} object.
-         *
-         * @throws IllegalStateException if required parameters are missing
-         */
-        public RangingParams build() {
-            if (mSamplePeriod == null) {
-                throw new IllegalStateException("No sample period provided");
-            }
-
-            if (mLocalDeviceAddress == null) {
-                throw new IllegalStateException("Local device address not provided");
-            }
-
-            if (mRemoteDeviceAddresses.size() == 0) {
-                throw new IllegalStateException("No remote device address(es) provided");
-            }
-
-            return new RangingParams(mIsInitiator, mIsController, mSamplePeriod,
-                    mLocalDeviceAddress, mRemoteDeviceAddresses, mChannelNumber,
-                    mTransmitPreambleCodeIndex, mReceivePreambleCodeIndex, mStsPhyPacketType,
-                    mSpecificationParameters);
-        }
-    }
-}
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index f4033fe..8639269 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -30,7 +30,7 @@
  * {@link RangingSession}.
  *
  * <p>To get an instance of {@link RangingSession}, first use
- * {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} to request to open a
+ * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a
  * session. Once the session is opened, a {@link RangingSession} object is provided through
  * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a
  * session fails, the failure is reported through {@link RangingSession.Callback#onClosed(int)} with
@@ -44,7 +44,7 @@
      */
     public interface Callback {
         /**
-         * Invoked when {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)}
+         * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
          * is successful
          *
          * @param session the newly opened {@link RangingSession}
@@ -77,7 +77,7 @@
 
         /**
          * Indicates that the session failed to open due to erroneous parameters passed
-         * to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)}
+         * to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
          */
         int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2;
 
@@ -137,8 +137,8 @@
      * will still be invoked.
      *
      * <p>{@link Callback#onClosed(int)} will be invoked using the same callback
-     * object given to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} when
-     * the {@link RangingSession} was opened. The callback will be invoked after each call to
+     * object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
+     * when the {@link RangingSession} was opened. The callback will be invoked after each call to
      * {@link #close()}, even if the {@link RangingSession} is already closed.
      */
     @Override
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index d58d5bf..2f1e2de 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -176,15 +176,14 @@
      * arrangement, a platform may only support hemi-spherical azimuth angles
      * ranging from -pi/2 to pi/2
      */
-    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 2;
+    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3;
 
     /**
      * Indicate support for three dimensional angle of arrival measurement.
      * Typically requires at least three antennas. This mode supports full
      * azimuth angles ranging from -pi to pi.
      */
-    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 3;
-
+    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4;
 
     /**
      * Gets the {@link AngleOfArrivalSupportType} supported on this platform
@@ -280,7 +279,10 @@
      * <p>An open {@link RangingSession} will be automatically closed if client application process
      * dies.
      *
-     * @param params {@link RangingParams} used to initialize this {@link RangingSession}
+     * <p>A UWB support library must be used in order to construct the {@code parameter}
+     * {@link PersistableBundle}.
+     *
+     * @param parameters the parameters that define the ranging session
      * @param executor {@link Executor} to run callbacks
      * @param callbacks {@link RangingSession.Callback} to associate with the
      *                  {@link RangingSession} that is being opened.
@@ -291,8 +293,9 @@
      *         {@link RangingSession.Callback#onOpenSuccess}.
      */
     @NonNull
-    public AutoCloseable openRangingSession(@NonNull RangingParams params,
-            @NonNull Executor executor, @NonNull RangingSession.Callback callbacks) {
+    public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
+            @NonNull Executor executor,
+            @NonNull RangingSession.Callback callbacks) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 606e8f9..29ce231 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -97,6 +97,7 @@
     public abstract void setVisibility(int visibility);
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void setAssistBlocked(boolean state);
 
     /**
@@ -431,6 +432,7 @@
     public abstract void asyncCommit();
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract Rect getTempRect();
 
     /**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index f2772d6..fb2412b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1384,6 +1384,7 @@
     }
 
     /** @hide */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void alwaysReadCloseOnTouchAttr();
 
@@ -1564,6 +1565,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void clearContentView();
 
     /**
@@ -2632,18 +2634,21 @@
      * Called when the activity changes from fullscreen mode to multi-window mode and visa-versa.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void onMultiWindowModeChanged();
 
     /**
      * Called when the activity changes to/from picture-in-picture mode.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
 
     /**
      * Called when the activity just relaunched.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void reportActivityRelaunched();
 
     /**
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index f62a28e..023d9ff2 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -154,6 +154,7 @@
      *               HTTP request header
      * @hide Used by Browser and by WebViewProvider implementations.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract String getCookie(String url, boolean privateBrowsing);
 
@@ -230,6 +231,7 @@
      * @param privateBrowsing whether to use the private browsing cookie jar
      * @hide Used by Browser and WebViewProvider implementations.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract boolean hasCookies(boolean privateBrowsing);
 
@@ -264,6 +266,7 @@
      *
      * @hide Only for use by WebViewProvider implementations
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     protected abstract boolean allowFileSchemeCookiesImpl();
 
@@ -299,6 +302,7 @@
      *
      * @hide Only for use by WebViewProvider implementations
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     protected abstract void setAcceptFileSchemeCookiesImpl(boolean accept);
 }
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index b9e7042..2cb37b4 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -35,6 +35,7 @@
      * @deprecated This method is now obsolete.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract int getId();
diff --git a/core/java/android/webkit/WebIconDatabase.java b/core/java/android/webkit/WebIconDatabase.java
index 08956e0..b705658 100644
--- a/core/java/android/webkit/WebIconDatabase.java
+++ b/core/java/android/webkit/WebIconDatabase.java
@@ -75,6 +75,7 @@
 
     /** {@hide}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract void bulkRequestIconForPageUrl(ContentResolver cr, String where,
             IconListener listener);
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 91b9390..9b753f1 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -268,6 +268,7 @@
      * @deprecated This method is now obsolete.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract void setNavDump(boolean enabled);
@@ -280,6 +281,7 @@
      * @deprecated This method is now obsolete.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract boolean getNavDump();
@@ -457,6 +459,7 @@
      * @deprecated This method is now obsolete.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract  void setUseWebViewBackgroundForOverscrollBackground(boolean view);
@@ -469,6 +472,7 @@
      * @deprecated This method is now obsolete.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract  boolean getUseWebViewBackgroundForOverscrollBackground();
@@ -534,6 +538,7 @@
      * Developers should access this via {@link CookieManager#setShouldAcceptThirdPartyCookies}.
      * @hide Internal API.
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract void setAcceptThirdPartyCookies(boolean accept);
 
@@ -542,6 +547,7 @@
      * Developers should access this via {@link CookieManager#getShouldAcceptThirdPartyCookies}.
      * @hide Internal API
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract boolean getAcceptThirdPartyCookies();
 
@@ -669,6 +675,7 @@
      * @deprecated Please use {@link #setUserAgentString} instead.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract void setUserAgent(int ua);
@@ -687,6 +694,7 @@
      * @deprecated Please use {@link #getUserAgentString} instead.
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract int getUserAgent();
@@ -1050,6 +1058,7 @@
      *             {@link #setPluginState}
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract void setPluginsEnabled(boolean flag);
@@ -1259,6 +1268,7 @@
      * @deprecated This method has been replaced by {@link #getPluginState}
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @Deprecated
     public abstract boolean getPluginsEnabled();
@@ -1445,6 +1455,7 @@
      * WebView.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag);
 
@@ -1455,6 +1466,7 @@
      * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled();
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 64e1ca8..a785a1a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5346,6 +5346,7 @@
      *
      * @param down true if the scroll is going down, false if it is going up
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     abstract void fillGap(boolean down);
 
     void hideSelector() {
@@ -5383,6 +5384,7 @@
      * @param y Where the user touched
      * @return The position of the first (or only) item in the row containing y
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     abstract int findMotionRow(int y);
 
@@ -5430,6 +5432,7 @@
      *
      * @param position the position of the new selection
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     abstract void setSelectionInt(int position);
 
     /**
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index daf6914..76e97ad 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -310,6 +310,7 @@
         }
     }
 
+    @SuppressWarnings("HiddenAbstractMethod")
     abstract void layout(int delta, boolean animate);
 
     @Override
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 60f1b44..8d1f16b 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -24,7 +24,6 @@
 import android.content.type.DefaultMimeMapFactory;
 import android.os.Build;
 import android.os.DeadObjectException;
-import android.os.Debug;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.SystemProperties;
@@ -257,18 +256,6 @@
          */
         NetworkManagementSocketTagger.install();
 
-        /*
-         * If we're running in an emulator launched with "-trace", put the
-         * VM into emulator trace profiling mode so that the user can hit
-         * F9/F10 at any time to capture traces.  This has performance
-         * consequences, so it's not something you want to do always.
-         */
-        String trace = SystemProperties.get("ro.kernel.android.tracing");
-        if (trace.equals("1")) {
-            Slog.i(TAG, "NOTE: emulator trace profiling enabled");
-            Debug.enableEmulatorTraceOutput();
-        }
-
         initialized = true;
     }
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index aa37334..6a67670 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -73,6 +73,7 @@
 import java.io.InputStreamReader;
 import java.security.Provider;
 import java.security.Security;
+import java.util.Optional;
 
 /**
  * Startup class for the zygote process.
@@ -225,7 +226,17 @@
         // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert
         // preferred providers. Note this is not done via security.properties as the JCA providers
         // are not on the classpath in the case of, for example, raw dalvikvm runtimes.
-        AndroidKeyStoreProvider.install();
+        // TODO b/171305684 This code is used to conditionally enable the installation of the
+        //      Keystore 2.0 provider to enable teams adjusting to Keystore 2.0 at their own
+        //      pace. This code will be removed when all calling code was adjusted to
+        //      Keystore 2.0.
+        Optional<Boolean> keystore2_enabled =
+                android.sysprop.Keystore2Properties.keystore2_enabled();
+        if (keystore2_enabled.isPresent() && keystore2_enabled.get()) {
+            android.security.keystore2.AndroidKeyStoreProvider.install();
+        } else {
+            AndroidKeyStoreProvider.install();
+        }
         Log.i(TAG, "Installed AndroidKeyStoreProvider in "
                 + (SystemClock.uptimeMillis() - startTime) + "ms.");
         Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 9c7ee0c..241570a 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -90,6 +90,14 @@
     env->CallVoidMethod(parcelObj, gParcelOffsets.recycle);
 }
 
+static void android_os_Parcel_markSensitive(jlong nativePtr)
+{
+    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+    if (parcel) {
+        parcel->markSensitive();
+    }
+}
+
 static jint android_os_Parcel_dataSize(jlong nativePtr)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -758,6 +766,8 @@
 
 static const JNINativeMethod gParcelMethods[] = {
     // @CriticalNative
+    {"nativeMarkSensitive",             "(J)V", (void*)android_os_Parcel_markSensitive},
+    // @CriticalNative
     {"nativeDataSize",            "(J)I", (void*)android_os_Parcel_dataSize},
     // @CriticalNative
     {"nativeDataAvail",           "(J)I", (void*)android_os_Parcel_dataAvail},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 95c295a4..70c2afb 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -804,10 +804,14 @@
   PrepareDir(dir, mode, uid, gid, fail_fn);
 }
 
+static bool BindMount(const std::string& source_dir, const std::string& target_dir) {
+  return !(TEMP_FAILURE_RETRY(mount(source_dir.c_str(), target_dir.c_str(), nullptr,
+                                    MS_BIND | MS_REC, nullptr)) == -1);
+}
+
 static void BindMount(const std::string& source_dir, const std::string& target_dir,
                       fail_fn_t fail_fn) {
-  if (TEMP_FAILURE_RETRY(mount(source_dir.c_str(), target_dir.c_str(), nullptr,
-                               MS_BIND | MS_REC, nullptr)) == -1) {
+  if (!BindMount(source_dir, target_dir)) {
     fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s",
                          source_dir.c_str(), target_dir.c_str(), strerror(errno)));
   }
@@ -1194,9 +1198,9 @@
 
 // Create an app data directory over tmpfs overlayed CE / DE storage, and bind mount it
 // from the actual app data directory in data mirror.
-static void createAndMountAppData(std::string_view package_name,
+static bool createAndMountAppData(std::string_view package_name,
     std::string_view mirror_pkg_dir_name, std::string_view mirror_data_path,
-    std::string_view actual_data_path, fail_fn_t fail_fn) {
+    std::string_view actual_data_path, fail_fn_t fail_fn, bool call_fail_fn) {
 
   char mirrorAppDataPath[PATH_MAX];
   char actualAppDataPath[PATH_MAX];
@@ -1207,6 +1211,29 @@
   PrepareDir(actualAppDataPath, 0700, AID_ROOT, AID_ROOT, fail_fn);
 
   // Bind mount from original app data directory in mirror.
+  if (call_fail_fn) {
+    BindMount(mirrorAppDataPath, actualAppDataPath, fail_fn);
+  } else if(!BindMount(mirrorAppDataPath, actualAppDataPath)) {
+    ALOGW("Failed to mount %s to %s: %s",
+          mirrorAppDataPath, actualAppDataPath, strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+// There is an app data directory over tmpfs overlaid CE / DE storage
+// bind mount it from the actual app data directory in data mirror.
+static void mountAppData(std::string_view package_name,
+    std::string_view mirror_pkg_dir_name, std::string_view mirror_data_path,
+    std::string_view actual_data_path, fail_fn_t fail_fn) {
+
+  char mirrorAppDataPath[PATH_MAX];
+  char actualAppDataPath[PATH_MAX];
+  snprintf(mirrorAppDataPath, PATH_MAX, "%s/%s", mirror_data_path.data(),
+      mirror_pkg_dir_name.data());
+  snprintf(actualAppDataPath, PATH_MAX, "%s/%s", actual_data_path.data(), package_name.data());
+
+  // Bind mount from original app data directory in mirror.
   BindMount(mirrorAppDataPath, actualAppDataPath, fail_fn);
 }
 
@@ -1284,10 +1311,17 @@
   snprintf(mirrorCePath, PATH_MAX, "%s/%d", mirrorCeParent, userId);
   snprintf(mirrorDePath, PATH_MAX, "/data_mirror/data_de/%s/%d", volume_uuid.data(), userId);
 
-  createAndMountAppData(package_name, package_name, mirrorDePath, actualDePath, fail_fn);
+  createAndMountAppData(package_name, package_name, mirrorDePath, actualDePath, fail_fn,
+                        true /*call_fail_fn*/);
 
   std::string ce_data_path = getAppDataDirName(mirrorCePath, package_name, ce_data_inode, fail_fn);
-  createAndMountAppData(package_name, ce_data_path, mirrorCePath, actualCePath, fail_fn);
+  if (!createAndMountAppData(package_name, ce_data_path, mirrorCePath, actualCePath, fail_fn,
+                             false /*call_fail_fn*/)) {
+    // CE might unlocks and the name is decrypted
+    // get the name and mount again
+    ce_data_path=getAppDataDirName(mirrorCePath, package_name, ce_data_inode, fail_fn);
+    mountAppData(package_name, ce_data_path, mirrorCePath, actualCePath, fail_fn);
+  }
 }
 
 // Relabel directory
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 38981b0..c73aae5 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -33,16 +33,6 @@
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
-        "/apex/com.android.conscrypt/javalib/conscrypt.jar",
-        "/apex/com.android.ipsec/javalib/ike.jar",
-        "/apex/com.android.i18n/javalib/core-icu4j.jar",
-        "/apex/com.android.media/javalib/updatable-media.jar",
-        "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
-        "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
-        "/apex/com.android.permission/javalib/framework-permission.jar",
-        "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar",
-        "/apex/com.android.wifi/javalib/framework-wifi.jar",
-        "/apex/com.android.tethering/javalib/framework-tethering.jar",
         "/dev/null",
         "/dev/socket/zygote",
         "/dev/socket/zygote_secondary",
@@ -100,11 +90,12 @@
     }
   }
 
-  // Jars from the ART APEX are allowed.
-  static const char* kArtApexPrefix = "/apex/com.android.art/javalib/";
-  if (android::base::StartsWith(path, kArtApexPrefix)
-      && android::base::EndsWith(path, kJarSuffix)) {
-    return true;
+  // Jars from APEXes are allowed. This matches /apex/**/javalib/*.jar.
+  static const char* kApexPrefix = "/apex/";
+  static const char* kApexJavalibPathSuffix = "/javalib";
+  if (android::base::StartsWith(path, kApexPrefix) && android::base::EndsWith(path, kJarSuffix) &&
+      android::base::EndsWith(android::base::Dirname(path), kApexJavalibPathSuffix)) {
+      return true;
   }
 
   // the in-memory file created by ART through memfd_create is allowed.
diff --git a/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java b/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java
deleted file mode 100644
index 8095c99..0000000
--- a/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Parcel;
-import android.os.PersistableBundle;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.Duration;
-
-/**
- * Test of {@link RangingParams}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RangingParamsTest {
-
-    @Test
-    public void testParams_Build() {
-        UwbAddress local = UwbAddress.fromBytes(new byte[] {(byte) 0xA0, (byte) 0x57});
-        UwbAddress remote = UwbAddress.fromBytes(new byte[] {(byte) 0x4D, (byte) 0x8C});
-        int channel = 9;
-        int rxPreamble = 16;
-        int txPreamble = 21;
-        boolean isController = true;
-        boolean isInitiator = false;
-        @RangingParams.StsPhyPacketType int stsPhyType = RangingParams.STS_PHY_PACKET_TYPE_SP2;
-        Duration samplePeriod = Duration.ofSeconds(1, 234);
-        PersistableBundle specParams = new PersistableBundle();
-        specParams.putString("protocol", "some_protocol");
-
-        RangingParams params = new RangingParams.Builder()
-                .setChannelNumber(channel)
-                .setReceivePreambleCodeIndex(rxPreamble)
-                .setTransmitPreambleCodeIndex(txPreamble)
-                .setLocalDeviceAddress(local)
-                .addRemoteDeviceAddress(remote)
-                .setIsController(isController)
-                .setIsInitiator(isInitiator)
-                .setSamplePeriod(samplePeriod)
-                .setStsPhPacketType(stsPhyType)
-                .setSpecificationParameters(specParams)
-                .build();
-
-        assertEquals(params.getLocalDeviceAddress(), local);
-        assertEquals(params.getRemoteDeviceAddresses().size(), 1);
-        assertEquals(params.getRemoteDeviceAddresses().get(0), remote);
-        assertEquals(params.getChannelNumber(), channel);
-        assertEquals(params.isController(), isController);
-        assertEquals(params.isInitiator(), isInitiator);
-        assertEquals(params.getRxPreambleIndex(), rxPreamble);
-        assertEquals(params.getTxPreambleIndex(), txPreamble);
-        assertEquals(params.getStsPhyPacketType(), stsPhyType);
-        assertEquals(params.getSamplingPeriod(), samplePeriod);
-        assertTrue(params.getSpecificationParameters().kindofEquals(specParams));
-    }
-
-    @Test
-    public void testParcel() {
-        Parcel parcel = Parcel.obtain();
-        RangingParams params = new RangingParams.Builder()
-                .setChannelNumber(9)
-                .setReceivePreambleCodeIndex(16)
-                .setTransmitPreambleCodeIndex(21)
-                .setLocalDeviceAddress(UwbTestUtils.getUwbAddress(false))
-                .addRemoteDeviceAddress(UwbTestUtils.getUwbAddress(true))
-                .setIsController(false)
-                .setIsInitiator(true)
-                .setSamplePeriod(Duration.ofSeconds(2))
-                .setStsPhPacketType(RangingParams.STS_PHY_PACKET_TYPE_SP1)
-                .build();
-        params.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        RangingParams fromParcel = RangingParams.CREATOR.createFromParcel(parcel);
-        assertEquals(params, fromParcel);
-    }
-}
diff --git a/keystore/java/android/security/CheckedRemoteRequest.java b/keystore/java/android/security/CheckedRemoteRequest.java
new file mode 100644
index 0000000..b6e7c1f
--- /dev/null
+++ b/keystore/java/android/security/CheckedRemoteRequest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.security;
+
+import android.os.RemoteException;
+
+/**
+ * This is a Producer of {@code R} that is expected to throw a {@link RemoteException}.
+ *
+ * It is used by Keystore2 service wrappers to handle and convert {@link RemoteException}
+ * and {@link android.os.ServiceSpecificException} into {@link KeyStoreException}.
+ *
+ * @hide
+ * @param <R>
+ */
+@FunctionalInterface
+interface CheckedRemoteRequest<R> {
+    R execute() throws RemoteException;
+}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
new file mode 100644
index 0000000..92d87aa
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2.java
@@ -0,0 +1,277 @@
+/*
+ * 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.security;
+
+import android.annotation.NonNull;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.system.keystore2.IKeystoreService;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyEntryResponse;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+import java.util.Calendar;
+
+/**
+ * @hide This should not be made public in its present form because it
+ * assumes that private and secret key bytes are available and would
+ * preclude the use of hardware crypto.
+ */
+public class KeyStore2 {
+    private static final String TAG = "KeyStore";
+
+    private static final int RECOVERY_GRACE_PERIOD_MS = 50;
+
+    /**
+     * Keystore operation creation may fail
+     *
+     * Keystore used to work under the assumption that the creation of cryptographic operations
+     * always succeeds. However, the KeyMint backend has only a limited number of operation slots.
+     * In order to keep up the appearance of "infinite" operation slots, the Keystore daemon
+     * would prune least recently used operations if there is no available operation slot.
+     * As a result, good operations could be terminated prematurely.
+     *
+     * This opens AndroidKeystore up to denial-of-service and unintended livelock situations.
+     * E.g.: if multiple apps wake up at the same time, e.g., due to power management optimizations,
+     * and attempt to perform crypto operations, they start terminating each others operations
+     * without making any progress.
+     *
+     * To break out of livelocks and to discourage DoS attempts we have changed the pruning
+     * strategy such that it prefers clients that use few operation slots and only briefly.
+     * As a result we can, almost, guarantee that single operations that don't linger inactive
+     * for more than 5 seconds will conclude unhampered by the pruning strategy. "Almost",
+     * because there are operations related to file system encryption that can prune even
+     * these operations, but those are extremely rare.
+     *
+     * As a side effect of this new pruning strategy operation creation can now fail if the
+     * client has a lower pruning power than all of the existing operations.
+     *
+     * Pruning strategy
+     *
+     * To find a suitable candidate we compute the malus for the caller and each existing
+     * operation. The malus is the inverse of the pruning power (caller) or pruning
+     * resistance (existing operation). For the caller to be able to prune an operation it must
+     * find an operation with a malus higher than its own.
+     *
+     * For more detail on the pruning strategy consult the implementation at
+     * https://android.googlesource.com/platform/system/security/+/refs/heads/master/keystore2/src/operation.rs
+     *
+     * For older SDK version, KeyStore2 will poll the Keystore daemon for a free operation
+     * slot. So to applications, targeting earlier SDK versions, it will still look like cipher and
+     * signature object initialization always succeeds, however, it may take longer to get an
+     * operation.
+     *
+     * All SDK version benefit from fairer operation slot scheduling and a better chance to
+     * successfully conclude an operation.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    static final long KEYSTORE_OPERATION_CREATION_MAY_FAIL = 169897160L;
+
+    // Never use mBinder directly, use KeyStore2.getService() instead or better yet
+    // handleRemoteExceptionWithRetry which retries connecting to Keystore once in case
+    // of a remote exception.
+    private IKeystoreService mBinder;
+
+
+    @FunctionalInterface
+    interface CheckedRemoteRequest<R> {
+        R execute(IKeystoreService service) throws RemoteException;
+    }
+
+    private <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
+            throws KeyStoreException {
+        IKeystoreService service = getService(false /* retryLookup */);
+        boolean firstTry = true;
+        while (true) {
+            try {
+                return request.execute(service);
+            } catch (ServiceSpecificException e) {
+                Log.e(TAG, "KeyStore exception", e);
+                throw new KeyStoreException(e.errorCode, "");
+            } catch (RemoteException e) {
+                if (firstTry) {
+                    Log.w(TAG, "Looks like we may have lost connection to the Keystore "
+                            + "daemon.");
+                    Log.w(TAG, "Retrying after giving Keystore "
+                            + RECOVERY_GRACE_PERIOD_MS + "ms to recover.");
+                    interruptedPreservingSleep(RECOVERY_GRACE_PERIOD_MS);
+                    service = getService(true /* retry Lookup */);
+                    firstTry = false;
+                } else {
+                    Log.e(TAG, "Cannot connect to Keystore daemon.", e);
+                    throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
+                }
+            }
+        }
+    }
+
+
+    private KeyStore2() {
+        mBinder = null;
+    }
+
+    public static KeyStore2 getInstance() {
+        return new KeyStore2();
+    }
+
+    private synchronized IKeystoreService getService(boolean retryLookup) {
+        if (mBinder == null || retryLookup) {
+            mBinder = IKeystoreService.Stub.asInterface(ServiceManager
+                    .getService("android.system.keystore2"));
+        }
+        return mBinder;
+    }
+
+    void delete(KeyDescriptor descriptor) throws KeyStoreException {
+        handleRemoteExceptionWithRetry((service) -> {
+            service.deleteKey(descriptor);
+            return 0;
+        });
+    }
+
+    /**
+     * List all entries in the keystore for in the given namespace.
+     */
+    public KeyDescriptor[] list(int domain, long namespace) throws KeyStoreException {
+        return handleRemoteExceptionWithRetry((service) -> service.listEntries(domain, namespace));
+    }
+
+    /**
+     * Create a grant that allows the grantee identified by {@code granteeUid} to use
+     * the key specified by {@code descriptor} withint the restrictions given by
+     * {@code accessVectore}.
+     * @see IKeystoreService#grant(KeyDescriptor, int, int) for more details.
+     * @param descriptor
+     * @param granteeUid
+     * @param accessVector
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyDescriptor grant(KeyDescriptor descriptor, int granteeUid, int accessVector)
+            throws  KeyStoreException {
+        return handleRemoteExceptionWithRetry(
+                (service) -> service.grant(descriptor, granteeUid, accessVector)
+        );
+    }
+
+    /**
+     * Destroys a grant.
+     * @see IKeystoreService#ungrant(KeyDescriptor, int) for more details.
+     * @param descriptor
+     * @param granteeUid
+     * @throws KeyStoreException
+     * @hide
+     */
+    public void ungrant(KeyDescriptor descriptor, int granteeUid)
+            throws KeyStoreException {
+        handleRemoteExceptionWithRetry((service) -> {
+            service.ungrant(descriptor, granteeUid);
+            return 0;
+        });
+    }
+
+    /**
+     * Retrieves a key entry from the keystore backend.
+     * @see IKeystoreService#getKeyEntry(KeyDescriptor) for more details.
+     * @param descriptor
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyEntryResponse getKeyEntry(@NonNull KeyDescriptor descriptor)
+            throws KeyStoreException {
+        return handleRemoteExceptionWithRetry((service) -> service.getKeyEntry(descriptor));
+    }
+
+    /**
+     * Get the security level specific keystore interface from the keystore daemon.
+     * @see IKeystoreService#getSecurityLevel(int) for more details.
+     * @param securityLevel
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyStoreSecurityLevel getSecurityLevel(int securityLevel)
+            throws KeyStoreException {
+        return handleRemoteExceptionWithRetry((service) ->
+            new KeyStoreSecurityLevel(
+                    service.getSecurityLevel(securityLevel)
+            )
+        );
+    }
+
+    /**
+     * Update the subcomponents of a key entry designated by the key descriptor.
+     * @see IKeystoreService#updateSubcomponent(KeyDescriptor, byte[], byte[]) for more details.
+     * @param key
+     * @param publicCert
+     * @param publicCertChain
+     * @throws KeyStoreException
+     * @hide
+     */
+    public void updateSubcomponents(@NonNull KeyDescriptor key, byte[] publicCert,
+            byte[] publicCertChain) throws KeyStoreException {
+        handleRemoteExceptionWithRetry((service) -> {
+            service.updateSubcomponent(key, publicCert, publicCertChain);
+            return 0;
+        });
+    }
+
+    /**
+     * Delete the key designed by the key descriptor.
+     * @see IKeystoreService#deleteKey(KeyDescriptor) for more details.
+     * @param descriptor
+     * @throws KeyStoreException
+     * @hide
+     */
+    public void deleteKey(@NonNull KeyDescriptor descriptor)
+            throws KeyStoreException {
+        handleRemoteExceptionWithRetry((service) -> {
+            service.deleteKey(descriptor);
+            return 0;
+        });
+    }
+
+    protected static void interruptedPreservingSleep(long millis) {
+        boolean wasInterrupted = false;
+        Calendar calendar = Calendar.getInstance();
+        long target = calendar.getTimeInMillis() + millis;
+        while (true) {
+            try {
+                Thread.sleep(target - calendar.getTimeInMillis());
+                break;
+            } catch (InterruptedException e) {
+                wasInterrupted = true;
+            } catch (IllegalArgumentException e) {
+                // This means that the argument to sleep was negative.
+                // So we are done sleeping.
+                break;
+            }
+        }
+        if (wasInterrupted) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
new file mode 100644
index 0000000..9af15a5
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -0,0 +1,141 @@
+/*
+ * 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.security;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.IKeystoreOperation;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class KeyStoreOperation {
+    static final String TAG = "KeyStoreOperation";
+    private final IKeystoreOperation mOperation;
+    private final Long mChallenge;
+    private final KeyParameter[] mParameters;
+
+    public KeyStoreOperation(
+            @NonNull IKeystoreOperation operation,
+            Long challenge,
+            KeyParameter[] parameters
+    ) {
+        this.mOperation = operation;
+        this.mChallenge = challenge;
+        this.mParameters = parameters;
+    }
+
+    /**
+     * Gets the challenge associated with this operation.
+     * @return null if the operation does not required authorization. A 64bit operation
+     *         challenge otherwise.
+     */
+    public Long getChallenge() {
+        return mChallenge;
+    }
+
+    /**
+     * Gets the parameters associated with this operation.
+     * @return
+     */
+    public KeyParameter[] getParameters() {
+        return mParameters;
+    }
+
+    private <R> R handleExceptions(@NonNull CheckedRemoteRequest<R> request)
+            throws KeyStoreException {
+        try {
+            return request.execute();
+        } catch (ServiceSpecificException e) {
+            switch(e.errorCode) {
+                case ResponseCode.OPERATION_BUSY: {
+                    throw new IllegalThreadStateException(
+                            "Cannot update the same operation concurrently."
+                    );
+                }
+                default:
+                    // TODO Human readable string. Use something like KeyStore.getKeyStoreException
+                    throw new KeyStoreException(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.
+            Log.e(
+                    TAG,
+                    "Remote exception while advancing a KeyStoreOperation.",
+                    e
+            );
+            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE, "");
+        }
+    }
+
+    /**
+     * Updates the Keystore operation represented by this object with more associated data.
+     * @see IKeystoreOperation#updateAad(byte[]) for more details.
+     * @param input
+     * @throws KeyStoreException
+     */
+    public void updateAad(@NonNull byte[] input) throws KeyStoreException {
+        handleExceptions(() -> {
+            mOperation.updateAad(input);
+            return 0;
+        });
+    }
+
+    /**
+     * Updates the Keystore operation represented by this object.
+     * @see IKeystoreOperation#update(byte[]) for more details.
+     * @param input
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public byte[] update(@NonNull byte[] input) throws KeyStoreException {
+        return handleExceptions(() -> mOperation.update(input));
+    }
+
+    /**
+     * Finalizes the Keystore operation represented by this object.
+     * @see IKeystoreOperation#finish(byte[], byte[]) for more details.
+     * @param input
+     * @param signature
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public byte[] finish(byte[] input, byte[] signature) throws KeyStoreException {
+        return handleExceptions(() -> mOperation.finish(input, signature));
+    }
+
+    /**
+     * Aborts the Keystore operation represented by this object.
+     * @see IKeystoreOperation#abort() for more details.
+     * @throws KeyStoreException
+     * @hide
+     */
+    public void abort() throws KeyStoreException {
+        handleExceptions(() -> {
+            mOperation.abort();
+            return 0;
+        });
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
new file mode 100644
index 0000000..9d3b622
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -0,0 +1,217 @@
+/*
+ * 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.security;
+
+import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keystore.BackendBusyException;
+import android.security.keystore.KeyStoreConnectException;
+import android.system.keystore2.AuthenticatorSpec;
+import android.system.keystore2.CreateOperationResponse;
+import android.system.keystore2.IKeystoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.Collection;
+
+/**
+ * This is a shim around the security level specific interface of Keystore 2.0. Services with
+ * this interface are instantiated per KeyMint backend, each having there own security level.
+ * Thus this object representation of a security level.
+ * @hide
+ */
+public class KeyStoreSecurityLevel {
+    private static final String TAG = "KeyStoreSecurityLevel";
+    private final IKeystoreSecurityLevel mSecurityLevel;
+
+    public KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel) {
+        this.mSecurityLevel = securityLevel;
+    }
+
+    private <R> R handleExceptions(CheckedRemoteRequest<R> request) throws KeyStoreException {
+        try {
+            return request.execute();
+        } catch (ServiceSpecificException e) {
+            throw new KeyStoreException(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.
+            Log.e(TAG, "Could not connect to Keystore.", e);
+            throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
+        }
+    }
+
+    /**
+     * Creates a new keystore operation.
+     * @see IKeystoreSecurityLevel#createOperation(KeyDescriptor, KeyParameter[], boolean) for more
+     * details.
+     * @param keyDescriptor
+     * @param args
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyStoreOperation createOperation(@NonNull KeyDescriptor keyDescriptor,
+            Collection<KeyParameter> args) throws KeyStoreException {
+        while (true) {
+            try {
+                CreateOperationResponse createOperationResponse =
+                        mSecurityLevel.createOperation(
+                                keyDescriptor,
+                                args.toArray(new KeyParameter[args.size()]),
+                                false /* forced */
+                        );
+                Long challenge = null;
+                if (createOperationResponse.operationChallenge != null) {
+                    challenge = createOperationResponse.operationChallenge.challenge;
+                }
+                KeyParameter[] parameters = null;
+                if (createOperationResponse.parameters != null) {
+                    parameters = createOperationResponse.parameters.keyParameter;
+                }
+                return new KeyStoreOperation(
+                        createOperationResponse.iOperation,
+                        challenge,
+                        parameters);
+            } catch (ServiceSpecificException e) {
+                switch (e.errorCode) {
+                    case ResponseCode.BACKEND_BUSY: {
+                        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();
+                        } 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.
+                            // 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));
+                        }
+                        break;
+                    }
+                    default:
+                        throw new KeyStoreException(e.errorCode, "");
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to keystore", e);
+                throw new KeyStoreConnectException();
+            }
+        }
+    }
+
+    /**
+     * Generates a new key in Keystore.
+     * @see IKeystoreSecurityLevel#generateKey(KeyDescriptor, KeyDescriptor, KeyParameter[], int,
+     * byte[]) for more details.
+     * @param descriptor
+     * @param attestationKey
+     * @param args
+     * @param flags
+     * @param entropy
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyMetadata generateKey(@NonNull KeyDescriptor descriptor, KeyDescriptor attestationKey,
+            Collection<KeyParameter> args, int flags, byte[] entropy)
+            throws KeyStoreException {
+        return handleExceptions(() -> mSecurityLevel.generateKey(
+                descriptor, attestationKey, args.toArray(new KeyParameter[args.size()]),
+                flags, entropy));
+    }
+
+    /**
+     * Imports a key into Keystore.
+     * @see IKeystoreSecurityLevel#importKey(KeyDescriptor, KeyDescriptor, KeyParameter[], int,
+     * byte[]) for more details.
+     * @param descriptor
+     * @param attestationKey
+     * @param args
+     * @param flags
+     * @param keyData
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyMetadata importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey,
+            Collection<KeyParameter> args, int flags, byte[] keyData)
+            throws KeyStoreException {
+        return handleExceptions(() -> mSecurityLevel.importKey(descriptor, attestationKey,
+                args.toArray(new KeyParameter[args.size()]), flags, keyData));
+    }
+
+    /**
+     * Imports a wrapped key into Keystore.
+     * @see IKeystoreSecurityLevel#importWrappedKey(KeyDescriptor, KeyDescriptor, byte[],
+     * KeyParameter[], AuthenticatorSpec[]) for more details.
+     * @param wrappedKeyDescriptor
+     * @param wrappingKeyDescriptor
+     * @param wrappedKey
+     * @param maskingKey
+     * @param args
+     * @param authenticatorSpecs
+     * @return
+     * @throws KeyStoreException
+     * @hide
+     */
+    public KeyMetadata importWrappedKey(@NonNull KeyDescriptor wrappedKeyDescriptor,
+            @NonNull KeyDescriptor wrappingKeyDescriptor,
+            @NonNull byte[] wrappedKey, byte[] maskingKey,
+            Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)
+            throws KeyStoreException {
+        KeyDescriptor keyDescriptor = new KeyDescriptor();
+        keyDescriptor.alias = wrappedKeyDescriptor.alias;
+        keyDescriptor.nspace = wrappedKeyDescriptor.nspace;
+        keyDescriptor.blob = wrappedKey;
+        keyDescriptor.domain = wrappedKeyDescriptor.domain;
+
+        return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+                wrappingKeyDescriptor, maskingKey,
+                args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
+    }
+
+    protected static void interruptedPreservingSleep(long millis) {
+        boolean wasInterrupted = false;
+        Calendar calendar = Calendar.getInstance();
+        long target = calendar.getTimeInMillis() + millis;
+        while (true) {
+            try {
+                Thread.sleep(target - calendar.getTimeInMillis());
+                break;
+            } catch (InterruptedException e) {
+                wasInterrupted = true;
+            } catch (IllegalArgumentException e) {
+                // This means that the argument to sleep was negative.
+                // So we are done sleeping.
+                break;
+            }
+        }
+        if (wasInterrupted) {
+            Thread.currentThread().interrupt();
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index 624321c..5730234 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -34,7 +34,7 @@
  *
  * @hide
  */
-class AndroidKeyStoreBCWorkaroundProvider extends Provider {
+public class AndroidKeyStoreBCWorkaroundProvider extends Provider {
 
     // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
     // classes when this provider is instantiated and installed early on during each app's
@@ -50,8 +50,14 @@
 
     private static final String DESEDE_SYSTEM_PROPERTY = "ro.hardware.keystore_desede";
 
-    AndroidKeyStoreBCWorkaroundProvider() {
-        super("AndroidKeyStoreBCWorkaround",
+    /** @hide */
+    public AndroidKeyStoreBCWorkaroundProvider() {
+        this("AndroidKeyStoreBCWorkaround");
+    }
+
+    /** @hide **/
+    public AndroidKeyStoreBCWorkaroundProvider(String providerName) {
+        super(providerName,
                 1.0,
                 "Android KeyStore security provider to work around Bouncy Castle");
 
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 71e6559..3ac9d68 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,6 +23,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterDefs;
+import android.sysprop.Keystore2Properties;
 
 import java.io.IOException;
 import java.security.KeyFactory;
@@ -70,14 +71,20 @@
     private static final String DESEDE_SYSTEM_PROPERTY =
             "ro.hardware.keystore_desede";
 
-    /** @hide **/
+    /** @hide */
     public AndroidKeyStoreProvider() {
-        super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
+        this(PROVIDER_NAME);
+    }
+
+    /** @hide **/
+    public AndroidKeyStoreProvider(String providerName) {
+        super(providerName, 1.0, "Android KeyStore security provider");
 
         boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY));
 
         // java.security.KeyStore
         put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
+        put("alg.alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore");
 
         // java.security.KeyPairGenerator
         put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
@@ -111,6 +118,26 @@
         putSecretKeyFactoryImpl("HmacSHA512");
     }
 
+    private static boolean sKeystore2Enabled;
+
+    /**
+     * This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
+     * Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
+     * the platform property that indicates that Keystore 2.0 is enabled is not readable
+     * by applications. So we set this value when {@code install()} is called because it
+     * is called by zygote, which can access Keystore2Properties.
+     *
+     * This function can be removed once the transition to Keystore 2.0 is complete.
+     * b/171305684
+     *
+     * @return true if Keystore 2.0 is enabled.
+     * @hide
+     */
+    public static boolean isKeystore2Enabled() {
+        return sKeystore2Enabled;
+    }
+
+
     /**
      * Installs a new instance of this provider (and the
      * {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -138,6 +165,11 @@
             // priority.
             Security.addProvider(workaroundProvider);
         }
+
+        // {@code install()} is run by zygote when this property is still accessible. We store its
+        // value so that the Keystore SPI can act accordingly without having to access an internal
+        // property.
+        sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
     }
 
     private void putSecretKeyFactoryImpl(String algorithm) {
@@ -412,8 +444,12 @@
     @NonNull
     public static java.security.KeyStore getKeyStoreForUid(int uid)
             throws KeyStoreException, NoSuchProviderException {
+        String providerName = PROVIDER_NAME;
+        if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+            providerName = "AndroidKeyStoreLegacy";
+        }
         java.security.KeyStore result =
-                java.security.KeyStore.getInstance("AndroidKeyStore", PROVIDER_NAME);
+                java.security.KeyStore.getInstance(providerName);
         try {
             result.load(new AndroidKeyStoreLoadStoreParameter(uid));
         } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 9707260..3694d63 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -211,7 +211,11 @@
                 userAuthenticationValidWhileOnBody,
                 trustedUserPresenceRequired,
                 invalidatedByBiometricEnrollment,
-                userConfirmationRequired);
+                userConfirmationRequired,
+                // Keystore 1.0 does not tell us the exact security level of the key
+                // so we report an unknown but secure security level.
+                insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE
+                        : KeyProperties.SECURITY_LEVEL_SOFTWARE);
     }
 
     private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/ArrayUtils.java b/keystore/java/android/security/keystore/ArrayUtils.java
index f519c7c..c8c1de4 100644
--- a/keystore/java/android/security/keystore/ArrayUtils.java
+++ b/keystore/java/android/security/keystore/ArrayUtils.java
@@ -18,6 +18,8 @@
 
 import libcore.util.EmptyArray;
 
+import java.util.function.Consumer;
+
 /**
  * @hide
  */
@@ -107,4 +109,16 @@
             return result;
         }
     }
+
+    /**
+     * Runs {@code consumer.accept()} for each element of {@code array}.
+     * @param array
+     * @param consumer
+     * @hide
+     */
+    public static void forEach(int[] array, Consumer<Integer> consumer) {
+        for (int i : array) {
+            consumer.accept(i);
+        }
+    }
 }
diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java
new file mode 100644
index 0000000..1a88469
--- /dev/null
+++ b/keystore/java/android/security/keystore/BackendBusyException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.security.keystore;
+
+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.
+ */
+public class BackendBusyException extends ProviderException {
+
+    /**
+     * Constructs a new {@code BackendBusyException} without detail message and cause.
+     */
+    public BackendBusyException() {
+        super("The keystore backend has no operation slots available. Retry later.");
+    }
+
+    /**
+     * Constructs a new {@code BackendBusyException} with the provided detail message and
+     * no cause.
+     */
+    public BackendBusyException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code BackendBusyException} with the provided detail message and
+     * cause.
+     */
+    public BackendBusyException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 688c4a7..e9aac7d 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -27,7 +27,6 @@
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.Build;
 import android.security.GateKeeper;
-import android.security.KeyStore;
 import android.text.TextUtils;
 
 import java.math.BigInteger;
@@ -246,7 +245,7 @@
     private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
 
     private final String mKeystoreAlias;
-    private final int mUid;
+    private final int mNamespace;
     private final int mKeySize;
     private final AlgorithmParameterSpec mSpec;
     private final X500Principal mCertificateSubject;
@@ -286,7 +285,7 @@
      */
     public KeyGenParameterSpec(
             String keyStoreAlias,
-            int uid,
+            int namespace,
             int keySize,
             AlgorithmParameterSpec spec,
             X500Principal certificateSubject,
@@ -337,7 +336,7 @@
         }
 
         mKeystoreAlias = keyStoreAlias;
-        mUid = uid;
+        mNamespace = namespace;
         mKeySize = keySize;
         mSpec = spec;
         mCertificateSubject = certificateSubject;
@@ -382,11 +381,43 @@
      * Returns the UID which will own the key. {@code -1} is an alias for the UID of the current
      * process.
      *
+     * @deprecated See deprecation message on {@link KeyGenParameterSpec.Builder#setUid(int)}.
+     *             Known namespaces will be translated to their legacy UIDs. Unknown
+     *             Namespaces will yield {@link IllegalStateException}.
+     *
      * @hide
      */
     @UnsupportedAppUsage
+    @Deprecated
     public int getUid() {
-        return mUid;
+        if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
+            // If Keystore2 has not been enabled we have to behave as if mNamespace is actually
+            // a UID, because we are still being used with the old Keystore SPI.
+            // TODO This if statement and body can be removed when the Keystore 2 migration is
+            //      complete. b/171563717
+            return mNamespace;
+        }
+        try {
+            return KeyProperties.namespaceToLegacyUid(mNamespace);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalStateException("getUid called on KeyGenParameterSpec with non legacy"
+                    + " keystore namespace.", e);
+        }
+    }
+
+    /**
+     * Returns the target namespace for the key.
+     * See {@link KeyGenParameterSpec.Builder#setNamespace(int)}.
+     *
+     * @return The numeric namespace as configured in the keystore2_key_contexts files of Android's
+     *         SEPolicy.
+     *         TODO b/171806779 link to public Keystore 2.0 documentation.
+     *              See bug for more details for now.
+     * @hide
+     */
+    @SystemApi
+    public int getNamespace() {
+        return mNamespace;
     }
 
     /**
@@ -767,7 +798,7 @@
         private final String mKeystoreAlias;
         private @KeyProperties.PurposeEnum int mPurposes;
 
-        private int mUid = KeyStore.UID_SELF;
+        private int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
         private int mKeySize = -1;
         private AlgorithmParameterSpec mSpec;
         private X500Principal mCertificateSubject;
@@ -830,7 +861,7 @@
          */
         public Builder(@NonNull KeyGenParameterSpec sourceSpec) {
             this(sourceSpec.getKeystoreAlias(), sourceSpec.getPurposes());
-            mUid = sourceSpec.getUid();
+            mNamespace = sourceSpec.getNamespace();
             mKeySize = sourceSpec.getKeySize();
             mSpec = sourceSpec.getAlgorithmParameterSpec();
             mCertificateSubject = sourceSpec.getCertificateSubject();
@@ -873,12 +904,51 @@
          *
          * @param uid UID or {@code -1} for the UID of the current process.
          *
+         * @deprecated Setting the UID of the target namespace is based on a hardcoded
+         * hack in the Keystore service. This is no longer supported with Keystore 2.0/Android S.
+         * Instead, dedicated non UID based namespaces can be configured in SEPolicy using
+         * the keystore2_key_contexts files. The functionality of this method will be supported
+         * by mapping knows special UIDs, such as WIFI, to the newly configured SELinux based
+         * namespaces. Unknown UIDs will yield {@link IllegalArgumentException}.
+         *
          * @hide
          */
         @SystemApi
         @NonNull
+        @Deprecated
         public Builder setUid(int uid) {
-            mUid = uid;
+            if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
+                // If Keystore2 has not been enabled we have to behave as if mNamespace is actually
+                // a UID, because we are still being used with the old Keystore SPI.
+                // TODO This if statement and body can be removed when the Keystore 2 migration is
+                //      complete. b/171563717
+                mNamespace = uid;
+                return this;
+            }
+            mNamespace = KeyProperties.legacyUidToNamespace(uid);
+            return this;
+        }
+
+        /**
+         * Set the designated SELinux namespace that the key shall live in. The caller must
+         * have sufficient permissions to install a key in the given namespace. Namespaces
+         * can be created using SEPolicy. The keystore2_key_contexts files map numeric
+         * namespaces to SELinux labels, and SEPolicy can be used to grant access to these
+         * namespaces to the desired target context. This is the preferred way to share
+         * keys between system and vendor components, e.g., WIFI settings and WPA supplicant.
+         *
+         * @param namespace Numeric SELinux namespace as configured in keystore2_key_contexts
+         *                  of Android's SEPolicy.
+         *                  TODO b/171806779 link to public Keystore 2.0 documentation.
+         *                       See bug for more details for now.
+         * @return this Builder object.
+         *
+         * @hide
+         */
+        @SystemApi
+        @NonNull
+        public Builder setNamespace(int namespace) {
+            mNamespace = namespace;
             return this;
         }
 
@@ -1489,7 +1559,7 @@
         public KeyGenParameterSpec build() {
             return new KeyGenParameterSpec(
                     mKeystoreAlias,
-                    mUid,
+                    mNamespace,
                     mKeySize,
                     mSpec,
                     mCertificateSubject,
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index d891a25..7158d0c 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -84,6 +84,7 @@
     private final boolean mTrustedUserPresenceRequired;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mUserConfirmationRequired;
+    private final @KeyProperties.SecurityLevelEnum int mSecurityLevel;
 
     /**
      * @hide
@@ -107,7 +108,8 @@
             boolean userAuthenticationValidWhileOnBody,
             boolean trustedUserPresenceRequired,
             boolean invalidatedByBiometricEnrollment,
-            boolean userConfirmationRequired) {
+            boolean userConfirmationRequired,
+            @KeyProperties.SecurityLevelEnum int securityLevel) {
         mKeystoreAlias = keystoreKeyAlias;
         mInsideSecureHardware = insideSecureHardware;
         mOrigin = origin;
@@ -131,6 +133,7 @@
         mTrustedUserPresenceRequired = trustedUserPresenceRequired;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mUserConfirmationRequired = userConfirmationRequired;
+        mSecurityLevel = securityLevel;
     }
 
     /**
@@ -144,7 +147,10 @@
      * Returns {@code true} if the key resides inside secure hardware (e.g., Trusted Execution
      * Environment (TEE) or Secure Element (SE)). Key material of such keys is available in
      * plaintext only inside the secure hardware and is not exposed outside of it.
+     *
+     * @deprecated This method is superseded by @see getSecurityLevel.
      */
+    @Deprecated
     public boolean isInsideSecureHardware() {
         return mInsideSecureHardware;
     }
@@ -355,4 +361,17 @@
     public boolean isTrustedUserPresenceRequired() {
         return mTrustedUserPresenceRequired;
     }
+
+    /**
+     * Returns the security level that the key is protected by.
+     * {@code KeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT} and
+     * {@code KeyProperties.SecurityLevelEnum.STRONGBOX} indicate that the key material resides
+     * in secure hardware. Key material of such keys is available in
+     * plaintext only inside the secure hardware and is not exposed outside of it.
+     *
+     * <p>See {@link KeyProperties}.{@code SecurityLevelEnum} constants.
+     */
+    public @KeyProperties.SecurityLevelEnum int getSecurityLevel() {
+        return mSecurityLevel;
+    }
 }
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 9050c69..5928540 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.security.KeyStore;
 import android.security.keymaster.KeymasterDefs;
 
 import libcore.util.EmptyArray;
@@ -857,4 +858,43 @@
         }
     }
 
+    /**
+     * This value indicates the implicit keystore namespace of the calling application.
+     * It is used by default. Only select system components can choose a different namespace
+     * which it must be configured in SEPolicy.
+     * @hide
+     */
+    public static final int NAMESPACE_APPLICATION = -1;
+
+    /**
+     * For legacy support, translate namespaces into known UIDs.
+     * @hide
+     */
+    public static int namespaceToLegacyUid(int namespace) {
+        switch (namespace) {
+            case NAMESPACE_APPLICATION:
+                return KeyStore.UID_SELF;
+            // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
+            //  b/171305388 and b/171305607
+            default:
+                throw new IllegalArgumentException("No UID corresponding to namespace "
+                        + namespace);
+        }
+    }
+
+    /**
+     * For legacy support, translate namespaces into known UIDs.
+     * @hide
+     */
+    public static int legacyUidToNamespace(int uid) {
+        switch (uid) {
+            case KeyStore.UID_SELF:
+                return NAMESPACE_APPLICATION;
+            // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
+            //  b/171305388 and b/171305607
+            default:
+                throw new IllegalArgumentException("No namespace corresponding to uid "
+                        + uid);
+        }
+    }
 }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
new file mode 100644
index 0000000..70713a4
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.annotation.NonNull;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyParameter;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Base class for Android Keystore 3DES {@link CipherSpi} implementations.
+ *
+ * @hide
+ */
+public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+    private static final int BLOCK_SIZE_BYTES = 8;
+
+    private final int mKeymasterBlockMode;
+    private final int mKeymasterPadding;
+    /** Whether this transformation requires an IV. */
+    private final boolean mIvRequired;
+
+    private byte[] mIv;
+
+    /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
+    private boolean mIvHasBeenUsed;
+
+    AndroidKeyStore3DESCipherSpi(
+            int keymasterBlockMode,
+            int keymasterPadding,
+            boolean ivRequired) {
+        mKeymasterBlockMode = keymasterBlockMode;
+        mKeymasterPadding = keymasterPadding;
+        mIvRequired = ivRequired;
+    }
+
+    abstract static class ECB extends AndroidKeyStore3DESCipherSpi {
+        protected ECB(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
+        }
+
+        public static class NoPadding extends ECB {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+
+        public static class PKCS7Padding extends ECB {
+            public PKCS7Padding() {
+                super(KeymasterDefs.KM_PAD_PKCS7);
+            }
+        }
+    }
+
+    abstract static class CBC extends AndroidKeyStore3DESCipherSpi {
+        protected CBC(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
+        }
+
+        public static class NoPadding extends CBC {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+
+        public static class PKCS7Padding extends CBC {
+            public PKCS7Padding() {
+                super(KeymasterDefs.KM_PAD_PKCS7);
+            }
+        }
+    }
+
+    @Override
+    protected void initKey(int i, Key key) throws InvalidKeyException {
+        if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException(
+                    "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
+                            KeyProperties.KEY_ALGORITHM_3DES + " supported");
+        }
+        setKey((AndroidKeyStoreSecretKey) key);
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        return BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        return inputLen + 3 * BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected final byte[] engineGetIV() {
+        return ArrayUtils.cloneIfNotEmpty(mIv);
+    }
+
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        if (!mIvRequired) {
+            return null;
+        }
+        if ((mIv != null) && (mIv.length > 0)) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("DESede");
+                params.init(new IvParameterSpec(mIv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException(
+                        "Failed to obtain 3DES AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new ProviderException(
+                        "Failed to initialize 3DES AlgorithmParameters with an IV",
+                        e);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
+        if (!mIvRequired) {
+            return;
+        }
+
+        // IV is used
+        if (!isEncrypting()) {
+            throw new InvalidKeyException("IV required when decrypting"
+                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+        }
+    }
+
+    @Override
+    protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvRequired) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException(
+                        "IvParameterSpec must be provided when decrypting");
+            }
+            return;
+        }
+        if (!(params instanceof IvParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
+        }
+        mIv = ((IvParameterSpec) params).getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
+        }
+    }
+
+    @Override
+    protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvRequired) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+            return;
+        }
+
+        if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
+                            + ". Supported: DESede");
+        }
+
+        IvParameterSpec ivSpec;
+        try {
+            ivSpec = params.getParameterSpec(IvParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ", but not found in parameters: " + params, e);
+            }
+            mIv = null;
+            return;
+        }
+        mIv = ivSpec.getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
+        }
+    }
+
+    @Override
+    protected final int getAdditionalEntropyAmountForBegin() {
+        if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
+            // IV will need to be generated
+            return BLOCK_SIZE_BYTES;
+        }
+
+        return 0;
+    }
+
+    @Override
+    protected int getAdditionalEntropyAmountForFinish() {
+        return 0;
+    }
+
+    @Override
+    protected void addAlgorithmSpecificParametersToBegin(@NonNull List<KeyParameter> parameters) {
+        if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
+            // IV is being reused for encryption: this violates security best practices.
+            throw new IllegalStateException(
+                    "IV has already been used. Reusing IV in encryption mode violates security best"
+                            + " practices.");
+        }
+
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM,
+                KeymasterDefs.KM_ALGORITHM_3DES
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_BLOCK_MODE,
+                mKeymasterBlockMode
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PADDING,
+                mKeymasterPadding
+        ));
+
+        if (mIvRequired && (mIv != null)) {
+            parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
+        }
+    }
+
+    @Override
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            KeyParameter[] parameters) {
+        mIvHasBeenUsed = true;
+
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = null;
+        if (parameters != null) {
+            for (KeyParameter p : parameters) {
+                if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
+                    returnedIv = p.blob;
+                    break;
+                }
+            }
+        }
+
+        if (mIvRequired) {
+            if (mIv == null) {
+                mIv = returnedIv;
+            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+                throw new ProviderException("IV in use differs from provided IV");
+            }
+        } else {
+            if (returnedIv != null) {
+                throw new ProviderException(
+                        "IV in use despite IV not being used by this transformation");
+            }
+        }
+    }
+
+    @Override
+    protected final void resetAll() {
+        mIv = null;
+        mIvHasBeenUsed = false;
+        super.resetAll();
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
new file mode 100644
index 0000000..dd094b7
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+import android.security.keystore.KeyProperties;
+import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
+import android.system.keystore2.KeyParameter;
+
+import libcore.util.EmptyArray;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * Base class for Android Keystore authenticated AES {@link CipherSpi} implementations.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+    abstract static class GCM extends AndroidKeyStoreAuthenticatedAESCipherSpi {
+        static final int MIN_SUPPORTED_TAG_LENGTH_BITS = 96;
+        private static final int MAX_SUPPORTED_TAG_LENGTH_BITS = 128;
+        private static final int DEFAULT_TAG_LENGTH_BITS = 128;
+        private static final int IV_LENGTH_BYTES = 12;
+
+        private int mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
+
+        GCM(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_GCM, keymasterPadding);
+        }
+
+        @Override
+        protected final void resetAll() {
+            mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
+            super.resetAll();
+        }
+
+        @Override
+        protected final void resetWhilePreservingInitState() {
+            super.resetWhilePreservingInitState();
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {
+            if (!isEncrypting()) {
+                throw new InvalidKeyException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            // IV is used
+            if (params == null) {
+                if (!isEncrypting()) {
+                    // IV must be provided by the caller
+                    throw new InvalidAlgorithmParameterException(
+                            "GCMParameterSpec must be provided when decrypting");
+                }
+                return;
+            }
+            if (!(params instanceof GCMParameterSpec)) {
+                throw new InvalidAlgorithmParameterException("Only GCMParameterSpec supported");
+            }
+            GCMParameterSpec spec = (GCMParameterSpec) params;
+            byte[] iv = spec.getIV();
+            if (iv == null) {
+                throw new InvalidAlgorithmParameterException("Null IV in GCMParameterSpec");
+            } else if (iv.length != IV_LENGTH_BYTES) {
+                throw new InvalidAlgorithmParameterException("Unsupported IV length: "
+                        + iv.length + " bytes. Only " + IV_LENGTH_BYTES
+                        + " bytes long IV supported");
+            }
+            int tagLengthBits = spec.getTLen();
+            if ((tagLengthBits < MIN_SUPPORTED_TAG_LENGTH_BITS)
+                    || (tagLengthBits > MAX_SUPPORTED_TAG_LENGTH_BITS)
+                    || ((tagLengthBits % 8) != 0)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported tag length: " + tagLengthBits + " bits"
+                        + ". Supported lengths: 96, 104, 112, 120, 128");
+            }
+            setIv(iv);
+            mTagLengthBits = tagLengthBits;
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters(AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+            if (params == null) {
+                if (!isEncrypting()) {
+                    // IV must be provided by the caller
+                    throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                            + ". Use GCMParameterSpec or GCM AlgorithmParameters to provide it.");
+                }
+                return;
+            }
+
+            if (!"GCM".equalsIgnoreCase(params.getAlgorithm())) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
+                        + ". Supported: GCM");
+            }
+
+            GCMParameterSpec spec;
+            try {
+                spec = params.getParameterSpec(GCMParameterSpec.class);
+            } catch (InvalidParameterSpecException e) {
+                if (!isEncrypting()) {
+                    // IV must be provided by the caller
+                    throw new InvalidAlgorithmParameterException("IV and tag length required when"
+                            + " decrypting, but not found in parameters: " + params, e);
+                }
+                setIv(null);
+                return;
+            }
+            initAlgorithmSpecificParameters(spec);
+        }
+
+        @Nullable
+        @Override
+        protected final AlgorithmParameters engineGetParameters() {
+            byte[] iv = getIv();
+            if ((iv != null) && (iv.length > 0)) {
+                try {
+                    AlgorithmParameters params = AlgorithmParameters.getInstance("GCM");
+                    params.init(new GCMParameterSpec(mTagLengthBits, iv));
+                    return params;
+                } catch (NoSuchAlgorithmException e) {
+                    throw new ProviderException(
+                            "Failed to obtain GCM AlgorithmParameters", e);
+                } catch (InvalidParameterSpecException e) {
+                    throw new ProviderException(
+                            "Failed to initialize GCM AlgorithmParameters", e);
+                }
+            }
+            return null;
+        }
+
+        @NonNull
+        @Override
+        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+                KeyStoreOperation operation) {
+            KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
+                    new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                            operation), 0);
+            if (isEncrypting()) {
+                return streamer;
+            } else {
+                // When decrypting, to avoid leaking unauthenticated plaintext, do not return any
+                // plaintext before ciphertext is authenticated by KeyStore.finish.
+                return new BufferAllOutputUntilDoFinalStreamer(streamer);
+            }
+        }
+
+        @NonNull
+        @Override
+        protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
+                KeyStoreOperation operation) {
+            return new KeyStoreCryptoOperationChunkedStreamer(
+                    new AdditionalAuthenticationDataStream(operation), 0);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            if ((getIv() == null) && (isEncrypting())) {
+                // IV will need to be generated
+                return IV_LENGTH_BYTES;
+            }
+
+            return 0;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForFinish() {
+            return 0;
+        }
+
+        @Override
+        protected final void addAlgorithmSpecificParametersToBegin(
+                @NonNull List<KeyParameter> parameters) {
+            super.addAlgorithmSpecificParametersToBegin(parameters);
+            parameters.add(KeyStore2ParameterUtils.makeInt(
+                    KeymasterDefs.KM_TAG_MAC_LENGTH,
+                    mTagLengthBits
+            ));
+        }
+
+        protected final int getTagLengthBits() {
+            return mTagLengthBits;
+        }
+
+        public static final class NoPadding extends GCM {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+
+            @Override
+            protected final int engineGetOutputSize(int inputLen) {
+                int tagLengthBytes = (getTagLengthBits() + 7) / 8;
+                long result;
+                if (isEncrypting()) {
+                    result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen
+                            + tagLengthBytes;
+                } else {
+                    result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen
+                            - tagLengthBytes;
+                }
+                if (result < 0) {
+                    return 0;
+                } else if (result > Integer.MAX_VALUE) {
+                    return Integer.MAX_VALUE;
+                }
+                return (int) result;
+            }
+        }
+    }
+
+    private static final int BLOCK_SIZE_BYTES = 16;
+
+    private final int mKeymasterBlockMode;
+    private final int mKeymasterPadding;
+
+    private byte[] mIv;
+
+    /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
+    private boolean mIvHasBeenUsed;
+
+    AndroidKeyStoreAuthenticatedAESCipherSpi(
+            int keymasterBlockMode,
+            int keymasterPadding) {
+        mKeymasterBlockMode = keymasterBlockMode;
+        mKeymasterPadding = keymasterPadding;
+    }
+
+    @Override
+    protected void resetAll() {
+        mIv = null;
+        mIvHasBeenUsed = false;
+        super.resetAll();
+    }
+
+    @Override
+    protected final void initKey(int opmode, Key key) throws InvalidKeyException {
+        if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException(
+                    "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
+                    KeyProperties.KEY_ALGORITHM_AES + " supported");
+        }
+        setKey((AndroidKeyStoreSecretKey) key);
+    }
+
+    @Override
+    protected void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters) {
+        if ((isEncrypting()) && (mIvHasBeenUsed)) {
+            // IV is being reused for encryption: this violates security best practices.
+            throw new IllegalStateException(
+                    "IV has already been used. Reusing IV in encryption mode violates security best"
+                    + " practices.");
+        }
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM,
+                KeymasterDefs.KM_ALGORITHM_AES
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_BLOCK_MODE,
+                mKeymasterBlockMode
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PADDING,
+                mKeymasterPadding
+        ));
+
+        if (mIv != null) {
+            parameters.add(KeyStore2ParameterUtils.makeBytes(KeymasterDefs.KM_TAG_NONCE, mIv));
+        }
+    }
+
+    @Override
+    protected final void loadAlgorithmSpecificParametersFromBeginResult(
+            KeyParameter[] parameters) {
+        mIvHasBeenUsed = true;
+
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = null;
+        if (parameters != null) {
+            for (KeyParameter p : parameters) {
+                if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
+                    returnedIv = p.blob;
+                    break;
+                }
+            }
+        }
+
+        if (mIv == null) {
+            mIv = returnedIv;
+        } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+            throw new ProviderException("IV in use differs from provided IV");
+        }
+    }
+
+    @Override
+    protected final int engineGetBlockSize() {
+        return BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected final byte[] engineGetIV() {
+        return ArrayUtils.cloneIfNotEmpty(mIv);
+    }
+
+    protected void setIv(byte[] iv) {
+        mIv = iv;
+    }
+
+    protected byte[] getIv() {
+        return mIv;
+    }
+
+    /**
+     * {@link KeyStoreCryptoOperationStreamer} which buffers all output until {@code doFinal} from
+     * which it returns all output in one go, provided {@code doFinal} succeeds.
+     */
+    private static class BufferAllOutputUntilDoFinalStreamer
+        implements KeyStoreCryptoOperationStreamer {
+
+        private final KeyStoreCryptoOperationStreamer mDelegate;
+        private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream();
+        private long mProducedOutputSizeBytes;
+
+        private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) {
+            mDelegate = delegate;
+        }
+
+        @Override
+        public byte[] update(byte[] input, int inputOffset, int inputLength)
+                throws KeyStoreException {
+            byte[] output = mDelegate.update(input, inputOffset, inputLength);
+            if (output != null) {
+                try {
+                    mBufferedOutput.write(output);
+                } catch (IOException e) {
+                    throw new ProviderException("Failed to buffer output", e);
+                }
+            }
+            return EmptyArray.BYTE;
+        }
+
+        @Override
+        public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
+                byte[] signature) throws KeyStoreException {
+            byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature);
+            if (output != null) {
+                try {
+                    mBufferedOutput.write(output);
+                } catch (IOException e) {
+                    throw new ProviderException("Failed to buffer output", e);
+                }
+            }
+            byte[] result = mBufferedOutput.toByteArray();
+            mBufferedOutput.reset();
+            mProducedOutputSizeBytes += result.length;
+            return result;
+        }
+
+        @Override
+        public long getConsumedInputSizeBytes() {
+            return mDelegate.getConsumedInputSizeBytes();
+        }
+
+        @Override
+        public long getProducedOutputSizeBytes() {
+            return mProducedOutputSizeBytes;
+        }
+    }
+
+    /**
+     * Additional Authentication Data (AAD) stream via a KeyStore streaming operation. This stream
+     * sends AAD into the KeyStore.
+     */
+    private static class AdditionalAuthenticationDataStream implements Stream {
+
+        private final KeyStoreOperation mOperation;
+
+        private AdditionalAuthenticationDataStream(KeyStoreOperation operation) {
+            mOperation = operation;
+        }
+
+        @Override
+        public byte[] update(byte[] input) throws KeyStoreException {
+            mOperation.updateAad(input);
+            return null;
+        }
+
+        @Override
+        public byte[] finish(byte[] input, byte[] signature) {
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
new file mode 100644
index 0000000..dd943d4
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 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 java.security.Provider;
+
+/**
+ * {@link Provider} of JCA crypto operations operating on Android KeyStore keys.
+ *
+ * <p>This provider was separated out of {@link AndroidKeyStoreProvider} to work around the issue
+ * that Bouncy Castle provider incorrectly declares that it accepts arbitrary keys (incl. Android
+ * KeyStore ones). This causes JCA to select the Bouncy Castle's implementation of JCA crypto
+ * operations for Android KeyStore keys unless Android KeyStore's own implementations are installed
+ * as higher-priority than Bouncy Castle ones. The purpose of this provider is to do just that: to
+ * offer crypto operations operating on Android KeyStore keys and to be installed at higher priority
+ * than the Bouncy Castle provider.
+ *
+ * <p>Once Bouncy Castle provider is fixed, this provider can be merged into the
+ * {@code AndroidKeyStoreProvider}.
+ *
+ * @hide
+ */
+class AndroidKeyStoreBCWorkaroundProvider extends Provider {
+
+    // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
+    // classes when this provider is instantiated and installed early on during each app's
+    // initialization process.
+
+    private static final String PACKAGE_NAME = "android.security.keystore2";
+    private static final String KEYSTORE_SECRET_KEY_CLASS_NAME =
+            PACKAGE_NAME + ".AndroidKeyStoreSecretKey";
+    private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME =
+            PACKAGE_NAME + ".AndroidKeyStorePrivateKey";
+    private static final String KEYSTORE_PUBLIC_KEY_CLASS_NAME =
+            PACKAGE_NAME + ".AndroidKeyStorePublicKey";
+
+    private static final String DESEDE_SYSTEM_PROPERTY = "ro.hardware.keystore_desede";
+
+    AndroidKeyStoreBCWorkaroundProvider() {
+        super("AndroidKeyStoreBCWorkaround",
+                1.0,
+                "Android KeyStore security provider to work around Bouncy Castle");
+
+        // --------------------- javax.crypto.Mac
+        putMacImpl("HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA1");
+        put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
+        put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1");
+        put("Alg.Alias.Mac.HMAC/SHA1", "HmacSHA1");
+
+        putMacImpl("HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA224");
+        put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA224");
+        put("Alg.Alias.Mac.HMAC-SHA224", "HmacSHA224");
+        put("Alg.Alias.Mac.HMAC/SHA224", "HmacSHA224");
+
+        putMacImpl("HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA256");
+        put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256");
+        put("Alg.Alias.Mac.HMAC-SHA256", "HmacSHA256");
+        put("Alg.Alias.Mac.HMAC/SHA256", "HmacSHA256");
+
+        putMacImpl("HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA384");
+        put("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384");
+        put("Alg.Alias.Mac.HMAC-SHA384", "HmacSHA384");
+        put("Alg.Alias.Mac.HMAC/SHA384", "HmacSHA384");
+
+        putMacImpl("HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA512");
+        put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512");
+        put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512");
+        put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512");
+
+        // --------------------- javax.crypto.Cipher
+        putSymmetricCipherImpl("AES/ECB/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$NoPadding");
+        putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$PKCS7Padding");
+
+        putSymmetricCipherImpl("AES/CBC/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CBC$NoPadding");
+        putSymmetricCipherImpl("AES/CBC/PKCS7Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CBC$PKCS7Padding");
+
+        putSymmetricCipherImpl("AES/CTR/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
+
+        if ("true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY))) {
+            putSymmetricCipherImpl("DESede/CBC/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding");
+            putSymmetricCipherImpl("DESede/CBC/PKCS7Padding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding");
+
+            putSymmetricCipherImpl("DESede/ECB/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding");
+            putSymmetricCipherImpl("DESede/ECB/PKCS7Padding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding");
+        }
+
+        putSymmetricCipherImpl("AES/GCM/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding");
+
+        putAsymmetricCipherImpl("RSA/ECB/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$NoPadding");
+        put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+        putAsymmetricCipherImpl("RSA/ECB/PKCS1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$PKCS1Padding");
+        put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA224AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA256AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA384AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-512AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
+
+        // --------------------- java.security.Signature
+        putSignatureImpl("NONEwithRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$NONEWithPKCS1Padding");
+
+        putSignatureImpl("MD5withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$MD5WithPKCS1Padding");
+        put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5withRSA");
+        put("Alg.Alias.Signature.MD5/RSA", "MD5withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5withRSA");
+
+        putSignatureImpl("SHA1withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA1WithRSAEncryption", "SHA1withRSA");
+        put("Alg.Alias.Signature.SHA1/RSA", "SHA1withRSA");
+        put("Alg.Alias.Signature.SHA-1/RSA", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA");
+
+        putSignatureImpl("SHA224withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA224WithRSAEncryption", "SHA224withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA224withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1",
+                "SHA224withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.11",
+                "SHA224withRSA");
+
+        putSignatureImpl("SHA256withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1",
+                "SHA256withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11",
+                "SHA256withRSA");
+
+        putSignatureImpl("SHA384withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1",
+                "SHA384withRSA");
+
+        putSignatureImpl("SHA512withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1",
+                "SHA512withRSA");
+
+        putSignatureImpl("SHA1withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPSSPadding");
+        putSignatureImpl("SHA224withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPSSPadding");
+        putSignatureImpl("SHA256withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPSSPadding");
+        putSignatureImpl("SHA384withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPSSPadding");
+        putSignatureImpl("SHA512withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPSSPadding");
+
+        putSignatureImpl("NONEwithECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE");
+
+        putSignatureImpl("SHA1withECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
+        put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
+        put("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
+        put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "SHA1withECDSA");
+
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+        putSignatureImpl("SHA224withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA224");
+        // ecdsa-with-SHA224(1)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA");
+
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+        putSignatureImpl("SHA256withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA256");
+        // ecdsa-with-SHA256(2)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA");
+
+        putSignatureImpl("SHA384withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA384");
+        // ecdsa-with-SHA384(3)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA");
+
+        putSignatureImpl("SHA512withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA512");
+        // ecdsa-with-SHA512(4)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA");
+    }
+
+    private void putMacImpl(String algorithm, String implClass) {
+        put("Mac." + algorithm, implClass);
+        put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
+    }
+
+    private void putSymmetricCipherImpl(String transformation, String implClass) {
+        put("Cipher." + transformation, implClass);
+        put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
+    }
+
+    private void putAsymmetricCipherImpl(String transformation, String implClass) {
+        put("Cipher." + transformation, implClass);
+        put("Cipher." + transformation + " SupportedKeyClasses",
+                KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+    }
+
+    private void putSignatureImpl(String algorithm, String implClass) {
+        put("Signature." + algorithm, implClass);
+        put("Signature." + algorithm + " SupportedKeyClasses",
+                KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+    }
+
+    public static String[] getSupportedEcdsaSignatureDigests() {
+        return new String[] {"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+    }
+
+    public static String[] getSupportedRsaSignatureWithPkcs1PaddingDigests() {
+        return new String[] {"NONE", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
new file mode 100644
index 0000000..b785ee5
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2015 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.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyStoreCryptoOperation;
+import android.system.keystore2.KeyParameter;
+
+import libcore.util.EmptyArray;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
+    private static final String TAG = "AndroidKeyStoreCipherSpiBase";
+
+    // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
+    // doFinal finishes.
+    private boolean mEncrypting;
+    private int mKeymasterPurposeOverride = -1;
+    private AndroidKeyStoreKey mKey;
+    private SecureRandom mRng;
+
+    /**
+     * Object representing this operation inside keystore service. It is initialized
+     * by {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some
+     * error conditions in between.
+     */
+    private KeyStoreOperation mOperation;
+    /**
+     * The operation challenge is required when an operation needs user authorization.
+     * The challenge is subjected to an authenticator, e.g., Gatekeeper or a biometric
+     * authenticator, and included in the authentication token minted by this authenticator.
+     * It may be null, if the operation does not require authorization.
+     */
+    private long mOperationChallenge;
+    private KeyStoreCryptoOperationStreamer mMainDataStreamer;
+    private KeyStoreCryptoOperationStreamer mAdditionalAuthenticationDataStreamer;
+    private boolean mAdditionalAuthenticationDataStreamerClosed;
+
+    /**
+     * Encountered exception which could not be immediately thrown because it was encountered inside
+     * a method that does not throw checked exception. This exception will be thrown from
+     * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
+     * {@code engineDoFinal} start ignoring input data.
+     */
+    private Exception mCachedException;
+
+    AndroidKeyStoreCipherSpiBase() {
+        mOperation = null;
+        mEncrypting = false;
+        mKeymasterPurposeOverride = -1;
+        mKey = null;
+        mRng = null;
+        mOperationChallenge = 0;
+        mMainDataStreamer = null;
+        mAdditionalAuthenticationDataStreamer = null;
+        mAdditionalAuthenticationDataStreamerClosed = false;
+        mCachedException = null;
+    }
+
+    @Override
+    protected final void engineInit(int opmode, Key key, SecureRandom random)
+            throws InvalidKeyException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(opmode, key, random);
+            initAlgorithmSpecificParameters();
+            try {
+                ensureKeystoreOperationInitialized();
+            } catch (InvalidAlgorithmParameterException e) {
+                throw new InvalidKeyException(e);
+            }
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    @Override
+    protected final void engineInit(int opmode, Key key, AlgorithmParameters params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(opmode, key, random);
+            initAlgorithmSpecificParameters(params);
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    @Override
+    protected final void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(opmode, key, random);
+            initAlgorithmSpecificParameters(params);
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        switch (opmode) {
+            case Cipher.ENCRYPT_MODE:
+            case Cipher.WRAP_MODE:
+                mEncrypting = true;
+                break;
+            case Cipher.DECRYPT_MODE:
+            case Cipher.UNWRAP_MODE:
+                mEncrypting = false;
+                break;
+            default:
+                throw new InvalidParameterException("Unsupported opmode: " + opmode);
+        }
+        initKey(opmode, key);
+        if (mKey == null) {
+            throw new ProviderException("initKey did not initialize the key");
+        }
+        mRng = random;
+    }
+
+    private void abortOperation() {
+        KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+        mOperation = null;
+    }
+
+    /**
+     * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
+     * cipher instance.
+     *
+     * <p>Subclasses storing additional state should override this method, reset the additional
+     * state, and then chain to superclass.
+     */
+    @CallSuper
+    protected void resetAll() {
+        abortOperation();
+        mEncrypting = false;
+        mKeymasterPurposeOverride = -1;
+        mKey = null;
+        mRng = null;
+        mOperationChallenge = 0;
+        mMainDataStreamer = null;
+        mAdditionalAuthenticationDataStreamer = null;
+        mAdditionalAuthenticationDataStreamerClosed = false;
+        mCachedException = null;
+    }
+
+    /**
+     * Resets this cipher while preserving the initialized state. This must be equivalent to
+     * rolling back the cipher's state to just after the most recent {@code engineInit} completed
+     * successfully.
+     *
+     * <p>Subclasses storing additional post-init state should override this method, reset the
+     * additional state, and then chain to superclass.
+     */
+    @CallSuper
+    protected void resetWhilePreservingInitState() {
+        abortOperation();
+        mOperationChallenge = 0;
+        mMainDataStreamer = null;
+        mAdditionalAuthenticationDataStreamer = null;
+        mAdditionalAuthenticationDataStreamerClosed = false;
+        mCachedException = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (mMainDataStreamer != null) {
+            return;
+        }
+        if (mCachedException != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        List<KeyParameter> parameters = new ArrayList<>();
+        addAlgorithmSpecificParametersToBegin(parameters);
+
+        int purpose;
+        if (mKeymasterPurposeOverride != -1) {
+            purpose = mKeymasterPurposeOverride;
+        } else {
+            purpose = mEncrypting
+                    ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT;
+        }
+
+        parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
+
+        try {
+            mOperation = mKey.getSecurityLevel().createOperation(
+                    mKey.getKeyIdDescriptor(),
+                    parameters
+            );
+        } catch (KeyStoreException keyStoreException) {
+            GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit(
+                    mKey, keyStoreException);
+            if (e != null) {
+                if (e instanceof InvalidKeyException) {
+                    throw (InvalidKeyException) e;
+                } else if (e instanceof InvalidAlgorithmParameterException) {
+                    throw (InvalidAlgorithmParameterException) e;
+                } else {
+                    throw new ProviderException("Unexpected exception type", e);
+                }
+            }
+        }
+
+        // Now we check if we got an operation challenge. This indicates that user authorization
+        // is required. And if we got a challenge we check if the authorization can possibly
+        // succeed.
+        mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
+                mOperation, mKey);
+
+        loadAlgorithmSpecificParametersFromBeginResult(mOperation.getParameters());
+        mMainDataStreamer = createMainDataStreamer(mOperation);
+        mAdditionalAuthenticationDataStreamer =
+                createAdditionalAuthenticationDataStreamer(mOperation);
+        mAdditionalAuthenticationDataStreamerClosed = false;
+    }
+
+    /**
+     * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives
+     * the corresponding ciphertext/plaintext from the KeyStore.
+     *
+     * <p>This implementation returns a working streamer.
+     */
+    @NonNull
+    protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+            KeyStoreOperation operation) {
+        return new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        operation), 0);
+    }
+
+    /**
+     * Creates a streamer which sends Additional Authentication Data (AAD) into the KeyStore.
+     *
+     * <p>This implementation returns {@code null}.
+     *
+     * @return stream or {@code null} if AAD is not supported by this cipher.
+     */
+    @Nullable
+    protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
+            @SuppressWarnings("unused") KeyStoreOperation operation) {
+        return null;
+    }
+
+    @Override
+    protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+        if (mCachedException != null) {
+            return null;
+        }
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            mCachedException = e;
+            return null;
+        }
+
+        if (inputLen == 0) {
+            return null;
+        }
+
+        byte[] output;
+        try {
+            flushAAD();
+            output = mMainDataStreamer.update(input, inputOffset, inputLen);
+        } catch (KeyStoreException e) {
+            mCachedException = e;
+            return null;
+        }
+
+        if (output.length == 0) {
+            return null;
+        }
+
+        return output;
+    }
+
+    private void flushAAD() throws KeyStoreException {
+        if ((mAdditionalAuthenticationDataStreamer != null)
+                && (!mAdditionalAuthenticationDataStreamerClosed)) {
+            byte[] output;
+            try {
+                output = mAdditionalAuthenticationDataStreamer.doFinal(
+                        EmptyArray.BYTE, 0, 0,
+                        null); // no signature
+            } finally {
+                mAdditionalAuthenticationDataStreamerClosed = true;
+            }
+            if ((output != null) && (output.length > 0)) {
+                throw new ProviderException(
+                        "AAD update unexpectedly returned data: " + output.length + " bytes");
+            }
+        }
+    }
+
+    @Override
+    protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException {
+        byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected final int engineUpdate(ByteBuffer input, ByteBuffer output)
+            throws ShortBufferException {
+        if (input == null) {
+            throw new NullPointerException("input == null");
+        }
+        if (output == null) {
+            throw new NullPointerException("output == null");
+        }
+
+        int inputSize = input.remaining();
+        byte[] outputArray;
+        if (input.hasArray()) {
+            outputArray =
+                    engineUpdate(
+                            input.array(), input.arrayOffset() + input.position(), inputSize);
+            input.position(input.position() + inputSize);
+        } else {
+            byte[] inputArray = new byte[inputSize];
+            input.get(inputArray);
+            outputArray = engineUpdate(inputArray, 0, inputSize);
+        }
+
+        int outputSize = (outputArray != null) ? outputArray.length : 0;
+        if (outputSize > 0) {
+            int outputBufferAvailable = output.remaining();
+            try {
+                output.put(outputArray);
+            } catch (BufferOverflowException e) {
+                throw new ShortBufferException(
+                        "Output buffer too small. Produced: " + outputSize + ", available: "
+                                + outputBufferAvailable);
+            }
+        }
+        return outputSize;
+    }
+
+    @Override
+    protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
+        if (mCachedException != null) {
+            return;
+        }
+
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            mCachedException = e;
+            return;
+        }
+
+        if (mAdditionalAuthenticationDataStreamerClosed) {
+            throw new IllegalStateException(
+                    "AAD can only be provided before Cipher.update is invoked");
+        }
+
+        if (mAdditionalAuthenticationDataStreamer == null) {
+            throw new IllegalStateException("This cipher does not support AAD");
+        }
+
+        byte[] output;
+        try {
+            output = mAdditionalAuthenticationDataStreamer.update(input, inputOffset, inputLen);
+        } catch (KeyStoreException e) {
+            mCachedException = e;
+            return;
+        }
+
+        if ((output != null) && (output.length > 0)) {
+            throw new ProviderException("AAD update unexpectedly produced output: "
+                    + output.length + " bytes");
+        }
+    }
+
+    @Override
+    protected final void engineUpdateAAD(ByteBuffer src) {
+        if (src == null) {
+            throw new IllegalArgumentException("src == null");
+        }
+        if (!src.hasRemaining()) {
+            return;
+        }
+
+        byte[] input;
+        int inputOffset;
+        int inputLen;
+        if (src.hasArray()) {
+            input = src.array();
+            inputOffset = src.arrayOffset() + src.position();
+            inputLen = src.remaining();
+            src.position(src.limit());
+        } else {
+            input = new byte[src.remaining()];
+            inputOffset = 0;
+            inputLen = input.length;
+            src.get(input);
+        }
+        engineUpdateAAD(input, inputOffset, inputLen);
+    }
+
+    @Override
+    protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        if (mCachedException != null) {
+            throw (IllegalBlockSizeException)
+                    new IllegalBlockSizeException().initCause(mCachedException);
+        }
+
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+        }
+
+        byte[] output;
+        try {
+            flushAAD();
+            output = mMainDataStreamer.doFinal(
+                    input, inputOffset, inputLen,
+                    null); // no signature involved
+        } catch (KeyStoreException e) {
+            switch (e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
+                    throw (BadPaddingException) new BadPaddingException().initCause(e);
+                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+                    throw (AEADBadTagException) new AEADBadTagException().initCause(e);
+                default:
+                    throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+            }
+        }
+
+        resetWhilePreservingInitState();
+        return output;
+    }
+
+    @Override
+    protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected final int engineDoFinal(ByteBuffer input, ByteBuffer output)
+            throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+        if (input == null) {
+            throw new NullPointerException("input == null");
+        }
+        if (output == null) {
+            throw new NullPointerException("output == null");
+        }
+
+        int inputSize = input.remaining();
+        byte[] outputArray;
+        if (input.hasArray()) {
+            outputArray =
+                    engineDoFinal(
+                            input.array(), input.arrayOffset() + input.position(), inputSize);
+            input.position(input.position() + inputSize);
+        } else {
+            byte[] inputArray = new byte[inputSize];
+            input.get(inputArray);
+            outputArray = engineDoFinal(inputArray, 0, inputSize);
+        }
+
+        int outputSize = (outputArray != null) ? outputArray.length : 0;
+        if (outputSize > 0) {
+            int outputBufferAvailable = output.remaining();
+            try {
+                output.put(outputArray);
+            } catch (BufferOverflowException e) {
+                throw new ShortBufferException(
+                        "Output buffer too small. Produced: " + outputSize + ", available: "
+                                + outputBufferAvailable);
+            }
+        }
+        return outputSize;
+    }
+
+    @Override
+    protected final byte[] engineWrap(Key key)
+            throws IllegalBlockSizeException, InvalidKeyException {
+        if (mKey == null) {
+            throw new IllegalStateException("Not initilized");
+        }
+
+        if (!isEncrypting()) {
+            throw new IllegalStateException(
+                    "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
+        }
+
+        if (key == null) {
+            throw new NullPointerException("key == null");
+        }
+        byte[] encoded = null;
+        if (key instanceof SecretKey) {
+            if ("RAW".equalsIgnoreCase(key.getFormat())) {
+                encoded = key.getEncoded();
+            }
+            if (encoded == null) {
+                try {
+                    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm());
+                    SecretKeySpec spec =
+                            (SecretKeySpec) keyFactory.getKeySpec(
+                                    (SecretKey) key, SecretKeySpec.class);
+                    encoded = spec.getEncoded();
+                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to wrap key because it does not export its key material",
+                            e);
+                }
+            }
+        } else if (key instanceof PrivateKey) {
+            if ("PKCS8".equalsIgnoreCase(key.getFormat())) {
+                encoded = key.getEncoded();
+            }
+            if (encoded == null) {
+                try {
+                    KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
+                    PKCS8EncodedKeySpec spec =
+                            keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class);
+                    encoded = spec.getEncoded();
+                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to wrap key because it does not export its key material",
+                            e);
+                }
+            }
+        } else if (key instanceof PublicKey) {
+            if ("X.509".equalsIgnoreCase(key.getFormat())) {
+                encoded = key.getEncoded();
+            }
+            if (encoded == null) {
+                try {
+                    KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
+                    X509EncodedKeySpec spec =
+                            keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
+                    encoded = spec.getEncoded();
+                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to wrap key because it does not export its key material",
+                            e);
+                }
+            }
+        } else {
+            throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName());
+        }
+
+        if (encoded == null) {
+            throw new InvalidKeyException(
+                    "Failed to wrap key because it does not export its key material");
+        }
+
+        try {
+            return engineDoFinal(encoded, 0, encoded.length);
+        } catch (BadPaddingException e) {
+            throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+        }
+    }
+
+    @Override
+    protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+            int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
+        if (mKey == null) {
+            throw new IllegalStateException("Not initilized");
+        }
+
+        if (isEncrypting()) {
+            throw new IllegalStateException(
+                    "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
+        }
+
+        if (wrappedKey == null) {
+            throw new NullPointerException("wrappedKey == null");
+        }
+
+        byte[] encoded;
+        try {
+            encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new InvalidKeyException("Failed to unwrap key", e);
+        }
+
+        switch (wrappedKeyType) {
+            case Cipher.SECRET_KEY:
+            {
+                return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+                // break;
+            }
+            case Cipher.PRIVATE_KEY:
+            {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                try {
+                    return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+                } catch (InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to create private key from its PKCS#8 encoded form", e);
+                }
+                // break;
+            }
+            case Cipher.PUBLIC_KEY:
+            {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                try {
+                    return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+                } catch (InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to create public key from its X.509 encoded form", e);
+                }
+                // break;
+            }
+            default:
+                throw new InvalidParameterException(
+                        "Unsupported wrappedKeyType: " + wrappedKeyType);
+        }
+    }
+
+    @Override
+    protected final void engineSetMode(String mode) throws NoSuchAlgorithmException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify block mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected final void engineSetPadding(String arg0) throws NoSuchPaddingException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify padding mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected final int engineGetKeySize(Key key) throws InvalidKeyException {
+        throw new UnsupportedOperationException();
+    }
+
+    @CallSuper
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            abortOperation();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public final long getOperationHandle() {
+        return mOperationChallenge;
+    }
+
+    protected final void setKey(@NonNull AndroidKeyStoreKey key) {
+        mKey = key;
+    }
+
+    /**
+     * Overrides the default purpose/type of the crypto operation.
+     */
+    protected final void setKeymasterPurposeOverride(int keymasterPurpose) {
+        mKeymasterPurposeOverride = keymasterPurpose;
+    }
+
+    protected final int getKeymasterPurposeOverride() {
+        return mKeymasterPurposeOverride;
+    }
+
+    /**
+     * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this
+     * cipher is initialized for decryption.
+     */
+    protected final boolean isEncrypting() {
+        return mEncrypting;
+    }
+
+    protected final long getConsumedInputSizeBytes() {
+        if (mMainDataStreamer == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        return mMainDataStreamer.getConsumedInputSizeBytes();
+    }
+
+    protected final long getProducedOutputSizeBytes() {
+        if (mMainDataStreamer == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        return mMainDataStreamer.getProducedOutputSizeBytes();
+    }
+
+    static String opmodeToString(int opmode) {
+        switch (opmode) {
+            case Cipher.ENCRYPT_MODE:
+                return "ENCRYPT_MODE";
+            case Cipher.DECRYPT_MODE:
+                return "DECRYPT_MODE";
+            case Cipher.WRAP_MODE:
+                return "WRAP_MODE";
+            case Cipher.UNWRAP_MODE:
+                return "UNWRAP_MODE";
+            default:
+                return String.valueOf(opmode);
+        }
+    }
+
+    // The methods below need to be implemented by subclasses.
+
+    /**
+     * Initializes this cipher with the provided key.
+     *
+     * @throws InvalidKeyException if the {@code key} is not suitable for this cipher in the
+     *         specified {@code opmode}.
+     *
+     * @see #setKey(AndroidKeyStoreKey)
+     */
+    protected abstract void initKey(int opmode, @Nullable Key key) throws InvalidKeyException;
+
+    /**
+     * Returns algorithm-specific parameters used by this cipher or {@code null} if no
+     * algorithm-specific parameters are used.
+     */
+    @Nullable
+    @Override
+    protected abstract AlgorithmParameters engineGetParameters();
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters when no additional
+     * initialization parameters were provided.
+     *
+     * @throws InvalidKeyException if this cipher cannot be configured based purely on the provided
+     *         key and needs additional parameters to be provided to {@code Cipher.init}.
+     */
+    protected abstract void initAlgorithmSpecificParameters() throws InvalidKeyException;
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional
+     * parameters were provided.
+     *
+     * @param params additional algorithm parameters or {@code null} if not specified.
+     *
+     * @throws InvalidAlgorithmParameterException if there is insufficient information to configure
+     *         this cipher or if the provided parameters are not suitable for this cipher.
+     */
+    protected abstract void initAlgorithmSpecificParameters(
+            @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException;
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional
+     * parameters were provided.
+     *
+     * @param params additional algorithm parameters or {@code null} if not specified.
+     *
+     * @throws InvalidAlgorithmParameterException if there is insufficient information to configure
+     *         this cipher or if the provided parameters are not suitable for this cipher.
+     */
+    protected abstract void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException;
+
+    /**
+     * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
+     * {@code begin} operation. This amount of entropy is typically what's consumed to generate
+     * random parameters, such as IV.
+     *
+     * <p>For decryption, the return value should be {@code 0} because decryption should not be
+     * consuming any entropy. For encryption, the value combined with
+     * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon
+     * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
+     * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC
+     * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for
+     * the case where IV is generated by the KeyStore's {@code begin} operation it should be
+     * {@code 16}.
+     */
+    protected abstract int getAdditionalEntropyAmountForBegin();
+
+    /**
+     * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
+     * {@code finish} operation. This amount of entropy is typically what's consumed by encryption
+     * padding scheme.
+     *
+     * <p>For decryption, the return value should be {@code 0} because decryption should not be
+     * consuming any entropy. For encryption, the value combined with
+     * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon
+     * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
+     * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with
+     * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding
+     * the return value should be the size of the padding string or could be raised (for simplicity)
+     * to the size of the modulus.
+     */
+    protected abstract int getAdditionalEntropyAmountForFinish();
+
+    /**
+     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+     *
+     * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
+     *        parameters.
+     */
+    protected abstract void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters);
+
+    /**
+     * Invoked to obtain algorithm-specific parameters from the result of the KeyStore's
+     * {@code begin} operation.
+     *
+     * <p>Some parameters, such as IV, are not required to be provided to {@code Cipher.init}. Such
+     * parameters, if not provided, must be generated by KeyStore and returned to the user of
+     * {@code Cipher} and potentially reused after {@code doFinal}.
+     *
+     * @param parameters keystore/keymaster arguments returned by KeyStore {@code createOperation}.
+     */
+    protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
+            KeyParameter[] parameters);
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
new file mode 100644
index 0000000..9f7f238
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyParameter;
+
+import libcore.util.EmptyArray;
+
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidKeyException;
+import java.security.SignatureSpi;
+import java.util.List;
+
+/**
+ * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
+
+    public final static class NONE extends AndroidKeyStoreECDSASignatureSpi {
+        public NONE() {
+            super(KeymasterDefs.KM_DIGEST_NONE);
+        }
+
+        @Override
+        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+                KeyStoreOperation operation) {
+            return new TruncateToFieldSizeMessageStreamer(
+                    super.createMainDataStreamer(operation),
+                    getGroupSizeBits());
+        }
+
+        /**
+         * Streamer which buffers all input, then truncates it to field size, and then sends it into
+         * KeyStore via the provided delegate streamer.
+         */
+        private static class TruncateToFieldSizeMessageStreamer
+                implements KeyStoreCryptoOperationStreamer {
+
+            private final KeyStoreCryptoOperationStreamer mDelegate;
+            private final int mGroupSizeBits;
+            private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
+            private long mConsumedInputSizeBytes;
+
+            private TruncateToFieldSizeMessageStreamer(
+                    KeyStoreCryptoOperationStreamer delegate,
+                    int groupSizeBits) {
+                mDelegate = delegate;
+                mGroupSizeBits = groupSizeBits;
+            }
+
+            @Override
+            public byte[] update(byte[] input, int inputOffset, int inputLength)
+                    throws KeyStoreException {
+                if (inputLength > 0) {
+                    mInputBuffer.write(input, inputOffset, inputLength);
+                    mConsumedInputSizeBytes += inputLength;
+                }
+                return EmptyArray.BYTE;
+            }
+
+            @Override
+            public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)
+                    throws KeyStoreException {
+                if (inputLength > 0) {
+                    mConsumedInputSizeBytes += inputLength;
+                    mInputBuffer.write(input, inputOffset, inputLength);
+                }
+
+                byte[] bufferedInput = mInputBuffer.toByteArray();
+                mInputBuffer.reset();
+                // Truncate input at field size (bytes)
+                return mDelegate.doFinal(bufferedInput,
+                        0,
+                        Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
+                        signature);
+            }
+
+            @Override
+            public long getConsumedInputSizeBytes() {
+                return mConsumedInputSizeBytes;
+            }
+
+            @Override
+            public long getProducedOutputSizeBytes() {
+                return mDelegate.getProducedOutputSizeBytes();
+            }
+        }
+    }
+
+    public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA1() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA224() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA256() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA384() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA512() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterDigest;
+
+    private int mGroupSizeBits = -1;
+
+    AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
+        mKeymasterDigest = keymasterDigest;
+    }
+
+    @Override
+    protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+        if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+                    + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
+        }
+
+        long keySizeBits = -1;
+        for (Authorization a : key.getAuthorizations()) {
+            if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
+                keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
+            }
+        }
+
+        if (keySizeBits == -1) {
+            throw new InvalidKeyException("Size of key not known");
+        } else if (keySizeBits > Integer.MAX_VALUE) {
+            throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
+        }
+        mGroupSizeBits = (int) keySizeBits;
+
+        super.initKey(key);
+    }
+
+    @Override
+    protected final void resetAll() {
+        mGroupSizeBits = -1;
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected final void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters) {
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+        ));
+    }
+
+    @Override
+    protected final int getAdditionalEntropyAmountForSign() {
+        return (mGroupSizeBits + 7) / 8;
+    }
+
+    protected final int getGroupSizeBits() {
+        if (mGroupSizeBits == -1) {
+            throw new IllegalStateException("Not initialized");
+        }
+        return mGroupSizeBits;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPrivateKey.java
new file mode 100644
index 0000000..35effde
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPrivateKey.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyDescriptor;
+
+import java.security.PrivateKey;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECParameterSpec;
+
+/**
+ * EC private key (instance of {@link PrivateKey} and {@link ECKey}) backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreECPrivateKey extends AndroidKeyStorePrivateKey implements ECKey {
+    private final ECParameterSpec mParams;
+
+    public AndroidKeyStoreECPrivateKey(@NonNull KeyDescriptor descriptor,
+            long keyId,
+            Authorization[] authorizations,
+            @NonNull KeyStoreSecurityLevel securityLevel,
+            @NonNull ECParameterSpec params) {
+        super(descriptor, keyId, authorizations, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
+        mParams = params;
+    }
+
+    @Override
+    public ECParameterSpec getParams() {
+        return mParams;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
new file mode 100644
index 0000000..6ddaa70
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+
+/**
+ * {@link ECPublicKey} backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey implements ECPublicKey {
+
+    private final ECParameterSpec mParams;
+    private final ECPoint mW;
+
+    public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata,
+            @NonNull KeyStoreSecurityLevel securityLevel,
+            @NonNull ECParameterSpec params, @NonNull ECPoint w) {
+        super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
+        mParams = params;
+        mW = w;
+    }
+
+    public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata,
+            @NonNull KeyStoreSecurityLevel securityLevel, @NonNull ECPublicKey info) {
+        this(descriptor, metadata, securityLevel, info.getParams(), info.getW());
+        if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+            throw new IllegalArgumentException(
+                    "Unsupported key export format: " + info.getFormat());
+        }
+    }
+
+    @Override
+    public AndroidKeyStorePrivateKey getPrivateKey() {
+        return new AndroidKeyStoreECPrivateKey(
+                getUserKeyDescriptor(), getKeyIdDescriptor().nspace, getAuthorizations(),
+                getSecurityLevel(), mParams);
+    }
+
+    @Override
+    public ECParameterSpec getParams() {
+        return mParams;
+    }
+
+    @Override
+    public ECPoint getW() {
+        return mW;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
new file mode 100644
index 0000000..3dde2e5
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2015 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.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyStoreCryptoOperation;
+import android.security.keystore.KeymasterUtils;
+import android.system.keystore2.KeyParameter;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.MacSpi;
+
+/**
+ * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
+
+    private static final String TAG = "AndroidKeyStoreHmacSpi";
+
+    public static class HmacSHA1 extends AndroidKeyStoreHmacSpi {
+        public HmacSHA1() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static class HmacSHA224 extends AndroidKeyStoreHmacSpi {
+        public HmacSHA224() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static class HmacSHA256 extends AndroidKeyStoreHmacSpi {
+        public HmacSHA256() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static class HmacSHA384 extends AndroidKeyStoreHmacSpi {
+        public HmacSHA384() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static class HmacSHA512 extends AndroidKeyStoreHmacSpi {
+        public HmacSHA512() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterDigest;
+    private final int mMacSizeBits;
+
+    // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+    private AndroidKeyStoreSecretKey mKey;
+
+    // Fields below are reset when engineDoFinal succeeds.
+    private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
+    private KeyStoreOperation mOperation;
+    private long mOperationChallenge;
+
+    protected AndroidKeyStoreHmacSpi(int keymasterDigest) {
+        mKeymasterDigest = keymasterDigest;
+        mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+        mOperation = null;
+        mOperationChallenge = 0;
+        mKey = null;
+        mChunkedStreamer = null;
+    }
+
+    @Override
+    protected int engineGetMacLength() {
+        return (mMacSizeBits + 7) / 8;
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(key, params);
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+        InvalidAlgorithmParameterException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Only Android KeyStore secret keys supported. Key: " + key);
+        }
+        mKey = (AndroidKeyStoreSecretKey) key;
+
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported algorithm parameters: " + params);
+        }
+
+    }
+
+    private void abortOperation() {
+        KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+        mOperation = null;
+    }
+
+    private void resetAll() {
+        abortOperation();
+        mOperationChallenge = 0;
+        mKey = null;
+        mChunkedStreamer = null;
+    }
+
+    private void resetWhilePreservingInitState() {
+        abortOperation();
+        mOperationChallenge = 0;
+        mChunkedStreamer = null;
+    }
+
+    @Override
+    protected void engineReset() {
+        resetWhilePreservingInitState();
+    }
+
+    private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
+        if (mChunkedStreamer != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        List<KeyParameter> parameters = new ArrayList<>();
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeInt(
+                KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits
+        ));
+
+        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;
+            }
+        }
+
+        // Now we check if we got an operation challenge. This indicates that user authorization
+        // is required. And if we got a challenge we check if the authorization can possibly
+        // succeed.
+        mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
+                mOperation, mKey);
+
+        mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mOperation));
+    }
+
+    @Override
+    protected void engineUpdate(byte input) {
+        engineUpdate(new byte[] {input}, 0, 1);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new ProviderException("Failed to reinitialize MAC", e);
+        }
+
+        byte[] output;
+        try {
+            output = mChunkedStreamer.update(input, offset, len);
+        } catch (KeyStoreException e) {
+            throw new ProviderException("Keystore operation failed", e);
+        }
+        if ((output != null) && (output.length != 0)) {
+            throw new ProviderException("Update operation unexpectedly produced output");
+        }
+    }
+
+    @Override
+    protected byte[] engineDoFinal() {
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new ProviderException("Failed to reinitialize MAC", e);
+        }
+
+        byte[] result;
+        try {
+            result = mChunkedStreamer.doFinal(
+                    null, 0, 0,
+                    null); // no signature provided -- this invocation will generate one
+        } catch (KeyStoreException e) {
+            throw new ProviderException("Keystore operation failed", e);
+        }
+
+        resetWhilePreservingInitState();
+        return result;
+    }
+
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            abortOperation();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public long getOperationHandle() {
+        return mOperationChallenge;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
new file mode 100644
index 0000000..32650ae
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.util.Log;
+
+import java.security.Key;
+
+/**
+ * {@link Key} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKey implements Key {
+    // This is the original KeyDescriptor by which the key was loaded from
+    // with alias and domain.
+    private final KeyDescriptor mDescriptor;
+    // The key id can be used make certain manipulations to the keystore database
+    // assuring that the manipulation is made to the exact key that was loaded
+    // from the database. Alias based manipulations can not assure this, because
+    // aliases can be rebound to other keys at any time.
+    private final long mKeyId;
+    private final Authorization[] mAuthorizations;
+    // TODO extract algorithm string from metadata.
+    private final String mAlgorithm;
+
+    // This is the security level interface, that this key is associated with.
+    // We do not include this member in comparisons.
+    private final KeyStoreSecurityLevel mSecurityLevel;
+
+    AndroidKeyStoreKey(@NonNull KeyDescriptor descriptor,
+            long keyId,
+            @NonNull Authorization[] authorizations,
+            @NonNull String algorithm,
+            @NonNull KeyStoreSecurityLevel securityLevel) {
+        mDescriptor = descriptor;
+        mKeyId = keyId;
+        mAuthorizations = authorizations;
+        mAlgorithm = algorithm;
+        mSecurityLevel = securityLevel;
+    }
+
+    KeyDescriptor getUserKeyDescriptor() {
+        return mDescriptor;
+    }
+
+    KeyDescriptor getKeyIdDescriptor() {
+        KeyDescriptor descriptor = new KeyDescriptor();
+        descriptor.nspace = mKeyId;
+        descriptor.domain = Domain.KEY_ID;
+        descriptor.alias = null;
+        descriptor.blob = null;
+        return descriptor;
+    }
+
+    Authorization[] getAuthorizations() {
+        return mAuthorizations;
+    }
+
+    KeyStoreSecurityLevel getSecurityLevel() {
+        return mSecurityLevel;
+    }
+
+
+    @Override
+    public String getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    @Override
+    public String getFormat() {
+        // This key does not export its key material
+        return null;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        // This key does not export its key material
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+
+        result = prime * result + ((mDescriptor == null) ? 0 : mDescriptor.hashCode());
+        result = prime * result + (int) (mKeyId >>> 32);
+        result = prime * result + (int) (mKeyId & 0xffffffff);
+        result = prime * result + ((mAuthorizations == null) ? 0 : mAuthorizations.hashCode());
+        result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AndroidKeyStoreKey other = (AndroidKeyStoreKey) obj;
+        if (mKeyId != other.mKeyId) {
+            return false;
+        }
+
+        // If the key ids are equal and the class matches all the other fields cannot differ
+        // unless we have a bug.
+        if (!mAlgorithm.equals(other.mAlgorithm)
+                || !mAuthorizations.equals(other.mAuthorizations)
+                || !mDescriptor.equals(other.mDescriptor)) {
+            Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata"
+                    + "differs.");
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
new file mode 100644
index 0000000..a8dd7f3
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 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.security.KeyStore;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * {@link KeyFactorySpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    @Override
+    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
+            throws InvalidKeySpecException {
+        if (key == null) {
+            throw new InvalidKeySpecException("key == null");
+        } else if ((!(key instanceof AndroidKeyStorePrivateKey))
+            && (!(key instanceof AndroidKeyStorePublicKey))) {
+            throw new InvalidKeySpecException(
+                    "Unsupported key type: " + key.getClass().getName()
+                    + ". This KeyFactory supports only Android Keystore asymmetric keys");
+        }
+
+        // key is an Android Keystore private or public key
+
+        if (keySpecClass == null) {
+            throw new InvalidKeySpecException("keySpecClass == null");
+        } else if (KeyInfo.class.equals(keySpecClass)) {
+            if (!(key instanceof AndroidKeyStorePrivateKey)) {
+                throw new InvalidKeySpecException(
+                        "Unsupported key type: " + key.getClass().getName()
+                        + ". KeyInfo can be obtained only for Android Keystore private keys");
+            }
+            AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(keystorePrivateKey);
+            return result;
+        } else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
+            if (!(key instanceof AndroidKeyStorePublicKey)) {
+                throw new InvalidKeySpecException(
+                        "Unsupported key type: " + key.getClass().getName()
+                        + ". X509EncodedKeySpec can be obtained only for Android Keystore public"
+                        + " keys");
+            }
+            @SuppressWarnings("unchecked")
+            T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded());
+            return result;
+        } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) {
+            if (key instanceof AndroidKeyStorePrivateKey) {
+                throw new InvalidKeySpecException(
+                        "Key material export of Android Keystore private keys is not supported");
+            } else {
+                throw new InvalidKeySpecException(
+                        "Cannot export key material of public key in PKCS#8 format."
+                        + " Only X.509 format (X509EncodedKeySpec) supported for public keys.");
+            }
+        } else if (RSAPublicKeySpec.class.equals(keySpecClass)) {
+            if (key instanceof AndroidKeyStoreRSAPublicKey) {
+                AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key;
+                @SuppressWarnings("unchecked")
+                T result =
+                        (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
+                return result;
+            } else {
+                throw new InvalidKeySpecException(
+                        "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " "
+                        + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
+                        + " key");
+            }
+        } else if (ECPublicKeySpec.class.equals(keySpecClass)) {
+            if (key instanceof AndroidKeyStoreECPublicKey) {
+                AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key;
+                @SuppressWarnings("unchecked")
+                T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
+                return result;
+            } else {
+                throw new InvalidKeySpecException(
+                        "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " "
+                        + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
+                        + " key");
+            }
+        } else {
+            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
+        }
+    }
+
+    @Override
+    protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
+        throw new InvalidKeySpecException(
+                "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
+                + " " + KeyGenParameterSpec.class.getName());
+    }
+
+    @Override
+    protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
+        throw new InvalidKeySpecException(
+                "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
+                + " " + KeyGenParameterSpec.class.getName());
+    }
+
+    @Override
+    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if ((!(key instanceof AndroidKeyStorePrivateKey))
+                && (!(key instanceof AndroidKeyStorePublicKey))) {
+            throw new InvalidKeyException(
+                    "To import a key into Android Keystore, use KeyStore.setEntry");
+        }
+        return key;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
new file mode 100644
index 0000000..ccd0a4b
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2015 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.security.KeyStore2;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeymasterUtils;
+import android.security.keystore.StrongBoxUnavailableException;
+import android.system.keystore2.Domain;
+import android.system.keystore2.IKeystoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.SecurityLevel;
+import android.util.Log;
+
+import libcore.util.EmptyArray;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.ProviderException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link KeyGeneratorSpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
+    private static final String TAG = "AndroidKeyStoreKeyGeneratorSpi";
+
+    public static class AES extends AndroidKeyStoreKeyGeneratorSpi {
+        public AES() {
+            super(KeymasterDefs.KM_ALGORITHM_AES, 128);
+        }
+
+        @Override
+        protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+                throws InvalidAlgorithmParameterException {
+            super.engineInit(params, random);
+            if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported key size: " + mKeySizeBits
+                        + ". Supported: 128, 192, 256.");
+            }
+        }
+    }
+
+    public static class DESede extends AndroidKeyStoreKeyGeneratorSpi {
+        public DESede() {
+            super(KeymasterDefs.KM_ALGORITHM_3DES, 168);
+        }
+    }
+
+    protected static abstract class HmacBase extends AndroidKeyStoreKeyGeneratorSpi {
+        protected HmacBase(int keymasterDigest) {
+            super(KeymasterDefs.KM_ALGORITHM_HMAC,
+                    keymasterDigest,
+                    KeymasterUtils.getDigestOutputSizeBits(keymasterDigest));
+        }
+    }
+
+    public static class HmacSHA1 extends HmacBase {
+        public HmacSHA1() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static class HmacSHA224 extends HmacBase {
+        public HmacSHA224() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static class HmacSHA256 extends HmacBase {
+        public HmacSHA256() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static class HmacSHA384 extends HmacBase {
+        public HmacSHA384() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static class HmacSHA512 extends HmacBase {
+        public HmacSHA512() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final KeyStore2 mKeyStore = KeyStore2.getInstance();
+    private final int mKeymasterAlgorithm;
+    private final int mKeymasterDigest;
+    private final int mDefaultKeySizeBits;
+
+    private KeyGenParameterSpec mSpec;
+    private SecureRandom mRng;
+
+    protected int mKeySizeBits;
+    private int[] mKeymasterPurposes;
+    private int[] mKeymasterBlockModes;
+    private int[] mKeymasterPaddings;
+    private int[] mKeymasterDigests;
+
+    protected AndroidKeyStoreKeyGeneratorSpi(
+            int keymasterAlgorithm,
+            int defaultKeySizeBits) {
+        this(keymasterAlgorithm, -1, defaultKeySizeBits);
+    }
+
+    protected AndroidKeyStoreKeyGeneratorSpi(
+            int keymasterAlgorithm,
+            int keymasterDigest,
+            int defaultKeySizeBits) {
+        mKeymasterAlgorithm = keymasterAlgorithm;
+        mKeymasterDigest = keymasterDigest;
+        mDefaultKeySizeBits = defaultKeySizeBits;
+        if (mDefaultKeySizeBits <= 0) {
+            throw new IllegalArgumentException("Default key size must be positive");
+        }
+
+        if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) {
+            throw new IllegalArgumentException(
+                    "Digest algorithm must be specified for HMAC key");
+        }
+    }
+
+    @Override
+    protected void engineInit(SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without a "
+                + KeyGenParameterSpec.class.getName() + " parameter");
+    }
+
+    @Override
+    protected void engineInit(int keySize, SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without a "
+                + KeyGenParameterSpec.class.getName() + " parameter");
+    }
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if ((params == null) || (!(params instanceof KeyGenParameterSpec))) {
+                throw new InvalidAlgorithmParameterException("Cannot initialize without a "
+                        + KeyGenParameterSpec.class.getName() + " parameter");
+            }
+            KeyGenParameterSpec spec = (KeyGenParameterSpec) params;
+            if (spec.getKeystoreAlias() == null) {
+                throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+            }
+
+            mRng = random;
+            mSpec = spec;
+
+            mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
+            if (mKeySizeBits <= 0) {
+                throw new InvalidAlgorithmParameterException(
+                        "Key size must be positive: " + mKeySizeBits);
+            } else if ((mKeySizeBits % 8) != 0) {
+                throw new InvalidAlgorithmParameterException(
+                        "Key size must be a multiple of 8: " + mKeySizeBits);
+            }
+
+            try {
+                mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+                mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
+                        spec.getEncryptionPaddings());
+                if (spec.getSignaturePaddings().length > 0) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Signature paddings not supported for symmetric key algorithms");
+                }
+                mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+                if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
+                        && (spec.isRandomizedEncryptionRequired())) {
+                    for (int keymasterBlockMode : mKeymasterBlockModes) {
+                        if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+                                keymasterBlockMode)) {
+                            throw new InvalidAlgorithmParameterException(
+                                    "Randomized encryption (IND-CPA) required but may be violated"
+                                    + " by block mode: "
+                                    + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+                                    + ". See " + KeyGenParameterSpec.class.getName()
+                                    + " documentation.");
+                        }
+                    }
+                }
+                if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
+                    if (mKeySizeBits != 168) {
+                        throw new InvalidAlgorithmParameterException(
+                            "3DES key size must be 168 bits.");
+                    }
+                }
+                if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
+                    if (mKeySizeBits < 64 || mKeySizeBits > 512) {
+                        throw new InvalidAlgorithmParameterException(
+                            "HMAC key sizes must be within 64-512 bits, inclusive.");
+                    }
+
+                    // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
+                    // implies SHA-256 digest). Because keymaster HMAC key is authorized only for
+                    // one digest, we don't let algorithm parameter spec override the digest implied
+                    // by the key. If the spec specifies digests at all, it must specify only one
+                    // digest, the only implied by key algorithm.
+                    mKeymasterDigests = new int[] {mKeymasterDigest};
+                    if (spec.isDigestsSpecified()) {
+                        // Digest(s) explicitly specified in the spec. Check that the list
+                        // consists of exactly one digest, the one implied by key algorithm.
+                        int[] keymasterDigestsFromSpec =
+                                KeyProperties.Digest.allToKeymaster(spec.getDigests());
+                        if ((keymasterDigestsFromSpec.length != 1)
+                                || (keymasterDigestsFromSpec[0] != mKeymasterDigest)) {
+                            throw new InvalidAlgorithmParameterException(
+                                    "Unsupported digests specification: "
+                                    + Arrays.asList(spec.getDigests()) + ". Only "
+                                    + KeyProperties.Digest.fromKeymaster(mKeymasterDigest)
+                                    + " supported for this HMAC key algorithm");
+                        }
+                    }
+                } else {
+                    // Key algorithm does not imply a digest.
+                    if (spec.isDigestsSpecified()) {
+                        mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
+                    } else {
+                        mKeymasterDigests = EmptyArray.INT;
+                    }
+                }
+
+                // Check that user authentication related parameters are acceptable. This method
+                // will throw an IllegalStateException if there are issues (e.g., secure lock screen
+                // not set up).
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
+            } catch (IllegalStateException | IllegalArgumentException e) {
+                throw new InvalidAlgorithmParameterException(e);
+            }
+
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void resetAll() {
+        mSpec = null;
+        mRng = null;
+        mKeySizeBits = -1;
+        mKeymasterPurposes = null;
+        mKeymasterPaddings = null;
+        mKeymasterBlockModes = null;
+    }
+
+    @Override
+    protected SecretKey engineGenerateKey() {
+        KeyGenParameterSpec spec = mSpec;
+        if (spec == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        List<KeyParameter> params = new ArrayList<>();
+
+        params.add(KeyStore2ParameterUtils.makeInt(
+                KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits
+        ));
+        params.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm
+        ));
+        ArrayUtils.forEach(mKeymasterPurposes, (purpose) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_PURPOSE, purpose
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterBlockModes, (blockMode) -> {
+            if (blockMode == KeymasterDefs.KM_MODE_GCM
+                    && mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) {
+                params.add(KeyStore2ParameterUtils.makeInt(
+                        KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
+                        AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
+                                .MIN_SUPPORTED_TAG_LENGTH_BITS
+                ));
+            }
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_BLOCK_MODE, blockMode
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterPaddings, (padding) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_PADDING, padding
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_DIGEST, digest
+            ));
+        });
+
+        if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC
+                && mKeymasterDigests.length != 0) {
+            int digestOutputSizeBits = KeymasterUtils.getDigestOutputSizeBits(mKeymasterDigests[0]);
+            if (digestOutputSizeBits == -1) {
+                throw new ProviderException(
+                        "HMAC key authorized for unsupported digest: "
+                                + KeyProperties.Digest.fromKeymaster(mKeymasterDigests[0]));
+            }
+            params.add(KeyStore2ParameterUtils.makeInt(
+                    KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits
+            ));
+        }
+
+        KeyStore2ParameterUtils.addUserAuthArgs(params, spec);
+
+        if (spec.getKeyValidityStart() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()
+            ));
+        }
+        if (spec.getKeyValidityForOriginationEnd() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                    spec.getKeyValidityForOriginationEnd()
+            ));
+        }
+        if (spec.getKeyValidityForConsumptionEnd() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                    spec.getKeyValidityForConsumptionEnd()
+            ));
+        }
+
+        if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
+                && (!spec.isRandomizedEncryptionRequired())) {
+            // Permit caller-provided IV when encrypting with this key
+            params.add(KeyStore2ParameterUtils.makeBool(
+                    KeymasterDefs.KM_TAG_CALLER_NONCE
+            ));
+        }
+
+        byte[] additionalEntropy =
+                KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                        mRng, (mKeySizeBits + 7) / 8);
+
+        @SecurityLevel int securityLevel = SecurityLevel.TRUSTED_ENVIRONMENT;
+        if (spec.isStrongBoxBacked()) {
+            securityLevel = SecurityLevel.STRONGBOX;
+        }
+
+        int flags = 0;
+        if (spec.isCriticalToDeviceEncryption()) {
+            flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
+        }
+
+        KeyDescriptor descriptor = new KeyDescriptor();
+        descriptor.alias = spec.getKeystoreAlias();
+        descriptor.nspace = spec.getNamespace();
+        descriptor.domain = descriptor.nspace == KeyProperties.NAMESPACE_APPLICATION
+                ? Domain.APP
+                : Domain.SELINUX;
+        descriptor.blob = null;
+
+        KeyMetadata metadata = null;
+        KeyStoreSecurityLevel iSecurityLevel = null;
+        try {
+            iSecurityLevel = mKeyStore.getSecurityLevel(securityLevel);
+            metadata = iSecurityLevel.generateKey(
+                    descriptor,
+                    null, /* Attestation key not applicable to symmetric keys. */
+                    params,
+                    flags,
+                    additionalEntropy);
+        } catch (android.security.KeyStoreException e) {
+            switch (e.getErrorCode()) {
+                // TODO replace with ErrorCode.HARDWARE_TYPE_UNAVAILABLE when KeyMint spec
+                //      becomes available.
+                case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
+                    throw new StrongBoxUnavailableException("Failed to generate key");
+                default:
+                    throw new ProviderException("Keystore key generation failed", e);
+            }
+        }
+        @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
+        try {
+            keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
+                    mKeymasterAlgorithm, mKeymasterDigest);
+        } catch (IllegalArgumentException e) {
+            try {
+                mKeyStore.deleteKey(descriptor);
+            } catch (android.security.KeyStoreException kse) {
+                Log.e(TAG, "Failed to delete key after generating successfully but"
+                        + " failed to get the algorithm string.", kse);
+            }
+            throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+        }
+        SecretKey result = new AndroidKeyStoreSecretKey(descriptor, metadata, keyAlgorithmJCA,
+                iSecurityLevel);
+        return result;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
new file mode 100644
index 0000000..a747a0e
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2012 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Build;
+import android.security.KeyPairGeneratorSpec;
+import android.security.KeyStore2;
+import android.security.KeyStoreException;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeymasterUtils;
+import android.security.keystore.SecureKeyImportUnavailableException;
+import android.security.keystore.StrongBoxUnavailableException;
+import android.system.keystore2.Domain;
+import android.system.keystore2.IKeystoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.ResponseCode;
+import android.system.keystore2.SecurityLevel;
+import android.util.Log;
+
+import libcore.util.EmptyArray;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyPairGeneratorSpi;
+import java.security.ProviderException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provides a way to create instances of a KeyPair which will be placed in the
+ * Android keystore service usable only by the application that called it. This
+ * can be used in conjunction with
+ * {@link java.security.KeyStore#getInstance(String)} using the
+ * {@code "AndroidKeyStore"} type.
+ * <p>
+ * This class can not be directly instantiated and must instead be used via the
+ * {@link KeyPairGenerator#getInstance(String)
+ * KeyPairGenerator.getInstance("AndroidKeyStore")} API.
+ *
+ * @hide
+ */
+public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
+    private static final String TAG = "AndroidKeyStoreKeyPairGeneratorSpi";
+
+    public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
+        public RSA() {
+            super(KeymasterDefs.KM_ALGORITHM_RSA);
+        }
+    }
+
+    public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
+        public EC() {
+            super(KeymasterDefs.KM_ALGORITHM_EC);
+        }
+    }
+
+    /*
+     * These must be kept in sync with system/security/keystore/defaults.h
+     */
+
+    /* EC */
+    private static final int EC_DEFAULT_KEY_SIZE = 256;
+
+    /* RSA */
+    private static final int RSA_DEFAULT_KEY_SIZE = 2048;
+    private static final int RSA_MIN_KEY_SIZE = 512;
+    private static final int RSA_MAX_KEY_SIZE = 8192;
+
+    private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE =
+            new HashMap<String, Integer>();
+    private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
+    private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>();
+    static {
+        // Aliases for NIST P-224
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
+
+
+        // Aliases for NIST P-256
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
+
+        // Aliases for NIST P-384
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
+
+        // Aliases for NIST P-521
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521);
+        SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
+
+        SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
+        Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
+
+        SUPPORTED_EC_NIST_CURVE_SIZES.addAll(
+                new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values()));
+        Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES);
+    }
+
+    private final int mOriginalKeymasterAlgorithm;
+
+    private KeyStore2 mKeyStore;
+
+    private KeyGenParameterSpec mSpec;
+
+    private String mEntryAlias;
+    private int mEntryUid;
+    private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
+    private int mKeymasterAlgorithm = -1;
+    private int mKeySizeBits;
+    private SecureRandom mRng;
+
+    private int[] mKeymasterPurposes;
+    private int[] mKeymasterBlockModes;
+    private int[] mKeymasterEncryptionPaddings;
+    private int[] mKeymasterSignaturePaddings;
+    private int[] mKeymasterDigests;
+
+    private Long mRSAPublicExponent;
+
+    protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) {
+        mOriginalKeymasterAlgorithm = keymasterAlgorithm;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        throw new IllegalArgumentException(
+                KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName()
+                + " required to initialize this KeyPairGenerator");
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if (params == null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Must supply params of type " + KeyGenParameterSpec.class.getName()
+                        + " or " + KeyPairGeneratorSpec.class.getName());
+            }
+
+            KeyGenParameterSpec spec;
+            boolean encryptionAtRestRequired = false;
+            int keymasterAlgorithm = mOriginalKeymasterAlgorithm;
+            if (params instanceof KeyGenParameterSpec) {
+                spec = (KeyGenParameterSpec) params;
+            } else if (params instanceof KeyPairGeneratorSpec) {
+                // Legacy/deprecated spec
+                KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
+                try {
+                    KeyGenParameterSpec.Builder specBuilder;
+                    String specKeyAlgorithm = legacySpec.getKeyType();
+                    if (specKeyAlgorithm != null) {
+                        // Spec overrides the generator's default key algorithm
+                        try {
+                            keymasterAlgorithm =
+                                    KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+                                            specKeyAlgorithm);
+                        } catch (IllegalArgumentException e) {
+                            throw new InvalidAlgorithmParameterException(
+                                    "Invalid key type in parameters", e);
+                        }
+                    }
+                    switch (keymasterAlgorithm) {
+                        case KeymasterDefs.KM_ALGORITHM_EC:
+                            specBuilder = new KeyGenParameterSpec.Builder(
+                                    legacySpec.getKeystoreAlias(),
+                                    KeyProperties.PURPOSE_SIGN
+                                    | KeyProperties.PURPOSE_VERIFY);
+                            // Authorized to be used with any digest (including no digest).
+                            // MD5 was never offered for Android Keystore for ECDSA.
+                            specBuilder.setDigests(
+                                    KeyProperties.DIGEST_NONE,
+                                    KeyProperties.DIGEST_SHA1,
+                                    KeyProperties.DIGEST_SHA224,
+                                    KeyProperties.DIGEST_SHA256,
+                                    KeyProperties.DIGEST_SHA384,
+                                    KeyProperties.DIGEST_SHA512);
+                            break;
+                        case KeymasterDefs.KM_ALGORITHM_RSA:
+                            specBuilder = new KeyGenParameterSpec.Builder(
+                                    legacySpec.getKeystoreAlias(),
+                                    KeyProperties.PURPOSE_ENCRYPT
+                                    | KeyProperties.PURPOSE_DECRYPT
+                                    | KeyProperties.PURPOSE_SIGN
+                                    | KeyProperties.PURPOSE_VERIFY);
+                            // Authorized to be used with any digest (including no digest).
+                            specBuilder.setDigests(
+                                    KeyProperties.DIGEST_NONE,
+                                    KeyProperties.DIGEST_MD5,
+                                    KeyProperties.DIGEST_SHA1,
+                                    KeyProperties.DIGEST_SHA224,
+                                    KeyProperties.DIGEST_SHA256,
+                                    KeyProperties.DIGEST_SHA384,
+                                    KeyProperties.DIGEST_SHA512);
+                            // Authorized to be used with any encryption and signature padding
+                            // schemes (including no padding).
+                            specBuilder.setEncryptionPaddings(
+                                    KeyProperties.ENCRYPTION_PADDING_NONE,
+                                    KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+                                    KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
+                            specBuilder.setSignaturePaddings(
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+                                    KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+                            // Disable randomized encryption requirement to support encryption
+                            // padding NONE above.
+                            specBuilder.setRandomizedEncryptionRequired(false);
+                            break;
+                        default:
+                            throw new ProviderException(
+                                    "Unsupported algorithm: " + mKeymasterAlgorithm);
+                    }
+
+                    if (legacySpec.getKeySize() != -1) {
+                        specBuilder.setKeySize(legacySpec.getKeySize());
+                    }
+                    if (legacySpec.getAlgorithmParameterSpec() != null) {
+                        specBuilder.setAlgorithmParameterSpec(
+                                legacySpec.getAlgorithmParameterSpec());
+                    }
+                    specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
+                    specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
+                    specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
+                    specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
+                    specBuilder.setUserAuthenticationRequired(false);
+
+                    spec = specBuilder.build();
+                } catch (NullPointerException | IllegalArgumentException e) {
+                    throw new InvalidAlgorithmParameterException(e);
+                }
+            } else {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported params class: " + params.getClass().getName()
+                        + ". Supported: " + KeyGenParameterSpec.class.getName()
+                        + ", " + KeyPairGeneratorSpec.class.getName());
+            }
+
+            mEntryAlias = spec.getKeystoreAlias();
+            mEntryUid = spec.getUid();
+            mSpec = spec;
+            mKeymasterAlgorithm = keymasterAlgorithm;
+            mKeySizeBits = spec.getKeySize();
+            initAlgorithmSpecificParameters();
+            if (mKeySizeBits == -1) {
+                mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
+            }
+            checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked());
+
+            if (spec.getKeystoreAlias() == null) {
+                throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+            }
+
+            String jcaKeyAlgorithm;
+            try {
+                jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
+                        keymasterAlgorithm);
+                mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+                mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+                mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
+                        spec.getEncryptionPaddings());
+                if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
+                        && (spec.isRandomizedEncryptionRequired())) {
+                    for (int keymasterPadding : mKeymasterEncryptionPaddings) {
+                        if (!KeymasterUtils
+                                .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+                                        keymasterPadding)) {
+                            throw new InvalidAlgorithmParameterException(
+                                    "Randomized encryption (IND-CPA) required but may be violated"
+                                    + " by padding scheme: "
+                                    + KeyProperties.EncryptionPadding.fromKeymaster(
+                                            keymasterPadding)
+                                    + ". See " + KeyGenParameterSpec.class.getName()
+                                    + " documentation.");
+                        }
+                    }
+                }
+                mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster(
+                        spec.getSignaturePaddings());
+                if (spec.isDigestsSpecified()) {
+                    mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
+                } else {
+                    mKeymasterDigests = EmptyArray.INT;
+                }
+
+                // Check that user authentication related parameters are acceptable. This method
+                // will throw an IllegalStateException if there are issues (e.g., secure lock screen
+                // not set up).
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
+            } catch (IllegalArgumentException | IllegalStateException e) {
+                throw new InvalidAlgorithmParameterException(e);
+            }
+
+            mJcaKeyAlgorithm = jcaKeyAlgorithm;
+            mRng = random;
+            mKeyStore = KeyStore2.getInstance();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void resetAll() {
+        mEntryAlias = null;
+        mEntryUid = KeyProperties.NAMESPACE_APPLICATION;
+        mJcaKeyAlgorithm = null;
+        mKeymasterAlgorithm = -1;
+        mKeymasterPurposes = null;
+        mKeymasterBlockModes = null;
+        mKeymasterEncryptionPaddings = null;
+        mKeymasterSignaturePaddings = null;
+        mKeymasterDigests = null;
+        mKeySizeBits = 0;
+        mSpec = null;
+        mRSAPublicExponent = null;
+        mRng = null;
+        mKeyStore = null;
+    }
+
+    private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
+        AlgorithmParameterSpec algSpecificSpec = mSpec.getAlgorithmParameterSpec();
+        switch (mKeymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+            {
+                BigInteger publicExponent = null;
+                if (algSpecificSpec instanceof RSAKeyGenParameterSpec) {
+                    RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec;
+                    if (mKeySizeBits == -1) {
+                        mKeySizeBits = rsaSpec.getKeysize();
+                    } else if (mKeySizeBits != rsaSpec.getKeysize()) {
+                        throw new InvalidAlgorithmParameterException("RSA key size must match "
+                                + " between " + mSpec + " and " + algSpecificSpec
+                                + ": " + mKeySizeBits + " vs " + rsaSpec.getKeysize());
+                    }
+                    publicExponent = rsaSpec.getPublicExponent();
+                } else if (algSpecificSpec != null) {
+                    throw new InvalidAlgorithmParameterException(
+                        "RSA may only use RSAKeyGenParameterSpec");
+                }
+                if (publicExponent == null) {
+                    publicExponent = RSAKeyGenParameterSpec.F4;
+                }
+                if (publicExponent.compareTo(BigInteger.ZERO) < 1) {
+                    throw new InvalidAlgorithmParameterException(
+                            "RSA public exponent must be positive: " + publicExponent);
+                }
+                if ((publicExponent.signum() == -1)
+                        || (publicExponent.compareTo(KeymasterArguments.UINT64_MAX_VALUE) > 0)) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported RSA public exponent: " + publicExponent
+                            + ". Maximum supported value: " + KeymasterArguments.UINT64_MAX_VALUE);
+                }
+                mRSAPublicExponent = publicExponent.longValue();
+                break;
+            }
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                if (algSpecificSpec instanceof ECGenParameterSpec) {
+                    ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
+                    String curveName = ecSpec.getName();
+                    Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
+                            curveName.toLowerCase(Locale.US));
+                    if (ecSpecKeySizeBits == null) {
+                        throw new InvalidAlgorithmParameterException(
+                                "Unsupported EC curve name: " + curveName
+                                + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
+                    }
+                    if (mKeySizeBits == -1) {
+                        mKeySizeBits = ecSpecKeySizeBits;
+                    } else if (mKeySizeBits != ecSpecKeySizeBits) {
+                        throw new InvalidAlgorithmParameterException("EC key size must match "
+                                + " between " + mSpec + " and " + algSpecificSpec
+                                + ": " + mKeySizeBits + " vs " + ecSpecKeySizeBits);
+                    }
+                } else if (algSpecificSpec != null) {
+                    throw new InvalidAlgorithmParameterException(
+                        "EC may only use ECGenParameterSpec");
+                }
+                break;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+        }
+    }
+
+    @Override
+    public KeyPair generateKeyPair() {
+        if (mKeyStore == null || mSpec == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        final @SecurityLevel int securityLevel =
+                mSpec.isStrongBoxBacked()
+                        ? SecurityLevel.STRONGBOX
+                        : SecurityLevel.TRUSTED_ENVIRONMENT;
+
+        final int flags =
+                mSpec.isCriticalToDeviceEncryption()
+                        ? IKeystoreSecurityLevel
+                                .KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING
+                        : 0;
+
+        byte[] additionalEntropy =
+                KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                        mRng, (mKeySizeBits + 7) / 8);
+
+        KeyDescriptor descriptor = new KeyDescriptor();
+        descriptor.alias = mEntryAlias;
+        descriptor.domain = mEntryUid == KeyProperties.NAMESPACE_APPLICATION
+                ? Domain.APP
+                : Domain.SELINUX;
+        descriptor.nspace = mEntryUid;
+        descriptor.blob = null;
+
+        boolean success = false;
+        try {
+            KeyStoreSecurityLevel iSecurityLevel = mKeyStore.getSecurityLevel(securityLevel);
+
+            KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, null,
+                    constructKeyGenerationArguments(), flags, additionalEntropy);
+
+            AndroidKeyStorePublicKey publicKey =
+                    AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
+                            descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);
+
+            success = true;
+            return new KeyPair(publicKey, publicKey.getPrivateKey());
+        } catch (android.security.KeyStoreException e) {
+            switch(e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
+                    throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
+                default:
+                    ProviderException p = new ProviderException("Failed to generate key pair.", e);
+                    if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
+                        throw new SecureKeyImportUnavailableException(p);
+                    }
+                    throw p;
+            }
+        } catch (UnrecoverableKeyException e) {
+            throw new ProviderException(
+                    "Failed to construct key object from newly generated key pair.", e);
+        } finally {
+            if (!success) {
+                try {
+                    mKeyStore.deleteKey(descriptor);
+                } catch (KeyStoreException e) {
+                    if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
+                        Log.e(TAG, "Failed to delete newly generated key after "
+                                + "generation failed unexpectedly.", e);
+                    }
+                }
+            }
+        }
+    }
+
+    private void addAttestationParameters(@NonNull List<KeyParameter> params)
+            throws ProviderException {
+        byte[] challenge = mSpec.getAttestationChallenge();
+
+        if (challenge != null) {
+            params.add(KeyStore2ParameterUtils.makeBytes(
+                    KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge
+            ));
+
+            if (mSpec.isDevicePropertiesAttestationIncluded()) {
+                params.add(KeyStore2ParameterUtils.makeBytes(
+                        KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND,
+                        Build.BRAND.getBytes(StandardCharsets.UTF_8)
+                ));
+                params.add(KeyStore2ParameterUtils.makeBytes(
+                        KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE,
+                        Build.DEVICE.getBytes(StandardCharsets.UTF_8)
+                ));
+                params.add(KeyStore2ParameterUtils.makeBytes(
+                        KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT,
+                        Build.PRODUCT.getBytes(StandardCharsets.UTF_8)
+                ));
+                params.add(KeyStore2ParameterUtils.makeBytes(
+                        KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER,
+                        Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)
+                ));
+                params.add(KeyStore2ParameterUtils.makeBytes(
+                        KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL,
+                        Build.MODEL.getBytes(StandardCharsets.UTF_8)
+                ));
+            }
+        } else {
+            if (mSpec.isDevicePropertiesAttestationIncluded()) {
+                throw new ProviderException("An attestation challenge must be provided when "
+                        + "requesting device properties attestation.");
+            }
+        }
+    }
+
+    private Collection<KeyParameter> constructKeyGenerationArguments() {
+        List<KeyParameter> params = new ArrayList<>();
+        params.add(KeyStore2ParameterUtils.makeInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits));
+        params.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm
+        ));
+        ArrayUtils.forEach(mKeymasterPurposes, (purpose) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_PURPOSE, purpose
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterBlockModes, (blockMode) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_BLOCK_MODE, blockMode
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterEncryptionPaddings, (padding) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_PADDING, padding
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_PADDING, padding
+            ));
+        });
+        ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+            params.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_DIGEST, digest
+            ));
+        });
+
+        KeyStore2ParameterUtils.addUserAuthArgs(params, mSpec);
+
+        if (mSpec.getKeyValidityStart() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart()
+            ));
+        }
+        if (mSpec.getKeyValidityForOriginationEnd() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                    mSpec.getKeyValidityForOriginationEnd()
+            ));
+        }
+        if (mSpec.getKeyValidityForConsumptionEnd() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                    mSpec.getKeyValidityForConsumptionEnd()
+            ));
+        }
+
+        addAlgorithmSpecificParameters(params);
+
+        if (mSpec.isUniqueIdIncluded()) {
+            params.add(KeyStore2ParameterUtils.makeBool(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID));
+        }
+
+        addAttestationParameters(params);
+
+        return params;
+    }
+
+    private void addAlgorithmSpecificParameters(List<KeyParameter> params) {
+        switch (mKeymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                params.add(KeyStore2ParameterUtils.makeLong(
+                        KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent
+                ));
+                break;
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                break;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
+        }
+    }
+
+    private static int getDefaultKeySize(int keymasterAlgorithm) {
+        switch (keymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                return EC_DEFAULT_KEY_SIZE;
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                return RSA_DEFAULT_KEY_SIZE;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
+        }
+    }
+
+    private static void checkValidKeySize(
+            int keymasterAlgorithm,
+            int keySize,
+            boolean isStrongBoxBacked)
+            throws InvalidAlgorithmParameterException {
+        switch (keymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+                if (isStrongBoxBacked && keySize != 256) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported StrongBox EC key size: "
+                            + keySize + " bits. Supported: 256");
+                }
+                if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) {
+                    throw new InvalidAlgorithmParameterException("Unsupported EC key size: "
+                            + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES);
+                }
+                break;
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+                if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+                    throw new InvalidAlgorithmParameterException("RSA key size must be >= "
+                            + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
+                }
+                break;
+            default:
+                throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
+        }
+    }
+
+    /**
+     * Returns the {@code Signature} algorithm to be used for signing a certificate using the
+     * specified key or {@code null} if the key cannot be used for signing a certificate.
+     */
+    @Nullable
+    private static String getCertificateSignatureAlgorithm(
+            int keymasterAlgorithm,
+            int keySizeBits,
+            KeyGenParameterSpec spec) {
+        // Constraints:
+        // 1. Key must be authorized for signing without user authentication.
+        // 2. Signature digest must be one of key's authorized digests.
+        // 3. For RSA keys, the digest output size must not exceed modulus size minus space overhead
+        //    of RSA PKCS#1 signature padding scheme (about 30 bytes).
+        // 4. For EC keys, the there is no point in using a digest whose output size is longer than
+        //    key/field size because the digest will be truncated to that size.
+
+        if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) {
+            // Key not authorized for signing
+            return null;
+        }
+        if (spec.isUserAuthenticationRequired()) {
+            // Key not authorized for use without user authentication
+            return null;
+        }
+        if (!spec.isDigestsSpecified()) {
+            // Key not authorized for any digests -- can't sign
+            return null;
+        }
+        switch (keymasterAlgorithm) {
+            case KeymasterDefs.KM_ALGORITHM_EC:
+            {
+                Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+                        spec.getDigests(),
+                        AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
+
+                int bestKeymasterDigest = -1;
+                int bestDigestOutputSizeBits = -1;
+                for (int keymasterDigest : availableKeymasterDigests) {
+                    int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+                    if (outputSizeBits == keySizeBits) {
+                        // Perfect match -- use this digest
+                        bestKeymasterDigest = keymasterDigest;
+                        bestDigestOutputSizeBits = outputSizeBits;
+                        break;
+                    }
+                    // Not a perfect match -- check against the best digest so far
+                    if (bestKeymasterDigest == -1) {
+                        // First digest tested -- definitely the best so far
+                        bestKeymasterDigest = keymasterDigest;
+                        bestDigestOutputSizeBits = outputSizeBits;
+                    } else {
+                        // Prefer output size to be as close to key size as possible, with output
+                        // sizes larger than key size preferred to those smaller than key size.
+                        if (bestDigestOutputSizeBits < keySizeBits) {
+                            // Output size of the best digest so far is smaller than key size.
+                            // Anything larger is a win.
+                            if (outputSizeBits > bestDigestOutputSizeBits) {
+                                bestKeymasterDigest = keymasterDigest;
+                                bestDigestOutputSizeBits = outputSizeBits;
+                            }
+                        } else {
+                            // Output size of the best digest so far is larger than key size.
+                            // Anything smaller is a win, as long as it's not smaller than key size.
+                            if ((outputSizeBits < bestDigestOutputSizeBits)
+                                    && (outputSizeBits >= keySizeBits)) {
+                                bestKeymasterDigest = keymasterDigest;
+                                bestDigestOutputSizeBits = outputSizeBits;
+                            }
+                        }
+                    }
+                }
+                if (bestKeymasterDigest == -1) {
+                    return null;
+                }
+                return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+                        bestKeymasterDigest) + "WithECDSA";
+            }
+            case KeymasterDefs.KM_ALGORITHM_RSA:
+            {
+                // Check whether this key is authorized for PKCS#1 signature padding.
+                // We use Bouncy Castle to generate self-signed RSA certificates. Bouncy Castle
+                // only supports RSA certificates signed using PKCS#1 padding scheme. The key needs
+                // to be authorized for PKCS#1 padding or padding NONE which means any padding.
+                boolean pkcs1SignaturePaddingSupported =
+                        com.android.internal.util.ArrayUtils.contains(
+                                KeyProperties.SignaturePadding.allToKeymaster(
+                                        spec.getSignaturePaddings()),
+                                KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+                if (!pkcs1SignaturePaddingSupported) {
+                    // Key not authorized for PKCS#1 signature padding -- can't sign
+                    return null;
+                }
+
+                Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
+                        spec.getDigests(),
+                        AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
+
+                // The amount of space available for the digest is less than modulus size by about
+                // 30 bytes because padding must be at least 11 bytes long (00 || 01 || PS || 00,
+                // where PS must be at least 8 bytes long), and then there's also the 15--19 bytes
+                // overhead (depending the on chosen digest) for encoding digest OID and digest
+                // value in DER.
+                int maxDigestOutputSizeBits = keySizeBits - 30 * 8;
+                int bestKeymasterDigest = -1;
+                int bestDigestOutputSizeBits = -1;
+                for (int keymasterDigest : availableKeymasterDigests) {
+                    int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
+                    if (outputSizeBits > maxDigestOutputSizeBits) {
+                        // Digest too long (signature generation will fail) -- skip
+                        continue;
+                    }
+                    if (bestKeymasterDigest == -1) {
+                        // First digest tested -- definitely the best so far
+                        bestKeymasterDigest = keymasterDigest;
+                        bestDigestOutputSizeBits = outputSizeBits;
+                    } else {
+                        // The longer the better
+                        if (outputSizeBits > bestDigestOutputSizeBits) {
+                            bestKeymasterDigest = keymasterDigest;
+                            bestDigestOutputSizeBits = outputSizeBits;
+                        }
+                    }
+                }
+                if (bestKeymasterDigest == -1) {
+                    return null;
+                }
+                return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
+                        bestKeymasterDigest) + "WithRSA";
+            }
+            default:
+                throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
+        }
+    }
+
+    private static Set<Integer> getAvailableKeymasterSignatureDigests(
+            @KeyProperties.DigestEnum String[] authorizedKeyDigests,
+            @KeyProperties.DigestEnum String[] supportedSignatureDigests) {
+        Set<Integer> authorizedKeymasterKeyDigests = new HashSet<Integer>();
+        for (int keymasterDigest : KeyProperties.Digest.allToKeymaster(authorizedKeyDigests)) {
+            authorizedKeymasterKeyDigests.add(keymasterDigest);
+        }
+        Set<Integer> supportedKeymasterSignatureDigests = new HashSet<Integer>();
+        for (int keymasterDigest
+                : KeyProperties.Digest.allToKeymaster(supportedSignatureDigests)) {
+            supportedKeymasterSignatureDigests.add(keymasterDigest);
+        }
+        Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests);
+        result.retainAll(authorizedKeymasterKeyDigests);
+        return result;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
new file mode 100644
index 0000000..afb1054
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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 java.security.KeyStore;
+import java.security.KeyStore.ProtectionParameter;
+
+/**
+ * @hide
+ */
+class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
+
+    private final int mNamespace;
+
+    AndroidKeyStoreLoadStoreParameter(int namespace) {
+        mNamespace = namespace;
+    }
+
+    @Override
+    public ProtectionParameter getProtectionParameter() {
+        return null;
+    }
+
+    int getNamespace() {
+        return mNamespace;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePrivateKey.java
new file mode 100644
index 0000000..8b331ee
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePrivateKey.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyDescriptor;
+
+import java.security.PrivateKey;
+
+/**
+ * {@link PrivateKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
+
+    public AndroidKeyStorePrivateKey(@NonNull KeyDescriptor descriptor,
+            long keyId, @NonNull Authorization[] authorizations, @NonNull String algorithm,
+            @NonNull KeyStoreSecurityLevel securityLevel) {
+        super(descriptor, keyId, authorizations, algorithm, securityLevel);
+    }
+
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
new file mode 100644
index 0000000..b2e32a3
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2012 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.annotation.NonNull;
+import android.security.KeyStore;
+import android.security.KeyStore2;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyStoreCryptoOperation;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyEntryResponse;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.ResponseCode;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.UnrecoverableKeyException;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A provider focused on providing JCA interfaces for the Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreProvider extends Provider {
+    private static final String PROVIDER_NAME = "AndroidKeyStore";
+
+    // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
+    // classes when this provider is instantiated and installed early on during each app's
+    // initialization process.
+    //
+    // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider.
+    // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc
+    // for details.
+
+    private static final String PACKAGE_NAME = "android.security.keystore2";
+
+    private static final String DESEDE_SYSTEM_PROPERTY =
+            "ro.hardware.keystore_desede";
+
+    /** @hide **/
+    public AndroidKeyStoreProvider() {
+        super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
+
+        boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY));
+
+        // java.security.KeyStore
+        put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
+
+        // java.security.KeyPairGenerator
+        put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
+        put("KeyPairGenerator.RSA", PACKAGE_NAME +  ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
+
+        // java.security.KeyFactory
+        putKeyFactoryImpl("EC");
+        putKeyFactoryImpl("RSA");
+
+        // javax.crypto.KeyGenerator
+        put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
+        put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
+        put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
+        put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
+        put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384");
+        put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512");
+
+        if (supports3DES) {
+            put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
+        }
+
+        // java.security.SecretKeyFactory
+        putSecretKeyFactoryImpl("AES");
+        if (supports3DES) {
+            putSecretKeyFactoryImpl("DESede");
+        }
+        putSecretKeyFactoryImpl("HmacSHA1");
+        putSecretKeyFactoryImpl("HmacSHA224");
+        putSecretKeyFactoryImpl("HmacSHA256");
+        putSecretKeyFactoryImpl("HmacSHA384");
+        putSecretKeyFactoryImpl("HmacSHA512");
+    }
+
+    private static boolean sInstalled = false;
+
+    /**
+     * This function indicates whether or not this provider was installed. This is manly used
+     * as indicator for
+     * {@link android.security.keystore.AndroidKeyStoreProvider#getKeyStoreForUid(int)}
+     * to whether or not to retrieve the Keystore provider by "AndroidKeyStoreLegacy".
+     * This function can be removed once the transition to Keystore 2.0 is complete.
+     * b/171305684
+     *
+     * @return true if this provider was installed.
+     * @hide
+     */
+    public static boolean isInstalled() {
+        return sInstalled;
+    }
+
+    /**
+     * Installs a new instance of this provider (and the
+     * {@link AndroidKeyStoreBCWorkaroundProvider}).
+     * @hide
+     */
+    public static void install() {
+        Provider[] providers = Security.getProviders();
+        int bcProviderIndex = -1;
+        for (int i = 0; i < providers.length; i++) {
+            Provider provider = providers[i];
+            if ("BC".equals(provider.getName())) {
+                bcProviderIndex = i;
+                break;
+            }
+        }
+        sInstalled = true;
+
+        Security.addProvider(new AndroidKeyStoreProvider());
+        Security.addProvider(
+                new android.security.keystore.AndroidKeyStoreProvider(
+                        "AndroidKeyStoreLegacy"));
+        Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
+        Provider legacyWorkaroundProvider =
+                new android.security.keystore.AndroidKeyStoreBCWorkaroundProvider(
+                        "AndroidKeyStoreBCWorkaroundLegacy");
+        if (bcProviderIndex != -1) {
+            // Bouncy Castle provider found -- install the workaround provider above it.
+            // insertProviderAt uses 1-based positions.
+            Security.insertProviderAt(legacyWorkaroundProvider, bcProviderIndex + 1);
+            Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
+        } else {
+            // Bouncy Castle provider not found -- install the workaround provider at lowest
+            // priority.
+            Security.addProvider(workaroundProvider);
+            Security.addProvider(legacyWorkaroundProvider);
+        }
+    }
+
+    private void putSecretKeyFactoryImpl(String algorithm) {
+        put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
+    }
+
+    private void putKeyFactoryImpl(String algorithm) {
+        put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
+    }
+
+    /**
+     * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+     * primitive.
+     *
+     * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
+     *
+     * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
+     *         is not in progress.
+     *
+     * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
+     *         by AndroidKeyStore provider.
+     * @throws IllegalStateException if the provided primitive is not initialized.
+     * @hide
+     */
+    public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
+        if (cryptoPrimitive == null) {
+            throw new NullPointerException();
+        }
+        Object spi;
+        if (cryptoPrimitive instanceof Signature) {
+            spi = ((Signature) cryptoPrimitive).getCurrentSpi();
+        } else if (cryptoPrimitive instanceof Mac) {
+            spi = ((Mac) cryptoPrimitive).getCurrentSpi();
+        } else if (cryptoPrimitive instanceof Cipher) {
+            spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
+        } else {
+            throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
+                    + ". Supported: Signature, Mac, Cipher");
+        }
+        if (spi == null) {
+            throw new IllegalStateException("Crypto primitive not initialized");
+        } else if (!(spi instanceof KeyStoreCryptoOperation)) {
+            throw new IllegalArgumentException(
+                    "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
+                    + ", spi: " + spi);
+        }
+        return ((KeyStoreCryptoOperation) spi).getOperationHandle();
+    }
+
+    /**
+     * This helper function gets called if the key loaded from the keystore daemon
+     * is for an asymmetric algorithm. It constructs an instance of {@link AndroidKeyStorePublicKey}
+     * which implements {@link PublicKey}.
+     *
+     * @param descriptor The original key descriptor that was used to load the key.
+     *
+     * @param metadata The key metadata which includes the public key material, a reference to the
+     *                 stored private key material, the key characteristics.
+     * @param iSecurityLevel A binder interface that allows using the private key.
+     * @param algorithm Must indicate EC or RSA.
+     * @return AndroidKeyStorePublicKey
+     * @throws UnrecoverableKeyException
+     * @hide
+     */
+    @NonNull
+    static AndroidKeyStorePublicKey makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
+            @NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata,
+            @NonNull KeyStoreSecurityLevel iSecurityLevel, int algorithm)
+            throws UnrecoverableKeyException {
+        if (metadata.certificate == null) {
+            throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key."
+                    + " Keystore has no public certificate stored.");
+        }
+        final byte[] x509EncodedPublicKey = metadata.certificate;
+
+        String jcaKeyAlgorithm;
+        try {
+            jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
+                    algorithm);
+        } catch (IllegalArgumentException e) {
+            throw (UnrecoverableKeyException)
+                    new UnrecoverableKeyException("Failed to load private key")
+                            .initCause(e);
+        }
+
+        PublicKey publicKey;
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance(jcaKeyAlgorithm);
+            publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedPublicKey));
+        } catch (NoSuchAlgorithmException e) {
+            throw new ProviderException(
+                    "Failed to obtain " + jcaKeyAlgorithm + " KeyFactory", e);
+        } catch (InvalidKeySpecException e) {
+            throw new ProviderException("Invalid X.509 encoding of public key", e);
+        }
+
+        KeyStoreSecurityLevel securityLevel = iSecurityLevel;
+        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
+
+            return new AndroidKeyStoreECPublicKey(descriptor, metadata,
+                    iSecurityLevel, (ECPublicKey) publicKey);
+        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) {
+            return new AndroidKeyStoreRSAPublicKey(descriptor, metadata,
+                    iSecurityLevel, (RSAPublicKey) publicKey);
+        } else {
+            throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+                    + jcaKeyAlgorithm);
+        }
+    }
+
+    /** @hide **/
+    @NonNull
+    public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
+            @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        AndroidKeyStoreKey key =
+                loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
+        if (key instanceof AndroidKeyStorePublicKey) {
+            return (AndroidKeyStorePublicKey) key;
+        } else {
+            throw new UnrecoverableKeyException("No asymmetric key found by the given alias.");
+        }
+    }
+
+    /** @hide **/
+    @NonNull
+    public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+            @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        AndroidKeyStoreKey key =
+                loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
+        if (key instanceof AndroidKeyStorePublicKey) {
+            AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key;
+            return new KeyPair(publicKey, publicKey.getPrivateKey());
+        } else {
+            throw new UnrecoverableKeyException("No asymmetric key found by the given alias.");
+        }
+    }
+
+    /** @hide **/
+    @NonNull
+    public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+            @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        AndroidKeyStoreKey key =
+                loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
+        if (key instanceof AndroidKeyStorePublicKey) {
+            return ((AndroidKeyStorePublicKey) key).getPrivateKey();
+        } else {
+            throw new UnrecoverableKeyException("No asymmetric key found by the given alias.");
+        }
+    }
+
+
+    @NonNull
+    private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(
+            @NonNull KeyDescriptor descriptor,
+            @NonNull KeyEntryResponse response, int algorithm, int digest)
+            throws UnrecoverableKeyException {
+
+        @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
+        try {
+            keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
+                    algorithm, digest);
+        } catch (IllegalArgumentException e) {
+            throw (UnrecoverableKeyException)
+                    new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
+        }
+
+        return new AndroidKeyStoreSecretKey(descriptor,
+                response.metadata, keyAlgorithmString,
+                new KeyStoreSecurityLevel(response.iSecurityLevel));
+    }
+
+    /**
+     * Loads an an AndroidKeyStoreKey from the AndroidKeyStore backend.
+     *
+     * @param keyStore The keystore2 backend.
+     * @param alias The alias of the key in the Keystore database.
+     * @param namespace The a Keystore namespace. This is used by system api only to request
+     *                  Android system specific keystore namespace, which can be configured
+     *                  in the device's SEPolicy. Third party apps and most system components
+     *                  set this parameter to -1 to indicate their application specific namespace.
+     *                  TODO b/171806779 link to public Keystore 2.0 documentation.
+     *                       See bug for more details for now.
+     * @hide
+     **/
+    @NonNull
+    public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+            @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException  {
+
+        KeyDescriptor descriptor = new KeyDescriptor();
+        if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
+            descriptor.nspace = 0; // ignored;
+            descriptor.domain = Domain.APP;
+        } else {
+            descriptor.nspace = namespace;
+            descriptor.domain = Domain.SELINUX;
+        }
+        descriptor.alias = alias;
+        descriptor.blob = null;
+        KeyEntryResponse response = null;
+        try {
+            response = keyStore.getKeyEntry(descriptor);
+        } catch (android.security.KeyStoreException e) {
+            if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
+                throw new KeyPermanentlyInvalidatedException(
+                        "User changed or deleted their auth credentials",
+                        e);
+            } else {
+                throw (UnrecoverableKeyException)
+                        new UnrecoverableKeyException("Failed to obtain information about key")
+                                .initCause(e);
+            }
+        }
+
+        Integer keymasterAlgorithm = null;
+        // We just need one digest for the algorithm name
+        int keymasterDigest = -1;
+        for (Authorization a : response.metadata.authorizations) {
+            switch (a.keyParameter.tag) {
+                case KeymasterDefs.KM_TAG_ALGORITHM:
+                    keymasterAlgorithm = a.keyParameter.integer;
+                    break;
+                case KeymasterDefs.KM_TAG_DIGEST:
+                    if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer;
+                    break;
+            }
+        }
+        if (keymasterAlgorithm == null) {
+            throw new UnrecoverableKeyException("Key algorithm unknown");
+        }
+
+        if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES ||
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
+            return makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(descriptor, response,
+                    keymasterAlgorithm, keymasterDigest);
+        } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
+            return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
+                    new KeyStoreSecurityLevel(response.iSecurityLevel),
+                    keymasterAlgorithm);
+        } else {
+            throw new UnrecoverableKeyException("Key algorithm unknown");
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
new file mode 100644
index 0000000..49dd77e
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keystore.ArrayUtils;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+
+import java.security.PublicKey;
+
+/**
+ * {@link PublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
+    private final byte[] mCertificate;
+    private final byte[] mCertificateChain;
+
+    public AndroidKeyStorePublicKey(@NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata, @NonNull String algorithm,
+            @NonNull KeyStoreSecurityLevel securityLevel) {
+        super(descriptor, metadata.key.nspace, metadata.authorizations, algorithm, securityLevel);
+        mCertificate = metadata.certificate;
+        mCertificateChain = metadata.certificateChain;
+    }
+
+    abstract AndroidKeyStorePrivateKey getPrivateKey();
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return ArrayUtils.cloneIfNotEmpty(mCertificate);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+
+        result = prime * result + super.hashCode();
+        result = prime * result + ((mCertificate == null) ? 0 : mCertificate.hashCode());
+        result = prime * result + ((mCertificateChain == null) ? 0 : mCertificateChain.hashCode());
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
new file mode 100644
index 0000000..a6ea972
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeymasterUtils;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyParameter;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.util.List;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+    /**
+     * Raw RSA cipher without any padding.
+     */
+    public static final class NoPadding extends AndroidKeyStoreRSACipherSpi {
+        public NoPadding() {
+            super(KeymasterDefs.KM_PAD_NONE);
+        }
+
+        @Override
+        protected boolean adjustConfigForEncryptingWithPrivateKey() {
+            // RSA encryption with no padding using private key is a way to implement raw RSA
+            // signatures which JCA does not expose via Signature. We thus have to support this.
+            setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
+            return true;
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected AlgorithmParameters engineGetParameters() {
+            return null;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return 0;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForFinish() {
+            return 0;
+        }
+    }
+
+    /**
+     * RSA cipher with PKCS#1 v1.5 encryption padding.
+     */
+    public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi {
+        public PKCS1Padding() {
+            super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+        }
+
+        @Override
+        protected boolean adjustConfigForEncryptingWithPrivateKey() {
+            // RSA encryption with PCKS#1 padding using private key is a way to implement RSA
+            // signatures with PKCS#1 padding. We have to support this for legacy reasons.
+            setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
+            setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+            return true;
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected AlgorithmParameters engineGetParameters() {
+            return null;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return 0;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForFinish() {
+            return (isEncrypting()) ? getModulusSizeBytes() : 0;
+        }
+    }
+
+    /**
+     * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
+     */
+    abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
+
+        private static final String MGF_ALGORITGM_MGF1 = "MGF1";
+
+        private int mKeymasterDigest = -1;
+        private int mDigestOutputSizeBytes;
+
+        OAEPWithMGF1Padding(int keymasterDigest) {
+            super(KeymasterDefs.KM_PAD_RSA_OAEP);
+            mKeymasterDigest = keymasterDigest;
+            mDigestOutputSizeBytes =
+                    (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+        @Override
+        protected final void initAlgorithmSpecificParameters(
+                @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
+            if (params == null) {
+                return;
+            }
+
+            if (!(params instanceof OAEPParameterSpec)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported parameter spec: " + params
+                        + ". Only OAEPParameterSpec supported");
+            }
+            OAEPParameterSpec spec = (OAEPParameterSpec) params;
+            if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF: " + spec.getMGFAlgorithm()
+                        + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
+            }
+            String jcaDigest = spec.getDigestAlgorithm();
+            int keymasterDigest;
+            try {
+                keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest);
+            } catch (IllegalArgumentException e) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported digest: " + jcaDigest, e);
+            }
+            switch (keymasterDigest) {
+                case KeymasterDefs.KM_DIGEST_SHA1:
+                case KeymasterDefs.KM_DIGEST_SHA_2_224:
+                case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                case KeymasterDefs.KM_DIGEST_SHA_2_384:
+                case KeymasterDefs.KM_DIGEST_SHA_2_512:
+                    // Permitted.
+                    break;
+                default:
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported digest: " + jcaDigest);
+            }
+            AlgorithmParameterSpec mgfParams = spec.getMGFParameters();
+            if (mgfParams == null) {
+                throw new InvalidAlgorithmParameterException("MGF parameters must be provided");
+            }
+            // Check whether MGF parameters match the OAEPParameterSpec
+            if (!(mgfParams instanceof MGF1ParameterSpec)) {
+                throw new InvalidAlgorithmParameterException("Unsupported MGF parameters"
+                        + ": " + mgfParams + ". Only MGF1ParameterSpec supported");
+            }
+            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
+            String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
+            if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF1 digest: " + mgf1JcaDigest
+                        + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
+            }
+            PSource pSource = spec.getPSource();
+            if (!(pSource instanceof PSource.PSpecified)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported source of encoding input P: " + pSource
+                        + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+            }
+            PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource;
+            byte[] pSourceValue = pSourceSpecified.getValue();
+            if ((pSourceValue != null) && (pSourceValue.length > 0)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported source of encoding input P: " + pSource
+                        + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+            }
+            mKeymasterDigest = keymasterDigest;
+            mDigestOutputSizeBytes =
+                    (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+            if (params == null) {
+                return;
+            }
+
+            OAEPParameterSpec spec;
+            try {
+                spec = params.getParameterSpec(OAEPParameterSpec.class);
+            } catch (InvalidParameterSpecException e) {
+                throw new InvalidAlgorithmParameterException("OAEP parameters required"
+                        + ", but not found in parameters: " + params, e);
+            }
+            if (spec == null) {
+                throw new InvalidAlgorithmParameterException("OAEP parameters required"
+                        + ", but not provided in parameters: " + params);
+            }
+            initAlgorithmSpecificParameters(spec);
+        }
+
+        @Override
+        protected final AlgorithmParameters engineGetParameters() {
+            OAEPParameterSpec spec =
+                    new OAEPParameterSpec(
+                            KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
+                            MGF_ALGORITGM_MGF1,
+                            MGF1ParameterSpec.SHA1,
+                            PSource.PSpecified.DEFAULT);
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
+                params.init(spec);
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException(
+                        "Failed to obtain OAEP AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new ProviderException(
+                        "Failed to initialize OAEP AlgorithmParameters with an IV",
+                        e);
+            }
+        }
+
+        @Override
+        protected final void addAlgorithmSpecificParametersToBegin(
+                @NonNull List<KeyParameter> parameters) {
+            super.addAlgorithmSpecificParametersToBegin(parameters);
+            parameters.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+            ));
+        }
+
+        @Override
+        protected final void loadAlgorithmSpecificParametersFromBeginResult(
+                KeyParameter[] parameters) {
+            super.loadAlgorithmSpecificParametersFromBeginResult(parameters);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return 0;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForFinish() {
+            return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
+        }
+    }
+
+    public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA1AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA224AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA256AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA384AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA512AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterPadding;
+    private int mKeymasterPaddingOverride;
+
+    private int mModulusSizeBytes = -1;
+
+    AndroidKeyStoreRSACipherSpi(int keymasterPadding) {
+        mKeymasterPadding = keymasterPadding;
+    }
+
+    @Override
+    protected final void initKey(int opmode, Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("Unsupported key: null");
+        }
+        if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+                    + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+        }
+        AndroidKeyStoreKey keystoreKey;
+        if (key instanceof AndroidKeyStorePrivateKey) {
+            keystoreKey = (AndroidKeyStoreKey) key;
+        } else if (key instanceof AndroidKeyStorePublicKey) {
+            keystoreKey = (AndroidKeyStoreKey) key;
+        } else {
+            throw new InvalidKeyException("Unsupported key type: " + key);
+        }
+
+        if (keystoreKey instanceof PrivateKey) {
+            // Private key
+            switch (opmode) {
+                case Cipher.DECRYPT_MODE:
+                case Cipher.UNWRAP_MODE:
+                    // Permitted
+                    break;
+                case Cipher.ENCRYPT_MODE:
+                case Cipher.WRAP_MODE:
+                    if (!adjustConfigForEncryptingWithPrivateKey()) {
+                        throw new InvalidKeyException(
+                                "RSA private keys cannot be used with " + opmodeToString(opmode)
+                                + " and padding "
+                                + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
+                                + ". Only RSA public keys supported for this mode");
+                    }
+                    break;
+                default:
+                    throw new InvalidKeyException(
+                            "RSA private keys cannot be used with opmode: " + opmode);
+            }
+        } else {
+            // Public key
+            switch (opmode) {
+                case Cipher.ENCRYPT_MODE:
+                case Cipher.WRAP_MODE:
+                    // Permitted
+                    break;
+                case Cipher.DECRYPT_MODE:
+                case Cipher.UNWRAP_MODE:
+                    throw new InvalidKeyException(
+                            "RSA public keys cannot be used with " + opmodeToString(opmode)
+                            + " and padding "
+                            + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
+                            + ". Only RSA private keys supported for this opmode.");
+                    // break;
+                default:
+                    throw new InvalidKeyException(
+                            "RSA public keys cannot be used with " + opmodeToString(opmode));
+            }
+        }
+
+        long keySizeBits = -1;
+        for (Authorization a : keystoreKey.getAuthorizations()) {
+            if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
+                keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
+            }
+        }
+
+        if (keySizeBits == -1) {
+            throw new InvalidKeyException("Size of key not known");
+        } else if (keySizeBits > Integer.MAX_VALUE) {
+            throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
+        }
+        mModulusSizeBytes = (int) ((keySizeBits + 7) / 8);
+
+        setKey(keystoreKey);
+    }
+
+    /**
+     * Adjusts the configuration of this cipher for encrypting using the private key.
+     *
+     * <p>The default implementation does nothing and refuses to adjust the configuration.
+     *
+     * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting
+     *         using private key is not permitted for this cipher.
+     */
+    protected boolean adjustConfigForEncryptingWithPrivateKey() {
+        return false;
+    }
+
+    @Override
+    protected final void resetAll() {
+        mModulusSizeBytes = -1;
+        mKeymasterPaddingOverride = -1;
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters) {
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA
+        ));
+        int keymasterPadding = getKeymasterPaddingOverride();
+        if (keymasterPadding == -1) {
+            keymasterPadding = mKeymasterPadding;
+        }
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PADDING, keymasterPadding
+        ));
+        int purposeOverride = getKeymasterPurposeOverride();
+        if ((purposeOverride != -1)
+                && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
+                || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
+            // Keymaster sign/verify requires digest to be specified.
+            // For raw sign/verify it's NONE.
+            parameters.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE
+            ));
+        }
+    }
+
+    @Override
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            KeyParameter[] parameters) {
+    }
+
+    @Override
+    protected final int engineGetBlockSize() {
+        // Not a block cipher, according to the RI
+        return 0;
+    }
+
+    @Override
+    protected final byte[] engineGetIV() {
+        // IV never used
+        return null;
+    }
+
+    @Override
+    protected final int engineGetOutputSize(int inputLen) {
+        return getModulusSizeBytes();
+    }
+
+    protected final int getModulusSizeBytes() {
+        if (mModulusSizeBytes == -1) {
+            throw new IllegalStateException("Not initialized");
+        }
+        return mModulusSizeBytes;
+    }
+
+    /**
+     * Overrides the default padding of the crypto operation.
+     */
+    protected final void setKeymasterPaddingOverride(int keymasterPadding) {
+        mKeymasterPaddingOverride = keymasterPadding;
+    }
+
+    protected final int getKeymasterPaddingOverride() {
+        return mKeymasterPaddingOverride;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPrivateKey.java
new file mode 100644
index 0000000..ef0d3bc
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPrivateKey.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyDescriptor;
+
+import java.math.BigInteger;
+import java.security.PrivateKey;
+import java.security.interfaces.RSAKey;
+
+/**
+ * RSA private key (instance of {@link PrivateKey} and {@link RSAKey}) backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreRSAPrivateKey extends AndroidKeyStorePrivateKey implements RSAKey {
+
+    private final BigInteger mModulus;
+
+
+    public AndroidKeyStoreRSAPrivateKey(@NonNull KeyDescriptor descriptor,
+            long keyId,
+            @NonNull Authorization[] authorizations,
+            @NonNull KeyStoreSecurityLevel securityLevel, @NonNull BigInteger modulus) {
+        super(descriptor, keyId, authorizations, KeyProperties.KEY_ALGORITHM_RSA, securityLevel);
+        mModulus = modulus;
+    }
+
+    @Override
+    public BigInteger getModulus() {
+        return mModulus;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
new file mode 100644
index 0000000..b578ea9
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+
+import java.math.BigInteger;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * {@link RSAPublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implements RSAPublicKey {
+    private final BigInteger mModulus;
+    private final BigInteger mPublicExponent;
+
+    public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata,
+            @NonNull KeyStoreSecurityLevel securityLevel, @NonNull BigInteger modulus,
+            @NonNull BigInteger publicExponent) {
+        super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_RSA, securityLevel);
+        mModulus = modulus;
+        mPublicExponent = publicExponent;
+    }
+
+    public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata,
+            @NonNull KeyStoreSecurityLevel securityLevel, @NonNull RSAPublicKey info) {
+        this(descriptor, metadata, securityLevel, info.getModulus(), info.getPublicExponent());
+        if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+            throw new IllegalArgumentException(
+                    "Unsupported key export format: " + info.getFormat());
+        }
+    }
+
+    @Override
+    public AndroidKeyStorePrivateKey getPrivateKey() {
+        return new AndroidKeyStoreRSAPrivateKey(getUserKeyDescriptor(), getKeyIdDescriptor().nspace,
+                getAuthorizations(), getSecurityLevel(), mModulus);
+    }
+
+    @Override
+    public BigInteger getModulus() {
+        return mModulus;
+    }
+
+    @Override
+    public BigInteger getPublicExponent() {
+        return mPublicExponent;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
new file mode 100644
index 0000000..5f1b9c0
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyParameter;
+
+import java.security.InvalidKeyException;
+import java.security.SignatureSpi;
+import java.util.List;
+
+/**
+ * Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
+
+    abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
+        PKCS1Padding(int keymasterDigest) {
+            super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForSign() {
+            // No entropy required for this deterministic signature scheme.
+            return 0;
+        }
+    }
+
+    public static final class NONEWithPKCS1Padding extends PKCS1Padding {
+        public NONEWithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_NONE);
+        }
+    }
+
+    public static final class MD5WithPKCS1Padding extends PKCS1Padding {
+        public MD5WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_MD5);
+        }
+    }
+
+    public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
+        public SHA1WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
+        public SHA224WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
+        public SHA256WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
+        public SHA384WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
+        public SHA512WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
+        private static final int SALT_LENGTH_BYTES = 20;
+
+        PSSPadding(int keymasterDigest) {
+            super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForSign() {
+            return SALT_LENGTH_BYTES;
+        }
+    }
+
+    public static final class SHA1WithPSSPadding extends PSSPadding {
+        public SHA1WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static final class SHA224WithPSSPadding extends PSSPadding {
+        public SHA224WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static final class SHA256WithPSSPadding extends PSSPadding {
+        public SHA256WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static final class SHA384WithPSSPadding extends PSSPadding {
+        public SHA384WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static final class SHA512WithPSSPadding extends PSSPadding {
+        public SHA512WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterDigest;
+    private final int mKeymasterPadding;
+
+    AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) {
+        mKeymasterDigest = keymasterDigest;
+        mKeymasterPadding = keymasterPadding;
+    }
+
+    @Override
+    protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+        if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+                    + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+        }
+        super.initKey(key);
+    }
+
+    @Override
+    protected final void resetAll() {
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected final void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters) {
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding
+        ));
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKey.java
new file mode 100644
index 0000000..4e45913
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKey.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+
+import javax.crypto.SecretKey;
+
+/**
+ * {@link SecretKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreSecretKey extends AndroidKeyStoreKey implements SecretKey {
+
+    public AndroidKeyStoreSecretKey(@NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata, @NonNull String algorithm,
+            @NonNull KeyStoreSecurityLevel securityLevel) {
+        super(descriptor, metadata.key.nspace, metadata.authorizations, algorithm, securityLevel);
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
new file mode 100644
index 0000000..9d3b970
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.GateKeeper;
+import android.security.KeyStore;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.Authorization;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.ProviderException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link SecretKeyFactorySpi} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    @Override
+    protected KeySpec engineGetKeySpec(SecretKey key,
+            @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
+        if (keySpecClass == null) {
+            throw new InvalidKeySpecException("keySpecClass == null");
+        }
+        if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
+                    ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
+            throw new InvalidKeySpecException(
+                    "Key material export of Android KeyStore keys is not supported");
+        }
+        if (!KeyInfo.class.equals(keySpecClass)) {
+            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
+        }
+        AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
+
+        return getKeyInfo(keystoreKey);
+    }
+
+    static @NonNull KeyInfo getKeyInfo(@NonNull AndroidKeyStoreKey key) {
+
+        @KeyProperties.SecurityLevelEnum int securityLevel =
+                KeyProperties.SECURITY_LEVEL_SOFTWARE;
+        boolean insideSecureHardware = false;
+        @KeyProperties.OriginEnum int origin = -1;
+        int keySize = -1;
+        @KeyProperties.PurposeEnum int purposes = 0;
+        String[] encryptionPaddings;
+        String[] signaturePaddings;
+        List<String> digestsList = new ArrayList<>();
+        List<String> blockModesList = new ArrayList<>();
+        int keymasterSwEnforcedUserAuthenticators = 0;
+        int keymasterHwEnforcedUserAuthenticators = 0;
+        List<BigInteger> keymasterSecureUserIds = new ArrayList<BigInteger>();
+        List<String> encryptionPaddingsList = new ArrayList<String>();
+        List<String> signaturePaddingsList = new ArrayList<String>();
+        Date keyValidityStart = null;
+        Date keyValidityForOriginationEnd = null;
+        Date keyValidityForConsumptionEnd = null;
+        long userAuthenticationValidityDurationSeconds = 0;
+        boolean userAuthenticationRequired = true;
+        boolean userAuthenticationValidWhileOnBody = false;
+        boolean trustedUserPresenceRequired = false;
+        boolean trustedUserConfirmationRequired = false;
+        try {
+            for (Authorization a : key.getAuthorizations()) {
+                switch (a.keyParameter.tag) {
+                    case KeymasterDefs.KM_TAG_ORIGIN:
+                        insideSecureHardware =
+                                KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
+                        securityLevel = a.securityLevel;
+                        origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer);
+                        break;
+                    case KeymasterDefs.KM_TAG_KEY_SIZE:
+                        long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a);
+                        if (keySizeUnsigned > Integer.MAX_VALUE) {
+                            throw new ProviderException(
+                                    "Key too large: " + keySizeUnsigned + " bits");
+                        }
+                        keySize = (int) keySizeUnsigned;
+                        break;
+                    case KeymasterDefs.KM_TAG_PURPOSE:
+                        purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer);
+                        break;
+                    case KeymasterDefs.KM_TAG_PADDING:
+                        try {
+                            if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
+                                    || a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) {
+                                @KeyProperties.SignaturePaddingEnum String padding =
+                                        KeyProperties.SignaturePadding.fromKeymaster(
+                                                a.keyParameter.integer);
+                                signaturePaddingsList.add(padding);
+                            } else {
+                                @KeyProperties.EncryptionPaddingEnum String jcaPadding =
+                                        KeyProperties.EncryptionPadding.fromKeymaster(
+                                                a.keyParameter.integer);
+                                encryptionPaddingsList.add(jcaPadding);
+                            }
+                        } catch (IllegalArgumentException e) {
+                            throw new ProviderException("Unsupported padding: "
+                                                + a.keyParameter.integer);
+                        }
+                        break;
+                    case KeymasterDefs.KM_TAG_DIGEST:
+                        digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer));
+                        break;
+                    case KeymasterDefs.KM_TAG_BLOCK_MODE:
+                        blockModesList.add(
+                                KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer)
+                        );
+                        break;
+                    case KeymasterDefs.KM_TAG_USER_AUTH_TYPE:
+                        if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) {
+                            keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer;
+                        } else {
+                            keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer;
+                        }
+                        break;
+                    case KeymasterDefs.KM_TAG_USER_SECURE_ID:
+                        keymasterSecureUserIds.add(
+                                KeymasterArguments.toUint64(a.keyParameter.longInteger));
+                        break;
+                    case KeymasterDefs.KM_TAG_ACTIVE_DATETIME:
+                        keyValidityStart = KeyStore2ParameterUtils.getDate(a);
+                        break;
+                    case KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME:
+                        keyValidityForOriginationEnd =
+                                KeyStore2ParameterUtils.getDate(a);
+                        break;
+                    case KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME:
+                        keyValidityForConsumptionEnd =
+                                KeyStore2ParameterUtils.getDate(a);
+                        break;
+                    case KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED:
+                        userAuthenticationRequired = false;
+                        break;
+                    case KeymasterDefs.KM_TAG_AUTH_TIMEOUT:
+                        userAuthenticationValidityDurationSeconds =
+                                KeyStore2ParameterUtils.getUnsignedInt(a);
+                        if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
+                            throw new ProviderException(
+                                    "User authentication timeout validity too long: "
+                                    + userAuthenticationValidityDurationSeconds + " seconds");
+                        }
+                        break;
+                    case KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY:
+                        userAuthenticationValidWhileOnBody =
+                                KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
+                        break;
+                    case KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
+                        trustedUserPresenceRequired =
+                                KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
+                        break;
+                    case KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
+                        trustedUserConfirmationRequired =
+                                KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
+                        break;
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            throw new ProviderException("Unsupported key characteristic", e);
+        }
+        if (keySize == -1) {
+            throw new ProviderException("Key size not available");
+        }
+        if (origin == -1) {
+            throw new ProviderException("Key origin not available");
+        }
+
+        encryptionPaddings =
+                encryptionPaddingsList.toArray(new String[0]);
+        signaturePaddings =
+                signaturePaddingsList.toArray(new String[0]);
+
+        boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
+                && (keymasterHwEnforcedUserAuthenticators != 0)
+                && (keymasterSwEnforcedUserAuthenticators == 0);
+
+        String[] digests = digestsList.toArray(new String[0]);
+        String[] blockModes = blockModesList.toArray(new String[0]);
+
+        boolean invalidatedByBiometricEnrollment = false;
+        if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC
+            || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC) {
+            // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list.
+            invalidatedByBiometricEnrollment = !keymasterSecureUserIds.isEmpty()
+                    && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
+        }
+
+        return new KeyInfo(key.getUserKeyDescriptor().alias,
+                insideSecureHardware,
+                origin,
+                keySize,
+                keyValidityStart,
+                keyValidityForOriginationEnd,
+                keyValidityForConsumptionEnd,
+                purposes,
+                encryptionPaddings,
+                signaturePaddings,
+                digests,
+                blockModes,
+                userAuthenticationRequired,
+                (int) userAuthenticationValidityDurationSeconds,
+                keymasterHwEnforcedUserAuthenticators,
+                userAuthenticationRequirementEnforcedBySecureHardware,
+                userAuthenticationValidWhileOnBody,
+                trustedUserPresenceRequired,
+                invalidatedByBiometricEnrollment,
+                trustedUserConfirmationRequired,
+                securityLevel);
+    }
+
+    private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
+    	try {
+    		return BigInteger.valueOf(GateKeeper.getSecureUserId());
+    	} catch (IllegalStateException e) {
+    		throw new ProviderException("Failed to get GateKeeper secure user ID", e);
+    	}
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
+        throw new InvalidKeySpecException(
+                "To generate secret key in Android Keystore, use KeyGenerator initialized with "
+                        + KeyGenParameterSpec.class.getName());
+    }
+
+    @Override
+    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "To import a secret key into Android Keystore, use KeyStore.setEntry");
+        }
+
+        return key;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
new file mode 100644
index 0000000..55414b7
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2015 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.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+import android.security.keystore.KeyStoreCryptoOperation;
+import android.system.keystore2.KeyParameter;
+
+import libcore.util.EmptyArray;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
+        implements KeyStoreCryptoOperation {
+    private static final String TAG = "AndroidKeyStoreSignatureSpiBase";
+
+    // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
+    // and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
+    private boolean mSigning;
+    private AndroidKeyStoreKey mKey;
+
+    /**
+     * Object representing this operation inside keystore service. It is initialized
+     * by {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some
+     * error conditions in between.
+     */
+    private KeyStoreOperation mOperation;
+    /**
+     * The operation challenge is required when an operation needs user authorization.
+     * The challenge is subjected to an authenticator, e.g., Gatekeeper or a biometric
+     * authenticator, and included in the authentication token minted by this authenticator.
+     * It may be null, if the operation does not require authorization.
+     */
+    private long mOperationChallenge;
+    private KeyStoreCryptoOperationStreamer mMessageStreamer;
+
+    /**
+     * Encountered exception which could not be immediately thrown because it was encountered inside
+     * a method that does not throw checked exception. This exception will be thrown from
+     * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
+     * {@code engineUpdate} starts ignoring input data.
+     */
+    private Exception mCachedException;
+
+    AndroidKeyStoreSignatureSpiBase() {
+        mOperation = null;
+        mOperationChallenge = 0;
+        mSigning = false;
+        mKey = null;
+        appRandom = null;
+        mMessageStreamer = null;
+        mCachedException = null;
+    }
+
+    @Override
+    protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
+        engineInitSign(key, null);
+    }
+
+    @Override
+    protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
+            throws InvalidKeyException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if (privateKey == null) {
+                throw new InvalidKeyException("Unsupported key: null");
+            }
+            AndroidKeyStoreKey keystoreKey;
+            if (privateKey instanceof AndroidKeyStorePrivateKey) {
+                keystoreKey = (AndroidKeyStoreKey) privateKey;
+            } else {
+                throw new InvalidKeyException("Unsupported private key type: " + privateKey);
+            }
+            mSigning = true;
+            initKey(keystoreKey);
+            appRandom = random;
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    @Override
+    protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if (publicKey == null) {
+                throw new InvalidKeyException("Unsupported key: null");
+            }
+            AndroidKeyStoreKey keystoreKey;
+            if (publicKey instanceof AndroidKeyStorePublicKey) {
+                keystoreKey = (AndroidKeyStorePublicKey) publicKey;
+            } else {
+                throw new InvalidKeyException("Unsupported public key type: " + publicKey);
+            }
+            mSigning = false;
+            initKey(keystoreKey);
+            appRandom = null;
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    /**
+     * Configures this signature instance to use the provided key.
+     *
+     * @throws InvalidKeyException if the {@code key} is not suitable.
+     */
+    @CallSuper
+    protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+        mKey = key;
+    }
+
+    private void abortOperation() {
+        KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+        mOperation = null;
+    }
+
+    /**
+     * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
+     * cipher instance.
+     *
+     * <p>Subclasses storing additional state should override this method, reset the additional
+     * state, and then chain to superclass.
+     */
+    @CallSuper
+    protected void resetAll() {
+        abortOperation();
+        mOperationChallenge = 0;
+        mSigning = false;
+        mKey = null;
+        appRandom = null;
+        mMessageStreamer = null;
+        mCachedException = null;
+    }
+
+    /**
+     * Resets this cipher while preserving the initialized state. This must be equivalent to
+     * rolling back the cipher's state to just after the most recent {@code engineInit} completed
+     * successfully.
+     *
+     * <p>Subclasses storing additional post-init state should override this method, reset the
+     * additional state, and then chain to superclass.
+     */
+    @CallSuper
+    protected void resetWhilePreservingInitState() {
+        abortOperation();
+        mOperationChallenge = 0;
+        mMessageStreamer = null;
+        mCachedException = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
+        if (mMessageStreamer != null) {
+            return;
+        }
+        if (mCachedException != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        List<KeyParameter> parameters = new ArrayList<>();
+        addAlgorithmSpecificParametersToBegin(parameters);
+
+        int purpose = mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY;
+
+        parameters.add(KeyStore2ParameterUtils.makeEnum(KeymasterDefs.KM_TAG_PURPOSE, purpose));
+
+        try {
+            mOperation = mKey.getSecurityLevel().createOperation(
+                    mKey.getKeyIdDescriptor(),
+                    parameters);
+        } catch (KeyStoreException keyStoreException) {
+            throw KeyStoreCryptoOperationUtils.getInvalidKeyException(
+                    mKey, keyStoreException);
+        }
+
+        // Now we check if we got an operation challenge. This indicates that user authorization
+        // is required. And if we got a challenge we check if the authorization can possibly
+        // succeed.
+        mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(
+                mOperation, mKey);
+
+        mMessageStreamer = createMainDataStreamer(mOperation);
+    }
+
+    /**
+     * Creates a streamer which sends the message to be signed/verified into the provided KeyStore
+     *
+     * <p>This implementation returns a working streamer.
+     */
+    @NonNull
+    protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+            @NonNull KeyStoreOperation operation) {
+        return new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        operation));
+    }
+
+    @Override
+    public final long getOperationHandle() {
+        return mOperationChallenge;
+    }
+
+    @Override
+    protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+        if (mCachedException != null) {
+            throw new SignatureException(mCachedException);
+        }
+
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new SignatureException(e);
+        }
+
+        if (len == 0) {
+            return;
+        }
+
+        byte[] output;
+        try {
+            output = mMessageStreamer.update(b, off, len);
+        } catch (KeyStoreException e) {
+            throw new SignatureException(e);
+        }
+
+        if (output.length != 0) {
+            throw new ProviderException(
+                    "Update operation unexpectedly produced output: " + output.length + " bytes");
+        }
+    }
+
+    @Override
+    protected final void engineUpdate(byte b) throws SignatureException {
+        engineUpdate(new byte[] {b}, 0, 1);
+    }
+
+    @Override
+    protected final void engineUpdate(ByteBuffer input) {
+        byte[] b;
+        int off;
+        int len = input.remaining();
+        if (input.hasArray()) {
+            b = input.array();
+            off = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            b = new byte[len];
+            off = 0;
+            input.get(b);
+        }
+
+        try {
+            engineUpdate(b, off, len);
+        } catch (SignatureException e) {
+            mCachedException = e;
+        }
+    }
+
+    @Override
+    protected final int engineSign(byte[] out, int outOffset, int outLen)
+            throws SignatureException {
+        return super.engineSign(out, outOffset, outLen);
+    }
+
+    @Override
+    protected final byte[] engineSign() throws SignatureException {
+        if (mCachedException != null) {
+            throw new SignatureException(mCachedException);
+        }
+
+        byte[] signature;
+        try {
+            ensureKeystoreOperationInitialized();
+
+            byte[] additionalEntropy =
+                    KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                            appRandom, getAdditionalEntropyAmountForSign());
+            signature = mMessageStreamer.doFinal(
+                    EmptyArray.BYTE, 0, 0,
+                    null); // no signature provided -- it'll be generated by this invocation
+        } catch (InvalidKeyException | KeyStoreException e) {
+            throw new SignatureException(e);
+        }
+
+        resetWhilePreservingInitState();
+        return signature;
+    }
+
+    @Override
+    protected final boolean engineVerify(byte[] signature) throws SignatureException {
+        if (mCachedException != null) {
+            throw new SignatureException(mCachedException);
+        }
+
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new SignatureException(e);
+        }
+
+        boolean verified;
+        try {
+            byte[] output = mMessageStreamer.doFinal(
+                    EmptyArray.BYTE, 0, 0,
+                    signature);
+            if (output.length != 0) {
+                throw new ProviderException(
+                        "Signature verification unexpected produced output: " + output.length
+                        + " bytes");
+            }
+            verified = true;
+        } catch (KeyStoreException e) {
+            switch (e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+                    verified = false;
+                    break;
+                default:
+                    throw new SignatureException(e);
+            }
+        }
+
+        resetWhilePreservingInitState();
+        return verified;
+    }
+
+    @Override
+    protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
+            throws SignatureException {
+        return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
+    }
+
+    @Deprecated
+    @Override
+    protected final Object engineGetParameter(String param) throws InvalidParameterException {
+        throw new InvalidParameterException();
+    }
+
+    @Deprecated
+    @Override
+    protected final void engineSetParameter(String param, Object value)
+            throws InvalidParameterException {
+        throw new InvalidParameterException();
+    }
+
+    /**
+     * Returns {@code true} if this signature is initialized for signing, {@code false} if this
+     * signature is initialized for verification.
+     */
+    protected final boolean isSigning() {
+        return mSigning;
+    }
+
+    // The methods below need to be implemented by subclasses.
+
+    /**
+     * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
+     * {@code finish} operation when generating a signature.
+     *
+     * <p>This value should match (or exceed) the amount of Shannon entropy of the produced
+     * signature assuming the key and the message are known. For example, for ECDSA signature this
+     * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this
+     * should be {@code 0}.
+     */
+    protected abstract int getAdditionalEntropyAmountForSign();
+
+    /**
+     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+     *
+     * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
+     *        parameters.
+     */
+    protected abstract void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters);
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
new file mode 100644
index 0000000..4c26864
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (C) 2012 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.annotation.NonNull;
+import android.hardware.biometrics.BiometricManager;
+import android.security.GateKeeper;
+import android.security.KeyStore2;
+import android.security.KeyStoreParameter;
+import android.security.KeyStoreSecurityLevel;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.security.keystore.KeymasterUtils;
+import android.security.keystore.SecureKeyImportUnavailableException;
+import android.security.keystore.WrappedKeyEntry;
+import android.system.keystore2.AuthenticatorSpec;
+import android.system.keystore2.Domain;
+import android.system.keystore2.IKeystoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyEntryResponse;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.ResponseCode;
+import android.system.keystore2.SecurityLevel;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.LoadStoreParameter;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.KeyStore.SecretKeyEntry;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+
+/**
+ * A java.security.KeyStore interface for the Android KeyStore. An instance of
+ * it can be created via the {@link java.security.KeyStore#getInstance(String)
+ * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
+ * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
+ * <p>
+ * This is built on top of Android's keystore daemon. The convention of alias
+ * use is:
+ * <p>
+ * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
+ * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
+ * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
+ * entry which will have the rest of the chain concatenated in BER format.
+ * <p>
+ * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
+ * with a single certificate.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreSpi extends KeyStoreSpi {
+    public static final String TAG = "AndroidKeyStoreSpi";
+    public static final String NAME = "AndroidKeyStore";
+
+    private KeyStore2 mKeyStore;
+    private int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
+
+    @Override
+    public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
+            UnrecoverableKeyException {
+        try {
+            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore,
+                                                                              alias,
+                                                                              mNamespace);
+        } catch (KeyPermanentlyInvalidatedException e) {
+            throw new UnrecoverableKeyException(e.getMessage());
+        } catch (UnrecoverableKeyException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof android.security.KeyStoreException) {
+                if (((android.security.KeyStoreException) cause).getErrorCode()
+                        == ResponseCode.KEY_NOT_FOUND) {
+                    return null;
+                }
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Make a key descriptor from the given alias and the mNamespace member.
+     * If mNamespace is -1 it sets the domain field to {@link Domain#APP} and {@link Domain#SELINUX}
+     * otherwise. The blob field is always set to null and the alias field to {@code alias}
+     * @param alias The alias of the new key descriptor.
+     * @return A new key descriptor.
+     */
+    private KeyDescriptor makeKeyDescriptor(@NonNull String alias) {
+        KeyDescriptor descriptor = new KeyDescriptor();
+        descriptor.domain = getTargetDomain();
+        descriptor.nspace = mNamespace; // ignored if Domain.App;
+        descriptor.alias = alias;
+        descriptor.blob = null;
+        return descriptor;
+    }
+
+    private @Domain int getTargetDomain() {
+        return mNamespace == KeyProperties.NAMESPACE_APPLICATION
+                ? Domain.APP
+                : Domain.SELINUX;
+    }
+    private KeyEntryResponse getKeyMetadata(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+
+        KeyDescriptor descriptor = makeKeyDescriptor(alias);
+
+        try {
+            return mKeyStore.getKeyEntry(descriptor);
+        } catch (android.security.KeyStoreException e) {
+            if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
+                Log.w(TAG, "Could not get key metadata from Keystore.", e);
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public Certificate[] engineGetCertificateChain(String alias) {
+        KeyEntryResponse response = getKeyMetadata(alias);
+
+        if (response == null || response.metadata.certificate == null) {
+            return null;
+        }
+
+        final X509Certificate leaf = (X509Certificate) toCertificate(response.metadata.certificate);
+        if (leaf == null) {
+            return null;
+        }
+
+        final Certificate[] caList;
+
+        final byte[] caBytes = response.metadata.certificateChain;
+
+        if (caBytes != null) {
+            final Collection<X509Certificate> caChain = toCertificates(caBytes);
+
+            caList = new Certificate[caChain.size() + 1];
+
+            final Iterator<X509Certificate> it = caChain.iterator();
+            int i = 1;
+            while (it.hasNext()) {
+                caList[i++] = it.next();
+            }
+        } else {
+            caList = new Certificate[1];
+        }
+
+        caList[0] = leaf;
+
+        return caList;
+    }
+
+    @Override
+    public Certificate engineGetCertificate(String alias) {
+        KeyEntryResponse response = getKeyMetadata(alias);
+
+        if (response == null) {
+            return null;
+        }
+
+        byte[] encodedCert = response.metadata.certificate;
+        if (encodedCert != null) {
+            return toCertificate(encodedCert);
+        }
+
+        encodedCert = response.metadata.certificateChain;
+        if (encodedCert != null) {
+            return toCertificate(encodedCert);
+        }
+
+        // This entry/alias does not contain a certificate.
+        return null;
+    }
+
+    private static X509Certificate toCertificate(byte[] bytes) {
+        try {
+            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+            return (X509Certificate) certFactory.generateCertificate(
+                    new ByteArrayInputStream(bytes));
+        } catch (CertificateException e) {
+            Log.w(NAME, "Couldn't parse certificate in keystore", e);
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Collection<X509Certificate> toCertificates(byte[] bytes) {
+        try {
+            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+            return (Collection<X509Certificate>) certFactory.generateCertificates(
+                            new ByteArrayInputStream(bytes));
+        } catch (CertificateException e) {
+            Log.w(NAME, "Couldn't parse certificates in keystore", e);
+            return new ArrayList<X509Certificate>();
+        }
+    }
+
+    @Override
+    public Date engineGetCreationDate(String alias) {
+        KeyEntryResponse response = getKeyMetadata(alias);
+
+        if (response == null) {
+            return null;
+        }
+
+
+        // TODO add modification time to key metadata.
+        return null;
+        // if (response.metadata.modificationTime == -1) {
+        //     return null;
+        // }
+        // return new Date(response.metadata.modificationTime);
+    }
+
+    @Override
+    public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
+            throws KeyStoreException {
+        if ((password != null) && (password.length > 0)) {
+            throw new KeyStoreException("entries cannot be protected with passwords");
+        }
+
+        if (key instanceof PrivateKey) {
+            setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
+        } else if (key instanceof SecretKey) {
+            setSecretKeyEntry(alias, (SecretKey) key, null);
+        } else {
+            throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
+        }
+    }
+
+    private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
+            throws KeyStoreException {
+        String keyAlgorithm = key.getAlgorithm();
+        KeyProtection.Builder specBuilder;
+        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
+            specBuilder =
+                    new KeyProtection.Builder(
+                            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
+            // Authorized to be used with any digest (including no digest).
+            // MD5 was never offered for Android Keystore for ECDSA.
+            specBuilder.setDigests(
+                    KeyProperties.DIGEST_NONE,
+                    KeyProperties.DIGEST_SHA1,
+                    KeyProperties.DIGEST_SHA224,
+                    KeyProperties.DIGEST_SHA256,
+                    KeyProperties.DIGEST_SHA384,
+                    KeyProperties.DIGEST_SHA512);
+        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
+            specBuilder =
+                    new KeyProtection.Builder(
+                            KeyProperties.PURPOSE_ENCRYPT
+                            | KeyProperties.PURPOSE_DECRYPT
+                            | KeyProperties.PURPOSE_SIGN
+                            | KeyProperties.PURPOSE_VERIFY);
+            // Authorized to be used with any digest (including no digest).
+            specBuilder.setDigests(
+                    KeyProperties.DIGEST_NONE,
+                    KeyProperties.DIGEST_MD5,
+                    KeyProperties.DIGEST_SHA1,
+                    KeyProperties.DIGEST_SHA224,
+                    KeyProperties.DIGEST_SHA256,
+                    KeyProperties.DIGEST_SHA384,
+                    KeyProperties.DIGEST_SHA512);
+            // Authorized to be used with any encryption and signature padding
+            // schemes (including no padding).
+            specBuilder.setEncryptionPaddings(
+                    KeyProperties.ENCRYPTION_PADDING_NONE,
+                    KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+                    KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
+            specBuilder.setSignaturePaddings(
+                    KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+                    KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+            // Disable randomized encryption requirement to support encryption
+            // padding NONE above.
+            specBuilder.setRandomizedEncryptionRequired(false);
+        } else {
+            throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
+        }
+        specBuilder.setUserAuthenticationRequired(false);
+
+        return specBuilder.build();
+    }
+
+    private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
+            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
+        @SecurityLevel int securitylevel = SecurityLevel.TRUSTED_ENVIRONMENT;
+        int flags = 0;
+        KeyProtection spec;
+        if (param == null) {
+            spec = getLegacyKeyProtectionParameter(key);
+        } else if (param instanceof KeyStoreParameter) {
+            spec = getLegacyKeyProtectionParameter(key);
+            KeyStoreParameter legacySpec = (KeyStoreParameter) param;
+        } else if (param instanceof KeyProtection) {
+            spec = (KeyProtection) param;
+            if (spec.isCriticalToDeviceEncryption()) {
+                // This key is should not be bound to the LSKF even if it is auth bound.
+                // This indicates that this key is used in the derivation for of the
+                // master key, that is used for the LSKF binding of other auth bound
+                // keys. This breaks up a circular dependency while retaining logical
+                // authentication binding of the key.
+                flags |= IKeystoreSecurityLevel
+                        .KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
+            }
+
+            if (spec.isStrongBoxBacked()) {
+                securitylevel = SecurityLevel.STRONGBOX;
+            }
+        } else {
+            throw new KeyStoreException(
+                    "Unsupported protection parameter class:" + param.getClass().getName()
+                    + ". Supported: " + KeyProtection.class.getName() + ", "
+                    + KeyStoreParameter.class.getName());
+        }
+
+        // Make sure the chain exists since this is a PrivateKey
+        if ((chain == null) || (chain.length == 0)) {
+            throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
+        }
+
+        // Do chain type checking.
+        X509Certificate[] x509chain = new X509Certificate[chain.length];
+        for (int i = 0; i < chain.length; i++) {
+            if (!"X.509".equals(chain[i].getType())) {
+                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
+                        + i);
+            }
+
+            if (!(chain[i] instanceof X509Certificate)) {
+                throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
+                        + i);
+            }
+
+            x509chain[i] = (X509Certificate) chain[i];
+        }
+
+        final byte[] userCertBytes;
+        try {
+            userCertBytes = x509chain[0].getEncoded();
+        } catch (CertificateEncodingException e) {
+            throw new KeyStoreException("Failed to encode certificate #0", e);
+        }
+
+        /*
+         * If we have a chain, store it in the CA certificate slot for this
+         * alias as concatenated DER-encoded certificates. These can be
+         * deserialized by {@link CertificateFactory#generateCertificates}.
+         */
+        final byte[] chainBytes;
+        if (chain.length > 1) {
+            /*
+             * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
+             * so we only need the certificates starting at index 1.
+             */
+            final byte[][] certsBytes = new byte[x509chain.length - 1][];
+            int totalCertLength = 0;
+            for (int i = 0; i < certsBytes.length; i++) {
+                try {
+                    certsBytes[i] = x509chain[i + 1].getEncoded();
+                    totalCertLength += certsBytes[i].length;
+                } catch (CertificateEncodingException e) {
+                    throw new KeyStoreException("Failed to encode certificate #" + i, e);
+                }
+            }
+
+            /*
+             * Serialize this into one byte array so we can later call
+             * CertificateFactory#generateCertificates to recover them.
+             */
+            chainBytes = new byte[totalCertLength];
+            int outputOffset = 0;
+            for (int i = 0; i < certsBytes.length; i++) {
+                final int certLength = certsBytes[i].length;
+                System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
+                outputOffset += certLength;
+                certsBytes[i] = null;
+            }
+        } else {
+            chainBytes = null;
+        }
+
+        @Domain int targetDomain = getTargetDomain();
+
+        // If the given key is an AndroidKeyStorePrivateKey, we attempt to update
+        // its subcomponents with the given certificate and certificate chain.
+        if (key instanceof AndroidKeyStorePrivateKey) {
+            AndroidKeyStoreKey ksKey = (AndroidKeyStoreKey) key;
+            KeyDescriptor descriptor = ksKey.getUserKeyDescriptor();
+
+            // This throws if the request cannot replace the entry.
+            assertCanReplace(alias, targetDomain, mNamespace, descriptor);
+
+            try {
+                mKeyStore.updateSubcomponents(
+                        ((AndroidKeyStorePrivateKey) key).getKeyIdDescriptor(),
+                        userCertBytes, chainBytes);
+            } catch (android.security.KeyStoreException e) {
+                throw new KeyStoreException("Failed to store certificate and certificate chain", e);
+            }
+            return;
+        }
+
+        // Make sure the PrivateKey format is the one we support.
+        final String keyFormat = key.getFormat();
+        if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
+            throw new KeyStoreException(
+                    "Unsupported private key export format: " + keyFormat
+                    + ". Only private keys which export their key material in PKCS#8 format are"
+                    + " supported.");
+        }
+
+        // Make sure we can actually encode the key.
+        byte[] pkcs8EncodedPrivateKeyBytes = key.getEncoded();
+        if (pkcs8EncodedPrivateKeyBytes == null) {
+            throw new KeyStoreException("Private key did not export any key material");
+        }
+
+        final List<KeyParameter> importArgs = new ArrayList<>();
+
+        try {
+            importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_ALGORITHM,
+                    KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+                            key.getAlgorithm()))
+            );
+            KeyStore2ParameterUtils.forEachSetFlag(spec.getPurposes(), (purpose) -> {
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_PURPOSE,
+                        KeyProperties.Purpose.toKeymaster(purpose)
+                ));
+            });
+            if (spec.isDigestsSpecified()) {
+                for (String digest : spec.getDigests()) {
+                    importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                            KeymasterDefs.KM_TAG_DIGEST,
+                            KeyProperties.Digest.toKeymaster(digest)
+                    ));
+                }
+            }
+            for (String blockMode : spec.getBlockModes()) {
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_BLOCK_MODE,
+                        KeyProperties.BlockMode.toKeymaster(blockMode)
+                ));
+            }
+            int[] keymasterEncryptionPaddings =
+                    KeyProperties.EncryptionPadding.allToKeymaster(
+                            spec.getEncryptionPaddings());
+            if (((spec.getPurposes() & KeyProperties.PURPOSE_DECRYPT) != 0)
+                    && (spec.isRandomizedEncryptionRequired())) {
+                for (int keymasterPadding : keymasterEncryptionPaddings) {
+                    if (!KeymasterUtils
+                            .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+                                    keymasterPadding)) {
+                        throw new KeyStoreException(
+                                "Randomized encryption (IND-CPA) required but is violated by"
+                                        + " encryption padding mode: "
+                                        + KeyProperties.EncryptionPadding.fromKeymaster(
+                                        keymasterPadding)
+                                        + ". See KeyProtection documentation.");
+                    }
+                }
+            }
+            for (int padding : keymasterEncryptionPaddings) {
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_PADDING,
+                        padding
+                ));
+            }
+            for (String padding : spec.getSignaturePaddings()) {
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_PADDING,
+                        KeyProperties.SignaturePadding.toKeymaster(padding)
+                ));
+            }
+            KeyStore2ParameterUtils.addUserAuthArgs(importArgs, spec);
+            if (spec.getKeyValidityStart() != null) {
+                importArgs.add(KeyStore2ParameterUtils.makeDate(
+                        KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()
+                ));
+            }
+            if (spec.getKeyValidityForOriginationEnd() != null) {
+                importArgs.add(KeyStore2ParameterUtils.makeDate(
+                        KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                        spec.getKeyValidityForOriginationEnd()
+                ));
+            }
+            if (spec.getKeyValidityForConsumptionEnd() != null) {
+                importArgs.add(KeyStore2ParameterUtils.makeDate(
+                        KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                        spec.getKeyValidityForConsumptionEnd()
+                ));
+            }
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw new KeyStoreException(e);
+        }
+
+        try {
+            KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
+                    securitylevel);
+
+            KeyDescriptor descriptor = makeKeyDescriptor(alias);
+
+            KeyMetadata metadata = securityLevelInterface.importKey(descriptor, null,
+                    importArgs, flags, pkcs8EncodedPrivateKeyBytes);
+
+            try {
+                mKeyStore.updateSubcomponents(metadata.key, userCertBytes, chainBytes);
+            } catch (android.security.KeyStoreException e) {
+                mKeyStore.deleteKey(metadata.key);
+                throw new KeyStoreException("Failed to store certificate and certificate chain", e);
+            }
+
+        } catch (android.security.KeyStoreException e) {
+            throw new KeyStoreException("Failed to store private key", e);
+        }
+    }
+
+    private static void assertCanReplace(String alias, @Domain int targetDomain,
+            int targetNamespace, KeyDescriptor descriptor)
+            throws KeyStoreException {
+        // If
+        //  * the alias does not match, or
+        //  * the domain does not match, or
+        //  * the domain is Domain.SELINUX and the namespaces don not match,
+        // then the designated key location is not equivalent to the location of the
+        // given key parameter and cannot be updated.
+        //
+        // Note: mNamespace == KeyProperties.NAMESPACE_APPLICATION implies that the target domain
+        // is Domain.APP and Domain.SELINUX is the target domain otherwise.
+        if (alias != descriptor.alias
+                || descriptor.domain != targetDomain
+                || (descriptor.domain == Domain.SELINUX && descriptor.nspace != targetNamespace)) {
+            throw new KeyStoreException("Can only replace keys with same alias: " + alias
+                    + " != " + descriptor.alias + " in the same target domain: " + targetDomain
+                    + " != " + descriptor.domain
+                    + (targetDomain == Domain.SELINUX ? " in the same target namespace: "
+                    + targetNamespace + " != " + descriptor.nspace : "")
+            );
+        }
+    }
+
+    private void setSecretKeyEntry(String alias, SecretKey key,
+            java.security.KeyStore.ProtectionParameter param)
+            throws KeyStoreException {
+        if ((param != null) && (!(param instanceof KeyProtection))) {
+            throw new KeyStoreException(
+                    "Unsupported protection parameter class: " + param.getClass().getName()
+                    + ". Supported: " + KeyProtection.class.getName());
+        }
+        KeyProtection params = (KeyProtection) param;
+
+        @SecurityLevel int securityLevel = params.isStrongBoxBacked() ? SecurityLevel.STRONGBOX :
+                SecurityLevel.TRUSTED_ENVIRONMENT;
+        @Domain int targetDomain = (getTargetDomain());
+
+        if (key instanceof AndroidKeyStoreSecretKey) {
+            String keyAliasInKeystore =
+                    ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor().alias;
+
+            KeyDescriptor descriptor = ((AndroidKeyStoreSecretKey) key).getUserKeyDescriptor();
+
+            // This throws if the request cannot replace the existing key.
+            assertCanReplace(alias, targetDomain, mNamespace, descriptor);
+
+            // This is the entry where this key is already stored. No need to do anything.
+            if (params != null) {
+                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
+                        + " parameters not supported");
+            }
+            return;
+        }
+
+        if (params == null) {
+            throw new KeyStoreException(
+                    "Protection parameters must be specified when importing a symmetric key");
+        }
+
+        // Not a KeyStore-backed secret key -- import its key material into keystore.
+        String keyExportFormat = key.getFormat();
+        if (keyExportFormat == null) {
+            throw new KeyStoreException(
+                    "Only secret keys that export their key material are supported");
+        } else if (!"RAW".equals(keyExportFormat)) {
+            throw new KeyStoreException(
+                    "Unsupported secret key material export format: " + keyExportFormat);
+        }
+        byte[] keyMaterial = key.getEncoded();
+        if (keyMaterial == null) {
+            throw new KeyStoreException("Key did not export its key material despite supporting"
+                    + " RAW format export");
+        }
+
+        final List<KeyParameter> importArgs = new ArrayList<>();
+
+        try {
+            int keymasterAlgorithm =
+                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(
+                            key.getAlgorithm());
+
+            importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_ALGORITHM,
+                    keymasterAlgorithm
+            ));
+
+            if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
+                // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
+                // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
+                // digest, we don't let import parameters override the digest implied by the key.
+                // If the parameters specify digests at all, they must specify only one digest, the
+                // only implied by key algorithm.
+                int keymasterImpliedDigest =
+                        KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
+                if (keymasterImpliedDigest == -1) {
+                    throw new ProviderException(
+                            "HMAC key algorithm digest unknown for key algorithm "
+                                    + key.getAlgorithm());
+                }
+
+                if (params.isDigestsSpecified()) {
+                    // Digest(s) explicitly specified in params -- check that the list consists of
+                    // exactly one digest, the one implied by key algorithm.
+                    int[] keymasterDigestsFromParams =
+                            KeyProperties.Digest.allToKeymaster(params.getDigests());
+                    if ((keymasterDigestsFromParams.length != 1)
+                            || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
+                        throw new KeyStoreException(
+                                "Unsupported digests specification: "
+                                        + Arrays.asList(params.getDigests()) + ". Only "
+                                        + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
+                                        + " supported for HMAC key algorithm "
+                                        + key.getAlgorithm());
+                    }
+                }
+                int outputBits = KeymasterUtils.getDigestOutputSizeBits(keymasterImpliedDigest);
+                if (outputBits == -1) {
+                    throw new ProviderException(
+                            "HMAC key authorized for unsupported digest: "
+                                    + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest));
+                }
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_DIGEST, keymasterImpliedDigest
+                ));
+                importArgs.add(KeyStore2ParameterUtils.makeInt(
+                        KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, outputBits
+                ));
+            } else {
+                if (params.isDigestsSpecified()) {
+                    for (String digest : params.getDigests()) {
+                        importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                                KeymasterDefs.KM_TAG_DIGEST,
+                                KeyProperties.Digest.toKeymaster(digest)
+                        ));
+                    }
+                }
+            }
+
+            KeyStore2ParameterUtils.forEachSetFlag(params.getPurposes(), (purpose) -> {
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_PURPOSE,
+                        KeyProperties.Purpose.toKeymaster(purpose)
+                ));
+            });
+
+            boolean indCpa = false;
+            if ((params.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) {
+                if (((KeyProtection) param).isRandomizedEncryptionRequired()) {
+                    indCpa = true;
+                } else {
+                    importArgs.add(KeyStore2ParameterUtils.makeBool(
+                            KeymasterDefs.KM_TAG_CALLER_NONCE
+                    ));
+                }
+            }
+
+            for (String blockMode : params.getBlockModes()) {
+                int keymasterBlockMode = KeyProperties.BlockMode.toKeymaster(blockMode);
+                if (indCpa
+                        && !KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+                                keymasterBlockMode)) {
+                    throw new KeyStoreException(
+                            "Randomized encryption (IND-CPA) required but may be violated by"
+                                    + " block mode: " + blockMode
+                                    + ". See KeyProtection documentation.");
+
+                }
+                if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES
+                        && keymasterBlockMode == KeymasterDefs.KM_MODE_GCM) {
+                    importArgs.add(KeyStore2ParameterUtils.makeInt(
+                            KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
+                            AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
+                                    .MIN_SUPPORTED_TAG_LENGTH_BITS
+                    ));
+                }
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_BLOCK_MODE,
+                        keymasterBlockMode
+                ));
+            }
+
+            if (params.getSignaturePaddings().length > 0) {
+                throw new KeyStoreException("Signature paddings not supported for symmetric keys");
+            }
+
+            for (String padding : params.getEncryptionPaddings()) {
+                importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_PADDING,
+                        KeyProperties.EncryptionPadding.toKeymaster(padding)
+                ));
+            }
+
+            KeyStore2ParameterUtils.addUserAuthArgs(importArgs, params);
+
+            if (params.getKeyValidityStart() != null) {
+                importArgs.add(KeyStore2ParameterUtils.makeDate(
+                        KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart()
+                ));
+            }
+            if (params.getKeyValidityForOriginationEnd() != null) {
+                importArgs.add(KeyStore2ParameterUtils.makeDate(
+                        KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                        params.getKeyValidityForOriginationEnd()
+                ));
+            }
+            if (params.getKeyValidityForConsumptionEnd() != null) {
+                importArgs.add(KeyStore2ParameterUtils.makeDate(
+                        KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                        params.getKeyValidityForConsumptionEnd()
+                ));
+            }
+        } catch (IllegalArgumentException | IllegalStateException e) {
+            throw new KeyStoreException(e);
+        }
+
+        int flags = 0;
+        if (params.isCriticalToDeviceEncryption()) {
+            flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING;
+        }
+
+        try {
+            KeyStoreSecurityLevel securityLevelInterface = mKeyStore.getSecurityLevel(
+                    securityLevel);
+
+            KeyDescriptor descriptor = makeKeyDescriptor(alias);
+
+            securityLevelInterface.importKey(descriptor, null /* TODO attestationKey */,
+                    importArgs, flags, keyMaterial);
+        } catch (android.security.KeyStoreException e) {
+            throw new KeyStoreException("Failed to import secret key.", e);
+        }
+    }
+
+    private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry,
+            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
+        if (param != null) {
+            throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
+        }
+
+        byte[] maskingKey = new byte[32];
+
+        String[] parts = entry.getTransformation().split("/");
+
+        List<KeyParameter> args = new ArrayList<>();
+
+        String algorithm = parts[0];
+        if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
+            args.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_ALGORITHM,
+                    KeymasterDefs.KM_ALGORITHM_RSA
+            ));
+        } else {
+            throw new KeyStoreException("Algorithm \"" + algorithm + "\" not supported for "
+                    + "wrapping. Only RSA wrapping keys are supported.");
+        }
+
+        if (parts.length > 1) {
+            String mode = parts[1];
+            args.add(KeyStore2ParameterUtils.makeEnum(
+                    KeymasterDefs.KM_TAG_BLOCK_MODE,
+                    KeyProperties.BlockMode.toKeymaster(mode)
+            ));
+        }
+
+        if (parts.length > 2) {
+            @KeyProperties.EncryptionPaddingEnum int padding =
+                    KeyProperties.EncryptionPadding.toKeymaster(parts[2]);
+            if (padding != KeymasterDefs.KM_PAD_NONE) {
+                args.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_PADDING,
+                        padding
+                ));
+            }
+        }
+
+        KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec();
+        if (spec.isDigestsSpecified()) {
+            @KeyProperties.DigestEnum int digest =
+                    KeyProperties.Digest.toKeymaster(spec.getDigests()[0]);
+            if (digest != KeymasterDefs.KM_DIGEST_NONE) {
+                args.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_DIGEST,
+                        digest
+                ));
+            }
+        }
+
+        KeyDescriptor wrappingkey = makeKeyDescriptor(entry.getWrappingKeyAlias());
+
+        KeyEntryResponse response = null;
+        try {
+            response = mKeyStore.getKeyEntry(wrappingkey);
+        } catch (android.security.KeyStoreException e) {
+            throw new KeyStoreException("Failed to load wrapping key.", e);
+        }
+
+        KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
+
+        KeyStoreSecurityLevel securityLevel = new KeyStoreSecurityLevel(response.iSecurityLevel);
+
+        final BiometricManager bm = android.app.AppGlobals.getInitialApplication()
+                .getSystemService(BiometricManager.class);
+
+        long[] biometricSids = bm.getAuthenticatorIds();
+
+        List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
+
+        AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
+        // TODO Replace with HardwareAuthenticatorType.PASSWORD when KeyMint AIDL spec has landed.
+        authenticatorSpec.authenticatorType = 1; // HardwareAuthenticatorType.PASSWORD
+        authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
+        authenticatorSpecs.add(authenticatorSpec);
+
+        for (long sid : biometricSids) {
+            AuthenticatorSpec authSpec = new AuthenticatorSpec();
+            // TODO Replace with HardwareAuthenticatorType.FINGERPRINT when KeyMint AIDL spec has
+            //      landed.
+            authSpec.authenticatorType = 2; // HardwareAuthenticatorType.FINGERPRINT
+            authSpec.authenticatorId = sid;
+            authenticatorSpecs.add(authSpec);
+        }
+
+        try {
+            securityLevel.importWrappedKey(
+                    wrappedKey, wrappingkey,
+                    entry.getWrappedKeyBytes(),
+                    null /* masking key is set to 32 bytes if null is given here */,
+                    args,
+                    authenticatorSpecs.toArray(new AuthenticatorSpec[0]));
+        } catch (android.security.KeyStoreException e) {
+            switch (e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_UNIMPLEMENTED: {
+                    throw new SecureKeyImportUnavailableException("Could not import wrapped key");
+                }
+                default:
+                    throw new KeyStoreException("Failed to import wrapped key. Keystore error "
+                            + "code: " + e.getErrorCode(), e);
+            }
+        }
+    }
+
+    @Override
+    public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
+            throws KeyStoreException {
+        throw new KeyStoreException("Operation not supported because key encoding is unknown");
+    }
+
+    /**
+     * This function sets a trusted certificate entry. It fails if the given
+     * alias is already taken by an actual key entry. However, if the entry is a
+     * trusted certificate it will get silently replaced.
+     * @param alias the alias name
+     * @param cert the certificate
+     *
+     * @throws KeyStoreException if the alias is already taken by a secret or private
+     *         key entry.
+     * @throws KeyStoreException with a nested {@link CertificateEncodingException}
+     *         if the {@code cert.getEncoded()} throws.
+     * @throws KeyStoreException with a nested {@link android.security.KeyStoreException} if
+     *         something went wrong while inserting the certificate into keystore.
+     * @throws NullPointerException if cert or alias is null.
+     *
+     * @hide
+     */
+    @Override
+    public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
+        if (isKeyEntry(alias)) {
+            throw new KeyStoreException("Entry exists and is not a trusted certificate");
+        }
+
+        // We can't set something to null.
+        if (cert == null) {
+            throw new NullPointerException("cert == null");
+        }
+
+        final byte[] encoded;
+        try {
+            encoded = cert.getEncoded();
+        } catch (CertificateEncodingException e) {
+            throw new KeyStoreException(e);
+        }
+
+        try {
+            mKeyStore.updateSubcomponents(makeKeyDescriptor(alias),
+                    null /* publicCert - unused when used as pure certificate store. */,
+                    encoded);
+        } catch (android.security.KeyStoreException e) {
+            throw new KeyStoreException("Couldn't insert certificate.", e);
+        }
+    }
+
+    @Override
+    public void engineDeleteEntry(String alias) throws KeyStoreException {
+        KeyDescriptor descriptor = makeKeyDescriptor(alias);
+        try {
+            mKeyStore.deleteKey(descriptor);
+        } catch (android.security.KeyStoreException e) {
+            if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) {
+                throw new KeyStoreException("Failed to delete entry: " + alias, e);
+            }
+        }
+    }
+
+    private Set<String> getUniqueAliases() {
+
+        try {
+            final KeyDescriptor[] keys = mKeyStore.list(
+                    getTargetDomain(),
+                    mNamespace
+            );
+            final Set<String> aliases = new HashSet<>(keys.length);
+            for (KeyDescriptor d : keys) {
+                aliases.add(d.alias);
+            }
+            return aliases;
+        } catch (android.security.KeyStoreException e) {
+            Log.e(TAG, "Failed to list keystore entries.", e);
+            return null;
+        }
+    }
+
+    @Override
+    public Enumeration<String> engineAliases() {
+        return Collections.enumeration(getUniqueAliases());
+    }
+
+    @Override
+    public boolean engineContainsAlias(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+
+        return getKeyMetadata(alias) != null;
+    }
+
+    @Override
+    public int engineSize() {
+        return getUniqueAliases().size();
+    }
+
+    @Override
+    public boolean engineIsKeyEntry(String alias) {
+        return isKeyEntry(alias);
+    }
+
+    private boolean isKeyEntry(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+
+        KeyEntryResponse response = getKeyMetadata(alias);
+        // If response is null, there is no such entry.
+        // If response.iSecurityLevel is null, there is no private or secret key material stored.
+        return response != null && response.iSecurityLevel != null;
+    }
+
+
+    @Override
+    public boolean engineIsCertificateEntry(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        KeyEntryResponse response = getKeyMetadata(alias);
+        // If response == null there is no such entry.
+        // If there is no certificateChain, then this is not a certificate entry.
+        // If there is a private key entry, this is the certificate chain for that
+        // key entry and not a CA certificate entry.
+        return response != null
+                && response.metadata.certificateChain != null
+                && response.iSecurityLevel == null;
+    }
+
+    @Override
+    public String engineGetCertificateAlias(Certificate cert) {
+        if (cert == null) {
+            return null;
+        }
+        if (!"X.509".equalsIgnoreCase(cert.getType())) {
+            Log.e(TAG, "In engineGetCertificateAlias: only X.509 certificates are supported.");
+            return null;
+        }
+        byte[] targetCertBytes;
+        try {
+            targetCertBytes = cert.getEncoded();
+        } catch (CertificateEncodingException e) {
+            Log.e(TAG, "While trying to get the alias for a certificate.", e);
+            return null;
+        }
+        if (targetCertBytes == null) {
+            return null;
+        }
+
+        KeyDescriptor[] keyDescriptors = null;
+        try {
+            keyDescriptors = mKeyStore.list(
+                    getTargetDomain(),
+                    mNamespace
+            );
+        } catch (android.security.KeyStoreException e) {
+            Log.w(TAG, "Failed to get list of keystore entries.", e);
+        }
+
+        String caAlias = null;
+        for (KeyDescriptor d : keyDescriptors) {
+            KeyEntryResponse response = getKeyMetadata(d.alias);
+            if (response == null) {
+                continue;
+            }
+            /*
+             * The KeyStoreSpi documentation says to only compare the first certificate in the
+             * chain which is equivalent to the {@code response.metadata.certificate} field.
+             * So we look for a hit in this field first. For pure CA certificate entries,
+             * we check the {@code response.metadata.certificateChain} field. But we only
+             * return a CA alias if there was no hit in the certificate field of any other
+             * entry.
+             */
+            if (response.metadata.certificate != null) {
+                if (Arrays.equals(response.metadata.certificate, targetCertBytes)) {
+                    return d.alias;
+                }
+            } else if (response.metadata.certificateChain != null && caAlias == null) {
+                if (Arrays.equals(response.metadata.certificateChain, targetCertBytes)) {
+                    caAlias =  d.alias;
+                }
+            }
+        }
+        return caAlias;
+    }
+
+    @Override
+    public void engineStore(OutputStream stream, char[] password) throws IOException,
+            NoSuchAlgorithmException, CertificateException {
+        throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
+    }
+
+    @Override
+    public void engineLoad(InputStream stream, char[] password) throws IOException,
+            NoSuchAlgorithmException, CertificateException {
+        if (stream != null) {
+            throw new IllegalArgumentException("InputStream not supported");
+        }
+
+        if (password != null) {
+            throw new IllegalArgumentException("password not supported");
+        }
+
+        // Unfortunate name collision.
+        mKeyStore = KeyStore2.getInstance();
+        mNamespace = KeyProperties.NAMESPACE_APPLICATION;
+    }
+
+    @Override
+    public void engineLoad(LoadStoreParameter param) throws IOException,
+            NoSuchAlgorithmException, CertificateException {
+        int namespace = KeyProperties.NAMESPACE_APPLICATION;
+        if (param != null) {
+            if (param instanceof AndroidKeyStoreLoadStoreParameter) {
+                namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace();
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported param type: " + param.getClass());
+            }
+        }
+        mKeyStore = KeyStore2.getInstance();
+        mNamespace = namespace;
+    }
+
+    @Override
+    public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
+            throws KeyStoreException {
+        if (entry == null) {
+            throw new KeyStoreException("entry == null");
+        }
+
+        if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
+            java.security.KeyStore.TrustedCertificateEntry trE =
+                    (java.security.KeyStore.TrustedCertificateEntry) entry;
+            // engineSetCertificateEntry does not overwrite if the existing entry
+            // is a key entry, but the semantic of engineSetEntry is such that it
+            // overwrites any existing entry. Thus we delete any possible existing
+            // entry by this alias.
+            engineDeleteEntry(alias);
+            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
+            return;
+        }
+
+        if (entry instanceof PrivateKeyEntry) {
+            PrivateKeyEntry prE = (PrivateKeyEntry) entry;
+            setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
+        } else if (entry instanceof SecretKeyEntry) {
+            SecretKeyEntry secE = (SecretKeyEntry) entry;
+            setSecretKeyEntry(alias, secE.getSecretKey(), param);
+        } else if (entry instanceof WrappedKeyEntry) {
+            WrappedKeyEntry wke = (WrappedKeyEntry) entry;
+            setWrappedKeyEntry(alias, wke, param);
+        } else {
+            throw new KeyStoreException(
+                    "Entry must be a PrivateKeyEntry, SecretKeyEntry, WrappedKeyEntry "
+                            + "or TrustedCertificateEntry; was " + entry);
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
new file mode 100644
index 0000000..3d5a8f6
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+import android.security.keystore.KeyProperties;
+import android.system.keystore2.KeyParameter;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Base class for Android Keystore unauthenticated AES {@link CipherSpi} implementations.
+ *
+ * @hide
+ */
+class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+    abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
+        protected ECB(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
+        }
+
+        public static class NoPadding extends ECB {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+
+        public static class PKCS7Padding extends ECB {
+            public PKCS7Padding() {
+                super(KeymasterDefs.KM_PAD_PKCS7);
+            }
+        }
+    }
+
+    abstract static class CBC extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
+        protected CBC(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
+        }
+
+        public static class NoPadding extends CBC {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+
+        public static class PKCS7Padding extends CBC {
+            public PKCS7Padding() {
+                super(KeymasterDefs.KM_PAD_PKCS7);
+            }
+        }
+    }
+
+    abstract static class CTR extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
+        protected CTR(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true);
+        }
+
+        public static class NoPadding extends CTR {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+    }
+
+    private static final int BLOCK_SIZE_BYTES = 16;
+
+    private final int mKeymasterBlockMode;
+    private final int mKeymasterPadding;
+    /** Whether this transformation requires an IV. */
+    private final boolean mIvRequired;
+
+    private byte[] mIv;
+
+    /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
+    private boolean mIvHasBeenUsed;
+
+    AndroidKeyStoreUnauthenticatedAESCipherSpi(
+            int keymasterBlockMode,
+            int keymasterPadding,
+            boolean ivRequired) {
+        mKeymasterBlockMode = keymasterBlockMode;
+        mKeymasterPadding = keymasterPadding;
+        mIvRequired = ivRequired;
+    }
+
+    @Override
+    protected final void resetAll() {
+        mIv = null;
+        mIvHasBeenUsed = false;
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected final void initKey(int opmode, Key key) throws InvalidKeyException {
+        if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException(
+                    "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
+                    KeyProperties.KEY_ALGORITHM_AES + " supported");
+        }
+        setKey((AndroidKeyStoreSecretKey) key);
+    }
+
+    @Override
+    protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {
+        if (!mIvRequired) {
+            return;
+        }
+
+        // IV is used
+        if (!isEncrypting()) {
+            throw new InvalidKeyException("IV required when decrypting"
+                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+        }
+    }
+
+    @Override
+    protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvRequired) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException(
+                        "IvParameterSpec must be provided when decrypting");
+            }
+            return;
+        }
+        if (!(params instanceof IvParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
+        }
+        mIv = ((IvParameterSpec) params).getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
+        }
+    }
+
+    @Override
+    protected final void initAlgorithmSpecificParameters(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvRequired) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+            return;
+        }
+
+        if (!"AES".equalsIgnoreCase(params.getAlgorithm())) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
+                    + ". Supported: AES");
+        }
+
+        IvParameterSpec ivSpec;
+        try {
+            ivSpec = params.getParameterSpec(IvParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ", but not found in parameters: " + params, e);
+            }
+            mIv = null;
+            return;
+        }
+        mIv = ivSpec.getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
+        }
+    }
+
+    @Override
+    protected final int getAdditionalEntropyAmountForBegin() {
+        if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
+            // IV will need to be generated
+            return BLOCK_SIZE_BYTES;
+        }
+
+        return 0;
+    }
+
+    @Override
+    protected final int getAdditionalEntropyAmountForFinish() {
+        return 0;
+    }
+
+    @Override
+    protected final void addAlgorithmSpecificParametersToBegin(
+            @NonNull List<KeyParameter> parameters) {
+        if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
+            // IV is being reused for encryption: this violates security best practices.
+            throw new IllegalStateException(
+                    "IV has already been used. Reusing IV in encryption mode violates security best"
+                    + " practices.");
+        }
+
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding
+        ));
+        if ((mIvRequired) && (mIv != null)) {
+            parameters.add(KeyStore2ParameterUtils.makeBytes(
+                    KeymasterDefs.KM_TAG_NONCE, mIv
+            ));
+        }
+    }
+
+    @Override
+    protected final void loadAlgorithmSpecificParametersFromBeginResult(
+            KeyParameter[] parameters) {
+        mIvHasBeenUsed = true;
+
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = null;
+        if (parameters != null) {
+            for (KeyParameter p : parameters) {
+                if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
+                    returnedIv = p.blob;
+                    break;
+                }
+            }
+        }
+
+        if (mIvRequired) {
+            if (mIv == null) {
+                mIv = returnedIv;
+            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+                throw new ProviderException("IV in use differs from provided IV");
+            }
+        } else {
+            if (returnedIv != null) {
+                throw new ProviderException(
+                        "IV in use despite IV not being used by this transformation");
+            }
+        }
+    }
+
+    @Override
+    protected final int engineGetBlockSize() {
+        return BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected final int engineGetOutputSize(int inputLen) {
+        return inputLen + 3 * BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected final byte[] engineGetIV() {
+        return ArrayUtils.cloneIfNotEmpty(mIv);
+    }
+
+    @Nullable
+    @Override
+    protected final AlgorithmParameters engineGetParameters() {
+        if (!mIvRequired) {
+            return null;
+        }
+        if ((mIv != null) && (mIv.length > 0)) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+                params.init(new IvParameterSpec(mIv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException(
+                        "Failed to obtain AES AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new ProviderException(
+                        "Failed to initialize AES AlgorithmParameters with an IV",
+                        e);
+            }
+        }
+        return null;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
new file mode 100644
index 0000000..ee67ed3
--- /dev/null
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -0,0 +1,313 @@
+/*
+ * 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.security.keystore2;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.BiometricManager;
+import android.security.GateKeeper;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserAuthArgs;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.SecurityLevel;
+
+import java.security.ProviderException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * @hide
+ */
+public abstract class KeyStore2ParameterUtils {
+
+    /**
+     * This function constructs a {@link KeyParameter} expressing a boolean value.
+     * @param tag Must be KeyMint tag with the associated type BOOL.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeBool(int tag) {
+        int type = KeymasterDefs.getTagType(tag);
+        if (type != KeymasterDefs.KM_BOOL) {
+            throw new IllegalArgumentException("Not a boolean tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.boolValue = true;
+        return p;
+    }
+
+    /**
+     * This function constructs a {@link KeyParameter} expressing an enum value.
+     * @param tag Must be KeyMint tag with the associated type ENUM or ENUM_REP.
+     * @param v A 32bit integer.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeEnum(int tag, int v) {
+        int type = KeymasterDefs.getTagType(tag);
+        if (type != KeymasterDefs.KM_ENUM && type != KeymasterDefs.KM_ENUM_REP) {
+            throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.integer = v;
+        return p;
+    }
+
+    /**
+     * This function constructs a {@link KeyParameter} expressing an integer value.
+     * @param tag Must be KeyMint tag with the associated type UINT or UINT_REP.
+     * @param v A 32bit integer.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeInt(int tag, int v) {
+        int type = KeymasterDefs.getTagType(tag);
+        if (type != KeymasterDefs.KM_UINT && type != KeymasterDefs.KM_UINT_REP) {
+            throw new IllegalArgumentException("Not an int or repeatable int tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.integer = v;
+        return p;
+    }
+
+    /**
+     * This function constructs a {@link KeyParameter} expressing a long integer value.
+     * @param tag Must be KeyMint tag with the associated type ULONG or ULONG_REP.
+     * @param v A 64bit integer.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeLong(int tag, long v) {
+        int type = KeymasterDefs.getTagType(tag);
+        if (type != KeymasterDefs.KM_ULONG && type != KeymasterDefs.KM_ULONG_REP) {
+            throw new IllegalArgumentException("Not a long or repeatable long tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.longInteger = v;
+        return p;
+    }
+
+    /**
+     * This function constructs a {@link KeyParameter} expressing a blob.
+     * @param tag Must be KeyMint tag with the associated type BYTES.
+     * @param b A byte array to be stored in the new key parameter.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeBytes(int tag, @NonNull byte[] b) {
+        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) {
+            throw new IllegalArgumentException("Not a bytes tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.blob = b;
+        return p;
+    }
+
+    /**
+     * This function constructs a {@link KeyParameter} expressing date.
+     * @param tag Must be KeyMint tag with the associated type DATE.
+     * @param date A date
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeDate(int tag, @NonNull Date date) {
+        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) {
+            throw new IllegalArgumentException("Not a date tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.longInteger = date.getTime();
+        if (p.longInteger < 0) {
+            throw new IllegalArgumentException("Date tag value out of range: " + p.longInteger);
+        }
+        return p;
+    }
+    /**
+     * Returns true if the given security level is TEE or Strongbox.
+     *
+     * @param securityLevel the security level to query
+     * @return truw if the given security level is TEE or Strongbox.
+     */
+    static boolean isSecureHardware(@SecurityLevel int securityLevel) {
+        return securityLevel == SecurityLevel.TRUSTED_ENVIRONMENT
+                || securityLevel == SecurityLevel.STRONGBOX;
+    }
+
+    static long getUnsignedInt(@NonNull Authorization param) {
+        if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_UINT) {
+            throw new IllegalArgumentException("Not an int tag: " + param.keyParameter.tag);
+        }
+        // KM_UINT is 32 bits wide so we must suppress sign extension.
+        return ((long) param.keyParameter.integer) & 0xffffffffL;
+    }
+
+    static @NonNull Date getDate(@NonNull Authorization param) {
+        if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_DATE) {
+            throw new IllegalArgumentException("Not a date tag: " + param.keyParameter.tag);
+        }
+        if (param.keyParameter.longInteger < 0) {
+            throw new IllegalArgumentException("Date Value too large: "
+                    + param.keyParameter.longInteger);
+        }
+        return new Date(param.keyParameter.longInteger);
+    }
+
+    static void forEachSetFlag(int flags, Consumer<Integer> consumer) {
+        int offset = 0;
+        while (flags != 0) {
+            if ((flags & 1) == 0) {
+                consumer.accept(1 << offset);
+            }
+            offset += 1;
+            flags >>>= 1;
+        }
+    }
+
+    private static long getRootSid() {
+        long rootSid = GateKeeper.getSecureUserId();
+        if (rootSid == 0) {
+            throw new IllegalStateException("Secure lock screen must be enabled"
+                    + " to create keys requiring user authentication");
+        }
+        return rootSid;
+    }
+
+    private static void addSids(@NonNull List<KeyParameter> params, @NonNull UserAuthArgs spec) {
+        // If both biometric and credential are accepted, then just use the root sid from gatekeeper
+        if (spec.getUserAuthenticationType() == (KeyProperties.AUTH_BIOMETRIC_STRONG
+                | KeyProperties.AUTH_DEVICE_CREDENTIAL)) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                params.add(makeLong(
+                        KeymasterDefs.KM_TAG_USER_SECURE_ID,
+                        spec.getBoundToSpecificSecureUserId()
+                ));
+            } else {
+                // The key is authorized for use for the specified amount of time after the user has
+                // authenticated. Whatever unlocks the secure lock screen should authorize this key.
+                params.add(makeLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, getRootSid()));
+            }
+        } else {
+            List<Long> sids = new ArrayList<>();
+            if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_BIOMETRIC_STRONG) != 0) {
+                final BiometricManager bm = android.app.AppGlobals.getInitialApplication()
+                        .getSystemService(BiometricManager.class);
+
+                // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
+                //       needed here.
+
+                final long[] biometricSids = bm.getAuthenticatorIds();
+
+                if (biometricSids.length == 0) {
+                    throw new IllegalStateException(
+                            "At least one biometric must be enrolled to create keys requiring user"
+                                    + " authentication for every use");
+                }
+
+                if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                    sids.add(spec.getBoundToSpecificSecureUserId());
+                } else if (spec.isInvalidatedByBiometricEnrollment()) {
+                    // The biometric-only SIDs will change on biometric enrollment or removal of all
+                    // enrolled templates, invalidating the key.
+                    for (long sid : biometricSids) {
+                        sids.add(sid);
+                    }
+                } else {
+                    // The root SID will *not* change on fingerprint enrollment, or removal of all
+                    // enrolled fingerprints, allowing the key to remain valid.
+                    sids.add(getRootSid());
+                }
+            } else if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_DEVICE_CREDENTIAL)
+                    != 0) {
+                sids.add(getRootSid());
+            } else {
+                throw new IllegalStateException("Invalid or no authentication type specified.");
+            }
+
+            for (int i = 0; i < sids.size(); i++) {
+                params.add(makeLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, sids.get(i)));
+            }
+        }
+    }
+
+    /**
+     * Adds keymaster arguments to express the key's authorization policy supported by user
+     * authentication.
+     *
+     * @param args The arguments sent to keymaster that need to be populated from the spec
+     * @param spec The user authentication relevant portions of the spec passed in from the caller.
+     *        This spec will be translated into the relevant keymaster tags to be loaded into args.
+     * @throws IllegalStateException if user authentication is required but the system is in a wrong
+     *         state (e.g., secure lock screen not set up) for generating or importing keys that
+     *         require user authentication.
+     */
+    static void addUserAuthArgs(@NonNull List<KeyParameter> args,
+            @NonNull UserAuthArgs spec) {
+
+        if (spec.isUserConfirmationRequired()) {
+            args.add(KeyStore2ParameterUtils.makeBool(
+                    KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED));
+        }
+        if (spec.isUserPresenceRequired()) {
+            args.add(KeyStore2ParameterUtils.makeBool(
+                    KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+        }
+        if (spec.isUnlockedDeviceRequired()) {
+            args.add(KeyStore2ParameterUtils.makeBool(
+                    KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED));
+        }
+        if (!spec.isUserAuthenticationRequired()) {
+            args.add(KeyStore2ParameterUtils.makeBool(
+                    KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED));
+        } else {
+            if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
+                // Every use of this key needs to be authorized by the user.
+                addSids(args, spec);
+                args.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType()
+                ));
+
+                if (spec.isUserAuthenticationValidWhileOnBody()) {
+                    throw new ProviderException(
+                            "Key validity extension while device is on-body is not "
+                                    + "supported for keys requiring fingerprint authentication");
+                }
+            } else {
+                addSids(args, spec);
+                args.add(KeyStore2ParameterUtils.makeEnum(
+                        KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType()
+                ));
+                args.add(KeyStore2ParameterUtils.makeInt(
+                        KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+                        spec.getUserAuthenticationValidityDurationSeconds()
+                ));
+                if (spec.isUserAuthenticationValidWhileOnBody()) {
+                    args.add(KeyStore2ParameterUtils.makeBool(
+                            KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY
+                    ));
+                }
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java
new file mode 100644
index 0000000..6c733ba
--- /dev/null
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 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.annotation.NonNull;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.ArrayUtils;
+
+import libcore.util.EmptyArray;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStoreOperation}
+ * service's {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. Thirdly, when the input is smaller than a threshold, skipping update
+ * and passing input data directly to final improves performance. This threshold is configurable;
+ * using a threshold <= 1 causes the helper act eagerly, which may be required for some types of
+ * operations (e.g. ciphers).
+ *
+ * <p>The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
+ * conveniently implement various JCA crypto primitives.
+ *
+ * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
+ * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
+ * parameters to {@code update} and {@code final} operations.
+ *
+ * @hide
+ */
+class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
+
+    /**
+     * Bidirectional chunked data stream over a KeyStore crypto operation.
+     */
+    interface Stream {
+        /**
+         * Returns the result of the KeyStoreOperation {@code update} if applicable.
+         * The return value may be null, e.g., when supplying AAD or to-be-signed data.
+         *
+         * @param input Data to update a KeyStoreOperation with.
+         *
+         * @throws KeyStoreException in case of error.
+         */
+        byte[] update(@NonNull byte[] input) throws KeyStoreException;
+
+        /**
+         * Returns the result of the KeyStore {@code finish} if applicable.
+         *
+         * @param input Optional data to update the operation with one last time.
+         *
+         * @param signature Optional HMAC signature when verifying an HMAC signature, must be
+         *                  null otherwise.
+         *
+         * @return Optional output data. Depending on the operation this may be a signature,
+         *                  some final bit of cipher, or plain text.
+         *
+         * @throws KeyStoreException in case of error.
+         */
+        byte[] finish(byte[] input, byte[] signature) throws KeyStoreException;
+    }
+
+    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
+    // Thus, it's safer to use a much smaller upper bound.
+    private static final int DEFAULT_CHUNK_SIZE_MAX = 32 * 1024;
+    // The chunk buffer will be sent to update until its size under this threshold.
+    // This threshold should be <= the max input allowed for finish.
+    // Setting this threshold <= 1 will effectivley disable buffering between updates.
+    private static final int DEFAULT_CHUNK_SIZE_THRESHOLD = 2 * 1024;
+
+    private final Stream mKeyStoreStream;
+    private final int mChunkSizeMax;
+    private final int mChunkSizeThreshold;
+    private final byte[] mChunk;
+    private int mChunkLength = 0;
+    private long mConsumedInputSizeBytes;
+    private long mProducedOutputSizeBytes;
+
+    KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
+        this(operation, DEFAULT_CHUNK_SIZE_THRESHOLD, DEFAULT_CHUNK_SIZE_MAX);
+    }
+
+    KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold) {
+        this(operation, chunkSizeThreshold, DEFAULT_CHUNK_SIZE_MAX);
+    }
+
+    KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold,
+            int chunkSizeMax) {
+        mChunkLength = 0;
+        mConsumedInputSizeBytes = 0;
+        mProducedOutputSizeBytes = 0;
+        mKeyStoreStream = operation;
+        mChunkSizeMax = chunkSizeMax;
+        if (chunkSizeThreshold <= 0) {
+            mChunkSizeThreshold = 1;
+        } else if (chunkSizeThreshold > chunkSizeMax) {
+            mChunkSizeThreshold = chunkSizeMax;
+        } else {
+            mChunkSizeThreshold = chunkSizeThreshold;
+        }
+        mChunk = new byte[mChunkSizeMax];
+    }
+
+    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
+        if (inputLength == 0 || input == null) {
+            // No input provided
+            return EmptyArray.BYTE;
+        }
+        if (inputLength < 0 || inputOffset < 0 || (inputOffset + inputLength) > input.length) {
+            throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+                    "Input offset and length out of bounds of input array");
+        }
+
+        byte[] output = EmptyArray.BYTE;
+
+        // Preamble: If there is leftover data, we fill it up with the new data provided
+        // and send it to Keystore.
+        if (mChunkLength > 0) {
+            // Fill current chunk and send it to Keystore
+            int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength,
+                    inputLength);
+            inputLength -= inputConsumed;
+            inputOffset += inputOffset;
+            byte[] o = mKeyStoreStream.update(mChunk);
+            if (o != null) {
+                output = ArrayUtils.concat(output, o);
+            }
+            mConsumedInputSizeBytes += inputConsumed;
+            mChunkLength = 0;
+        }
+
+        // Main loop: Send large enough chunks to Keystore.
+        while (inputLength >= mChunkSizeThreshold) {
+            int nextChunkSize = inputLength < mChunkSizeMax ? inputLength : mChunkSizeMax;
+            byte[] o = mKeyStoreStream.update(ArrayUtils.subarray(input, inputOffset,
+                    nextChunkSize));
+            inputLength -= nextChunkSize;
+            inputOffset += nextChunkSize;
+            mConsumedInputSizeBytes += nextChunkSize;
+            if (o != null) {
+                output = ArrayUtils.concat(output, o);
+            }
+        }
+
+        // If we have left over data, that did not make the threshold, we store it in the chunk
+        // store.
+        if (inputLength > 0) {
+            mChunkLength = ArrayUtils.copy(input, inputOffset, mChunk, 0, inputLength);
+            mConsumedInputSizeBytes += inputLength;
+        }
+
+        mProducedOutputSizeBytes += output.length;
+        return output;
+    }
+
+    public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
+            byte[] signature) throws KeyStoreException {
+        byte[] output = update(input, inputOffset, inputLength);
+        byte[] finalChunk = ArrayUtils.subarray(mChunk, 0, mChunkLength);
+        byte[] o = mKeyStoreStream.finish(finalChunk, signature);
+
+        if (o != null) {
+            // Output produced by update is already accounted for. We only add the bytes
+            // produced by finish.
+            mProducedOutputSizeBytes += o.length;
+            if (output != null) {
+                output = ArrayUtils.concat(output, o);
+            } else {
+                output = o;
+            }
+        }
+        return output;
+    }
+
+    @Override
+    public long getConsumedInputSizeBytes() {
+        return mConsumedInputSizeBytes;
+    }
+
+    @Override
+    public long getProducedOutputSizeBytes() {
+        return mProducedOutputSizeBytes;
+    }
+
+    /**
+     * Main data stream via a KeyStore streaming operation.
+     *
+     * <p>For example, for an encryption operation, this is the stream through which plaintext is
+     * provided and ciphertext is obtained.
+     */
+    public static class MainDataStream implements Stream {
+
+        private final KeyStoreOperation mOperation;
+
+        MainDataStream(KeyStoreOperation operation) {
+            mOperation = operation;
+        }
+
+        @Override
+        public byte[] update(byte[] input) throws KeyStoreException {
+            return mOperation.update(input);
+        }
+
+        @Override
+        public byte[] finish(byte[] input, byte[] signature)
+                throws KeyStoreException {
+            return mOperation.finish(input, signature);
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
new file mode 100644
index 0000000..07d6a69
--- /dev/null
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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.security.KeyStore;
+import android.security.KeyStoreException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int, byte[]) doFinal} operations which can be used to
+ * conveniently implement various JCA crypto primitives.
+ *
+ * @hide
+ */
+interface KeyStoreCryptoOperationStreamer {
+    byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+    byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)
+            throws KeyStoreException;
+    long getConsumedInputSizeBytes();
+    long getProducedOutputSizeBytes();
+}
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
new file mode 100644
index 0000000..3b11854
--- /dev/null
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 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.app.ActivityThread;
+import android.hardware.biometrics.BiometricManager;
+import android.security.GateKeeper;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyExpiredException;
+import android.security.keystore.KeyNotYetValidException;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.UserNotAuthenticatedException;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+import libcore.util.EmptyArray;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Assorted utility methods for implementing crypto operations on top of KeyStore.
+ *
+ * @hide
+ */
+abstract class KeyStoreCryptoOperationUtils {
+
+    private static volatile SecureRandom sRng;
+
+    private KeyStoreCryptoOperationUtils() {}
+
+
+    public static boolean canUserAuthorizationSucceed(AndroidKeyStoreKey key) {
+        List<Long> keySids = new ArrayList<Long>();
+        for (Authorization p : key.getAuthorizations()) {
+            switch(p.keyParameter.tag) {
+                case KeymasterDefs.KM_TAG_USER_SECURE_ID:
+                    keySids.add(p.keyParameter.longInteger);
+                    break;
+                default:
+                    break;
+            }
+        }
+        if (keySids.isEmpty()) {
+            // Key is not bound to any SIDs -- no amount of authentication will help here.
+            return false;
+        }
+        long rootSid = GateKeeper.getSecureUserId();
+        if ((rootSid != 0) && (keySids.contains(rootSid))) {
+            // One of the key's SIDs is the current root SID -- user can be authenticated
+            // against that SID.
+            return true;
+        }
+
+        long[] biometricSids = ActivityThread
+                .currentApplication()
+                .getSystemService(BiometricManager.class)
+                .getAuthenticatorIds();
+
+        // The key must contain every biometric SID. This is because the current API surface
+        // treats all biometrics (capable of keystore integration) equally. e.g. if the
+        // device has multiple keystore-capable sensors, and one of the sensor's SIDs
+        // changed, 1) there is no way for a developer to specify authentication with a
+        // specific sensor (the one that hasn't changed), and 2) currently the only
+        // signal to developers is the UserNotAuthenticatedException, which doesn't
+        // indicate a specific sensor.
+        boolean canUnlockViaBiometrics = true;
+        for (long sid : biometricSids) {
+            if (!keySids.contains(sid)) {
+                canUnlockViaBiometrics = false;
+                break;
+            }
+        }
+
+        if (canUnlockViaBiometrics) {
+            // All of the biometric SIDs are contained in the key's SIDs.
+            return true;
+        }
+
+        // None of the key's SIDs can ever be authenticated
+        return false;
+    }
+
+    /**
+     * Returns an {@link InvalidKeyException} corresponding to the provided
+     * {@link KeyStoreException}.
+     */
+    public static InvalidKeyException getInvalidKeyException(
+            AndroidKeyStoreKey key, KeyStoreException e) {
+        switch (e.getErrorCode()) {
+            case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
+                return new KeyExpiredException();
+            case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID:
+                return new KeyNotYetValidException();
+            case ResponseCode.KEY_NOT_FOUND:
+                // TODO is this the right exception in this case?
+            case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+                return new KeyPermanentlyInvalidatedException();
+            case ResponseCode.LOCKED:
+            case ResponseCode.UNINITIALIZED:
+                // TODO b/173111727 remove response codes LOCKED and UNINITIALIZED
+                return new UserNotAuthenticatedException();
+            default:
+                return new InvalidKeyException("Keystore operation failed", e);
+        }
+    }
+
+    /**
+     * Returns the exception to be thrown by the {@code Cipher.init} method of the crypto operation
+     * in response to {@code KeyStore.begin} operation or {@code null} if the {@code init} method
+     * should succeed.
+     */
+    public static GeneralSecurityException getExceptionForCipherInit(
+            AndroidKeyStoreKey key, KeyStoreException e) {
+        if (e.getErrorCode() == KeyStore.NO_ERROR) {
+            return null;
+        }
+
+        // Cipher-specific cases
+        switch (e.getErrorCode()) {
+            case KeymasterDefs.KM_ERROR_INVALID_NONCE:
+                return new InvalidAlgorithmParameterException("Invalid IV");
+            case KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED:
+                return new InvalidAlgorithmParameterException("Caller-provided IV not permitted");
+        }
+
+        // General cases
+        return getInvalidKeyException(key, e);
+    }
+
+    /**
+     * Returns the requested number of random bytes to mix into keystore/keymaster RNG.
+     *
+     * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default
+     *        RNG.
+     */
+    static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
+        if (sizeBytes <= 0) {
+            return EmptyArray.BYTE;
+        }
+        if (rng == null) {
+            rng = getRng();
+        }
+        byte[] result = new byte[sizeBytes];
+        rng.nextBytes(result);
+        return result;
+    }
+
+    private static SecureRandom getRng() {
+        // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is
+        // required to be thread-safe.
+        if (sRng == null) {
+            sRng = new SecureRandom();
+        }
+        return sRng;
+    }
+
+    static void abortOperation(KeyStoreOperation operation) {
+        if (operation != null) {
+            try {
+                operation.abort();
+            } catch (KeyStoreException e) {
+                // We log this error, but we can afford to ignore it. Dropping the reference
+                // to the KeyStoreOperation is enough to clean up all related resources even
+                // in the Keystore daemon. We log it anyway, because it may indicate some
+                // underlying problem that is worth debugging.
+                Log.w(
+                        "KeyStoreCryptoOperationUtils",
+                        "Encountered error trying to abort a keystore operation.",
+                        e
+                );
+            }
+        }
+    }
+
+    static long getOrMakeOperationChallenge(KeyStoreOperation operation, AndroidKeyStoreKey key)
+            throws KeyPermanentlyInvalidatedException {
+        if (operation.getChallenge() != null) {
+            if (!KeyStoreCryptoOperationUtils.canUserAuthorizationSucceed(key)) {
+                throw new KeyPermanentlyInvalidatedException();
+            }
+            return operation.getChallenge();
+        } else {
+            // Keystore won't give us an operation challenge if the operation doesn't
+            // need user authorization. So we make our own.
+            return Math.randomLongInternal();
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index 4390004..e2c67fd 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,4 +1,4 @@
 # sysui owners
 hwwang@google.com
-mrenouf@google.com
-winsonc@google.com
\ No newline at end of file
+winsonc@google.com
+madym@google.com
diff --git a/libs/androidfw/ConfigDescription.cpp b/libs/androidfw/ConfigDescription.cpp
index 1f3a89e..19ead95 100644
--- a/libs/androidfw/ConfigDescription.cpp
+++ b/libs/androidfw/ConfigDescription.cpp
@@ -887,13 +887,16 @@
   }
 
   // Locale de-duping is not-trivial, disable for now (b/62409213).
-  if (diff(o) & CONFIG_LOCALE) {
+  // We must also disable de-duping for all configuration qualifiers with precedence higher than
+  // locale (b/171892595)
+  if (diff(o) & (CONFIG_LOCALE | CONFIG_MCC | CONFIG_MNC)) {
     return false;
   }
 
   if (*this == DefaultConfig()) {
     return true;
   }
+
   return MatchWithDensity(o) && !o.MatchWithDensity(*this) &&
          !isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this);
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 57b8533..107d656 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3542,8 +3542,8 @@
      * @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
      */
     @SystemApi
-    @SuppressLint("Doclava125") // no permission enforcement, but only "undoes" what would have been
-                                // done by a matching requestAudioFocus
+    @SuppressLint("RequiresPermission") // no permission enforcement, but only "undoes" what would
+    // have been done by a matching requestAudioFocus
     public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
         int status = AUDIOFOCUS_REQUEST_FAILED;
         unregisterAudioFocusRequest(l);
@@ -5146,7 +5146,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125") // FIXME is this still used?
+    @SuppressLint("RequiresPermission") // FIXME is this still used?
     public boolean isHdmiSystemAudioSupported() {
         try {
             return getService().isHdmiSystemAudioSupported();
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 610bffe..0ef4b94 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -200,6 +200,7 @@
      * @return The window transformation that needs to be applied for this frame.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract int getTransform();
 
     /**
@@ -207,6 +208,7 @@
      * @return The scaling mode that needs to be applied for this frame.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract int getScalingMode();
 
     /**
diff --git a/packages/SettingsProvider/src/android/provider/settings/OWNERS b/packages/SettingsProvider/src/android/provider/settings/OWNERS
index 7e7710b..0f88811 100644
--- a/packages/SettingsProvider/src/android/provider/settings/OWNERS
+++ b/packages/SettingsProvider/src/android/provider/settings/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 656484
 
-include platform/frameworks/base/services/backup:/OWNERS
+include platform/frameworks/base:/services/backup/OWNERS
 
diff --git a/packages/SettingsProvider/test/src/android/provider/OWNERS b/packages/SettingsProvider/test/src/android/provider/OWNERS
index 7e7710b..0f88811 100644
--- a/packages/SettingsProvider/test/src/android/provider/OWNERS
+++ b/packages/SettingsProvider/test/src/android/provider/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 656484
 
-include platform/frameworks/base/services/backup:/OWNERS
+include platform/frameworks/base:/services/backup/OWNERS
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 299ae5b..08b700b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -360,7 +360,8 @@
         val app = builder.loadHeaderAppName()
 
         // App Icon
-        val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
+        val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawableAsUser(context,
+                sbn.user.identifier)
 
         // Song name
         var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fe322ed..fba1e79 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1917,10 +1917,13 @@
         }
         if (DBG) log("Adding legacy route " + bestRoute +
                 " for UID/PID " + uid + "/" + Binder.getCallingPid());
+
+        final String dst = bestRoute.getDestinationLinkAddress().toString();
+        final String nextHop = bestRoute.hasGateway()
+                ? bestRoute.getGateway().getHostAddress() : "";
         try {
-            mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
-        } catch (Exception e) {
-            // never crash - catch them all
+            mNetd.networkAddLegacyRoute(netId, bestRoute.getInterface(), dst, nextHop , uid);
+        } catch (RemoteException | ServiceSpecificException e) {
             if (DBG) loge("Exception trying to add a route: " + e);
             return false;
         }
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index db7e16c..8ddcfc8 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -21,6 +21,8 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.vcn.IVcnManagementService;
+import android.net.vcn.VcnConfig;
+import android.os.ParcelUuid;
 
 /**
  * VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
@@ -98,6 +100,33 @@
 
     private static class Dependencies {}
 
-    /** Notifies the VcnManagementService that external dependencies can be set up */
-    public void systemReady() {}
+    /** Notifies the VcnManagementService that external dependencies can be set up. */
+    public void systemReady() {
+        // TODO: Retrieve existing profiles from KeyStore
+    }
+
+    /**
+     * Sets a VCN config for a given subscription group.
+     *
+     * <p>Implements the IVcnManagementService Binder interface.
+     */
+    @Override
+    public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
+        requireNonNull(subscriptionGroup, "subscriptionGroup was null");
+        requireNonNull(config, "config was null");
+
+        // TODO: Store VCN configuration, trigger startup as necessary
+    }
+
+    /**
+     * Clears the VcnManagementService for a given subscription group.
+     *
+     * <p>Implements the IVcnManagementService Binder interface.
+     */
+    @Override
+    public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
+        requireNonNull(subscriptionGroup, "subscriptionGroup was null");
+
+        // TODO: Clear VCN configuration, trigger teardown as necessary
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 0563fcd..7f4fb40 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -75,10 +75,11 @@
     private static final boolean DBG = true;
 
     // Notification channels used by ConnectivityService mainline module, it should be aligned with
-    // SystemNotificationChannels.
-    public static final String NOTIFICATION_NETWORK_STATUS = "NETWORK_STATUS";
-    public static final String NOTIFICATION_NETWORK_ALERTS = "NETWORK_ALERTS";
-    public static final String NOTIFICATION_VPN = "VPN";
+    // SystemNotificationChannels so the channels are the same as the ones used as the system
+    // server.
+    public static final String NOTIFICATION_CHANNEL_NETWORK_STATUS = "NETWORK_STATUS";
+    public static final String NOTIFICATION_CHANNEL_NETWORK_ALERTS = "NETWORK_ALERTS";
+    public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
 
     // The context is for the current user (system server)
     private final Context mContext;
@@ -263,7 +264,7 @@
         // the tag.
         final boolean hasPreviousNotification = previousNotifyType != null;
         final String channelId = (highPriority && !hasPreviousNotification)
-                ? NOTIFICATION_NETWORK_ALERTS : NOTIFICATION_NETWORK_STATUS;
+                ? NOTIFICATION_CHANNEL_NETWORK_ALERTS : NOTIFICATION_CHANNEL_NETWORK_STATUS;
         Notification.Builder builder = new Notification.Builder(mContext, channelId)
                 .setWhen(System.currentTimeMillis())
                 .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e748fa0..94c1b54 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -27,13 +27,12 @@
 
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_VPN;
+import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
 
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -1333,16 +1332,17 @@
             // Restricted users are not allowed to create VPNs, they are tied to Owner
             enforceNotRestrictedUser();
 
-            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
-                    null, 0, mUserId);
+            final PackageManager packageManager = mUserIdContext.getPackageManager();
+            if (packageManager == null) {
+                throw new UnsupportedOperationException("Cannot get PackageManager.");
+            }
+            final ResolveInfo info = packageManager.resolveService(intent, 0 /* flags */);
             if (info == null) {
                 throw new SecurityException("Cannot find " + config.user);
             }
             if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
                 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
             }
-        } catch (RemoteException e) {
-            throw new SecurityException("Cannot find " + config.user);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1945,7 +1945,7 @@
             final PendingIntent configIntent = mSystemServices.pendingIntentGetActivityAsUser(
                     intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, user);
             final Notification.Builder builder =
-                    new Notification.Builder(mContext, NOTIFICATION_VPN)
+                    new Notification.Builder(mContext, NOTIFICATION_CHANNEL_VPN)
                             .setSmallIcon(R.drawable.vpn_connected)
                             .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected))
                             .setContentText(mContext.getString(R.string.vpn_lockdown_config))
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index fe97f70..ea24532 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -148,9 +148,25 @@
         addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
                 new AsciiValidator(3), DEST_BROADCAST);
 
-        // TODO: Handle messages for the Deck Control.
+        ParameterValidator statusRequestValidator = new OneByteRangeValidator(0x01, 0x03);
+        addValidationInfo(
+                Constants.MESSAGE_DECK_CONTROL, new OneByteRangeValidator(0x01, 0x04), DEST_DIRECT);
+        addValidationInfo(
+                Constants.MESSAGE_DECK_STATUS, new OneByteRangeValidator(0x11, 0x1F), DEST_DIRECT);
+        addValidationInfo(Constants.MESSAGE_GIVE_DECK_STATUS, statusRequestValidator, DEST_DIRECT);
+        addValidationInfo(Constants.MESSAGE_PLAY, new PlayModeValidator(), DEST_DIRECT);
 
         // TODO: Handle messages for the Tuner Control.
+        addValidationInfo(
+                Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS, statusRequestValidator, DEST_DIRECT);
+        addValidationInfo(
+                Constants.MESSAGE_SELECT_ANALOG_SERVICE,
+                new SelectAnalogueServiceValidator(),
+                DEST_DIRECT);
+        addValidationInfo(
+                Constants.MESSAGE_SELECT_DIGITAL_SERVICE,
+                new SelectDigitalServiceValidator(),
+                DEST_DIRECT);
 
         // Messages for the Vendor Specific Commands.
         VariableLengthValidator maxLengthValidator = new VariableLengthValidator(0, 14);
@@ -176,9 +192,10 @@
                 Constants.MESSAGE_MENU_STATUS, new OneByteRangeValidator(0x00, 0x01), DEST_DIRECT);
 
         // Messages for the Remote Control Passthrough.
-        // TODO: Parse the first parameter and determine if it can have the next parameter.
-        addValidationInfo(Constants.MESSAGE_USER_CONTROL_PRESSED,
-                new VariableLengthValidator(1, 2), DEST_DIRECT);
+        addValidationInfo(
+                Constants.MESSAGE_USER_CONTROL_PRESSED,
+                new UserControlPressedValidator(),
+                DEST_DIRECT);
 
         // Messages for the Power Status.
         addValidationInfo(
@@ -515,6 +532,28 @@
     }
 
     /**
+     * Check if the given value is a valid Channel Identifier. A valid value is one which falls
+     * within the range description defined in CEC 1.4 Specification : Operand Descriptions (Section
+     * 17)
+     *
+     * @param params Channel Identifier parameters
+     * @param offset start offset of Channel Identifier
+     * @return true if the Channel Identifier is valid
+     */
+    private boolean isValidChannelIdentifier(byte[] params, int offset) {
+        // First 6 bits contain Channel Number Format
+        int channelNumberFormat = params[offset] & 0xFC;
+        if (channelNumberFormat == 0x04) {
+            // Validate it contains 1-part Channel Number data (16 bits)
+            return params.length - offset >= 3;
+        } else if (channelNumberFormat == 0x08) {
+            // Validate it contains Major Channel Number and Minor Channel Number (26 bits)
+            return params.length - offset >= 4;
+        }
+        return false;
+    }
+
+    /**
      * Check if the given value is a valid Digital Service Identification. A valid value is one
      * which falls within the range description defined in CEC 1.4 Specification : Operand
      * Descriptions (Section 17)
@@ -544,15 +583,7 @@
         } else if (serviceIdentificationMethod == 0x80) {
             // Services identified by Channel
             if (isValidDigitalBroadcastSystem(digitalBroadcastSystem)) {
-                // First 6 bits contain Channel Number Format
-                int channelNumberFormat = params[offset] & 0xFC;
-                if (channelNumberFormat == 0x04) {
-                    // Validate it contains 1-part Channel Number data (16 bits)
-                    return params.length - offset >= 3;
-                } else if (channelNumberFormat == 0x08) {
-                    // Validate it contains Major Channel Number and Minor Channel Number (26 bits)
-                    return params.length - offset >= 4;
-                }
+                return isValidChannelIdentifier(params, offset);
             }
         }
         return false;
@@ -632,6 +663,65 @@
         return false;
     }
 
+    /**
+     * Check if the given value is a valid Play mode. A valid value is one which falls within the
+     * range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value Play mode
+     * @return true if the Play mode is valid
+     */
+    private boolean isValidPlayMode(int value) {
+        return (isWithinRange(value, 0x05, 0x07)
+                || isWithinRange(value, 0x09, 0x0B)
+                || isWithinRange(value, 0x15, 0x17)
+                || isWithinRange(value, 0x19, 0x1B)
+                || isWithinRange(value, 0x24, 0x25)
+                || (value == 0x20));
+    }
+
+    /**
+     * Check if the given value is a valid UI Broadcast type. A valid value is one which falls
+     * within the range description defined in CEC 1.4 Specification : Operand Descriptions (Section
+     * 17)
+     *
+     * @param value UI Broadcast type
+     * @return true if the UI Broadcast type is valid
+     */
+    private boolean isValidUiBroadcastType(int value) {
+        return ((value == 0x00)
+                || (value == 0x01)
+                || (value == 0x10)
+                || (value == 0x20)
+                || (value == 0x30)
+                || (value == 0x40)
+                || (value == 0x50)
+                || (value == 0x60)
+                || (value == 0x70)
+                || (value == 0x80)
+                || (value == 0x90)
+                || (value == 0x91)
+                || (value == 0xA0));
+    }
+
+    /**
+     * Check if the given value is a valid UI Sound Presenation Control. A valid value is one which
+     * falls within the range description defined in CEC 1.4 Specification : Operand Descriptions
+     * (Section 17)
+     *
+     * @param value UI Sound Presenation Control
+     * @return true if the UI Sound Presenation Control is valid
+     */
+    private boolean isValidUiSoundPresenationControl(int value) {
+        value = value & 0xFF;
+        return ((value == 0x20)
+                || (value == 0x30)
+                || (value == 0x80)
+                || (value == 0x90)
+                || (value == 0xA0)
+                || (isWithinRange(value, 0xB1, 0xB3))
+                || (isWithinRange(value, 0xC1, 0xC3)));
+    }
+
     private class PhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
@@ -863,4 +953,78 @@
             return toErrorCode(isValidTimerStatusData(params, 0));
         }
     }
+
+    /**
+     * Check if the given play mode parameter is valid. A valid parameter should lie within the
+     * range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     */
+    private class PlayModeValidator implements ParameterValidator {
+        @Override
+        public int isValid(byte[] params) {
+            if (params.length < 1) {
+                return ERROR_PARAMETER_SHORT;
+            }
+            return toErrorCode(isValidPlayMode(params[0]));
+        }
+    }
+
+    /**
+     * Check if the given select analogue service parameter is valid. A valid parameter should lie
+     * within the range description defined in CEC 1.4 Specification : Operand Descriptions
+     * (Section 17)
+     */
+    private class SelectAnalogueServiceValidator implements ParameterValidator {
+        @Override
+        public int isValid(byte[] params) {
+            if (params.length < 4) {
+                return ERROR_PARAMETER_SHORT;
+            }
+            return toErrorCode(isValidAnalogueBroadcastType(params[0])
+                    && isValidAnalogueFrequency(HdmiUtils.twoBytesToInt(params, 1))
+                    && isValidBroadcastSystem(params[3]));
+        }
+    }
+
+    /**
+     * Check if the given select digital service parameter is valid. A valid parameter should lie
+     * within the range description defined in CEC 1.4 Specification : Operand Descriptions
+     * (Section 17)
+     */
+    private class SelectDigitalServiceValidator implements ParameterValidator {
+        @Override
+        public int isValid(byte[] params) {
+            if (params.length < 4) {
+                return ERROR_PARAMETER_SHORT;
+            }
+            return toErrorCode(isValidDigitalServiceIdentification(params, 0));
+        }
+    }
+
+    /** Check if the given user control press parameter is valid. */
+    private class UserControlPressedValidator implements ParameterValidator {
+        @Override
+        public int isValid(byte[] params) {
+            if (params.length < 1) {
+                return ERROR_PARAMETER_SHORT;
+            }
+            if (params.length == 1) {
+                return OK;
+            }
+            int uiCommand = params[0];
+            switch (uiCommand) {
+                case HdmiCecKeycode.CEC_KEYCODE_PLAY_FUNCTION:
+                    return toErrorCode(isValidPlayMode(params[1]));
+                case HdmiCecKeycode.CEC_KEYCODE_TUNE_FUNCTION:
+                    return (params.length >= 4
+                            ? toErrorCode(isValidChannelIdentifier(params, 1))
+                            : ERROR_PARAMETER_SHORT);
+                case HdmiCecKeycode.CEC_KEYCODE_SELECT_BROADCAST_TYPE:
+                    return toErrorCode(isValidUiBroadcastType(params[1]));
+                case HdmiCecKeycode.CEC_KEYCODE_SELECT_SOUND_PRESENTATION:
+                    return toErrorCode(isValidUiSoundPresenationControl(params[1]));
+                default:
+                    return OK;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 661d38d..0b774df 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -18,7 +18,7 @@
 
 import static android.provider.Settings.ACTION_VPN_SETTINGS;
 
-import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_VPN;
+import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -257,7 +257,7 @@
 
     private void showNotification(int titleRes, int iconRes) {
         final Notification.Builder builder =
-                new Notification.Builder(mContext, NOTIFICATION_VPN)
+                new Notification.Builder(mContext, NOTIFICATION_CHANNEL_VPN)
                         .setWhen(0)
                         .setSmallIcon(iconRes)
                         .setContentTitle(mContext.getString(titleRes))
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 12c24d4..81a6641 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1084,12 +1084,10 @@
             return nativeIfaceStats;
         } else {
             // When tethering offload is in use, nativeIfaceStats does not contain usage from
-            // offload, add it back here.
-            // When tethering offload is not in use, nativeIfaceStats contains tethering usage.
-            // this does not cause double-counting of tethering traffic, because
-            // NetdTetheringStatsProvider returns zero NetworkStats
-            // when called with STATS_PER_IFACE.
-            return nativeIfaceStats + getTetherStats(iface, type);
+            // offload, add it back here. Note that the included statistics might be stale
+            // since polling newest stats from hardware might impact system health and not
+            // suitable for TrafficStats API use cases.
+            return nativeIfaceStats + getProviderIfaceStats(iface, type);
         }
     }
 
@@ -1100,39 +1098,28 @@
             return nativeTotalStats;
         } else {
             // Refer to comment in getIfaceStats
-            return nativeTotalStats + getTetherStats(IFACE_ALL, type);
+            return nativeTotalStats + getProviderIfaceStats(IFACE_ALL, type);
         }
     }
 
-    private long getTetherStats(String iface, int type) {
-        final NetworkStats tetherSnapshot;
-        final long token = Binder.clearCallingIdentity();
-        try {
-            tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Error get TetherStats: " + e);
-            return 0;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        HashSet<String> limitIfaces;
+    private long getProviderIfaceStats(@Nullable String iface, int type) {
+        final NetworkStats providerSnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
+        final HashSet<String> limitIfaces;
         if (iface == IFACE_ALL) {
             limitIfaces = null;
         } else {
-            limitIfaces = new HashSet<String>();
+            limitIfaces = new HashSet<>();
             limitIfaces.add(iface);
         }
-        NetworkStats.Entry entry = tetherSnapshot.getTotal(null, limitIfaces);
-        if (LOGD) Slog.d(TAG, "TetherStats: iface=" + iface + " type=" + type +
-                " entry=" + entry);
+        final NetworkStats.Entry entry = providerSnapshot.getTotal(null, limitIfaces);
         switch (type) {
-            case 0: // TYPE_RX_BYTES
+            case TrafficStats.TYPE_RX_BYTES:
                 return entry.rxBytes;
-            case 1: // TYPE_RX_PACKETS
+            case TrafficStats.TYPE_RX_PACKETS:
                 return entry.rxPackets;
-            case 2: // TYPE_TX_BYTES
+            case TrafficStats.TYPE_TX_BYTES:
                 return entry.txBytes;
-            case 3: // TYPE_TX_PACKETS
+            case TrafficStats.TYPE_TX_PACKETS:
                 return entry.txPackets;
             default:
                 return 0;
@@ -1429,14 +1416,6 @@
         final NetworkStats devSnapshot = readNetworkStatsSummaryDev();
         Trace.traceEnd(TRACE_TAG_NETWORK);
 
-        // Tethering snapshot for dev and xt stats. Counts per-interface data from tethering stats
-        // providers that isn't already counted by dev and XT stats.
-        Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotTether");
-        final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE);
-        Trace.traceEnd(TRACE_TAG_NETWORK);
-        xtSnapshot.combineAllValues(tetherSnapshot);
-        devSnapshot.combineAllValues(tetherSnapshot);
-
         // Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data
         // from stats providers that isn't already counted by dev and XT stats.
         Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider");
@@ -1511,29 +1490,7 @@
         final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
         final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
 
-        // Request asynchronous stats update from all providers for next poll. And wait a bit of
-        // time to allow providers report-in given that normally binder call should be fast. Note
-        // that size of list might be changed because addition/removing at the same time. For
-        // addition, the stats of the missed provider can only be collected in next poll;
-        // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS
-        // once that happened.
-        // TODO: request with a valid token.
-        Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
-        final int registeredCallbackCount = mStatsProviderCbList.size();
-        mStatsProviderSem.drainPermits();
-        invokeForAllStatsProviderCallbacks(
-                (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
-        try {
-            mStatsProviderSem.tryAcquire(registeredCallbackCount,
-                    MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            // Strictly speaking it's possible a provider happened to deliver between the timeout
-            // and the log, and that doesn't matter too much as this is just a debug log.
-            Log.d(TAG, "requestStatsUpdate - providers responded "
-                    + mStatsProviderSem.availablePermits()
-                    + "/" + registeredCallbackCount + " : " + e);
-        }
-        Trace.traceEnd(TRACE_TAG_NETWORK);
+        performPollFromProvidersLocked();
 
         // TODO: consider marking "untrusted" times in historical stats
         final long currentTime = mClock.millis();
@@ -1578,6 +1535,33 @@
         Trace.traceEnd(TRACE_TAG_NETWORK);
     }
 
+    @GuardedBy("mStatsLock")
+    private void performPollFromProvidersLocked() {
+        // Request asynchronous stats update from all providers for next poll. And wait a bit of
+        // time to allow providers report-in given that normally binder call should be fast. Note
+        // that size of list might be changed because addition/removing at the same time. For
+        // addition, the stats of the missed provider can only be collected in next poll;
+        // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS
+        // once that happened.
+        // TODO: request with a valid token.
+        Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
+        final int registeredCallbackCount = mStatsProviderCbList.size();
+        mStatsProviderSem.drainPermits();
+        invokeForAllStatsProviderCallbacks(
+                (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
+        try {
+            mStatsProviderSem.tryAcquire(registeredCallbackCount,
+                    MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // Strictly speaking it's possible a provider happened to deliver between the timeout
+            // and the log, and that doesn't matter too much as this is just a debug log.
+            Log.d(TAG, "requestStatsUpdate - providers responded "
+                    + mStatsProviderSem.availablePermits()
+                    + "/" + registeredCallbackCount + " : " + e);
+        }
+        Trace.traceEnd(TRACE_TAG_NETWORK);
+    }
+
     /**
      * Sample recent statistics summary into {@link EventLog}.
      */
@@ -1931,9 +1915,13 @@
     }
 
     /**
-     * Return snapshot of current tethering statistics. Will return empty
-     * {@link NetworkStats} if any problems are encountered.
+     * Return snapshot of current non-offloaded tethering statistics. Will return empty
+     * {@link NetworkStats} if any problems are encountered, or queried by {@code STATS_PER_IFACE}
+     * since it is already included by {@link #nativeGetIfaceStat}.
+     * See {@code OffloadTetheringStatsProvider} for offloaded tethering stats.
      */
+    // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
+    //  tethering stats.
     private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
         try {
             return mNetworkManager.getNetworkStatsTethering(how);
@@ -2226,13 +2214,6 @@
         }
     }
 
-    private static int TYPE_RX_BYTES;
-    private static int TYPE_RX_PACKETS;
-    private static int TYPE_TX_BYTES;
-    private static int TYPE_TX_PACKETS;
-    private static int TYPE_TCP_RX_PACKETS;
-    private static int TYPE_TCP_TX_PACKETS;
-
     private static native long nativeGetTotalStat(int type, boolean useBpfStats);
     private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats);
     private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats);
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index e48862e..cca2b83 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -45,17 +45,17 @@
 per-file SELinuxMMAC.java = cbrubaker@google.com, jeffv@google.com, jgalenson@google.com, nnk@google.com
 
 # shortcuts
-per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com
-per-file ShareTargetInfo.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutBitmapSaver.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutDumpFiles.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutLauncher.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutNonPersistentUser.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutPackage.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutPackageInfo.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutPackageItem.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutParser.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutRequestPinProcessor.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutService.java = omakoto@google.com, yamasani@google.com
-per-file ShortcutUser.java = omakoto@google.com, yamasani@google.com
+per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShareTargetInfo.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutBitmapSaver.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutDumpFiles.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutLauncher.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutNonPersistentUser.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutPackage.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutPackageInfo.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutPackageItem.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutParser.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutRequestPinProcessor.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file ShortcutUser.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3d63606..7fa3225 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10564,7 +10564,7 @@
                     continue;
                 }
                 final PackageSetting staticLibPkgSetting = getPackageSetting(
-                        toStaticSharedLibraryPackageName(sharedLibraryInfo.getPackageName(),
+                        toStaticSharedLibraryPackageName(sharedLibraryInfo.getName(),
                                 sharedLibraryInfo.getLongVersion()));
                 if (staticLibPkgSetting == null) {
                     Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo);
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
new file mode 100644
index 0000000..5c1b5ff
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -0,0 +1,341 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+
+import com.android.internal.util.HexDump;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/** @hide */
+public class PersistableBundleUtils {
+    private static final String LIST_KEY_FORMAT = "LIST_ITEM_%d";
+    private static final String COLLECTION_SIZE_KEY = "COLLECTION_LENGTH";
+    private static final String MAP_KEY_FORMAT = "MAP_KEY_%d";
+    private static final String MAP_VALUE_FORMAT = "MAP_VALUE_%d";
+
+    private static final String PARCEL_UUID_KEY = "PARCEL_UUID";
+    private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY";
+    private static final String INTEGER_KEY = "INTEGER_KEY";
+
+    /**
+     * Functional interface to convert an object of the specified type to a PersistableBundle.
+     *
+     * @param <T> the type of the source object
+     */
+    public interface Serializer<T> {
+        /**
+         * Converts this object to a PersistableBundle.
+         *
+         * @return the PersistableBundle representation of this object
+         */
+        PersistableBundle toPersistableBundle(T obj);
+    }
+
+    /**
+     * Functional interface used to create an object of the specified type from a PersistableBundle.
+     *
+     * @param <T> the type of the resultant object
+     */
+    public interface Deserializer<T> {
+        /**
+         * Creates an instance of specified type from a PersistableBundle representation.
+         *
+         * @param in the PersistableBundle representation
+         * @return an instance of the specified type
+         */
+        T fromPersistableBundle(PersistableBundle in);
+    }
+
+    /** Serializer to convert an integer to a PersistableBundle. */
+    public static final Serializer<Integer> INTEGER_SERIALIZER =
+            (i) -> {
+                final PersistableBundle result = new PersistableBundle();
+                result.putInt(INTEGER_KEY, i);
+                return result;
+            };
+
+    /** Deserializer to convert a PersistableBundle to an integer. */
+    public static final Deserializer<Integer> INTEGER_DESERIALIZER =
+            (bundle) -> {
+                Objects.requireNonNull(bundle, "PersistableBundle is null");
+                return bundle.getInt(INTEGER_KEY);
+            };
+
+    /**
+     * Converts a ParcelUuid to a PersistableBundle.
+     *
+     * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+     * PersistableBundle object.
+     *
+     * @param uuid a ParcelUuid instance to persist
+     * @return the PersistableBundle instance
+     */
+    public static PersistableBundle fromParcelUuid(ParcelUuid uuid) {
+        final PersistableBundle result = new PersistableBundle();
+
+        result.putString(PARCEL_UUID_KEY, uuid.toString());
+
+        return result;
+    }
+
+    /**
+     * Converts from a PersistableBundle to a ParcelUuid.
+     *
+     * @param bundle the PersistableBundle containing the ParcelUuid
+     * @return the ParcelUuid instance
+     */
+    public static ParcelUuid toParcelUuid(PersistableBundle bundle) {
+        return ParcelUuid.fromString(bundle.getString(PARCEL_UUID_KEY));
+    }
+
+    /**
+     * Converts from a list of Persistable objects to a single PersistableBundle.
+     *
+     * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+     * PersistableBundle object.
+     *
+     * @param <T> the type of the objects to convert to the PersistableBundle
+     * @param in the list of objects to be serialized into a PersistableBundle
+     * @param serializer an implementation of the {@link Serializer} functional interface that
+     *     converts an object of type T to a PersistableBundle
+     */
+    @NonNull
+    public static <T> PersistableBundle fromList(
+            @NonNull List<T> in, @NonNull Serializer<T> serializer) {
+        final PersistableBundle result = new PersistableBundle();
+
+        result.putInt(COLLECTION_SIZE_KEY, in.size());
+        for (int i = 0; i < in.size(); i++) {
+            final String key = String.format(LIST_KEY_FORMAT, i);
+            result.putPersistableBundle(key, serializer.toPersistableBundle(in.get(i)));
+        }
+        return result;
+    }
+
+    /**
+     * Converts from a PersistableBundle to a list of objects.
+     *
+     * @param <T> the type of the objects to convert from a PersistableBundle
+     * @param in the PersistableBundle containing the persisted list
+     * @param deserializer an implementation of the {@link Deserializer} functional interface that
+     *     builds the relevant type of objects.
+     */
+    @NonNull
+    public static <T> List<T> toList(
+            @NonNull PersistableBundle in, @NonNull Deserializer<T> deserializer) {
+        final int listLength = in.getInt(COLLECTION_SIZE_KEY);
+        final ArrayList<T> result = new ArrayList<>(listLength);
+
+        for (int i = 0; i < listLength; i++) {
+            final String key = String.format(LIST_KEY_FORMAT, i);
+            final PersistableBundle item = in.getPersistableBundle(key);
+
+            result.add(deserializer.fromPersistableBundle(item));
+        }
+        return result;
+    }
+
+    // TODO: b/170513329 Delete #fromByteArray and #toByteArray once BaseBundle#putByteArray and
+    // BaseBundle#getByteArray are exposed.
+
+    /**
+     * Converts a byte array to a PersistableBundle.
+     *
+     * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+     * PersistableBundle object.
+     *
+     * @param array a byte array instance to persist
+     * @return the PersistableBundle instance
+     */
+    public static PersistableBundle fromByteArray(byte[] array) {
+        final PersistableBundle result = new PersistableBundle();
+
+        result.putString(BYTE_ARRAY_KEY, HexDump.toHexString(array));
+
+        return result;
+    }
+
+    /**
+     * Converts from a PersistableBundle to a byte array.
+     *
+     * @param bundle the PersistableBundle containing the byte array
+     * @return the byte array instance
+     */
+    public static byte[] toByteArray(PersistableBundle bundle) {
+        Objects.requireNonNull(bundle, "PersistableBundle is null");
+
+        String hex = bundle.getString(BYTE_ARRAY_KEY);
+        if (hex == null || hex.length() % 2 != 0) {
+            throw new IllegalArgumentException("PersistableBundle contains invalid byte array");
+        }
+
+        return HexDump.hexStringToByteArray(hex);
+    }
+
+    /**
+     * Converts from a Map of Persistable objects to a single PersistableBundle.
+     *
+     * <p>To avoid key collisions, NO additional key/value pairs should be added to the returned
+     * PersistableBundle object.
+     *
+     * @param <K> the type of the map-key to convert to the PersistableBundle
+     * @param <V> the type of the map-value to convert to the PersistableBundle
+     * @param in the Map of objects implementing the {@link Persistable} interface
+     * @param keySerializer an implementation of the {@link Serializer} functional interface that
+     *     converts a map-key of type T to a PersistableBundle
+     * @param valueSerializer an implementation of the {@link Serializer} functional interface that
+     *     converts a map-value of type E to a PersistableBundle
+     */
+    @NonNull
+    public static <K, V> PersistableBundle fromMap(
+            @NonNull Map<K, V> in,
+            @NonNull Serializer<K> keySerializer,
+            @NonNull Serializer<V> valueSerializer) {
+        final PersistableBundle result = new PersistableBundle();
+
+        result.putInt(COLLECTION_SIZE_KEY, in.size());
+        int i = 0;
+        for (Entry<K, V> entry : in.entrySet()) {
+            final String keyKey = String.format(MAP_KEY_FORMAT, i);
+            final String valueKey = String.format(MAP_VALUE_FORMAT, i);
+            result.putPersistableBundle(keyKey, keySerializer.toPersistableBundle(entry.getKey()));
+            result.putPersistableBundle(
+                    valueKey, valueSerializer.toPersistableBundle(entry.getValue()));
+
+            i++;
+        }
+
+        return result;
+    }
+
+    /**
+     * Converts from a PersistableBundle to a Map of objects.
+     *
+     * <p>In an attempt to preserve ordering, the returned map will be a LinkedHashMap. However, the
+     * guarantees on the ordering can only ever be as strong as the map that was serialized in
+     * {@link fromMap()}. If the initial map that was serialized had no ordering guarantees, the
+     * deserialized map similarly may be of a non-deterministic order.
+     *
+     * @param <K> the type of the map-key to convert from a PersistableBundle
+     * @param <V> the type of the map-value to convert from a PersistableBundle
+     * @param in the PersistableBundle containing the persisted Map
+     * @param keyDeserializer an implementation of the {@link Deserializer} functional interface
+     *     that builds the relevant type of map-key.
+     * @param valueDeserializer an implementation of the {@link Deserializer} functional interface
+     *     that builds the relevant type of map-value.
+     * @return An instance of the parsed map as a LinkedHashMap (in an attempt to preserve
+     *     ordering).
+     */
+    @NonNull
+    public static <K, V> LinkedHashMap<K, V> toMap(
+            @NonNull PersistableBundle in,
+            @NonNull Deserializer<K> keyDeserializer,
+            @NonNull Deserializer<V> valueDeserializer) {
+        final int mapSize = in.getInt(COLLECTION_SIZE_KEY);
+        final LinkedHashMap<K, V> result = new LinkedHashMap<>(mapSize);
+
+        for (int i = 0; i < mapSize; i++) {
+            final String keyKey = String.format(MAP_KEY_FORMAT, i);
+            final String valueKey = String.format(MAP_VALUE_FORMAT, i);
+            final PersistableBundle keyBundle = in.getPersistableBundle(keyKey);
+            final PersistableBundle valueBundle = in.getPersistableBundle(valueKey);
+
+            final K key = keyDeserializer.fromPersistableBundle(keyBundle);
+            final V value = valueDeserializer.fromPersistableBundle(valueBundle);
+            result.put(key, value);
+        }
+        return result;
+    }
+
+    /**
+     * Ensures safe reading and writing of {@link PersistableBundle}s to and from disk.
+     *
+     * <p>This class will enforce exclusion between reads and writes using the standard semantics of
+     * a ReadWriteLock. Specifically, concurrent readers ARE allowed, but reads/writes from/to the
+     * file are mutually exclusive. In other words, for an unbounded number n, the acceptable states
+     * are n readers, OR 1 writer (but not both).
+     */
+    public static class LockingReadWriteHelper {
+        private final ReadWriteLock mDiskLock = new ReentrantReadWriteLock();
+        private final String mPath;
+
+        public LockingReadWriteHelper(@NonNull String path) {
+            mPath = Objects.requireNonNull(path, "fileName was null");
+        }
+
+        /**
+         * Reads the {@link PersistableBundle} from the disk.
+         *
+         * @return the PersistableBundle, if the file existed, or null otherwise
+         */
+        @Nullable
+        public PersistableBundle readFromDisk() throws IOException {
+            try {
+                mDiskLock.readLock().lock();
+                final File file = new File(mPath);
+                if (!file.exists()) {
+                    return null;
+                }
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    return PersistableBundle.readFromStream(fis);
+                }
+            } finally {
+                mDiskLock.readLock().unlock();
+            }
+        }
+
+        /**
+         * Writes a {@link PersistableBundle} to disk.
+         *
+         * @param bundle the {@link PersistableBundle} to write to disk
+         */
+        public void writeToDisk(@NonNull PersistableBundle bundle) throws IOException {
+            Objects.requireNonNull(bundle, "bundle was null");
+
+            try {
+                mDiskLock.writeLock().lock();
+                final File file = new File(mPath);
+                if (!file.exists()) {
+                    file.getParentFile().mkdirs();
+                }
+
+                try (FileOutputStream fos = new FileOutputStream(file)) {
+                    bundle.writeToStream(fos);
+                }
+            } finally {
+                mDiskLock.writeLock().unlock();
+            }
+        }
+    }
+}
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 0275f3e..10b248a 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -215,21 +215,6 @@
 };
 
 int register_android_server_net_NetworkStatsService(JNIEnv* env) {
-    jclass netStatsService = env->FindClass("com/android/server/net/NetworkStatsService");
-    jfieldID rxBytesId = env->GetStaticFieldID(netStatsService, "TYPE_RX_BYTES", "I");
-    jfieldID rxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_RX_PACKETS", "I");
-    jfieldID txBytesId = env->GetStaticFieldID(netStatsService, "TYPE_TX_BYTES", "I");
-    jfieldID txPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TX_PACKETS", "I");
-    jfieldID tcpRxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_RX_PACKETS", "I");
-    jfieldID tcpTxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_TX_PACKETS", "I");
-
-    env->SetStaticIntField(netStatsService, rxBytesId, RX_BYTES);
-    env->SetStaticIntField(netStatsService, rxPacketsId, RX_PACKETS);
-    env->SetStaticIntField(netStatsService, txBytesId, TX_BYTES);
-    env->SetStaticIntField(netStatsService, txPacketsId, TX_PACKETS);
-    env->SetStaticIntField(netStatsService, tcpRxPacketsId, TCP_RX_PACKETS);
-    env->SetStaticIntField(netStatsService, tcpTxPacketsId, TCP_TX_PACKETS);
-
     return jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsService", gMethods,
                                     NELEM(gMethods));
 }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 72ad366..29bf374 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -22,13 +22,14 @@
 
 // Version of services.net for usage by the wifi mainline module.
 // Note: This is compiled against module_current.
-// TODO(b/145825329): This should be moved to networkstack-client,
+// TODO(b/172457099): This should be moved to networkstack-client,
 // with dependencies moved to frameworks/libs/net right.
 java_library {
     name: "services.net-module-wifi",
     srcs: [
         ":framework-services-net-module-wifi-shared-srcs",
         ":net-module-utils-srcs",
+        ":net-utils-services-common-srcs",
         "java/android/net/ip/IpClientCallbacks.java",
         "java/android/net/ip/IpClientManager.java",
         "java/android/net/ip/IpClientUtil.java",
@@ -39,6 +40,7 @@
         "java/android/net/TcpKeepalivePacketData.java",
     ],
     sdk_version: "module_current",
+    min_sdk_version: "30",
     libs: [
         "unsupportedappusage",
         "framework-wifi-util-lib",
@@ -49,7 +51,6 @@
         "netd_aidl_interface-V3-java",
         "netlink-client",
         "networkstack-client",
-        "net-utils-services-common",
     ],
     apex_available: [
         "com.android.wifi",
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 1944965..0d878b4 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -28,7 +28,6 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.UpdateEngine;
 import android.os.UpdateEngineCallback;
 import android.provider.DeviceConfig;
@@ -227,8 +226,8 @@
         }
 
         // Sample for a fraction of app launches.
-        int traceFrequency =
-                SystemProperties.getInt("persist.profcollectd.applaunch_trace_freq", 2);
+        int traceFrequency = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+                "applaunch_trace_freq", 2);
         int randomNum = ThreadLocalRandom.current().nextInt(100);
         if (randomNum < traceFrequency) {
             try {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 553df3b..63d7dbd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -353,6 +353,147 @@
         assertMessageValidity("40:35:EE:52:4A").isEqualTo(ERROR_PARAMETER);
     }
 
+    @Test
+    public void isValid_deckControl() {
+        assertMessageValidity("40:42:01:6E").isEqualTo(OK);
+        assertMessageValidity("40:42:04").isEqualTo(OK);
+
+        assertMessageValidity("4F:42:01").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:42:04").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:42").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("40:42:05").isEqualTo(ERROR_PARAMETER);
+    }
+
+    @Test
+    public void isValid_deckStatus() {
+        assertMessageValidity("40:1B:11:58").isEqualTo(OK);
+        assertMessageValidity("40:1B:1F").isEqualTo(OK);
+
+        assertMessageValidity("4F:1B:11").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:1B:1F").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:1B").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("40:1B:10").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:1B:20").isEqualTo(ERROR_PARAMETER);
+    }
+
+    @Test
+    public void isValid_statusRequest() {
+        assertMessageValidity("40:08:01").isEqualTo(OK);
+        assertMessageValidity("40:08:02:5C").isEqualTo(OK);
+        assertMessageValidity("40:1A:01:F8").isEqualTo(OK);
+        assertMessageValidity("40:1A:03").isEqualTo(OK);
+
+        assertMessageValidity("4F:08:01").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:08:03").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("4F:1A:01").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:1A:03").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:08").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("40:1A").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("40:08:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:08:05").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:1A:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:1A:04").isEqualTo(ERROR_PARAMETER);
+    }
+
+    @Test
+    public void isValid_play() {
+        assertMessageValidity("40:41:16:E3").isEqualTo(OK);
+        assertMessageValidity("40:41:20").isEqualTo(OK);
+
+        assertMessageValidity("4F:41:16").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:41:20").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:41").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("40:41:04").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:41:18").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:41:23").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:41:26").isEqualTo(ERROR_PARAMETER);
+    }
+
+    @Test
+    public void isValid_selectAnalogueService() {
+        assertMessageValidity("40:92:00:13:0F:00:96").isEqualTo(OK);
+        assertMessageValidity("40:92:02:EA:60:1F").isEqualTo(OK);
+
+        assertMessageValidity("4F:92:00:13:0F:00").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:92:02:EA:60:1F").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:92:00:13:0F").isEqualTo(ERROR_PARAMETER_SHORT);
+        // Invalid Analogue Broadcast type
+        assertMessageValidity("40:92:03:EA:60:1F").isEqualTo(ERROR_PARAMETER);
+        // Invalid Analogue Frequency
+        assertMessageValidity("40:92:00:FF:FF:00").isEqualTo(ERROR_PARAMETER);
+        // Invalid Broadcast system
+        assertMessageValidity("40:92:02:EA:60:20").isEqualTo(ERROR_PARAMETER);
+    }
+
+    @Test
+    public void isValid_selectDigitalService() {
+        assertMessageValidity("40:93:00:11:CE:90:0F:00:78").isEqualTo(OK);
+        assertMessageValidity("40:93:10:13:0B:34:38").isEqualTo(OK);
+        assertMessageValidity("40:93:9A:06:F9:D3:E6").isEqualTo(OK);
+        assertMessageValidity("40:93:91:09:F4:40:C8").isEqualTo(OK);
+
+        assertMessageValidity("4F:93:00:11:CE:90:0F:00:78").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:93:10:13:0B:34:38").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:93:9A:06:F9").isEqualTo(ERROR_PARAMETER_SHORT);
+        // Invalid Digital Broadcast System
+        assertMessageValidity("40:93:14:11:CE:90:0F:00:78").isEqualTo(ERROR_PARAMETER);
+        // Invalid Digital Broadcast System
+        assertMessageValidity("40:93:A0:07:95:F1").isEqualTo(ERROR_PARAMETER);
+        // Insufficient data for ARIB Broadcast system
+        assertMessageValidity("40:93:00:11:CE:90:0F:00").isEqualTo(ERROR_PARAMETER);
+        // Insufficient data for ATSC Broadcast system
+        assertMessageValidity("40:93:10:13:0B:34").isEqualTo(ERROR_PARAMETER);
+        // Insufficient data for DVB Broadcast system
+        assertMessageValidity("40:93:18:BE:77:00:7D:01").isEqualTo(ERROR_PARAMETER);
+        // Invalid channel number format
+        assertMessageValidity("40:93:9A:10:F9:D3").isEqualTo(ERROR_PARAMETER);
+        // Insufficient data for 2 part channel number
+        assertMessageValidity("40:93:91:09:F4:40").isEqualTo(ERROR_PARAMETER);
+    }
+
+    @Test
+    public void isValid_UserControlPressed() {
+        assertMessageValidity("40:44:07").isEqualTo(OK);
+        assertMessageValidity("40:44:52:A7").isEqualTo(OK);
+
+        assertMessageValidity("40:44:60").isEqualTo(OK);
+        assertMessageValidity("40:44:60:1A").isEqualTo(OK);
+
+        assertMessageValidity("40:44:67").isEqualTo(OK);
+        assertMessageValidity("40:44:67:04:00:B1").isEqualTo(OK);
+        assertMessageValidity("40:44:67:09:C8:72:C8").isEqualTo(OK);
+
+        assertMessageValidity("40:44:68").isEqualTo(OK);
+        assertMessageValidity("40:44:68:93").isEqualTo(OK);
+        assertMessageValidity("40:44:69").isEqualTo(OK);
+        assertMessageValidity("40:44:69:7C").isEqualTo(OK);
+        assertMessageValidity("40:44:6A").isEqualTo(OK);
+        assertMessageValidity("40:44:6A:B4").isEqualTo(OK);
+
+        assertMessageValidity("40:44:56").isEqualTo(OK);
+        assertMessageValidity("40:44:56:60").isEqualTo(OK);
+
+        assertMessageValidity("40:44:57").isEqualTo(OK);
+        assertMessageValidity("40:44:57:A0").isEqualTo(OK);
+
+        assertMessageValidity("4F:44:07").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:44:52:A7").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:44").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("40:44:67:04:B1").isEqualTo(ERROR_PARAMETER_SHORT);
+        // Invalid Play mode
+        assertMessageValidity("40:44:60:04").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:44:60:08").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:44:60:26").isEqualTo(ERROR_PARAMETER);
+        // Invalid Channel Identifier - Channel number format
+        assertMessageValidity("40:44:67:11:8A:42").isEqualTo(ERROR_PARAMETER);
+        // Insufficient data for 2 - part channel number
+        assertMessageValidity("40:44:67:09:C8:72").isEqualTo(ERROR_PARAMETER);
+        // Invalid UI Broadcast type
+        assertMessageValidity("40:44:56:11").isEqualTo(ERROR_PARAMETER);
+        // Invalid UI Sound Presentation Control
+        assertMessageValidity("40:44:57:40").isEqualTo(ERROR_PARAMETER);
+    }
+
     private IntegerSubject assertMessageValidity(String message) {
         return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
     }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index bbf34df..724a9e4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -767,6 +767,19 @@
             "android.telecom.extra.AUDIO_CODEC";
 
     /**
+     * Float connection extra key used to store the audio codec bitrate in kbps for the current
+     * {@link Connection}.
+     */
+    public static final String EXTRA_AUDIO_CODEC_BITRATE_KBPS =
+            "android.telecom.extra.AUDIO_CODEC_BITRATE_KBPS";
+
+    /**
+     * Float connection extra key used to store the audio codec bandwidth in khz for the current
+     * {@link Connection}.
+     */
+    public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ =
+            "android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ";
+    /**
      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
      * {@link #sendConnectionEvent(String, Bundle)}.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 982e5f3..07de617 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -680,6 +680,7 @@
     public static abstract class VideoCall {
 
         /** @hide */
+        @SuppressWarnings("HiddenAbstractMethod")
         public abstract void destroy();
 
         /**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 724c171..da2d4d8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -32,6 +32,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -1227,7 +1228,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public List<PhoneAccountHandle> getPhoneAccountsForPackage() {
         try {
             if (isServiceConnected()) {
@@ -1355,7 +1356,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void clearPhoneAccounts() {
         clearAccounts();
     }
@@ -1365,7 +1366,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void clearAccounts() {
         try {
             if (isServiceConnected()) {
@@ -1397,7 +1398,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public ComponentName getDefaultPhoneApp() {
         try {
             if (isServiceConnected()) {
@@ -1589,6 +1590,30 @@
     }
 
     /**
+     * Returns whether the caller has {@link InCallService} access for companion apps.
+     *
+     * A companion app is an app associated with a physical wearable device via the
+     * {@link android.companion.CompanionDeviceManager} API.
+     *
+     * @return {@code true} if the caller has {@link InCallService} access for
+     *      companion app; {@code false} otherwise.
+     */
+    public boolean hasCompanionInCallServiceAccess() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().hasCompanionInCallServiceAccess(
+                        mContext.getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e);
+            if (!isSystemProcess()) {
+                e.rethrowAsRuntimeException();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns whether there is an ongoing call originating from a managed
      * {@link ConnectionService}.  An ongoing call can be in dialing, ringing, active or holding
      * states.
@@ -2384,6 +2409,10 @@
         }
     }
 
+    private boolean isSystemProcess() {
+        return Process.myUid() == Process.SYSTEM_UID;
+    }
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index a28a999..6dc096d 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -179,6 +179,11 @@
     boolean isInCall(String callingPackage, String callingFeatureId);
 
     /**
+     * @see TelecomServiceImpl#hasCompanionInCallServiceAccess
+     */
+    boolean hasCompanionInCallServiceAccess(String callingPackage);
+
+    /**
      * @see TelecomServiceImpl#isInManagedCall
      */
     boolean isInManagedCall(String callingPackage, String callingFeatureId);
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 7aecfdd..d1412b7 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -314,7 +315,7 @@
                 String[] packageNames = new String[enabledCarrierPackages.size()];
                 enabledCarrierPackages.toArray(packageNames);
                 permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames,
-                        UserHandle.of(userId), Runnable::run, isSuccess -> { });
+                        UserHandle.of(userId), TelephonyUtils.DIRECT_EXECUTOR, isSuccess -> { });
             }
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(TAG, "Could not reach PackageManager", e);
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 7736473..02d7410 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -34,6 +34,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
@@ -44,6 +45,8 @@
     public static boolean IS_USER = "user".equals(android.os.Build.TYPE);
     public static boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1;
 
+    public static final Executor DIRECT_EXECUTOR = Runnable::run;
+
     /**
      * Verify that caller holds {@link android.Manifest.permission#DUMP}.
      *
diff --git a/telephony/java/android/telephony/CarrierBandwidth.aidl b/telephony/java/android/telephony/CarrierBandwidth.aidl
new file mode 100644
index 0000000..d0861b8
--- /dev/null
+++ b/telephony/java/android/telephony/CarrierBandwidth.aidl
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+parcelable CarrierBandwidth;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java
new file mode 100644
index 0000000..17747a3
--- /dev/null
+++ b/telephony/java/android/telephony/CarrierBandwidth.java
@@ -0,0 +1,208 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Defines downlink and uplink capacity of a network in kbps
+ * @hide
+ */
+@SystemApi
+public final class CarrierBandwidth implements Parcelable {
+    /**
+     * Any field that is not reported shall be set to INVALID
+     */
+    public static final int INVALID = -1;
+
+    /**
+     * Estimated downlink capacity in kbps of the primary carrier.
+     * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth.
+     * This will be {@link #INVALID} if the network is not connected
+     */
+    private int mPrimaryDownlinkCapacityKbps;
+
+    /**
+     * Estimated uplink capacity in kbps of the primary carrier.
+     * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth.
+     * This will be {@link #INVALID} if the network is not connected
+     */
+    private int mPrimaryUplinkCapacityKbps;
+
+    /**
+     * Estimated downlink capacity in kbps of the secondary carrier in a dual connected network.
+     * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth.
+     * This will be {@link #INVALID} if the network is not connected
+     */
+    private int mSecondaryDownlinkCapacityKbps;
+
+    /**
+     * Estimated uplink capacity in kbps of the secondary carrier in a dual connected network.
+     * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth.
+     * This will be {@link #INVALID} if the network is not connected
+     */
+    private int mSecondaryUplinkCapacityKbps;
+
+    /** @hide **/
+    public CarrierBandwidth(Parcel in) {
+        mPrimaryDownlinkCapacityKbps = in.readInt();
+        mPrimaryUplinkCapacityKbps = in.readInt();
+        mSecondaryDownlinkCapacityKbps = in.readInt();
+        mSecondaryUplinkCapacityKbps = in.readInt();
+    }
+
+    /** @hide **/
+    public CarrierBandwidth() {
+        mPrimaryDownlinkCapacityKbps = INVALID;
+        mPrimaryUplinkCapacityKbps = INVALID;
+        mSecondaryDownlinkCapacityKbps = INVALID;
+        mSecondaryUplinkCapacityKbps = INVALID;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param primaryDownlinkCapacityKbps Estimated downlink capacity in kbps of
+     *        the primary carrier.
+     * @param primaryUplinkCapacityKbps Estimated uplink capacity in kbps of
+     *        the primary carrier.
+     * @param secondaryDownlinkCapacityKbps Estimated downlink capacity in kbps of
+     *        the secondary carrier
+     * @param secondaryUplinkCapacityKbps Estimated uplink capacity in kbps of
+     *        the secondary carrier
+     */
+    public CarrierBandwidth(int primaryDownlinkCapacityKbps, int primaryUplinkCapacityKbps,
+            int secondaryDownlinkCapacityKbps, int secondaryUplinkCapacityKbps) {
+        mPrimaryDownlinkCapacityKbps = primaryDownlinkCapacityKbps;
+        mPrimaryUplinkCapacityKbps = primaryUplinkCapacityKbps;
+        mSecondaryDownlinkCapacityKbps = secondaryDownlinkCapacityKbps;
+        mSecondaryUplinkCapacityKbps = secondaryUplinkCapacityKbps;
+    }
+
+    /**
+     * Retrieves the upstream bandwidth for the primary network in Kbps.  This always only refers to
+     * the estimated first hop transport bandwidth.
+     * This will be INVALID if the network is not connected
+     *
+     * @return The estimated first hop upstream (device to network) bandwidth.
+     */
+    public int getPrimaryDownlinkCapacityKbps() {
+        return mPrimaryDownlinkCapacityKbps;
+    }
+
+    /**
+     * Retrieves the downstream bandwidth for the primary network in Kbps.  This always only refers
+     * to the estimated first hop transport bandwidth.
+     * This will be INVALID if the network is not connected
+     *
+     * @return The estimated first hop downstream (network to device) bandwidth.
+     */
+    public int getPrimaryUplinkCapacityKbps() {
+        return mPrimaryUplinkCapacityKbps;
+    }
+
+    /**
+     * Retrieves the upstream bandwidth for the secondary network in Kbps.  This always only refers
+     * to the estimated first hop transport bandwidth.
+     * This will be INVALID if the network is not connected
+     *
+     * @return The estimated first hop upstream (device to network) bandwidth.
+     */
+    public int getSecondaryDownlinkCapacityKbps() {
+        return mSecondaryDownlinkCapacityKbps;
+    }
+
+    /**
+     * Retrieves the downstream bandwidth for the secondary network in Kbps.  This always only
+     * refers to the estimated first hop transport bandwidth.
+     * This will be INVALID if the network is not connected
+     *
+     * @return The estimated first hop downstream (network to device) bandwidth.
+     */
+    public int getSecondaryUplinkCapacityKbps() {
+        return mSecondaryUplinkCapacityKbps;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "CarrierBandwidth: {primaryDownlinkCapacityKbps=" + mPrimaryDownlinkCapacityKbps
+                + " primaryUplinkCapacityKbps=" + mPrimaryUplinkCapacityKbps
+                + " secondaryDownlinkCapacityKbps=" + mSecondaryDownlinkCapacityKbps
+                + " secondaryUplinkCapacityKbps=" + mSecondaryUplinkCapacityKbps
+                + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mPrimaryDownlinkCapacityKbps,
+                mPrimaryUplinkCapacityKbps,
+                mSecondaryDownlinkCapacityKbps,
+                mSecondaryUplinkCapacityKbps);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) {
+            return false;
+        }
+        if (this == o) {
+            return true;
+        }
+        CarrierBandwidth s = (CarrierBandwidth) o;
+        return (mPrimaryDownlinkCapacityKbps == s.mPrimaryDownlinkCapacityKbps
+                && mPrimaryUplinkCapacityKbps == s.mPrimaryUplinkCapacityKbps
+                && mSecondaryDownlinkCapacityKbps == s.mSecondaryDownlinkCapacityKbps
+                && mSecondaryDownlinkCapacityKbps == s.mSecondaryDownlinkCapacityKbps);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mPrimaryDownlinkCapacityKbps);
+        dest.writeInt(mPrimaryUplinkCapacityKbps);
+        dest.writeInt(mSecondaryDownlinkCapacityKbps);
+        dest.writeInt(mSecondaryUplinkCapacityKbps);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CarrierBandwidth> CREATOR =
+            new Parcelable.Creator() {
+            public CarrierBandwidth createFromParcel(Parcel in) {
+                return new CarrierBandwidth(in);
+            }
+
+            public CarrierBandwidth[] newArray(int size) {
+                return new CarrierBandwidth[size];
+            }
+    };
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0d55706..0c0943d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -79,6 +79,30 @@
      */
     public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
 
+    /**
+     * Only send USSD over IMS while CS is out of service, otherwise send USSD over CS.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_CS_PREFERRED   = 0;
+
+    /**
+     * Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_IMS_PREFERRED  = 1;
+
+    /**
+     * Only send USSD over CS.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_CS_ONLY        = 2;
+
+    /**
+     * Only send USSD over IMS and disallow silent redial over CS.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_IMS_ONLY       = 3;
+
     private final Context mContext;
 
     /**
@@ -584,6 +608,20 @@
     public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
 
     /**
+     * Specify the method of selection for UE sending USSD requests. The default value is
+     * {@link #USSD_OVER_CS_PREFERRED}.
+     * <p> Available options:
+     * <ul>
+     *   <li>0: {@link #USSD_OVER_CS_PREFERRED} </li>
+     *   <li>1: {@link #USSD_OVER_IMS_PREFERRED} </li>
+     *   <li>2: {@link #USSD_OVER_CS_ONLY} </li>
+     *   <li>3: {@link #USSD_OVER_IMS_ONLY} </li>
+     * </ul>
+     */
+    public static final String KEY_CARRIER_USSD_METHOD_INT =
+            "carrier_ussd_method_int";
+
+    /**
      * Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
      * By default this value is {@code false}.
      *
@@ -3969,6 +4007,7 @@
         sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+        sDefaults.putInt(KEY_CARRIER_USSD_METHOD_INT, USSD_OVER_CS_PREFERRED);
         sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false);
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
@@ -4743,7 +4782,7 @@
      */
     @NonNull
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public static PersistableBundle getDefaultConfig() {
         return new PersistableBundle(sDefaults);
     }
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 8f5ec36..e595002 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -98,12 +98,14 @@
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract void fillInNotifierBundle(Bundle bundle);
 
     /**
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract boolean isEmpty();
 
@@ -111,6 +113,7 @@
      * Invalidate this object.  The location area code and the cell id are set to -1.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract void setStateInvalid();
 
     /**
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 28feab2..42d7707 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -22,7 +22,12 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.telephony.BinderCacheManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+
+import com.android.internal.telephony.ITelephony;
 
 /**
  * Provides access to information about Telephony IMS services on the device.
@@ -30,8 +35,6 @@
 @SystemService(Context.TELEPHONY_IMS_SERVICE)
 public class ImsManager {
 
-    private Context mContext;
-
     /**
      * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the
      * network due to the network returning a "forbidden" response. This may be due to a
@@ -87,6 +90,14 @@
     public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
             "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
 
+    // Cache Telephony Binder interfaces, one cache per process.
+    private static final BinderCacheManager<ITelephony> sTelephonyCache =
+            new BinderCacheManager<>(ImsManager::getITelephonyInterface);
+    private static final BinderCacheManager<IImsRcsController> sRcsCache =
+            new BinderCacheManager<>(ImsManager::getIImsRcsControllerInterface);
+
+    private final Context mContext;
+
     /**
      * Use {@link Context#getSystemService(String)} to get an instance of this class.
      * @hide
@@ -108,7 +119,7 @@
             throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
         }
 
-        return new ImsRcsManager(mContext, subscriptionId);
+        return new ImsRcsManager(mContext, subscriptionId, sRcsCache);
     }
 
     /**
@@ -124,17 +135,19 @@
             throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
         }
 
-        return new ImsMmTelManager(subscriptionId);
+        return new ImsMmTelManager(subscriptionId, sTelephonyCache);
     }
 
     /**
-     * Create an instance of SipDelegateManager for the subscription id specified.
+     * Create an instance of {@link SipDelegateManager} for the subscription id specified.
      * <p>
-     * Used for RCS single registration cases, where an IMS application needs to forward SIP
-     * traffic through the device's IMS service.
-     * @param subscriptionId The ID of the subscription that this SipDelegateManager will use.
+     * Allows an IMS application to forward SIP traffic through the device's IMS service,
+     * which is used for cellular carriers that require the device to share a single IMS
+     * registration for both MMTEL and RCS features.
+     * @param subscriptionId The ID of the subscription that this {@link SipDelegateManager} will
+     *                       be bound to.
      * @throws IllegalArgumentException if the subscription is invalid.
-     * @return a SipDelegateManager instance for the specified subscription ID.
+     * @return a {@link SipDelegateManager} instance for the specified subscription ID.
      * @hide
      */
     @SystemApi
@@ -144,6 +157,22 @@
             throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
         }
 
-        return new SipDelegateManager(mContext, subscriptionId);
+        return new SipDelegateManager(mContext, subscriptionId, sRcsCache);
+    }
+
+    private static IImsRcsController getIImsRcsControllerInterface() {
+        return IImsRcsController.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyImsServiceRegisterer()
+                        .get());
+    }
+
+    private static ITelephony getITelephonyInterface() {
+        return ITelephony.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
     }
 }
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index f8a200a..8507d85 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.TransportType;
@@ -635,7 +636,8 @@
                 .append(" cellIdentity=").append(mCellIdentity)
                 .append(" voiceSpecificInfo=").append(mVoiceSpecificInfo)
                 .append(" dataSpecificInfo=").append(mDataSpecificInfo)
-                .append(" nrState=").append(nrStateToString(mNrState))
+                .append(" nrState=").append(Build.IS_DEBUGGABLE
+                        ? nrStateToString(mNrState) : "****")
                 .append(" rRplmn=").append(mRplmn)
                 .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
                 .append("}").toString();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 41b3ee6..dedb1af 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1102,7 +1102,8 @@
                     .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation())
                     .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
                     .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos)
-                    .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
+                    .append(", mNrFrequencyRange=").append(Build.IS_DEBUGGABLE
+                            ? mNrFrequencyRange : FREQUENCY_RANGE_UNKNOWN)
                     .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
                     .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
                     .append(", mIsDataRoamingFromRegistration=")
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 88aec51..6f88cbd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4656,7 +4656,7 @@
      * be implemented instead.
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
     }
 
@@ -4671,7 +4671,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
         return false;
     }
@@ -4690,7 +4690,7 @@
      * @hide
      */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     @Nullable
     public Bundle getVisualVoicemailSettings(){
         try {
@@ -8524,7 +8524,7 @@
 
     /** @hide */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public int checkCarrierPrivilegesForPackage(String pkgName) {
         try {
             ITelephony telephony = getITelephony();
@@ -8540,7 +8540,7 @@
 
     /** @hide */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
         try {
             ITelephony telephony = getITelephony();
@@ -8616,7 +8616,7 @@
 
     /** @hide */
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void dial(String number) {
         try {
             ITelephony telephony = getITelephony();
@@ -8675,7 +8675,7 @@
      */
     @Deprecated
     @SystemApi
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void silenceRinger() {
         // No-op
     }
@@ -13440,6 +13440,33 @@
     }
 
     /**
+     * Get carrier bandwidth. In case of Dual connected network this will report
+     * bandwidth per primary and secondary network.
+     * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull
+    public CarrierBandwidth getCarrierBandwidth() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCarrierBandwidth(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getCarrierBandwidth RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+
+        //Should not reach. Adding return statement to make compiler happy
+        return null;
+    }
+
+    /**
      * Called when userActivity is signalled in the power manager.
      * This should only be called from system Uid.
      * @hide
@@ -13756,6 +13783,15 @@
     }
 
     /**
+     * Setup sITelephony for testing.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void setupITelephonyForTest(ITelephony telephony) {
+        sITelephony = telephony;
+    }
+
+    /**
      * Whether device can connect to 5G network when two SIMs are active.
      * @hide
      * TODO b/153669716: remove or make system API.
diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
new file mode 100644
index 0000000..beec4a6
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Callback interface provided to the SipTransport implementation to notify a remote application of
+ * the following:
+ * <ul>
+ *     <li>A new incoming SIP message associated with the feature tags the SipDelegate registered
+ *     with has been received or an in-dialog request to this SipDelegate has been received.</li>
+ *     <li>Acknowledge that an outgoing SIP message from the RCS application has been sent
+ *     successfully or notify the application of the reason why it was not sent</li>
+ * </ul>
+ * @hide
+ */
+public interface DelegateMessageCallback {
+
+    /**
+     * Send a new incoming SIP message to the remote application for processing.
+     */
+    void onMessageReceived(@NonNull SipMessage message);
+
+    /**
+     * Notify the remote application that a previous request to send a SIP message using
+     * {@link SipDelegate#sendMessage} has succeeded.
+     *
+     * @param viaTransactionId The transaction ID found in the via header field of the
+     *         previously sent {@link SipMessage}.
+     */
+    void onMessageSent(@NonNull String viaTransactionId);
+
+    /**
+     * Notify the remote application that a previous request to send a SIP message using
+     * {@link SipDelegate#sendMessage} has failed.
+     *
+     * @param viaTransactionId The Transaction ID found in the via header field of the previously
+     *         sent {@link SipMessage}.
+     * @param reason The reason for the failure.
+     */
+    void onMessageSendFailure(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl
new file mode 100644
index 0000000..756ea92
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable DelegateRegistrationState;
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
new file mode 100644
index 0000000..4facfa7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the full state of the IMS feature tags associated with a SipDelegate and managed by the
+ * ImsService.
+ * @hide
+ */
+public final class DelegateRegistrationState implements Parcelable {
+
+    /**
+     * This feature tag has been deregistered for an unknown reason. Outgoing out-of-dialog SIP
+     * messages associated with feature tags that are not registered will fail.
+     */
+    public static final int DEREGISTERED_REASON_UNKNOWN = 0;
+
+    /**
+     * This feature tag has been deregistered because it is not provisioned to be used on this radio
+     * access technology or PDN. Outgoing out-of-dialog SIP messages associated with feature tags
+     * that are not registered will fail.
+     * <p>
+     * There may be new incoming SIP dialog requests on a feature that that is not provisioned. It
+     * is still expected that the SipDelegateConnection responds to the request.
+     */
+    public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1;
+
+    /**
+     * This feature tag has been deregistered because IMS has been deregistered. All outgoing SIP
+     * messages will fail until IMS registration occurs.
+     */
+    public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2;
+
+    /**
+     * This feature tag is being deregistered because the PDN that the IMS registration is on is
+     *changing.
+     * All open SIP dialogs need to be closed before the PDN change can proceed.
+     */
+    public static final int DEREGISTERING_REASON_PDN_CHANGE = 3;
+
+    /**
+     * This feature tag is being deregistered due to a provisioning change. This can be triggered by
+     * many things, such as a provisioning change triggered by the carrier network, a radio access
+     * technology change by the modem causing a different set of feature tags to be provisioned, or
+     * a user triggered hange, such as data being enabled/disabled.
+     * <p>
+     * All open SIP dialogs associated with the new deprovisioned feature tag need to be closed
+     * before the IMS registration modification can proceed.
+     */
+    public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4;
+
+    /**
+     * This feature tag is deregistering because the SipDelegate associated with this feature tag
+     * needs to change its supported feature set.
+     * <p>
+     * All open SIP Dialogs associated with this feature tag must be closed before this operation
+     * can proceed.
+     */
+    public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5;
+
+    /**
+     * This feature tag is deregistering because the SipDelegate is in the process of being
+     * destroyed.
+     * <p>
+     * All open SIP Dialogs associated with this feature tag must be closed before this operation
+     * can proceed.
+     */
+    public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DEREGISTERED_REASON_", value = {
+            DEREGISTERED_REASON_UNKNOWN,
+            DEREGISTERED_REASON_NOT_PROVISIONED,
+            DEREGISTERED_REASON_NOT_REGISTERED
+    })
+    public @interface DeregisteredReason {}
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DEREGISTERING_REASON_", value = {
+            DEREGISTERING_REASON_PDN_CHANGE,
+            DEREGISTERING_REASON_PROVISIONING_CHANGE,
+            DEREGISTERING_REASON_FEATURE_TAGS_CHANGING,
+            DEREGISTERING_REASON_DESTROY_PENDING
+    })
+    public @interface DeregisteringReason {}
+
+    private final ArrayList<String> mRegisteredTags = new ArrayList<>();
+    private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>();
+    private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>();
+
+    /**
+     * Builder used to create new instances of {@link DelegateRegistrationState}.
+     */
+    public static class Builder {
+
+        private final DelegateRegistrationState mState;
+
+        /* Create a new instance of {@link Builder} */
+        public Builder() {
+            mState = new DelegateRegistrationState();
+        }
+
+        /**
+         * Add a feature tag that is currently included in the current network IMS Registration.
+         * @param featureTag The IMS media feature tag included in the current IMS registration.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public Builder addRegisteredFeatureTag(@NonNull String featureTag) {
+            if (!mState.mRegisteredTags.contains(featureTag)) {
+                mState.mRegisteredTags.add(featureTag);
+            }
+            return this;
+        }
+
+        /**
+         * Add a list of feature tags that are currently included in the current network IMS
+         * Registration.
+         * @param featureTags The IMS media feature tags included in the current IMS registration.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
+            mState.mRegisteredTags.addAll(featureTags);
+            return this;
+        }
+
+        /**
+         * Add a feature tag that is in the current network IMS Registration, but is in the progress
+         * of being deregistered and requires action from the RCS application before the IMS
+         * registration can be modified.
+         *
+         * See {@link DeregisteringReason} for more information regarding what is required by the
+         * RCS application to proceed.
+         *
+         * @param featureTag The media feature tag that has limited or no availability due to its
+         *         current deregistering state.
+         * @param reason The reason why the media feature tag has moved to the deregistering state.
+         *         The availability of the feature tag depends on the {@link DeregisteringReason}.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public Builder addDeregisteringFeatureTag(@NonNull String featureTag,
+                @DeregisteringReason int reason) {
+            boolean ftExists = mState.mDeregisteringTags.stream().anyMatch(
+                    f -> f.getFeatureTag().equals(featureTag));
+            if (!ftExists) {
+                mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
+            }
+            return this;
+        }
+
+        /**
+         * Add a feature tag that is currently not included in the network RCS registration. See
+         * {@link DeregisteredReason} for more information regarding the reason for why the feature
+         * tag is not registered.
+         * @param featureTag The media feature tag that is not registered.
+         * @param reason The reason why the media feature tag has been deregistered.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public Builder addDeregisteredFeatureTag(@NonNull String featureTag,
+                @DeregisteredReason int reason) {
+            boolean ftExists = mState.mDeregisteredTags.stream().anyMatch(
+                    f -> f.getFeatureTag().equals(featureTag));
+            if (!ftExists) {
+                mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
+            }
+            return this;
+        }
+
+        /**
+         * @return the finalized instance.
+         */
+        public DelegateRegistrationState build() {
+            return mState;
+        }
+    }
+
+    /**
+     * The builder should be used to construct a new instance of this class.
+     */
+    private DelegateRegistrationState() {}
+
+    /**
+     * Used for unparcelling only.
+     */
+    private DelegateRegistrationState(Parcel source) {
+        source.readList(mRegisteredTags, null /*classloader*/);
+        readStateFromParcel(source, mDeregisteringTags);
+        readStateFromParcel(source, mDeregisteredTags);
+    }
+
+    /**
+     * Get the feature tags that this SipDelegate is associated with that are currently part of the
+     * network IMS registration. SIP Messages both in and out of a SIP Dialog may be sent and
+     * received using these feature tags.
+     * @return A Set of feature tags that the SipDelegate has associated with that are included in
+     * the network IMS registration.
+     */
+    public @NonNull Set<String> getRegisteredFeatureTags() {
+        return new ArraySet<>(mRegisteredTags);
+    }
+
+    /**
+     * Get the feature tags that this SipDelegate is associated with that are currently part of the
+     * network IMS registration but are in the process of being deregistered.
+     * <p>
+     * Any incoming SIP messages associated with a feature tag included in this list will still be
+     * delivered. Outgoing SIP messages that are still in-dialog will be delivered to the
+     * SipDelegate, but outgoing out-of-dialog SIP messages with  a feature tag that is included in
+     * this list will fail.
+     * <p>
+     * The SipDelegate will stay in this state for a limited period of time while it waits for the
+     * RCS application to perform a specific action. More details on the actions that can cause this
+     * state as well as the expected response are included in the reason codes and can be found in
+     * {@link DeregisteringReason}.
+     * @return A Set of feature tags that the SipDelegate has associated with that are included in
+     * the network IMS registration but are in the process of deregistering.
+     */
+    public @NonNull Set<FeatureTagState> getDeregisteringFeatureTags() {
+        return new ArraySet<>(mDeregisteringTags);
+    }
+
+    /**
+     * Get the list of feature tags that are associated with this SipDelegate but are not currently
+     * included in the network IMS registration.
+     * <p>
+     * See {@link DeregisteredReason} codes for more information related to the reasons why this may
+     * occur.
+     * <p>
+     * Due to network race conditions, there may still be onditions where an incoming out-of-dialog
+     * SIP message is delivered for a feature tag that is considered deregistered. Due to this
+     * condition, in-dialog outgoing SIP messages for deregistered feature tags will still be
+     * allowed as long as they are in response to a dialog started by a remote party. Any outgoing
+     * out-of-dialog SIP messages associated with feature tags included in this list will fail to be
+     * sent.
+     * @return A list of feature tags that the SipDelegate has associated with that not included in
+     * the network IMS registration.
+     */
+    public @NonNull Set<FeatureTagState> getDeregisteredFeatureTags() {
+        return new ArraySet<>(mDeregisteredTags);
+    }
+
+    public static final Creator<DelegateRegistrationState> CREATOR =
+            new Creator<DelegateRegistrationState>() {
+        @Override
+        public DelegateRegistrationState createFromParcel(Parcel source) {
+            return new DelegateRegistrationState(source);
+        }
+
+        @Override
+        public DelegateRegistrationState[] newArray(int size) {
+            return new DelegateRegistrationState[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeList(mRegisteredTags);
+        writeStateToParcel(dest, mDeregisteringTags);
+        writeStateToParcel(dest, mDeregisteredTags);
+    }
+
+    private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) {
+        dest.writeInt(state.size());
+        for (FeatureTagState s : state) {
+            dest.writeString(s.getFeatureTag());
+            dest.writeInt(s.getState());
+        }
+    }
+
+    private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) {
+        int len = source.readInt();
+        for (int i = 0; i < len; i++) {
+            String ft = source.readString();
+            int reason = source.readInt();
+            emptyState.add(new FeatureTagState(ft, reason));
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DelegateRegistrationState that = (DelegateRegistrationState) o;
+        return mRegisteredTags.equals(that.mRegisteredTags)
+                && mDeregisteringTags.equals(that.mDeregisteringTags)
+                && mDeregisteredTags.equals(that.mDeregisteredTags);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.aidl b/telephony/java/android/telephony/ims/DelegateRequest.aidl
new file mode 100644
index 0000000..60c990f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable DelegateRequest;
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java
new file mode 100644
index 0000000..f384901
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRequest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains information required for the creation of a {@link SipDelegate} and the associated
+ * SipDelegateConnection given back to the requesting application.
+ * @hide
+ */
+public final class DelegateRequest implements Parcelable {
+
+    private final ArrayList<String> mFeatureTags;
+
+    /**
+     * Create a new DelegateRequest, which will be used to create a SipDelegate by the ImsService.
+     * @param featureTags The list of IMS feature tags that will be associated with the SipDelegate
+     *                    created using this DelegateRequest. All feature tags are expected to be in
+     *                    the format defined in RCC.07 section 2.6.1.3.
+     */
+    public DelegateRequest(@NonNull Set<String> featureTags) {
+        if (featureTags == null) {
+            throw new IllegalStateException("Invalid arguments, featureTags List can not be null");
+        }
+        mFeatureTags = new ArrayList<>(featureTags);
+    }
+
+    /**
+     * @return the list of IMS feature tag associated with this DelegateRequest in the format
+     * defined in RCC.07 section 2.6.1.3.
+     */
+    public Set<String> getFeatureTags() {
+        return new ArraySet<>(mFeatureTags);
+    }
+
+    /**
+     * Internal constructor used only for unparcelling.
+     */
+    private DelegateRequest(Parcel in) {
+        mFeatureTags = new ArrayList<>();
+        in.readList(mFeatureTags, null /*classLoader*/);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeList(mFeatureTags);
+    }
+
+    public static final @NonNull Creator<DelegateRequest> CREATOR = new Creator<DelegateRequest>() {
+        @Override
+        public DelegateRequest createFromParcel(Parcel source) {
+            return new DelegateRequest(source);
+        }
+
+        @Override
+        public DelegateRequest[] newArray(int size) {
+            return new DelegateRequest[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DelegateRequest that = (DelegateRequest) o;
+        return mFeatureTags.equals(that.mFeatureTags);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFeatureTags);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
new file mode 100644
index 0000000..0f1afc4
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.ims.stub.SipDelegate;
+import android.telephony.ims.stub.SipTransportImplBase;
+
+import java.util.List;
+
+/**
+ * Callback interface to notify a remote application of the following:
+ * <ul>
+ *     <li>the {@link SipDelegate} associated with this callback has been created or destroyed in
+ *         response to a creation or destruction request from the framework</li>
+ *     <li>the SIP IMS configuration associated with this {@link SipDelegate} has changed</li>
+ *     <li>the IMS registration of the feature tags associated with this {@link SipDelegate} have
+ *         changed.</li>
+ * </ul>
+ * @hide
+ */
+public interface DelegateStateCallback {
+
+    /**
+     * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is
+     * called by the framework to notify the framework and remote application that the
+     * {@link SipDelegate} has been successfully created.
+     *
+     * @param delegate The SipDelegate created to service the DelegateRequest.
+     * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags
+     *    associated with this {@link SipDelegate} that have no access to send/receive SIP messages
+     *    as well as a reason for why the feature tag is denied. For more information on the reason
+     *    why the feature tag was denied access, see the
+     *    {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due
+     *    to this {@link SipDelegate} not supporting a feature or this ImsService already
+     *    implementing this feature elsewhere. If all features of this {@link SipDelegate} are
+     *    denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will
+     *    later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the
+     *    delegate up.
+     */
+    void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags);
+
+    /**
+     * This must be called by the ImsService after the framework calls
+     * {@link SipTransportImplBase#destroySipDelegate} to notify the framework and remote
+     * application that the procedure to destroy the {@link SipDelegate} has been completed.
+     * @param reasonCode The reason for closing this delegate.
+     */
+    void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reasonCode);
+
+    /**
+     * Call to notify the remote application of a configuration change associated with this
+     * {@link SipDelegate}.
+     * <p>
+     * The remote application will not be able to proceed sending SIP messages until after this
+     * configuration is sent the first time, so this configuration should be sent as soon as the
+     * {@link SipDelegate} has access to these configuration parameters.
+     * <p>
+     * Incoming SIP messages should not be routed to the remote application until AFTER this
+     * configuration change is sent to ensure that the remote application can respond correctly.
+     * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+     * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+     * change event to reduce conditions where the remote application is using a stale IMS
+     * configuration.
+     */
+    void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config);
+
+    /**
+     * Call to notify the remote application that the {@link SipDelegate} has modified the IMS
+     * registration state of the RCS feature tags that were requested as part of the initial
+     * {@link DelegateRequest}.
+     * <p>
+     * See {@link DelegateRegistrationState} for more information about how IMS Registration state
+     * should be communicated the associated SipDelegateConnection in cases such as
+     * IMS deregistration, handover, PDN change, provisioning changes, etc…
+     * <p>
+     * Note: Even after the status of the feature tags are updated here to deregistered, the
+     * SipDelegate must still be able to handle these messages and call
+     * {@link DelegateMessageCallback#onMessageSendFailure} to notify the RCS application that the
+     * message was not sent.
+     *
+     * @param registrationState The current network IMS registration state for all feature tags
+     *         associated with this SipDelegate.
+     */
+    void onFeatureTagRegistrationChanged(@NonNull DelegateRegistrationState registrationState);
+}
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl
new file mode 100644
index 0000000..bce5574
--- /dev/null
+++ b/telephony/java/android/telephony/ims/FeatureTagState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable FeatureTagState;
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java
new file mode 100644
index 0000000..060be6f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/FeatureTagState.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Maps an IMS media feature tag 3gpp universal resource name (URN) previously mapped to a
+ * {@link SipDelegate} in the associated {@link DelegateRequest} to its current availability
+ * state as set by the ImsService managing the related IMS registration.
+ *
+ * This class is only used to report more information about a IMS feature tag that is not fully
+ * available at this time.
+ * <p>
+ * Please see {@link DelegateRegistrationState}, {@link DelegateStateCallback}, and
+ * {@link DelegateConnectionStateCallback} for more information about how this class is used to
+ * convey the state of IMS feature tags that were requested by {@link DelegateRequest} but are not
+ * currently available.
+ * @hide
+ */
+public final class FeatureTagState implements Parcelable {
+
+    private final String mFeatureTag;
+    private final int mState;
+
+    /**
+     * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState}
+     * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged(
+     * DelegateRegistrationState, List)} and
+     * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this
+     * is used.
+     *
+     * @param featureTag The IMS feature tag that is deregistered, in the process of
+     *                   deregistering, or denied.
+     * @param state The {@link DelegateRegistrationState.DeregisteredReason},
+     *         {@link DelegateRegistrationState.DeregisteringReason}, or
+     *         {@link SipDelegateManager.DeniedReason} associated with this feature tag.
+     */
+    public FeatureTagState(@NonNull String featureTag, int state) {
+        mFeatureTag = featureTag;
+        mState = state;
+    }
+
+    /**
+     * Used for constructing instances during un-parcelling.
+     */
+    private FeatureTagState(Parcel source) {
+        mFeatureTag = source.readString();
+        mState = source.readInt();
+    }
+
+    /**
+     * @return The IMS feature tag string that is in the process of deregistering,
+     * deregistered, or denied.
+     */
+    public @NonNull String getFeatureTag() {
+        return mFeatureTag;
+    }
+
+    /**
+     * @return The reason for why the feature tag is currently in the process of deregistering,
+     * has been deregistered, or has been denied. See {@link DelegateRegistrationState} and
+     * {@link DelegateConnectionStateCallback} for more information.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mFeatureTag);
+        dest.writeInt(mState);
+    }
+
+    public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
+        @Override
+        public FeatureTagState createFromParcel(Parcel source) {
+            return new FeatureTagState(source);
+        }
+
+        @Override
+        public FeatureTagState[] newArray(int size) {
+            return new FeatureTagState[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        FeatureTagState that = (FeatureTagState) o;
+        return mState == that.mState
+                && mFeatureTag.equals(that.mFeatureTag);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFeatureTag, mState);
+    }
+
+    @Override
+    public String toString() {
+        return "FeatureTagState{" + "mFeatureTag='" + mFeatureTag + ", mState=" + mState + '}';
+    }
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9a55cec..1b51936 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -450,8 +450,6 @@
     /** Indicates if we have known the intent of the user for the call is emergency */
     private boolean mHasKnownUserIntentEmergency = false;
 
-    private Set<RtpHeaderExtensionType> mOfferedRtpHeaderExtensionTypes = new ArraySet<>();
-
     private Set<RtpHeaderExtensionType> mAcceptedRtpHeaderExtensionTypes = new ArraySet<>();
 
     /**
@@ -692,7 +690,6 @@
         out.writeBoolean(mHasKnownUserIntentEmergency);
         out.writeInt(mRestrictCause);
         out.writeInt(mCallerNumberVerificationStatus);
-        out.writeArray(mOfferedRtpHeaderExtensionTypes.toArray());
         out.writeArray(mAcceptedRtpHeaderExtensionTypes.toArray());
     }
 
@@ -708,9 +705,6 @@
         mHasKnownUserIntentEmergency = in.readBoolean();
         mRestrictCause = in.readInt();
         mCallerNumberVerificationStatus = in.readInt();
-        Object[] offered = in.readArray(RtpHeaderExtensionType.class.getClassLoader());
-        mOfferedRtpHeaderExtensionTypes = Arrays.stream(offered)
-                .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
         Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader());
         mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted)
                 .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
@@ -1106,46 +1100,13 @@
     }
 
     /**
-     * For an incoming or outgoing call, indicates the {@link RtpHeaderExtensionType}s which the
-     * caller is offering to make available.
-     * <p>
-     * For outgoing calls, an {@link ImsService} will reserve
-     * {@link RtpHeaderExtensionType#getLocalIdentifier()} identifiers the telephony stack has
-     * proposed to use and not use these same local identifiers.  The offered header extension
-     * types for an outgoing call can be found in the
-     * {@link ImsCallProfile#getOfferedRtpHeaderExtensionTypes()} and will be available to the
-     * {@link ImsService} in {@link MmTelFeature#createCallSession(ImsCallProfile)}.
-     * The {@link ImsService} sets the accepted {@link #setAcceptedRtpHeaderExtensionTypes(Set)}
-     * when the SDP offer/accept process has completed.
-     * <p>
-     * According to RFC8285, RTP header extensions available to a call are determined using the
-     * offer/accept phase of the SDP protocol (see RFC4566).
-     *
-     * @return the {@link RtpHeaderExtensionType}s which were offered by other end of the call.
-     */
-    public @NonNull Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() {
-        return mOfferedRtpHeaderExtensionTypes;
-    }
-
-    /**
-     * Sets the offered {@link RtpHeaderExtensionType}s for this call.
-     * <p>
-     * According to RFC8285, RTP header extensions available to a call are determined using the
-     * offer/accept phase of the SDP protocol (see RFC4566).
-     *
-     * @param rtpHeaderExtensions the {@link RtpHeaderExtensionType}s which are offered.
-     */
-    public void setOfferedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType>
-                    rtpHeaderExtensions) {
-        mOfferedRtpHeaderExtensionTypes.clear();
-        mOfferedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions);
-    }
-
-    /**
      * Gets the {@link RtpHeaderExtensionType}s which have been accepted by both ends of the call.
      * <p>
      * According to RFC8285, RTP header extensions available to a call are determined using the
      * offer/accept phase of the SDP protocol (see RFC4566).
+     * <p>
+     * The offered header extension types supported by the framework and exposed to the
+     * {@link ImsService} via {@link MmTelFeature#changeOfferedRtpHeaderExtensionTypes(Set)}.
      *
      * @return the {@link RtpHeaderExtensionType}s which were accepted by the other end of the call.
      */
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index a4f2a31..d1a893f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -213,6 +214,7 @@
     }
 
     private final int mSubId;
+    private final BinderCacheManager<ITelephony> mBinderCache;
 
     /**
      * Create an instance of {@link ImsMmTelManager} for the subscription id specified.
@@ -242,7 +244,8 @@
             throw new IllegalArgumentException("Invalid subscription ID");
         }
 
-        return new ImsMmTelManager(subId);
+        return new ImsMmTelManager(subId, new BinderCacheManager<>(
+                ImsMmTelManager::getITelephonyInterface));
     }
 
     /**
@@ -250,8 +253,9 @@
      * @hide
      */
     @VisibleForTesting
-    public ImsMmTelManager(int subId) {
+    public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) {
         mSubId = subId;
+        mBinderCache = binderCache;
     }
 
     /**
@@ -1367,7 +1371,11 @@
         }
     }
 
-    private static ITelephony getITelephony() {
+    private ITelephony getITelephony() {
+        return mBinderCache.getBinder();
+    }
+
+    private static ITelephony getITelephonyInterface() {
         ITelephony binder = ITelephony.Stub.asInterface(
                 TelephonyFrameworkInitializer
                         .getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 8b6dac8..4292aae 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -149,14 +150,17 @@
 
     private final int mSubId;
     private final Context mContext;
+    private final BinderCacheManager<IImsRcsController> mBinderCache;
 
     /**
      * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
      * @hide
      */
-    public ImsRcsManager(Context context, int subId) {
+    public ImsRcsManager(Context context, int subId,
+            BinderCacheManager<IImsRcsController> binderCache) {
         mSubId = subId;
         mContext = context;
+        mBinderCache = binderCache;
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
new file mode 100644
index 0000000..6bfdc2c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Represents a connection to the remote {@link SipDelegate} that is managed by the
+ * {@link ImsService} implementing IMS for the subscription that is associated with it.
+ * <p>
+ * The remote delegate will handle messages sent by this {@link SipDelegateConnection}, notifying
+ * the associated {@link DelegateMessageCallback} when the message was either sent successfully or
+ * failed to be sent.
+ * <p>
+ * It is also the responsibility of this {@link SipDelegateConnection} to acknowledge when incoming
+ * SIP messages have been received successfully via
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} or when there was an error
+ * receiving the message using {@link #notifyMessageReceived(String)} and
+ * {@link #notifyMessageReceiveError(String, int)}.
+ *
+ * @see SipDelegateManager#createSipDelegate
+ * @hide
+ */
+public interface SipDelegateConnection {
+
+    /**
+     * Send a SIP message to the SIP delegate to be sent over the carrier’s network. The
+     * {@link SipMessage} will either be acknowledged with
+     * {@link DelegateMessageCallback#onMessageSent(String)} upon successful sending of this message
+     * or {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if there was an error
+     * sending the message.
+     * @param sipMessage The SipMessage to be sent.
+     * @param configVersion The SipDelegateImsConfiguration version used to construct the
+     *                      SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
+     *                      information on this parameter and why it is used.
+     */
+    void sendMessage(@NonNull SipMessage sipMessage, int configVersion);
+
+    /**
+     * Notify the {@link SipDelegate} that a SIP message received from
+     * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} has been received successfully
+     * and is being processed.
+     * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+     *         branch parameter.
+     */
+    void notifyMessageReceived(@NonNull String viaTransactionId);
+
+    /**
+     * Notify the SIP delegate that the SIP message has been received from
+     * {@link DelegateMessageCallback#onMessageReceived(SipMessage)}, however there was an error
+     * processing it.
+     * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+     *         branch parameter.
+     * @param reason The reason why the error occurred.
+     */
+    void notifyMessageReceiveError(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl
new file mode 100644
index 0000000..44ae1b1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable SipDelegateImsConfiguration;
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
new file mode 100644
index 0000000..8abd0ee
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
+ * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
+ * <p>
+ * The IMS service must generate new instances of this configuration as the IMS configuration
+ * managed by the IMS service changes. Along with each {@link SipDelegateImsConfiguration} instance
+ * containing the configuration is the "version", which should be incremented every time a new
+ * {@link SipDelegateImsConfiguration} instance is created. The {@link SipDelegateConnection} will
+ * include the version of the {@link SipDelegateImsConfiguration} instance that it used in order for
+ * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
+ * to generate the {@link SipMessage} and return
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
+ * regenerate that {@link SipMessage} using the correct {@link SipDelegateImsConfiguration}
+ * instance.
+ * <p>
+ * Every time the IMS configuration state changes in the IMS service, a full configuration should
+ * be generated. The new  {@link SipDelegateImsConfiguration} instance should not be an incremental
+ * update.
+ * @hide
+ */
+public class SipDelegateImsConfiguration implements Parcelable {
+
+    /**
+     * IPV4 Address type.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+     */
+    public static final String IPTYPE_IPV4 = "IPV4";
+
+    /**
+     * IPV6 Address type.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+     */
+    public static final String IPTYPE_IPV6 = "IPV6";
+
+    /**
+     * The SIP transport uses UDP.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+     */
+    public static final String SIP_TRANSPORT_UDP = "UDP";
+
+    /**
+     * The SIP transport uses TCP.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+     */
+    public static final String SIP_TRANSPORT_TCP = "TCP";
+
+    /**
+     * Flag specifying if SIP compact form is enabled
+     */
+    public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL =
+            "sip_config_is_compact_form_enabled_bool";
+
+    /**
+     * Flag specifying if SIP keepalives are enabled
+     */
+    public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL =
+            "sip_config_is_keepalive_enabled_bool";
+
+    /**
+     * Maximum SIP payload to be sent on UDP. If the SIP message payload is greater than max udp
+     * payload size, then TCP must be used
+     */
+    public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT =
+            "sip_config_udp_max_payload_size_int";
+
+    /**
+     * Transport protocol used for SIP signaling.
+     * Available options are: {@link #SIP_TRANSPORT_UDP }, {@link #SIP_TRANSPORT_TCP }
+     */
+    public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING =
+            "sip_config_protocol_type_string";
+
+    /**
+     * IMS public user identifier string
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING =
+            "sip_config_ue_public_user_id_string";
+
+    /**
+     * IMS private user identifier string
+     */
+    public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING =
+            "sip_config_ue_private_user_id_string";
+
+    /**
+     * IMS home domain string
+     */
+    public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+
+    /**
+     * IMEI string. Application can include the Instance-ID feature tag " +sip.instance" in the
+     * Contact header with a value of the device IMEI in the form "urn:gsma:imei:<device IMEI>".
+     */
+    public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+
+    /**
+     * IP address type for SIP signaling.
+     * Available options are: {@link #IPTYPE_IPV6}, {@link #IPTYPE_IPV4}
+     */
+    public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+
+    /**
+     * Local IPaddress used for SIP signaling.
+     */
+    public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING =
+            "sip_config_ue_default_ipaddress_string";
+
+    /**
+     * Local port used for sending SIP traffic
+     */
+    public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT =
+            "sip_config_ue_default_port_int";
+
+    /**
+     * SIP server / PCSCF default ip address
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING =
+            "sip_config_server_default_ipaddress_string";
+
+    /**
+     * SIP server / PCSCF port used for sending SIP traffic
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT =
+            "sip_config_server_default_port_int";
+
+    /**
+     * Flag specifying if Network Address Translation is enabled and UE is behind a NAT.
+     */
+    public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL =
+            "sip_config_is_nat_enabled_bool";
+
+    /**
+     * UE's public IPaddress when UE is behind a NAT.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING =
+            "sip_config_ue_public_ipaddress_with_nat_string";
+
+    /**
+     * UE's public SIP port when UE is behind a NAT.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT =
+            "sip_config_ue_public_port_with_nat_int";
+
+    /**
+     * Flag specifying if Globally routable user-agent uri (GRUU) is enabled as per TS 23.808
+     */
+    public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL =
+            "sip_config_is_gruu_enabled_bool";
+
+    /**
+     * UE's Globally routable user-agent uri if this feature is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING =
+            "sip_config_ue_public_gruu_string";
+
+    /**
+     * Flag specifying if SIP over IPSec is enabled.
+     */
+    public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL =
+            "sip_config_is_ipsec_enabled_bool";
+    /**
+     * UE's SIP port used to send traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT =
+            "sip_config_ue_ipsec_client_port_int";
+
+    /**
+     * UE's SIP port used to receive traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT =
+            "sip_config_ue_ipsec_server_port_int";
+
+    /**
+     * UE's SIP port used for the previous IPsec security association if IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT =
+            "sip_config_ue_ipsec_old_client_port_int";
+
+    /**
+     * Port number used by the SIP server to send SIP traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT =
+            "sip_config_server_ipsec_client_port_int";
+
+    /**
+     * Port number used by the SIP server to receive incoming SIP traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT =
+            "sip_config_server_ipsec_server_port_int";
+
+    /**
+     * Port number used by the SIP server to send SIP traffic on the previous IPSec security
+     * association when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT =
+            "sip_config_server_ipsec_old_client_port_int";
+    /**
+     * SIP Authentication header string
+     */
+    public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING =
+            "sip_config_auhentication_header_string";
+
+    /**
+     * SIP Authentication nonce string
+     */
+    public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING =
+            "sip_config_authentication_nonce_string";
+
+    /**
+     * SIP service route header string
+     */
+    public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING =
+            "sip_config_service_route_header_string";
+
+    /**
+     * SIP security verify header string
+     */
+    public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING =
+            "sip_config_security_verify_header_string";
+
+    /**
+     * SIP Path header string
+     */
+    public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING =
+            "sip_config_path_header_string";
+
+    /**
+     * SIP User part string in contact header
+     */
+    public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING =
+            "sip_config_uri_user_part_string";
+
+    /**
+     * SIP P-access-network-info header string
+     */
+    public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING =
+            "sip_config_p_access_network_info_header_string";
+
+    /**
+     * SIP P-last-access-network-info header string
+     */
+    public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING =
+            "sip_config_p_last_access_network_info_header_string";
+
+    /**
+     * SIP P-associated-uri header string
+     */
+    public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING =
+            "sip_config_p_associated_uri_header_string";
+
+    /**@hide*/
+    @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_STRING", value = {
+            KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING,
+            KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING,
+            KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING,
+            KEY_SIP_CONFIG_HOME_DOMAIN_STRING,
+            KEY_SIP_CONFIG_IMEI_STRING,
+            KEY_SIP_CONFIG_IPTYPE_STRING,
+            KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING,
+            KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING,
+            KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING,
+            KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING,
+            KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING,
+            KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING,
+            KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
+            KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
+            KEY_SIP_CONFIG_PATH_HEADER_STRING,
+            KEY_SIP_CONFIG_URI_USER_PART_STRING,
+            KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING,
+            KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING,
+            KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringConfigKey {}
+
+    /**@hide*/
+    @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_INT", value = {
+            KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
+            KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT,
+            KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT,
+            KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT,
+            KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT,
+            KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IntConfigKey {}
+
+    /**@hide*/
+    @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_BOOL", value = {
+            KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BooleanConfigKey {}
+
+    /**
+     * Builder class to be used when constructing a new SipDelegateImsConfiguration.
+     */
+    public static class Builder {
+        private final long mVersion;
+        private final PersistableBundle mBundle;
+
+        /**
+         * Creates an empty implementation of SipDelegateImsConfiguration.
+         * @param version The version associated with the SipDelegateImsConfiguration being built.
+         *                See {@link #getVersion} for more information.
+         */
+        public Builder(int version) {
+            mVersion = version;
+            mBundle = new PersistableBundle();
+        }
+        /**
+         * Clones an existing implementation of SipDelegateImsConfiguration to handle situations
+         * where only a small number of parameters have changed from the previous configuration.
+         * <p>
+         * Automatically increments the version of this configuration by 1. See {@link #getVersion}
+         * for more information.
+         */
+        public Builder(@NonNull SipDelegateImsConfiguration config) {
+            mVersion = config.getVersion() + 1;
+            mBundle = config.copyBundle();
+        }
+        /**
+         * Put a string value into this configuration bundle for the given key.
+         */
+        public Builder putString(@StringConfigKey String key, String value) {
+            mBundle.putString(key, value);
+            return this;
+        }
+
+        /**
+         * Replace the existing default value with a new value for a given key.
+         */
+        public Builder putInt(@IntConfigKey String key, int value) {
+            mBundle.putInt(key, value);
+            return this;
+        }
+
+        /**
+         * Replace the existing default value with a new value for a given key.
+         */
+        public Builder putBoolean(@BooleanConfigKey String key, boolean value) {
+            mBundle.putBoolean(key, value);
+            return this;
+        }
+
+        /**
+         * @return a new SipDelegateImsConfiguration from this Builder.
+         */
+        public SipDelegateImsConfiguration build() {
+            return new SipDelegateImsConfiguration(mVersion, mBundle);
+        }
+    }
+
+    private final long mVersion;
+    private final PersistableBundle mBundle;
+
+    private SipDelegateImsConfiguration(long version, PersistableBundle bundle) {
+        mVersion = version;
+        mBundle = bundle;
+    }
+
+    private SipDelegateImsConfiguration(Parcel source) {
+        mVersion = source.readLong();
+        mBundle = source.readPersistableBundle();
+    }
+
+    /**
+     * @return the string value associated with a given key or {@code null} if it doesn't exist.
+     */
+    public @StringConfigKey String getString(String key) {
+        return mBundle.getString(key);
+    }
+
+    /**
+     * @return the Integer value associated with a given key or {@code null} if the value doesn't
+     * exist.
+     */
+    public @IntConfigKey Integer getInt(String key) {
+        if (!mBundle.containsKey(key)) {
+            return null;
+        }
+        return mBundle.getInt(key);
+    }
+
+    /**
+     * @return the Integer value associated with a given key or {@code null} if the value doesn't
+     * exist.
+     */
+    public @BooleanConfigKey Boolean getBoolen(String key) {
+        if (!mBundle.containsKey(key)) {
+            return null;
+        }
+        return mBundle.getBoolean(key);
+    }
+
+    /**
+     * @return a shallow copy of the full configuration.
+     */
+    public PersistableBundle copyBundle() {
+        return new PersistableBundle(mBundle);
+    }
+
+    /**
+     * An integer representing the version number of this SipDelegateImsConfiguration.
+     * {@link SipMessage}s that are created using this configuration will also have a this
+     * version number associated with them, which will allow the IMS service to validate that the
+     * {@link SipMessage} was using the latest configuration during creation and not a stale
+     * configuration due to race conditions between the configuration being updated and the RCS
+     * application not receiving the updated configuration before generating a new message.
+     *
+     * @return the version number associated with this {@link SipDelegateImsConfiguration}.
+     */
+    public long getVersion() {
+        return mVersion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mVersion);
+        dest.writePersistableBundle(mBundle);
+    }
+
+    public static final Creator<SipDelegateImsConfiguration> CREATOR =
+            new Creator<SipDelegateImsConfiguration>() {
+        @Override
+        public SipDelegateImsConfiguration createFromParcel(Parcel source) {
+            return new SipDelegateImsConfiguration(source);
+        }
+
+        @Override
+        public SipDelegateImsConfiguration[] newArray(int size) {
+            return new SipDelegateImsConfiguration[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 82c8a9c..337b7d4 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -17,29 +17,251 @@
 package android.telephony.ims;
 
 import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.telephony.BinderCacheManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
 /**
- * Manages the creation and destruction of SipDelegates, which allow an IMS application to forward
- * SIP messages for the purposes of providing a single IMS registration to the carrier's IMS network
- * from multiple sources.
+ * Manages the creation and destruction of SipDelegates for the {@link ImsService} managing IMS
+ * for the subscription ID that this SipDelegateManager has been created for.
+ *
+ * This allows multiple IMS applications to forward SIP messages to/from their application for the
+ * purposes of providing a single IMS registration to the carrier's IMS network from potentially
+ * many IMS stacks implementing a subset of the supported MMTEL/RCS features.
  * @hide
  */
 @SystemApi
 public class SipDelegateManager {
 
+    /**
+     * The SIP message has failed being sent or received for an unknown reason.
+     * <p>
+     * The caller should retry a message that failed with this response.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0;
+
+    /**
+     * The remote service associated with this connection has died and the message was not
+     * properly sent/received.
+     * <p>
+     * This is considered a permanent error and the system will automatically begin the teardown and
+     * destruction of the SipDelegate. No further messages should be sent on this transport.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1;
+
+    /**
+     * The message has not been sent/received because the delegate is in the process of closing and
+     * has become unavailable. No further messages should be sent/received on this delegate.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
+
+    /**
+     * The SIP message has an invalid start line and the message can not be sent.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
+
+    /**
+     * One or more of the header fields in the header section of the outgoing SIP message is invalid
+     * and the SIP message can not be sent.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
+
+    /**
+     * The body content of the SIP message is invalid and the message can not be sent.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5;
+
+    /**
+     * The feature tag associated with the outgoing message does not match any known feature tags
+     * and this message can not be sent.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6;
+
+    /**
+     * The feature tag associated with the outgoing message is not enabled for the associated
+     * SipDelegateConnection and can not be sent.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7;
+
+    /**
+     * The link to the network has been lost and the outgoing message has failed to send.
+     * <p>
+     * This message should be retried when connectivity to the network is re-established. See
+     * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8;
+
+    /**
+     * The outgoing SIP message has not been sent due to the SipDelegate not being registered for
+     * IMS at this time.
+     * <p>
+     * This is considered a temporary failure, the message should not be retried until an IMS
+     * registration change callback is received via
+     * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged}
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
+
+    /**
+     * The outgoing SIP message has not been sent because the {@link SipDelegateImsConfiguration}
+     * version associated with the outgoing {@link SipMessage} is now stale and has failed
+     * validation checks.
+     * <p>
+     * The @link SipMessage} should be recreated using the newest
+     * {@link SipDelegateImsConfiguration} and sent again.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
+
+    /**
+     * The outgoing SIP message has not been sent because the internal state of the associated
+     * {@link SipDelegate} is changing and has temporarily brought the transport down.
+     * <p>
+     * This is considered a temporary error and the {@link SipDelegateConnection} should resend the
+     * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is
+     * no longer reported.
+     * @hide
+     */
+    public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "MESSAGE_FAILURE_REASON_", value = {
+            MESSAGE_FAILURE_REASON_UNKNOWN,
+            MESSAGE_FAILURE_REASON_DELEGATE_DEAD,
+            MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+            MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+            MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+            MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT,
+            MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+            MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE,
+            MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE,
+            MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+            MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+            MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION
+    })
+    public @interface MessageFailureReason {}
+
+
+    /**
+     * Access to use this feature tag has been denied for an unknown reason.
+     * @hide
+     */
+    public static final int DENIED_REASON_UNKNOWN = 0;
+
+    /**
+     * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by
+     * another SipDelegateConnection and can not be associated with this delegate. The feature tag
+     * will stay in this state until the feature tag is release by the other application.
+     * @hide
+     */
+    public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1;
+
+    /**
+     * Access to use this feature tag has been denied because this application does not have the
+     * permissions required to access this feature tag.
+     * @hide
+     */
+    public static final int DENIED_REASON_NOT_ALLOWED = 2;
+
+    /**
+     * Access to use this feature tag has been denied because single registration is not allowed by
+     * the carrier at this time. The application should fall back to dual registration if
+     * applicable.
+     * @hide
+     */
+    public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3;
+
+    /**
+     * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been
+     * denied.
+     * @hide
+     */
+    public static final int DENIED_REASON_INVALID = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DENIED_REASON_", value = {
+            DENIED_REASON_UNKNOWN,
+            DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
+            DENIED_REASON_NOT_ALLOWED,
+            DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED,
+            DENIED_REASON_INVALID
+    })
+    public @interface DeniedReason {}
+
+    /**
+     * The SipDelegate has closed due to an unknown reason.
+     * @hide
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0;
+
+    /**
+     * The SipDelegate has closed because the IMS service has died unexpectedly.
+     * @hide
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1;
+
+    /**
+     * The SipDelegate has closed because the IMS application has requested that the connection be
+     * destroyed.
+     * @hide
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
+
+    /**
+     * The SipDelegate has closed because the IMS service does not support the creation of
+     * SipDelegates.
+     * @hide
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED = 3;
+
+    /**
+     * The SipDelegate has been closed due to the user disabling RCS.
+     * @hide
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SIP_DELEGATE_DESTROY_REASON", value = {
+            SIP_DELEGATE_DESTROY_REASON_UNKNOWN,
+            SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD,
+            SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+            SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED,
+            SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS
+    })
+    public @interface SipDelegateDestroyReason {}
+
     private final Context mContext;
     private final int mSubId;
+    private final BinderCacheManager<IImsRcsController> mBinderCache;
 
     /**
      * Only visible for testing. To instantiate an instance of this class, please use
@@ -47,9 +269,11 @@
      * @hide
      */
     @VisibleForTesting
-    public SipDelegateManager(Context context, int subId) {
+    public SipDelegateManager(Context context, int subId,
+            BinderCacheManager<IImsRcsController> binderCache) {
         mContext = context;
         mSubId = subId;
+        mBinderCache = binderCache;
     }
 
     /**
@@ -69,7 +293,7 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isSupported() throws ImsException {
         try {
-            IImsRcsController controller = getIImsRcsController();
+            IImsRcsController controller = mBinderCache.getBinder();
             if (controller == null) {
                 throw new ImsException("Telephony server is down",
                         ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
@@ -83,11 +307,90 @@
         }
     }
 
-    private IImsRcsController getIImsRcsController() {
-        IBinder binder = TelephonyFrameworkInitializer
-                .getTelephonyServiceManager()
-                .getTelephonyImsServiceRegisterer()
-                .get();
-        return IImsRcsController.Stub.asInterface(binder);
+    /**
+     * Request that the ImsService implementation create a SipDelegate, which will configure the
+     * ImsService to forward SIP traffic that matches the filtering criteria set in supplied
+     * {@link DelegateRequest} to the application that the supplied callbacks are registered for.
+     * <p>
+     * This API requires that the caller is running as part of a long-running process and will
+     * always be available to handle incoming messages. One mechanism that can be used for this is
+     * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps
+     * a persistent binding to when the app is the default SMS application.
+     * @param request The parameters that are associated with the SipDelegate creation request that
+     *                will be used to create the SipDelegate connection.
+     * @param executor The executor that will be used to call the callbacks associated with this
+     *          SipDelegate.
+     * @param dc The callback that will be used to notify the listener of the creation/destruction
+     *           of the remote SipDelegate as well as changes to the state of the remote SipDelegate
+     *           connection.
+     * @param mc The callback that will be used to notify the listener of new incoming SIP messages
+     *           as well as the status of messages that were sent by the associated
+     *           SipDelegateConnection.
+     * @throws ImsException Thrown if there was a problem communicating with the ImsService
+     * associated with this SipDelegateManager. See {@link ImsException#getCode()}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
+            @NonNull DelegateConnectionStateCallback dc,
+            @NonNull DelegateConnectionMessageCallback mc) throws ImsException {
+        if (request == null || executor == null || dc == null || mc == null) {
+            throw new IllegalArgumentException("Invalid arguments passed into createSipDelegate");
+        }
+        try {
+            SipDelegateConnectionAidlWrapper wrapper =
+                    new SipDelegateConnectionAidlWrapper(executor, dc, mc);
+            IImsRcsController controller = mBinderCache.listenOnBinder(wrapper,
+                    wrapper::binderDied);
+            if (controller == null) {
+                throw new ImsException("Telephony server is down",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            controller.createSipDelegate(mSubId, request, wrapper.getStateCallbackBinder(),
+                    wrapper.getMessageCallbackBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(),
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Destroy a previously created {@link SipDelegateConnection} that was created using
+     * {@link #createSipDelegate}.
+     * <p>
+     * This will also clean up all related callbacks in the associated ImsService.
+     * @param delegateConnection The SipDelegateConnection to destroy.
+     * @param reason The reason for why this SipDelegateConnection was destroyed.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
+            @SipDelegateDestroyReason int reason) {
+
+        if (delegateConnection == null) {
+            throw new IllegalArgumentException("invalid argument passed into destroySipDelegate");
+        }
+        if (delegateConnection instanceof SipDelegateConnectionAidlWrapper) {
+            SipDelegateConnectionAidlWrapper w =
+                    (SipDelegateConnectionAidlWrapper) delegateConnection;
+            try {
+                IImsRcsController c = mBinderCache.removeRunnable(w);
+                c.destroySipDelegate(mSubId, w.getSipDelegateBinder(), reason);
+            } catch (RemoteException e) {
+                // Connection to telephony died, but this will signal destruction of SipDelegate
+                // eventually anyway, so return normally.
+                try {
+                    w.getStateCallbackBinder().onDestroyed(
+                            SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+                } catch (RemoteException ignore) {
+                    // Local to process.
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed"
+                    + " into this method");
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/SipMessage.aidl b/telephony/java/android/telephony/ims/SipMessage.aidl
new file mode 100644
index 0000000..5140f8a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable SipMessage;
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
new file mode 100644
index 0000000..c3b1be2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP
+ * messages are structured and used.
+ * <p>
+ * The SIP message is represented in a partially encoded form in order to allow for easier
+ * verification and should not be used as a generic SIP message container.
+ * @hide
+ */
+public final class SipMessage implements Parcelable {
+    // Should not be set to true for production!
+    private static final boolean IS_DEBUGGING = Build.IS_ENG;
+
+    private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
+            "BYE", "CANCEL", "REGISTER"};
+
+    private final String mStartLine;
+    private final String mHeaderSection;
+    private final byte[] mContent;
+
+    /**
+     * Represents a partially encoded SIP message.
+     *
+     * @param startLine The start line of the message, containing either the request-line or
+     *                  status-line.
+     * @param headerSection A String containing the full unencoded SIP message header.
+     * @param content UTF-8 encoded SIP message body.
+     */
+    public SipMessage(@NonNull String startLine, @NonNull String headerSection,
+            @NonNull byte[] content) {
+        if (startLine == null || headerSection == null || content == null) {
+            throw new IllegalArgumentException("One or more null parameters entered");
+        }
+        mStartLine = startLine;
+        mHeaderSection = headerSection;
+        mContent = content;
+    }
+
+    /**
+     * Private constructor used only for unparcelling.
+     */
+    private SipMessage(Parcel source) {
+        mStartLine = source.readString();
+        mHeaderSection = source.readString();
+        mContent = new byte[source.readInt()];
+        source.readByteArray(mContent);
+    }
+    /**
+     * @return The start line of the SIP message, which contains either the request-line or
+     * status-line.
+     */
+    public @NonNull String getStartLine() {
+        return mStartLine;
+    }
+
+    /**
+     * @return The full, unencoded header section of the SIP message.
+     */
+    public @NonNull String getHeaderSection() {
+        return mHeaderSection;
+    }
+
+    /**
+     * @return only the UTF-8 encoded SIP message body.
+     */
+    public @NonNull byte[] getContent() {
+        return mContent;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mStartLine);
+        dest.writeString(mHeaderSection);
+        dest.writeInt(mContent.length);
+        dest.writeByteArray(mContent);
+    }
+
+    public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
+        @Override
+        public SipMessage createFromParcel(Parcel source) {
+            return new SipMessage(source);
+        }
+
+        @Override
+        public SipMessage[] newArray(int size) {
+            return new SipMessage[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("StartLine: [");
+        if (IS_DEBUGGING) {
+            b.append(mStartLine);
+        } else {
+            b.append(sanitizeStartLineRequest(mStartLine));
+        }
+        b.append("], [");
+        b.append("Header: [");
+        if (IS_DEBUGGING) {
+            b.append(mHeaderSection);
+        } else {
+            // only identify transaction id/call ID when it is available.
+            b.append("***");
+        }
+        b.append("], ");
+        b.append("Content: [NOT SHOWN]");
+        return b.toString();
+    }
+
+    /**
+     * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF.
+     * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII.
+     */
+    private String sanitizeStartLineRequest(String startLine) {
+        String[] splitLine = startLine.split(" ");
+        if (splitLine == null || splitLine.length == 0)  {
+            return "(INVALID STARTLINE)";
+        }
+        for (String method : SIP_REQUEST_METHODS) {
+            if (splitLine[0].contains(method)) {
+                return splitLine[0] + " <Request-URI> " + splitLine[2];
+            }
+        }
+        return startLine;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index b9a6b3c..37fec7a 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -21,6 +21,7 @@
 import android.telephony.ims.aidl.IImsSmsListener;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.RtpHeaderExtensionType;
 
 import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
@@ -29,6 +30,8 @@
 import com.android.ims.internal.IImsRegistrationListener;
 import com.android.ims.internal.IImsUt;
 
+import java.util.List;
+
 /**
  * See MmTelFeature for more information.
  * {@hide}
@@ -37,6 +40,7 @@
     void setListener(IImsMmTelListener l);
     int getFeatureState();
     ImsCallProfile createCallProfile(int callSessionType, int callType);
+    void changeOfferedRtpHeaderExtensionTypes(in List<RtpHeaderExtensionType> types);
     IImsCallSession createCallSession(in ImsCallProfile profile);
     int shouldProcessCall(in String[] uris);
     IImsUt getUtInterface();
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 8e84e93..f218e35 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -17,10 +17,15 @@
 package android.telephony.ims.aidl;
 
 import android.net.Uri;
+import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
 
 import com.android.ims.ImsFeatureContainer;
 import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -58,6 +63,10 @@
 
     // SipDelegateManager
     boolean isSipDelegateSupported(int subId);
+    void createSipDelegate(int subId, in DelegateRequest request,
+            ISipDelegateConnectionStateCallback delegateState,
+            ISipDelegateMessageCallback delegateMessage);
+    void destroySipDelegate(int subId, ISipDelegate connection, int reason);
 
     // Internal commands that should not be made public
     void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
new file mode 100644
index 0000000..477ee95
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.telephony.ims.SipMessage;
+
+/**
+ * See {@link SipDelegate} and {@link SipDelegateConnection} for docs regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegate {
+    void sendMessage(in SipMessage sipMessage, int configVersion);
+    void notifyMessageReceived(in String viaTransactionId);
+    void notifyMessageReceiveError(in String viaTransactionId, int reason);
+
+    // only used by SipDelegate.
+    void closeDialog(in String callId);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
new file mode 100644
index 0000000..ddfcb99
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.aidl.ISipDelegate;
+
+/**
+ * See {@link SipDelegateConnectionStateCallback} for docs regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegateConnectionStateCallback {
+    void onCreated(ISipDelegate c);
+    void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState,
+                in List<FeatureTagState> deniedFeatureTags);
+    void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+    void onDestroyed(int reason);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl
new file mode 100644
index 0000000..30b7d6c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.telephony.ims.SipMessage;
+
+/**
+ * See {@link DelegateMessageCallback} and {@link DelegateConnectionMessageCallback} for docs
+ * regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegateMessageCallback {
+   void onMessageReceived(in SipMessage message);
+   void onMessageSent(in String viaTransactionId);
+   void onMessageSendFailure(in String viaTransactionId, int reason);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
new file mode 100644
index 0000000..609ee26
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.aidl.ISipDelegate;
+
+/**
+ * See {@link SipDelegateStateCallback} for docs regarding this callback.
+ * {@hide}
+ */
+oneway interface ISipDelegateStateCallback {
+    void onCreated(ISipDelegate c, in List<FeatureTagState> deniedFeatureTags);
+    void onFeatureTagRegistrationChanged(in DelegateRegistrationState registrationState);
+    void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+    void onDestroyed(int reason);
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
index fe23343..cd88839 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
@@ -16,9 +16,17 @@
 
 package android.telephony.ims.aidl;
 
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
+
 /**
  * Interface for commands to the SIP Transport implementation.
  * {@hide}
  */
-interface ISipTransport {
+oneway interface ISipTransport {
+    void createSipDelegate(in DelegateRequest request, ISipDelegateStateCallback dc,
+            ISipDelegateMessageCallback mc);
+    void destroySipDelegate(ISipDelegate delegate, int reason);
 }
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
new file mode 100644
index 0000000..a7f62cc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements
+ * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called
+ * in order to trampoline events back to telephony.
+ * @hide
+ */
+public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback {
+
+    private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
+        @Override
+        public void sendMessage(SipMessage sipMessage, int configVersion) {
+            SipDelegate d = mDelegate;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.sendMessage(sipMessage, configVersion));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void notifyMessageReceived(String viaTransactionId)  {
+            SipDelegate d = mDelegate;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.notifyMessageReceived(viaTransactionId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+        }
+
+        @Override
+        public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+            SipDelegate d = mDelegate;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.notifyMessageReceiveError(viaTransactionId, reason));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+        }
+
+        @Override
+        public void closeDialog(String callId)  {
+            SipDelegate d = mDelegate;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.closeDialog(callId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    private final ISipDelegateMessageCallback mMessageBinder;
+    private final ISipDelegateStateCallback mStateBinder;
+    private final Executor mExecutor;
+
+    private volatile SipDelegate mDelegate;
+
+    public SipDelegateAidlWrapper(Executor executor, ISipDelegateStateCallback stateBinder,
+            ISipDelegateMessageCallback messageBinder) {
+        mExecutor = executor;
+        mStateBinder = stateBinder;
+        mMessageBinder = messageBinder;
+    }
+
+    @Override
+    public void onMessageReceived(SipMessage message) {
+        try {
+            mMessageBinder.onMessageReceived(message);
+        } catch (RemoteException e) {
+            // BinderDied will be called on SipTransport instance to trigger destruction. Notify
+            // failure message failure locally for now.
+            SipDelegate d = mDelegate;
+            if (d != null) {
+                notifyLocalMessageFailedToBeReceived(message,
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+            }
+        }
+    }
+
+    @Override
+    public void onMessageSent(String viaTransactionId) {
+        try {
+            mMessageBinder.onMessageSent(viaTransactionId);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onMessageSendFailure(String viaTransactionId, int reason) {
+        try {
+            mMessageBinder.onMessageSendFailure(viaTransactionId, reason);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onCreated(@NonNull SipDelegate delegate,
+            @Nullable List<FeatureTagState> deniedTags) {
+        mDelegate = delegate;
+        try {
+            mStateBinder.onCreated(mDelegateBinder, deniedTags);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onFeatureTagRegistrationChanged(DelegateRegistrationState registrationState) {
+        try {
+            mStateBinder.onFeatureTagRegistrationChanged(registrationState);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config) {
+        try {
+            mStateBinder.onImsConfigurationChanged(config);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onDestroyed(int reasonCode) {
+        mDelegate = null;
+        try {
+            mStateBinder.onDestroyed(reasonCode);
+        } catch (RemoteException e) {
+            // Do not worry about this if the remote side is already dead.
+        }
+    }
+
+    public SipDelegate getDelegate()  {
+        return mDelegate;
+    }
+
+    public ISipDelegate getDelegateBinder() {
+        return mDelegateBinder;
+    }
+
+    private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
+        //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
+        // transaction ID can not be parsed.
+        SipDelegate d = mDelegate;
+        if (d != null) {
+            mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason));
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
new file mode 100644
index 0000000..3bd1a46
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Wrapper class implementing {@link SipDelegateConnection} using AIDL, which is returned to the
+ * local process. Also holds a reference to incoming connection message and state AIDL impl to
+ * trampoline events to callbacks as well as notify the local process in the event that the remote
+ * process becomes unavailable.
+ * <p>
+ * When the remote {@link SipDelegate} is created, this instance tracks the
+ * {@link ISipDelegate} associated with it and implements the
+ * {@link SipDelegateConnection} sent back to the local callback.
+ * @hide
+ */
+public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection,
+        IBinder.DeathRecipient {
+    private static final String LOG_TAG = "SipDelegateCAW";
+
+    private final ISipDelegateConnectionStateCallback.Stub mStateBinder =
+            new ISipDelegateConnectionStateCallback.Stub() {
+        @Override
+        public void onCreated(ISipDelegate c) {
+            associateSipDelegate(c);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState,
+                List<FeatureTagState> deniedFeatureTags) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onFeatureTagStatusChanged(registrationState,
+                                new ArraySet<>(deniedFeatureTags)));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onImsConfigurationChanged(SipDelegateImsConfiguration registeredSipConfig) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onImsConfigurationChanged(registeredSipConfig));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onDestroyed(int reason) {
+            invalidateSipDelegateBinder();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onDestroyed(reason));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    private final ISipDelegateMessageCallback.Stub mMessageBinder =
+            new ISipDelegateMessageCallback.Stub() {
+                @Override
+                public void onMessageReceived(SipMessage message) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() ->
+                                mMessageCallback.onMessageReceived(message));
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+
+                @Override
+                public void onMessageSent(String viaTransactionId) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() ->
+                                mMessageCallback.onMessageSent(viaTransactionId));
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+
+                @Override
+                public void onMessageSendFailure(String viaTransactionId, int reason) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() ->
+                                mMessageCallback.onMessageSendFailure(viaTransactionId, reason));
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            };
+
+
+    private final Executor mExecutor;
+    private final DelegateConnectionStateCallback mStateCallback;
+    private final DelegateConnectionMessageCallback mMessageCallback;
+    private final AtomicReference<ISipDelegate> mDelegateBinder =
+            new AtomicReference<>();
+
+    /**
+     * Wrap the local state and message callbacks, calling the implementation of these interfaces
+     * when the remote process calls these methods.
+     */
+    public SipDelegateConnectionAidlWrapper(Executor executor,
+            DelegateConnectionStateCallback stateCallback,
+            DelegateConnectionMessageCallback messageCallback) {
+        mExecutor = executor;
+        mStateCallback = stateCallback;
+        mMessageCallback = messageCallback;
+    }
+
+    @Override
+    public void sendMessage(SipMessage sipMessage, int configVersion) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                notifyLocalMessageFailedToSend(sipMessage,
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+                return;
+            }
+            conn.sendMessage(sipMessage, configVersion);
+        } catch (RemoteException e) {
+            notifyLocalMessageFailedToSend(sipMessage,
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+        }
+    }
+
+    @Override
+    public void notifyMessageReceived(String viaTransactionId) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                return;
+            }
+            conn.notifyMessageReceived(viaTransactionId);
+        } catch (RemoteException e) {
+            // Nothing to do here, app will eventually get remote death callback.
+        }
+    }
+
+    @Override
+    public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                return;
+            }
+            conn.notifyMessageReceiveError(viaTransactionId, reason);
+        } catch (RemoteException e) {
+            // Nothing to do here, app will eventually get remote death callback.
+        }
+    }
+
+    // Also called upon IImsRcsController death (telephony process dies).
+    @Override
+    public void binderDied() {
+        invalidateSipDelegateBinder();
+        mExecutor.execute(() -> mStateCallback.onDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD));
+    }
+
+    /**
+     * @return Implementation of state binder.
+     */
+    public ISipDelegateConnectionStateCallback getStateCallbackBinder() {
+        return mStateBinder;
+    }
+
+    /**
+     * @return Implementation of message binder.
+     */
+    public ISipDelegateMessageCallback getMessageCallbackBinder() {
+        return mMessageBinder;
+    }
+
+    /**
+     * @return The ISipDelegateConnection associated with this wrapper.
+     */
+    public ISipDelegate getSipDelegateBinder() {
+        return mDelegateBinder.get();
+    }
+
+    private void associateSipDelegate(ISipDelegate c) {
+        if (c != null) {
+            try {
+                c.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                // already dead.
+                c = null;
+            }
+        }
+        mDelegateBinder.set(c);
+    }
+
+    private void invalidateSipDelegateBinder() {
+        ISipDelegate oldVal = mDelegateBinder.getAndUpdate((unused) -> null);
+        if (oldVal != null) {
+            try {
+                oldVal.asBinder().unlinkToDeath(this, 0 /*flags*/);
+            } catch (NoSuchElementException e) {
+                Log.i(LOG_TAG, "invalidateSipDelegateBinder: " + e);
+            }
+        }
+    }
+
+    private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
+        //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage
+        // transaction ID can not be parsed.
+        mExecutor.execute(() ->
+                mMessageCallback.onMessageSendFailure(null, reason));
+    }
+}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b0a7b62..96ca022 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -509,6 +509,7 @@
      * @return true if the capability is enabled, false otherwise.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
 
     /**
@@ -547,5 +548,6 @@
      * @return Binder instance that the framework will use to communicate with this feature.
      * @hide
      */
+    @SuppressWarnings("HiddenAbstractMethod")
     protected abstract IInterface getBinder();
 }
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index d7b0e0f0..e570fb6 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -27,6 +27,8 @@
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.RtpHeaderExtensionType;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsMmTelListener;
@@ -37,6 +39,7 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsSmsImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.ArraySet;
 
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsEcbm;
@@ -45,6 +48,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
@@ -93,6 +98,18 @@
         }
 
         @Override
+        public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
+                throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types));
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
         public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
             synchronized (mLock) {
                 return createCallSessionInterface(profile);
@@ -623,6 +640,24 @@
     }
 
     /**
+     * Called by the framework to report a change to the RTP header extension types which should be
+     * offered during SDP negotiation (see RFC8285 for more information).
+     * <p>
+     * The {@link ImsService} should report the RTP header extensions which were accepted during
+     * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}.
+     *
+     * @param extensionTypes The RTP header extensions the framework wishes to offer during
+     *                       outgoing and incoming call setup.  An empty list indicates that there
+     *                       are no framework defined RTP header extension types to offer.
+     * @hide
+     */
+    @SystemApi
+    public void changeOfferedRtpHeaderExtensionTypes(
+            @NonNull Set<RtpHeaderExtensionType> extensionTypes) {
+        // Base implementation - should be overridden if RTP header extension handling is supported.
+    }
+
+    /**
      * @hide
      */
     public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
new file mode 100644
index 0000000..59f9601
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The callback associated with a {@link SipDelegateConnection}, which handles newly received
+ * messages as well as the result of sending a SIP message.
+ * @hide
+ */
+public interface DelegateConnectionMessageCallback {
+
+    /**
+     * A new {@link SipMessage} has been received from the delegate.
+     * @param message the {@link SipMessage} routed to this RCS application.
+     */
+    void onMessageReceived(@NonNull SipMessage message);
+
+    /**
+     * A message previously sent to the SIP delegate using
+     * {@link SipDelegateConnection#sendMessage} has been successfully sent.
+     * @param viaTransactionId The transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     */
+    void onMessageSent(@NonNull String viaTransactionId);
+
+    /**
+     * A message previously sent to the SIP delegate using
+     * {@link SipDelegateConnection#sendMessage} has failed to be sent.
+     * @param viaTransactionId The Transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     * @param reason The reason for the failure.
+     */
+    void onMessageSendFailure(String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
new file mode 100644
index 0000000..9761805
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+
+import java.util.Set;
+
+/**
+ * The callback associated with a {@link SipDelegateConnection} that manages the state of the
+ * SipDelegateConnection.
+ * <p>
+ * After {@link SipDelegateManager#createSipDelegate} is used to request a new
+ * {@link SipDelegateConnection} be created, {@link #onCreated} will be called with the
+ * {@link SipDelegateConnection} instance that must be used to communicate with the remote
+ * {@link SipDelegate}.
+ * <p>
+ * After, {@link #onFeatureTagStatusChanged} will always be called at least once with the current
+ * status of the feature tags that have been requested. The application may receive multiple
+ * {@link #onFeatureTagStatusChanged} callbacks over the lifetime of the associated
+ * {@link SipDelegateConnection}, which will signal changes to how SIP messages associated with
+ * those feature tags will be handled.
+ * <p>
+ * In order to start sending SIP messages, the SIP configuration parameters will need to be
+ * received, so the messaging application should make no assumptions about these parameters and wait
+ * until {@link #onImsConfigurationChanged(SipDelegateImsConfiguration)} has been called. This is
+ * guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one
+ * feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all
+ * feature tags were denied, no IMS configuration will be sent.
+ * <p>
+ * The {@link SipDelegateConnection} will stay associated with this RCS application until either the
+ * RCS application calls {@link SipDelegateManager#destroySipDelegate} or telephony destroys the
+ * {@link SipDelegateConnection}. In both cases, {@link #onDestroyed(int)}  will be called.
+ * Telephony destroying the {@link SipDelegateConnection} instance is rare and will only happen in
+ * rare cases, such as if telephony itself or IMS service dies unexpectedly. See
+ * {@link SipDelegateManager.SipDelegateDestroyReason} reasons for more information on all of the
+ * cases that will trigger the {@link SipDelegateConnection} to be destroyed.
+ *
+ * @hide
+ */
+public interface DelegateConnectionStateCallback {
+
+    /**
+     * A {@link SipDelegateConnection} has been successfully created for the
+     * {@link DelegateRequest} used when calling {@link SipDelegateManager#createSipDelegate}.
+     */
+    void onCreated(@NonNull SipDelegateConnection c);
+
+    /**
+     * The status of the RCS feature tags that were requested as part of the initial
+     * {@link DelegateRequest}.
+     * <p>
+     * There are four states that each RCS feature tag can be in: registered, deregistering,
+     * deregistered, and denied.
+     * <p>
+     * When a feature tag is considered registered, SIP messages associated with that feature tag
+     * may be sent and received freely.
+     * <p>
+     * When a feature tag is deregistering, the network IMS registration still contains the feature
+     * tag, however the IMS service and associated {@link SipDelegate} is in the progress of
+     * modifying the IMS registration to remove this feature tag and requires the application to
+     * perform an action before the IMS registration can change. The specific action required for
+     * the SipDelegate to continue modifying the IMS registration can be found in the definition of
+     * each {@link DelegateRegistrationState.DeregisteringReason}.
+     * <p>
+     * When a feature tag is in the deregistered state, new out-of-dialog SIP messages for that
+     * feature tag will be rejected, however due to network race conditions, the RCS application
+     * should still be able to handle new out-of-dialog SIP requests from the network. This may not
+     * be possible, however, if the IMS registration itself was lost. See the
+     * {@link DelegateRegistrationState.DeregisteredReason} reasons for more information on how SIP
+     * messages are handled in each of these cases.
+     * <p>
+     * If a feature tag is denied, no incoming messages will be routed to the associated
+     * {@link DelegateConnectionMessageCallback} and all outgoing SIP messages related to this
+     * feature tag will be rejected. See {@link SipDelegateManager.DeniedReason}
+     * reasons for more information about the conditions when this will happen.
+     * <p>
+     * The set of feature tags contained in the registered, deregistering, deregistered, and denied
+     * lists will always equal the set of feature tags requested in the initial
+     * {@link DelegateRequest}.
+     * <p>
+     * Transitions of feature tags from registered, deregistering, and deregistered and vice-versa
+     * may happen quite often, however transitions to/from denied are rare and only occur if the
+     * user has changed the role of your application to add/remove support for one or more requested
+     * feature tags or carrier provisioning has enabled or disabled single registration entirely.
+     * Please see {@link SipDelegateManager.DeniedReason} reasons for an explanation of each of
+     * these cases as well as what may cause them to change.
+     *
+     * @param registrationState The new IMS registration state of each of the feature tags
+     *     associated with the {@link SipDelegate}.
+     * @param deniedFeatureTags A list of {@link FeatureTagState} objects, each containing a feature
+     *     tag associated with this {@link SipDelegateConnection} that has no access to
+     *     send/receive SIP messages as well as a reason for why the feature tag is denied. For more
+     *     information on the reason why the feature tag was denied access, see the
+     *     {@link SipDelegateManager.DeniedReason} reasons.
+     */
+    void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState,
+            @NonNull Set<FeatureTagState> deniedFeatureTags);
+
+
+    /**
+     * IMS configuration of the underlying IMS stack used by this IMS application for construction
+     * of the SIP messages that will be sent over the carrier's network.
+     * <p>
+     * There should never be assumptions made about the configuration of the underling IMS stack and
+     * the IMS application should wait for this indication before sending out any outgoing SIP
+     * messages.
+     * <p>
+     * Configuration may change due to IMS registration changes as well as
+     * other optional events on the carrier network. If IMS stack is already registered at the time
+     * of callback registration, then this method shall be invoked with the current configuration.
+     * Otherwise, there may be a delay in this method being called if initial IMS registration has
+     * not compleed yet.
+     *
+     * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+     */
+    void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration registeredSipConfig);
+
+    /**
+     * The previously created {@link SipDelegateConnection} instance delivered via
+     * {@link #onCreated(SipDelegateConnection)} has been destroyed. This interface should no longer
+     * be used for any SIP message handling.
+     *
+     * @param reason The reason for the failure.
+     */
+    void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 12abdd1..a6f5c45 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -17,6 +17,8 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -126,6 +128,57 @@
     }
 
     /**
+     * Called by the framework to request that the ImsService perform the network registration
+     * of all SIP delegates associated with this ImsService.
+     * <p>
+     * If the SIP delegate feature tag configuration has changed, then this method will be
+     * called in order to let the ImsService know that it can pick up these changes in the IMS
+     * registration.
+     * @hide
+     */
+    public void updateSipDelegateRegistration() {
+        // Stub implementation, ImsService should implement this
+    }
+
+
+    /**
+     * Called by the framework to request that the ImsService perform the network deregistration of
+     * all SIP delegates associated with this ImsService.
+     * <p>
+     * This is typically called in situations where the user has changed the configuration of the
+     * device (for example, the default messaging application) and the framework is reconfiguring
+     * the tags associated with each IMS application.
+     * <p>
+     * This should not affect the registration of features managed by the ImsService itself, such as
+     * feature tags related to MMTEL registration.
+     * @hide
+     */
+    public void triggerSipDelegateDeregistration() {
+        // Stub implementation, ImsService should implement this
+    }
+
+    /**
+     * Called by the framework to notify the ImsService that a SIP delegate connection has received
+     * a SIP message containing a permanent failure response (such as a 403) or an indication that a
+     * SIP response timer has timed out in response to an outgoing SIP message. This method will be
+     * called when this condition occurs to trigger the ImsService to tear down the full IMS
+     * registration and re-register again.
+     *
+     * @param sipCode The SIP error code that represents a permanent failure that was received in
+     *    response to a request generated by the IMS application. See RFC3261 7.2 for the general
+     *    classes of responses available here, however the codes that generate this condition may
+     *    be carrier specific.
+     * @param sipReason The reason associated with the SIP error code. {@code null} if there was no
+     *    reason associated with the error.
+     * @hide
+     */
+    public void triggerNetworkReregistration(@IntRange(from = 100, to = 699) int sipCode,
+            @Nullable String sipReason) {
+        // Stub implementation, ImsService should implement this
+    }
+
+
+    /**
      * Notify the framework that the device is connected to the IMS network.
      *
      * @param imsRadioTech the radio access technology. Valid values are defined as
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
new file mode 100644
index 0000000..3ec9709
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The {@link SipDelegate} is implemented by the {@link ImsService} and allows a privileged
+ * IMS application to use this delegate to send SIP messages as well as acknowledge the receipt of
+ * incoming SIP messages delivered to the application over the existing IMS registration, allowing
+ * for a single IMS registration for multiple IMS applications.
+ * <p>
+ * Once the SIP delegate is created for that application,
+ * {@link ImsRegistrationImplBase#updateSipDelegateRegistration()} will be called, indicating that
+ * the application is finished setting up SipDelegates and the existing IMS registration may be
+ * modified to include the features managed by these SipDelegates.
+ * <p>
+ * This SipDelegate will need to notify the remote application of the registration of these features
+ * as well as the associated {@link SipDelegateImsConfiguration} before the application can start
+ * sending/receiving SIP messages via the transport. See
+ * {@link android.telephony.ims.DelegateStateCallback} for more information.
+ * @hide
+ */
+public interface SipDelegate {
+
+    /**
+     * The framework calls this method when a remote RCS application wishes to send a new outgoing
+     * SIP message.
+     * <p>
+     * Once sent, this SIP delegate should notify the remote application of the success or
+     * failure using {@link DelegateMessageCallback#onMessageSent(String)} or
+     * {@link DelegateMessageCallback#onMessageSendFailure(String, int)}.
+     * @param message The SIP message to be sent over the operator’s network.
+     * @param configVersion The SipDelegateImsConfiguration version used to construct the
+     *         SipMessage. See {@link SipDelegateImsConfiguration} for more information. If the
+     *         version specified here does not match the most recently constructed
+     *         {@link SipDelegateImsConfiguration}, this message should fail validation checks and
+     *         {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
+     *         {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
+     */
+    void sendMessage(@NonNull SipMessage message, int configVersion);
+
+    /**
+     * The framework is requesting that routing resources associated with the SIP dialog using the
+     * provided Call-ID to be cleaned up.
+     * <p>
+     * Typically a SIP Dialog close event will be signalled by that dialog receiving a BYE or 200 OK
+     * message, however, in some cases, the framework will request that the ImsService close the
+     * dialog due to the open dialog holding up an event such as applying a provisioning change or
+     * handing over to another transport type.
+     * @param callId The call-ID header value associated with the ongoing SIP Dialog that the
+     *         framework is requesting be closed.
+     */
+    void closeDialog(@NonNull String callId);
+
+    /**
+     * The remote application has received the SIP message and is processing it.
+     * @param viaTransactionId The Transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     */
+    void notifyMessageReceived(@NonNull String viaTransactionId);
+
+    /**
+     * The remote application has either not received the SIP message or there was an error
+     * processing it.
+     * @param viaTransactionId The Transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     * @param reason The reason why the message was not correctly received.
+     */
+    void notifyMessageReceiveError(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index b2b2914..b48f631 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -18,27 +18,75 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.IBinder;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
 import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.aidl.SipDelegateAidlWrapper;
+import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
- * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
- * IMS applications in order to support IMS single registration.
+ * The ImsService implements this class to manage the creation and destruction of
+ * {@link SipDelegate}s.
+ *
+ * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS
+ * applications as a delegate to the associated carrier's IMS Network in order to support using a
+ * single IMS registration for all MMTEL and RCS signalling traffic.
  * @hide
  */
 @SystemApi
 public class SipTransportImplBase {
+    private static final String LOG_TAG = "SipTransportIB";
+
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            mBinderExecutor.execute(() -> binderDiedInternal());
+        }
+    };
+
+    private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() {
+        @Override
+        public void createSipDelegate(DelegateRequest request, ISipDelegateStateCallback dc,
+                ISipDelegateMessageCallback mc) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mBinderExecutor.execute(() -> createSipDelegateInternal(request, dc, mc));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void destroySipDelegate(ISipDelegate delegate, int reason) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
 
     private final Executor mBinderExecutor;
-    private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() {
-
-    };
+    private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
 
     /**
      * Create an implementation of SipTransportImplBase.
      *
-     * @param executor The executor that remote calls from the framework should be called on.
+     * @param executor The executor that remote calls from the framework will be called on. This
+     *                 includes the methods here as well as the methods in {@link SipDelegate}.
      */
     public SipTransportImplBase(@NonNull Executor executor) {
         if (executor == null) {
@@ -49,6 +97,79 @@
     }
 
     /**
+     * Called by the Telephony framework to request the creation of a new {@link SipDelegate}.
+     * <p>
+     * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with
+     * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
+     * <p>
+     * This method will be called on the Executor specified in
+     * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+     *
+     * @param request A SIP delegate request containing the parameters that the remote RCS
+     * application wishes to use.
+     * @param dc A callback back to the remote application to be used to communicate state callbacks
+     *           for the SipDelegate.
+     * @param mc A callback back to the remote application to be used to send SIP messages to the
+     *           remote application and acknowledge the sending of outgoing SIP messages.
+     * @hide
+     */
+    public void createSipDelegate(@NonNull DelegateRequest request,
+            @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
+        throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+    }
+
+    /**
+     * Destroys the SipDelegate associated with a remote IMS application.
+     * <p>
+     * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be
+     * called to notify listeners of its destruction to release associated resources.
+     * <p>
+     * This method will be called on the Executor specified in
+     * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+     * @param delegate The delegate to be destroyed.
+     * @param reason The reason the remote connection to this {@link SipDelegate} is being
+     *         destroyed.
+     * @hide
+     */
+    public void destroySipDelegate(@NonNull SipDelegate delegate,
+            @SipDelegateManager.SipDelegateDestroyReason int reason) {
+        throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+    }
+
+    private void createSipDelegateInternal(DelegateRequest r, ISipDelegateStateCallback cb,
+            ISipDelegateMessageCallback mc) {
+        SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
+        mDelegates.add(wrapper);
+        createSipDelegate(r, wrapper, wrapper);
+    }
+
+    private void destroySipDelegateInternal(ISipDelegate d, int reason) {
+        SipDelegateAidlWrapper result = null;
+        for (SipDelegateAidlWrapper w : mDelegates) {
+            if (Objects.equals(d, w.getDelegateBinder())) {
+                result = w;
+                break;
+            }
+        }
+
+        if (result != null) {
+            mDelegates.remove(result);
+            destroySipDelegate(result.getDelegate(), reason);
+        } else {
+            Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to "
+                    + d);
+        }
+    }
+
+    private void binderDiedInternal() {
+        for (SipDelegateAidlWrapper w : mDelegates) {
+            destroySipDelegate(w.getDelegate(),
+                    SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+        }
+        mDelegates.clear();
+    }
+
+    /**
      * @return The IInterface used by the framework.
      * @hide
      */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 36d01f4..d16cb16 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -31,6 +31,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.CallForwardingInfo;
+import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
@@ -2232,4 +2233,10 @@
      * @return true if dual connectivity is enabled else false
      */
     boolean isNrDualConnectivityEnabled(int subId);
+
+    /**
+     * Get carrier bandwidth per primary and secondary carrier
+     * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
+     */
+    CarrierBandwidth getCarrierBandwidth(int subId);
 }
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
index a50f046..a67156a 100644
--- a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
+++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
@@ -19,13 +19,19 @@
 import android.net.wifi.aware.DiscoverySession
 import android.net.wifi.aware.PeerHandle
 import android.net.wifi.aware.WifiAwareNetworkSpecifier
+import android.os.Build
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 
 import com.android.testutils.assertParcelSane
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 
 import java.lang.IllegalStateException
 
+import org.junit.Assert.assertFalse
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito
@@ -33,18 +39,32 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class MatchAllNetworkSpecifierTest {
+    @Rule @JvmField
+    val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+    private val specifier = MatchAllNetworkSpecifier()
+    private val discoverySession = Mockito.mock(DiscoverySession::class.java)
+    private val peerHandle = Mockito.mock(PeerHandle::class.java)
+    private val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession,
+            peerHandle).build()
+
     @Test
     fun testParcel() {
         assertParcelSane(MatchAllNetworkSpecifier(), 0)
     }
 
+    @Test @IgnoreAfter(Build.VERSION_CODES.R)
+    fun testCanBeSatisfiedBy_BeforeS() {
+        // MatchAllNetworkSpecifier didn't follow its parent class to change the satisfiedBy() to
+        // canBeSatisfiedBy(), so if a caller calls MatchAllNetworkSpecifier#canBeSatisfiedBy(), the
+        // NetworkSpecifier#canBeSatisfiedBy() will be called actually, and false will be returned.
+        // Although it's not meeting the expectation, the behavior still needs to be verified.
+        assertFalse(specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier))
+    }
+
     @Test(expected = IllegalStateException::class)
+    @IgnoreUpTo(Build.VERSION_CODES.R)
     fun testCanBeSatisfiedBy() {
-        val specifier = MatchAllNetworkSpecifier()
-        val discoverySession = Mockito.mock(DiscoverySession::class.java)
-        val peerHandle = Mockito.mock(PeerHandle::class.java)
-        val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession,
-                peerHandle).build()
         specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier)
     }
 }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 7abe189..cd9406c 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -35,7 +35,6 @@
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.STATS_PER_IFACE;
 import static android.net.NetworkStats.STATS_PER_UID;
 import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkStats.TAG_NONE;
@@ -994,7 +993,7 @@
     public void testTethering() throws Exception {
         // pretend first mobile network comes online
         expectDefaultSettings();
-        NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
+        final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
 
@@ -1004,23 +1003,39 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
 
+        // Register custom provider and retrieve callback.
+        final TestableNetworkStatsProviderBinder provider =
+                new TestableNetworkStatsProviderBinder();
+        final INetworkStatsProviderCallback cb =
+                mService.registerNetworkStatsProvider("TEST-TETHERING-OFFLOAD", provider);
+        assertNotNull(cb);
+        final long now = getElapsedRealtime();
+
         // Traffic seen by kernel counters (includes software tethering).
-        final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
+        final NetworkStats swIfaceStats = new NetworkStats(now, 1)
                 .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L);
         // Hardware tethering traffic, not seen by kernel counters.
-        final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L);
+        final NetworkStats tetherHwIfaceStats = new NetworkStats(now, 1)
+                .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_ALL, SET_DEFAULT,
+                        TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+                        512L, 4L, 128L, 1L, 0L));
+        final NetworkStats tetherHwUidStats = new NetworkStats(now, 1)
+                .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_TETHERING, SET_DEFAULT,
+                        TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+                        512L, 4L, 128L, 1L, 0L));
+        cb.notifyStatsUpdated(0 /* unused */, tetherHwIfaceStats, tetherHwUidStats);
 
-        // Traffic for UID_RED.
-        final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
+        // Fake some traffic done by apps on the device (as opposed to tethering), and record it
+        // into UID stats (as opposed to iface stats).
+        final NetworkStats localUidStats = new NetworkStats(now, 1)
                 .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
-        // All tethering traffic, both hardware and software.
-        final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+        // Software per-uid tethering traffic.
+        final NetworkStats tetherSwUidStats = new NetworkStats(now, 1)
+                .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1408L, 10L, 256L, 1L,
                         0L);
 
-        expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
-        expectNetworkStatsUidDetail(uidStats, tetherStats);
+        expectNetworkStatsSummary(swIfaceStats);
+        expectNetworkStatsUidDetail(localUidStats, tetherSwUidStats);
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -1362,12 +1377,6 @@
     }
 
     private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
-        expectNetworkStatsSummary(summary, new NetworkStats(0L, 0));
-    }
-
-    private void expectNetworkStatsSummary(NetworkStats summary, NetworkStats tetherStats)
-            throws Exception {
-        expectNetworkStatsTethering(STATS_PER_IFACE, tetherStats);
         expectNetworkStatsSummaryDev(summary.clone());
         expectNetworkStatsSummaryXt(summary.clone());
     }
@@ -1380,11 +1389,6 @@
         when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary);
     }
 
-    private void expectNetworkStatsTethering(int how, NetworkStats stats)
-            throws Exception {
-        when(mNetManager.getNetworkStatsTethering(how)).thenReturn(stats);
-    }
-
     private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
         expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0));
     }
diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
new file mode 100644
index 0000000..a44a734
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PersistableBundleUtilsTest {
+    private static final String TEST_KEY = "testKey";
+    private static final String TEST_STRING_PREFIX = "testString";
+    private static final int[] TEST_INT_ARRAY = new int[] {0, 1, 2, 3, 4};
+
+    private static final int NUM_COLLECTION_ENTRIES = 10;
+
+    private static class TestKey {
+        private static final String TEST_INTEGER_KEY =
+                "mTestInteger"; // Purposely colliding with keys of test class to ensure namespacing
+        private final int mTestInteger;
+
+        TestKey(int testInteger) {
+            mTestInteger = testInteger;
+        }
+
+        TestKey(PersistableBundle in) {
+            mTestInteger = in.getInt(TEST_INTEGER_KEY);
+        }
+
+        public PersistableBundle toPersistableBundle() {
+            final PersistableBundle result = new PersistableBundle();
+
+            result.putInt(TEST_INTEGER_KEY, mTestInteger);
+
+            return result;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mTestInteger);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof TestKey)) {
+                return false;
+            }
+
+            final TestKey other = (TestKey) o;
+            return mTestInteger == other.mTestInteger;
+        }
+    }
+
+    private static class TestClass {
+        private static final String TEST_INTEGER_KEY = "mTestInteger";
+        private final int mTestInteger;
+
+        private static final String TEST_INT_ARRAY_KEY = "mTestIntArray";
+        private final int[] mTestIntArray;
+
+        private static final String TEST_STRING_KEY = "mTestString";
+        private final String mTestString;
+
+        private static final String TEST_PERSISTABLE_BUNDLE_KEY = "mTestPersistableBundle";
+        private final PersistableBundle mTestPersistableBundle;
+
+        TestClass(
+                int testInteger,
+                int[] testIntArray,
+                String testString,
+                PersistableBundle testPersistableBundle) {
+            mTestInteger = testInteger;
+            mTestIntArray = testIntArray;
+            mTestString = testString;
+            mTestPersistableBundle = testPersistableBundle;
+        }
+
+        TestClass(PersistableBundle in) {
+            mTestInteger = in.getInt(TEST_INTEGER_KEY);
+            mTestIntArray = in.getIntArray(TEST_INT_ARRAY_KEY);
+            mTestString = in.getString(TEST_STRING_KEY);
+            mTestPersistableBundle = in.getPersistableBundle(TEST_PERSISTABLE_BUNDLE_KEY);
+        }
+
+        public PersistableBundle toPersistableBundle() {
+            final PersistableBundle result = new PersistableBundle();
+
+            result.putInt(TEST_INTEGER_KEY, mTestInteger);
+            result.putIntArray(TEST_INT_ARRAY_KEY, mTestIntArray);
+            result.putString(TEST_STRING_KEY, mTestString);
+            result.putPersistableBundle(TEST_PERSISTABLE_BUNDLE_KEY, mTestPersistableBundle);
+
+            return result;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                    mTestInteger,
+                    Arrays.hashCode(mTestIntArray),
+                    mTestString,
+                    mTestPersistableBundle);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof TestClass)) {
+                return false;
+            }
+
+            final TestClass other = (TestClass) o;
+
+            // TODO: Add a proper equals() to PersistableBundle. But in the meantime, force
+            // TODO: unparcelling in order to allow test comparison.
+            if (mTestPersistableBundle.size() != other.mTestPersistableBundle.size()) {
+                return false;
+            }
+
+            return mTestInteger == other.mTestInteger
+                    && Arrays.equals(mTestIntArray, other.mTestIntArray)
+                    && mTestString.equals(other.mTestString)
+                    && mTestPersistableBundle.kindofEquals(other.mTestPersistableBundle);
+        }
+    }
+
+    @Test
+    public void testListConversionLossless() throws Exception {
+        final List<TestClass> sourceList = new ArrayList<>();
+        for (int i = 0; i < NUM_COLLECTION_ENTRIES; i++) {
+            final PersistableBundle innerBundle = new PersistableBundle();
+            innerBundle.putInt(TEST_KEY, i);
+
+            sourceList.add(new TestClass(i, TEST_INT_ARRAY, TEST_STRING_PREFIX + i, innerBundle));
+        }
+
+        final PersistableBundle bundled =
+                PersistableBundleUtils.fromList(sourceList, TestClass::toPersistableBundle);
+        final List<TestClass> resultList = PersistableBundleUtils.toList(bundled, TestClass::new);
+
+        assertEquals(sourceList, resultList);
+    }
+
+    @Test
+    public void testMapConversionLossless() throws Exception {
+        final LinkedHashMap<TestKey, TestClass> sourceMap = new LinkedHashMap<>();
+        for (int i = 0; i < NUM_COLLECTION_ENTRIES; i++) {
+            final TestKey key = new TestKey(i * i);
+
+            final PersistableBundle innerBundle = new PersistableBundle();
+            innerBundle.putInt(TEST_KEY, i);
+            final TestClass value =
+                    new TestClass(i, TEST_INT_ARRAY, TEST_STRING_PREFIX + i, innerBundle);
+
+            sourceMap.put(key, value);
+        }
+
+        final PersistableBundle bundled =
+                PersistableBundleUtils.fromMap(
+                        sourceMap, TestKey::toPersistableBundle, TestClass::toPersistableBundle);
+        final LinkedHashMap<TestKey, TestClass> resultList =
+                PersistableBundleUtils.toMap(bundled, TestKey::new, TestClass::new);
+
+        assertEquals(sourceMap, resultList);
+    }
+
+    @Test
+    public void testByteArrayConversionLossless() {
+        final byte[] byteArray = "testByteArrayConversionLossless".getBytes();
+
+        PersistableBundle bundle = PersistableBundleUtils.fromByteArray(byteArray);
+        byte[] result = PersistableBundleUtils.toByteArray(bundle);
+
+        assertArrayEquals(byteArray, result);
+    }
+
+    @Test
+    public void testIntegerConversionLossless() throws Exception {
+        final int testInt = 1;
+        final PersistableBundle integerBundle =
+                PersistableBundleUtils.INTEGER_SERIALIZER.toPersistableBundle(testInt);
+        final int result =
+                PersistableBundleUtils.INTEGER_DESERIALIZER.fromPersistableBundle(integerBundle);
+
+        assertEquals(testInt, result);
+    }
+}
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
index 3e49034..52949da 100644
--- a/tools/aapt2/DominatorTree_test.cpp
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -198,5 +198,33 @@
   EXPECT_EQ(expected, printer.ToString(&tree));
 }
 
+TEST(DominatorTreeTest, MccMncIsPeertoLocale) {
+  const ConfigDescription default_config = {};
+  const ConfigDescription de_config = test::ParseConfigOrDie("de");
+  const ConfigDescription fr_config = test::ParseConfigOrDie("fr");
+  const ConfigDescription mcc_config = test::ParseConfigOrDie("mcc262");
+  const ConfigDescription mcc_fr_config = test::ParseConfigOrDie("mcc262-fr");
+  const ConfigDescription mnc_config = test::ParseConfigOrDie("mnc2");
+  const ConfigDescription mnc_fr_config = test::ParseConfigOrDie("mnc2-fr");
+  std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+  configs.push_back(util::make_unique<ResourceConfigValue>(default_config, ""));
+  configs.push_back(util::make_unique<ResourceConfigValue>(de_config, ""));
+  configs.push_back(util::make_unique<ResourceConfigValue>(fr_config, ""));
+  configs.push_back(util::make_unique<ResourceConfigValue>(mcc_config, ""));
+  configs.push_back(util::make_unique<ResourceConfigValue>(mcc_fr_config, ""));
+  configs.push_back(util::make_unique<ResourceConfigValue>(mnc_config, ""));
+  configs.push_back(util::make_unique<ResourceConfigValue>(mnc_fr_config, ""));
+  DominatorTree tree(configs);
+  PrettyPrinter printer;
+  std::string expected =
+      "<default>\n"
+      "de\n"
+      "fr\n"
+      "mcc262\n"
+      "mcc262-fr\n"
+      "mnc2\n"
+      "mnc2-fr\n";
+  EXPECT_EQ(expected, printer.ToString(&tree));
+}
 
 }  // namespace aapt
diff --git a/tools/aapt2/optimize/ResourceDeduper_test.cpp b/tools/aapt2/optimize/ResourceDeduper_test.cpp
index 048e318..888de40 100644
--- a/tools/aapt2/optimize/ResourceDeduper_test.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper_test.cpp
@@ -52,9 +52,11 @@
           .Build();
 
   ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/dedupe", default_config));
   EXPECT_THAT(table, Not(HasValue("android:string/dedupe", ldrtl_config)));
   EXPECT_THAT(table, Not(HasValue("android:string/dedupe", land_config)));
 
+  EXPECT_THAT(table, HasValue("android:string/dedupe2", default_config));
   EXPECT_THAT(table, HasValue("android:string/dedupe2", ldrtl_v21_config));
   EXPECT_THAT(table, Not(HasValue("android:string/dedupe2", ldrtl_config)));
 
@@ -151,4 +153,24 @@
   EXPECT_THAT(table, HasValue("android:string/keep", fr_rCA_config));
 }
 
+TEST(ResourceDeduperTest, MccMncValuesAreKept) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  const ConfigDescription default_config = {};
+  const ConfigDescription mcc_config = test::ParseConfigOrDie("mcc262");
+  const ConfigDescription mnc_config = test::ParseConfigOrDie("mnc2");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+          .AddString("android:string/keep", ResourceId{}, mcc_config, "keep")
+          .AddString("android:string/keep", ResourceId{}, mnc_config, "keep")
+          .Build();
+
+  ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+  EXPECT_THAT(table, HasValue("android:string/keep", mcc_config));
+  EXPECT_THAT(table, HasValue("android:string/keep", mnc_config));
+}
+
+
 }  // namespace aapt
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 1cc5073..8b89959 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -76,25 +76,34 @@
     "//packages/apps/Settings/tests/robotests", // TODO(b/161767237): remove
 ]
 
-// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility
-// classes before they are renamed.
-java_library {
-    name: "framework-wifi-pre-jarjar",
+// defaults shared between `framework-wifi` & `framework-wifi-pre-jarjar`
+// java_sdk_library `framework-wifi` needs sources to generate stubs, so it cannot reuse
+// `framework-wifi-pre-jarjar`
+java_defaults {
+    name: "framework-wifi-defaults",
     defaults: ["wifi-module-sdk-version-defaults"],
-    sdk_version: "module_current",
     static_libs: [
         "framework-wifi-util-lib",
         "android.hardware.wifi-V1.0-java-constants",
+        "modules-utils-build",
     ],
     libs: [
-        "framework-annotations-lib",
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
         ":framework-wifi-util-lib-aidls",
     ],
-    // java_api_finder must accompany `srcs`
+}
+
+// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility
+// classes before they are renamed.
+java_library {
+    name: "framework-wifi-pre-jarjar",
+    defaults: ["framework-wifi-defaults"],
+    sdk_version: "module_current",
+    libs: ["framework-annotations-lib"],
+    // java_api_finder must accompany `srcs` (`srcs` defined in `framework-wifi-defaults`)
     plugins: ["java_api_finder"],
     installable: false,
     visibility: [
@@ -108,18 +117,7 @@
     name: "framework-wifi",
     defaults: [
         "framework-module-defaults",
-        "wifi-module-sdk-version-defaults",
-    ],
-    static_libs: [
-        "framework-wifi-util-lib",
-        "android.hardware.wifi-V1.0-java-constants",
-    ],
-    libs: [
-        "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
-    ],
-    srcs: [
-        ":framework-wifi-updatable-sources",
-        ":framework-wifi-util-lib-aidls",
+        "framework-wifi-defaults",
     ],
 
     jarjar_rules: ":wifi-jarjar-rules",
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index b489be2..ff06a18 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -124,3 +124,4 @@
 rule com.android.internal.util.Protocol* com.android.wifi.x.@0
 
 rule com.android.net.module.util.** com.android.wifi.x.@0
+rule com.android.modules.utils.** com.android.wifi.x.@0
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 73c52ab..034defb 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -173,7 +173,7 @@
 
     /** @deprecated Use the new {@link android.net.wifi.RttManager#getRttCapabilities()} API.*/
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public Capabilities getCapabilities() {
         throw new UnsupportedOperationException(
                 "getCapabilities is not supported in the adaptation layer");
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 94771ac..4163c88 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1239,7 +1239,7 @@
      * @param bssidInfos access points to watch
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void configureWifiChange(
             int rssiSampleSize,                             /* sample size for RSSI averaging */
             int lostApSampleSize,                           /* samples to confirm AP's loss */
@@ -1273,7 +1273,7 @@
      *                 provided on {@link #stopTrackingWifiChange}
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void startTrackingWifiChange(WifiChangeListener listener) {
         throw new UnsupportedOperationException();
     }
@@ -1284,7 +1284,7 @@
      * #stopTrackingWifiChange}
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void stopTrackingWifiChange(WifiChangeListener listener) {
         throw new UnsupportedOperationException();
     }
@@ -1292,7 +1292,7 @@
     /** @hide */
     @SystemApi
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void configureWifiChange(WifiChangeSettings settings) {
         throw new UnsupportedOperationException();
     }
@@ -1348,7 +1348,7 @@
      *                 also be provided on {@link #stopTrackingBssids}
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void startTrackingBssids(BssidInfo[] bssidInfos,
                                     int apLostThreshold, BssidListener listener) {
         throw new UnsupportedOperationException();
@@ -1359,7 +1359,7 @@
      * @param listener same object provided in {@link #startTrackingBssids}
      */
     @Deprecated
-    @SuppressLint("Doclava125")
+    @SuppressLint("RequiresPermission")
     public void stopTrackingBssids(BssidListener listener) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index 6a39959..b710a14 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -31,10 +31,11 @@
     static_libs: [
         "androidx.test.rules",
         "core-test-rules",
+        "frameworks-base-testutils",
         "guava",
         "mockito-target-minus-junit4",
+        "modules-utils-build",
         "net-tests-utils",
-        "frameworks-base-testutils",
         "truth-prebuilt",
     ],
 
@@ -47,4 +48,8 @@
         "device-tests",
         "mts",
     ],
+
+    // static libs used by both framework-wifi & FrameworksWifiApiTests. Need to rename test usage
+    // to a different package name to prevent conflict with the copy in production code.
+    jarjar_rules: "test-jarjar-rules.txt",
 }
diff --git a/wifi/tests/test-jarjar-rules.txt b/wifi/tests/test-jarjar-rules.txt
new file mode 100644
index 0000000..41b97ab
--- /dev/null
+++ b/wifi/tests/test-jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.android.modules.utils.** com.android.wifi.test.x.@0