Merge "Remove annotations from MmTelFeature.MmTelCapabilities.toString"
diff --git a/Android.bp b/Android.bp
index f57acfa..3046b20 100644
--- a/Android.bp
+++ b/Android.bp
@@ -53,6 +53,7 @@
         "core/java/android/view/DisplayAdjustments.java",
     ],
     path: "core/java",
+    visibility: ["//frameworks/base/test-mock"],
 }
 
 filegroup {
@@ -73,6 +74,14 @@
 }
 
 filegroup {
+    name: "framework-identity-sources",
+    srcs: [
+        "identity/java/**/*.java",
+    ],
+    path: "identity/java",
+}
+
+filegroup {
     name: "framework-keystore-sources",
     srcs: [
         "keystore/java/**/*.java",
@@ -216,6 +225,7 @@
         ":framework-drm-sources",
         ":framework-graphics-sources",
         ":framework-keystore-sources",
+        ":framework-identity-sources",
         ":framework-location-sources",
         ":framework-lowpan-sources",
         ":framework-media-sources",
@@ -238,6 +248,7 @@
         ":platform-compat-native-aidl",
 
         // AIDL sources from external directories
+        ":credstore_aidl",
         ":dumpstate_aidl",
         ":framework_native_aidl",
         ":gatekeeper_aidl",
@@ -289,6 +300,7 @@
             "core/java",
             "drm/java",
             "graphics/java",
+            "identity/java",
             "keystore/java",
             "location/java",
             "lowpan/java",
diff --git a/api/current.txt b/api/current.txt
index d61a6a7..9b6dcc7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9824,6 +9824,7 @@
     field public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
     field public static final String CLIPBOARD_SERVICE = "clipboard";
     field public static final String COMPANION_DEVICE_SERVICE = "companiondevice";
+    field public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
     field public static final String CONNECTIVITY_SERVICE = "connectivity";
     field public static final String CONSUMER_IR_SERVICE = "consumer_ir";
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
@@ -9882,6 +9883,7 @@
     field public static final String USB_SERVICE = "usb";
     field public static final String USER_SERVICE = "user";
     field public static final String VIBRATOR_SERVICE = "vibrator";
+    field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
     field public static final String WALLPAPER_SERVICE = "wallpaper";
     field public static final String WIFI_AWARE_SERVICE = "wifiaware";
     field public static final String WIFI_P2P_SERVICE = "wifip2p";
@@ -16637,7 +16639,9 @@
     ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
+    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
     method public javax.crypto.Cipher getCipher();
+    method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method public javax.crypto.Mac getMac();
     method public java.security.Signature getSignature();
   }
@@ -17575,7 +17579,9 @@
     ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
     ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
     ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
+    ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull android.security.identity.IdentityCredential);
     method @Deprecated public javax.crypto.Cipher getCipher();
+    method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method @Deprecated public javax.crypto.Mac getMac();
     method @Deprecated public java.security.Signature getSignature();
   }
@@ -18287,6 +18293,7 @@
     field public static final int CHEROKEE_SUPPLEMENT_ID = 255; // 0xff
     field public static final android.icu.lang.UCharacter.UnicodeBlock CHESS_SYMBOLS;
     field public static final int CHESS_SYMBOLS_ID = 281; // 0x119
+    field public static final android.icu.lang.UCharacter.UnicodeBlock CHORASMIAN;
     field public static final int CHORASMIAN_ID = 301; // 0x12d
     field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY;
     field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY_FORMS;
@@ -18315,6 +18322,7 @@
     field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E_ID = 256; // 0x100
     field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F;
     field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F_ID = 274; // 0x112
+    field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G;
     field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G_ID = 302; // 0x12e
     field public static final int CJK_UNIFIED_IDEOGRAPHS_ID = 71; // 0x47
     field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS;
@@ -18365,6 +18373,7 @@
     field public static final int DEVANAGARI_ID = 15; // 0xf
     field public static final android.icu.lang.UCharacter.UnicodeBlock DINGBATS;
     field public static final int DINGBATS_ID = 56; // 0x38
+    field public static final android.icu.lang.UCharacter.UnicodeBlock DIVES_AKURU;
     field public static final int DIVES_AKURU_ID = 303; // 0x12f
     field public static final android.icu.lang.UCharacter.UnicodeBlock DOGRA;
     field public static final int DOGRA_ID = 282; // 0x11a
@@ -22537,6 +22546,7 @@
     field public static final android.icu.util.VersionInfo UNICODE_11_0;
     field public static final android.icu.util.VersionInfo UNICODE_12_0;
     field public static final android.icu.util.VersionInfo UNICODE_12_1;
+    field public static final android.icu.util.VersionInfo UNICODE_13_0;
     field public static final android.icu.util.VersionInfo UNICODE_1_0;
     field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
     field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -28683,8 +28693,6 @@
   public class ConnectivityDiagnosticsManager {
     method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
     method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
-    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
-    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
   }
 
   public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
@@ -28694,21 +28702,44 @@
     method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
   }
 
-  public static class ConnectivityDiagnosticsManager.ConnectivityReport {
+  public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
     ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
-    field @NonNull public final android.os.PersistableBundle additionalInfo;
-    field @NonNull public final android.net.LinkProperties linkProperties;
-    field @NonNull public final android.net.Network network;
-    field @NonNull public final android.net.NetworkCapabilities networkCapabilities;
-    field public final long reportTimestamp;
+    method public int describeContents();
+    method @NonNull public android.os.PersistableBundle getAdditionalInfo();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
+    method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+    method public long getReportTimestamp();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
+    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped";
+    field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
+    field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+    field public static final int NETWORK_PROBE_DNS = 4; // 0x4
+    field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
+    field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
+    field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
+    field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
+    field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
+    field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
+    field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
   }
 
-  public static class ConnectivityDiagnosticsManager.DataStallReport {
+  public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
     ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
-    field public final int detectionMethod;
-    field @NonNull public final android.net.Network network;
-    field public final long reportTimestamp;
-    field @NonNull public final android.os.PersistableBundle stallDetails;
+    method public int describeContents();
+    method public int getDetectionMethod();
+    method @NonNull public android.net.Network getNetwork();
+    method public long getReportTimestamp();
+    method @NonNull public android.os.PersistableBundle getStallDetails();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
+    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+    field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+    field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
+    field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
   }
 
   public class ConnectivityManager {
@@ -28851,6 +28882,35 @@
     field public final int code;
   }
 
+  public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile {
+    method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms();
+    method public int getMaxMtu();
+    method @Nullable public String getPassword();
+    method @Nullable public byte[] getPresharedKey();
+    method @Nullable public android.net.ProxyInfo getProxyInfo();
+    method @Nullable public java.security.PrivateKey getRsaPrivateKey();
+    method @NonNull public String getServerAddr();
+    method @Nullable public java.security.cert.X509Certificate getServerRootCaCert();
+    method @Nullable public java.security.cert.X509Certificate getUserCert();
+    method @NonNull public String getUserIdentity();
+    method @Nullable public String getUsername();
+    method public boolean isBypassable();
+    method public boolean isMetered();
+  }
+
+  public static final class Ikev2VpnProfile.Builder {
+    ctor public Ikev2VpnProfile.Builder(@NonNull String, @NonNull String);
+    method @NonNull public android.net.Ikev2VpnProfile build();
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setAllowedAlgorithms(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey, @Nullable java.security.cert.X509Certificate);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthPsk(@NonNull byte[]);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthUsernamePassword(@NonNull String, @NonNull String, @Nullable java.security.cert.X509Certificate);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setBypassable(boolean);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
+  }
+
   public class InetAddresses {
     method public static boolean isNumericAddress(@NonNull String);
     method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
@@ -29197,6 +29257,14 @@
     field public String response;
   }
 
+  public abstract class PlatformVpnProfile {
+    method public final int getType();
+    method @NonNull public final String getTypeString();
+    field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7
+    field public static final int TYPE_IKEV2_IPSEC_RSA = 8; // 0x8
+    field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6
+  }
+
   public final class Proxy {
     ctor public Proxy();
     method @Deprecated public static String getDefaultHost();
@@ -29477,6 +29545,13 @@
     method public String sanitize(String);
   }
 
+  public class VpnManager {
+    method public void deleteProvisionedVpnProfile();
+    method @Nullable public android.content.Intent provisionVpnProfile(@NonNull android.net.PlatformVpnProfile);
+    method public void startProvisionedVpnProfile();
+    method public void stopProvisionedVpnProfile();
+  }
+
   public class VpnService extends android.app.Service {
     ctor public VpnService();
     method public final boolean isAlwaysOn();
@@ -41059,6 +41134,138 @@
 
 }
 
+package android.security.identity {
+
+  public class AccessControlProfile {
+  }
+
+  public static final class AccessControlProfile.Builder {
+    ctor public AccessControlProfile.Builder(@NonNull android.security.identity.AccessControlProfileId);
+    method @NonNull public android.security.identity.AccessControlProfile build();
+    method @NonNull public android.security.identity.AccessControlProfile.Builder setReaderCertificate(@NonNull java.security.cert.X509Certificate);
+    method @NonNull public android.security.identity.AccessControlProfile.Builder setUserAuthenticationRequired(boolean);
+    method @NonNull public android.security.identity.AccessControlProfile.Builder setUserAuthenticationTimeout(long);
+  }
+
+  public class AccessControlProfileId {
+    ctor public AccessControlProfileId(int);
+    method public int getId();
+  }
+
+  public class AlreadyPersonalizedException extends android.security.identity.IdentityCredentialException {
+    ctor public AlreadyPersonalizedException(@NonNull String);
+    ctor public AlreadyPersonalizedException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class CipherSuiteNotSupportedException extends android.security.identity.IdentityCredentialException {
+    ctor public CipherSuiteNotSupportedException(@NonNull String);
+    ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
+    ctor public DocTypeNotSupportedException(@NonNull String);
+    ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class EphemeralPublicKeyNotFoundException extends android.security.identity.IdentityCredentialException {
+    ctor public EphemeralPublicKeyNotFoundException(@NonNull String);
+    ctor public EphemeralPublicKeyNotFoundException(@NonNull String, @NonNull Throwable);
+  }
+
+  public abstract class IdentityCredential {
+    method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
+    method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+    method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
+    method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
+    method @NonNull public abstract int[] getAuthenticationDataUsageCount();
+    method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
+    method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+    method public abstract void setAllowUsingExhaustedKeys(boolean);
+    method public abstract void setAvailableAuthenticationKeys(int, int);
+    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+  }
+
+  public class IdentityCredentialException extends java.lang.Exception {
+    ctor public IdentityCredentialException(@NonNull String);
+    ctor public IdentityCredentialException(@NonNull String, @NonNull Throwable);
+  }
+
+  public abstract class IdentityCredentialStore {
+    method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
+    method @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
+    method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
+    method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
+    method @Nullable public static android.security.identity.IdentityCredentialStore getInstance(@NonNull android.content.Context);
+    method @NonNull public abstract String[] getSupportedDocTypes();
+    field public static final int CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1; // 0x1
+  }
+
+  public class InvalidReaderSignatureException extends android.security.identity.IdentityCredentialException {
+    ctor public InvalidReaderSignatureException(@NonNull String);
+    ctor public InvalidReaderSignatureException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class InvalidRequestMessageException extends android.security.identity.IdentityCredentialException {
+    ctor public InvalidRequestMessageException(@NonNull String);
+    ctor public InvalidRequestMessageException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class MessageDecryptionException extends android.security.identity.IdentityCredentialException {
+    ctor public MessageDecryptionException(@NonNull String);
+    ctor public MessageDecryptionException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class NoAuthenticationKeyAvailableException extends android.security.identity.IdentityCredentialException {
+    ctor public NoAuthenticationKeyAvailableException(@NonNull String);
+    ctor public NoAuthenticationKeyAvailableException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class PersonalizationData {
+  }
+
+  public static final class PersonalizationData.Builder {
+    ctor public PersonalizationData.Builder();
+    method @NonNull public android.security.identity.PersonalizationData.Builder addAccessControlProfile(@NonNull android.security.identity.AccessControlProfile);
+    method @NonNull public android.security.identity.PersonalizationData build();
+    method @NonNull public android.security.identity.PersonalizationData.Builder setEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
+  }
+
+  public abstract class ResultData {
+    method @NonNull public abstract byte[] getAuthenticatedData();
+    method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
+    method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @Nullable public abstract byte[] getMessageAuthenticationCode();
+    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaceNames();
+    method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method @NonNull public abstract byte[] getStaticAuthenticationData();
+    method public abstract int getStatus(@NonNull String, @NonNull String);
+    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  }
+
+  public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
+    ctor public SessionTranscriptMismatchException(@NonNull String);
+    ctor public SessionTranscriptMismatchException(@NonNull String, @NonNull Throwable);
+  }
+
+  public class UnknownAuthenticationKeyException extends android.security.identity.IdentityCredentialException {
+    ctor public UnknownAuthenticationKeyException(@NonNull String);
+    ctor public UnknownAuthenticationKeyException(@NonNull String, @NonNull Throwable);
+  }
+
+  public abstract class WritableIdentityCredential {
+    method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[]);
+    method @NonNull public abstract byte[] personalize(@NonNull android.security.identity.PersonalizationData);
+  }
+
+}
+
 package android.security.keystore {
 
   public class KeyExpiredException extends java.security.InvalidKeyException {
@@ -43468,6 +43675,7 @@
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+    field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
@@ -43545,6 +43753,7 @@
   public abstract class Conference extends android.telecom.Conferenceable {
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
+    method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
     method public final void destroy();
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
@@ -43559,6 +43768,8 @@
     method public final android.telecom.StatusHints getStatusHints();
     method public android.telecom.Connection.VideoProvider getVideoProvider();
     method public int getVideoState();
+    method public final boolean isRingbackRequested();
+    method public void onAnswer(int);
     method public void onCallAudioStateChanged(android.telecom.CallAudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -43567,6 +43778,7 @@
     method public void onMerge(android.telecom.Connection);
     method public void onMerge();
     method public void onPlayDtmfTone(char);
+    method public void onReject();
     method public void onSeparate(android.telecom.Connection);
     method public void onStopDtmfTone();
     method public void onSwap();
@@ -43586,6 +43798,8 @@
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setExtras(@Nullable android.os.Bundle);
     method public final void setOnHold();
+    method public final void setRingbackRequested(boolean);
+    method public final void setRinging();
     method public final void setStatusHints(android.telecom.StatusHints);
     method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
     method public final void setVideoState(android.telecom.Connection, int);
@@ -43743,6 +43957,7 @@
     field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
+    field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
@@ -43815,8 +44030,10 @@
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public android.net.Uri getAddress();
     method public android.os.Bundle getExtras();
+    method @Nullable public java.util.List<android.net.Uri> getParticipants();
     method public android.telecom.Connection.RttTextStream getRttTextStream();
     method public int getVideoState();
+    method public boolean isAdhocConferenceCall();
     method public boolean isRequestingRtt();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
@@ -43836,9 +44053,13 @@
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
+    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -43953,6 +44174,7 @@
     method public boolean supportsUriScheme(String);
     method public android.telecom.PhoneAccount.Builder toBuilder();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000
     field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
     field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
@@ -44148,6 +44370,7 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
+    method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
     method public android.content.Intent createManageBlockedNumbersIntent();
     method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
@@ -44175,6 +44398,7 @@
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
+    method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -44653,6 +44877,7 @@
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+    field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
     field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
     field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
@@ -45183,7 +45408,7 @@
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
     field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field public static final int LISTEN_NONE = 0; // 0x0
-    field @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
     field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
@@ -45610,6 +45835,7 @@
     method public String getNetworkCountryIso();
     method public String getNetworkOperator();
     method public String getNetworkOperatorName();
+    method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
     method public String getNetworkSpecifier();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
     method @Deprecated public int getPhoneCount();
@@ -45742,6 +45968,9 @@
     field public static final int MULTISIM_ALLOWED = 0; // 0x0
     field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2
     field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1
+    field public static final int NETWORK_SELECTION_MODE_AUTO = 1; // 0x1
+    field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2
+    field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
     field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -49424,6 +49653,71 @@
 
 }
 
+package android.util.proto {
+
+  public final class ProtoOutputStream {
+    ctor public ProtoOutputStream();
+    ctor public ProtoOutputStream(int);
+    ctor public ProtoOutputStream(@NonNull java.io.OutputStream);
+    method public static int checkFieldId(long, long);
+    method public void dump(@NonNull String);
+    method public void end(long);
+    method public void flush();
+    method @NonNull public byte[] getBytes();
+    method @Nullable public static String getFieldCountString(long);
+    method @NonNull public static String getFieldIdString(long);
+    method @Nullable public static String getFieldTypeString(long);
+    method public int getRawSize();
+    method @Nullable public static String getWireTypeString(int);
+    method public static long makeFieldId(int, long);
+    method public static long makeToken(int, boolean, int, int, int);
+    method public long start(long);
+    method @NonNull public static String token2String(long);
+    method public void write(long, double);
+    method public void write(long, float);
+    method public void write(long, int);
+    method public void write(long, long);
+    method public void write(long, boolean);
+    method public void write(long, @Nullable String);
+    method public void write(long, @Nullable byte[]);
+    method public void writeTag(int, int);
+    field public static final long FIELD_COUNT_MASK = 16492674416640L; // 0xf0000000000L
+    field public static final long FIELD_COUNT_PACKED = 5497558138880L; // 0x50000000000L
+    field public static final long FIELD_COUNT_REPEATED = 2199023255552L; // 0x20000000000L
+    field public static final int FIELD_COUNT_SHIFT = 40; // 0x28
+    field public static final long FIELD_COUNT_SINGLE = 1099511627776L; // 0x10000000000L
+    field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L
+    field public static final int FIELD_ID_SHIFT = 3; // 0x3
+    field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L
+    field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L
+    field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L
+    field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L
+    field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L
+    field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L
+    field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L
+    field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L
+    field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
+    field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
+    field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
+    field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
+    field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
+    field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
+    field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L
+    field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L
+    field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L
+    field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
+    field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
+    field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
+    field public static final int WIRE_TYPE_FIXED32 = 5; // 0x5
+    field public static final int WIRE_TYPE_FIXED64 = 1; // 0x1
+    field public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; // 0x2
+    field public static final int WIRE_TYPE_MASK = 7; // 0x7
+    field public static final int WIRE_TYPE_START_GROUP = 3; // 0x3
+    field public static final int WIRE_TYPE_VARINT = 0; // 0x0
+  }
+
+}
+
 package android.view {
 
   public abstract class AbsSavedState implements android.os.Parcelable {
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index 07106ba..5c31f41 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -485,6 +485,12 @@
     
 
 
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CHORASMIAN:
+    Missing nullability on field `CHORASMIAN` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G:
+    Missing nullability on field `CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#DIVES_AKURU:
+    Missing nullability on field `DIVES_AKURU` in class `class android.icu.lang.UCharacter.UnicodeBlock`
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS:
     
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC:
@@ -529,6 +535,8 @@
     
 MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
     
+MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0:
+    Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo`
 
 
 RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index c8253a0..c657e00 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -118,9 +118,31 @@
   }
 
   public final class TimeZoneFinder {
+    method @Nullable public String getIanaVersion();
     method @NonNull public static android.timezone.TimeZoneFinder getInstance();
     method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String);
   }
 
+  public final class TzDataSetVersion {
+    method public static int currentFormatMajorVersion();
+    method public static int currentFormatMinorVersion();
+    method public int getFormatMajorVersion();
+    method public int getFormatMinorVersion();
+    method public int getRevision();
+    method @NonNull public String getRulesVersion();
+    method public static boolean isCompatibleWithThisDevice(android.timezone.TzDataSetVersion);
+    method @NonNull public static android.timezone.TzDataSetVersion read() throws java.io.IOException, android.timezone.TzDataSetVersion.TzDataSetException;
+  }
+
+  public static class TzDataSetVersion.TzDataSetException extends java.lang.Exception {
+    ctor public TzDataSetVersion.TzDataSetException(String);
+    ctor public TzDataSetVersion.TzDataSetException(String, Throwable);
+  }
+
+  public final class ZoneInfoDb {
+    method @NonNull public static android.timezone.ZoneInfoDb getInstance();
+    method @NonNull public String getVersion();
+  }
+
 }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index e2fb99e..9acd67f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1440,12 +1440,13 @@
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
   }
 
-  public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
-    method protected void finalize();
+  public final class BluetoothPan implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public void close();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) protected void finalize();
     method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
-    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
-    method public boolean isTetheringOn();
-    method public void setBluetoothTethering(boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isTetheringOn();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
     field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
@@ -1463,10 +1464,14 @@
   }
 
   public interface BluetoothProfile {
+    field public static final int A2DP_SINK = 11; // 0xb
+    field public static final int AVRCP_CONTROLLER = 12; // 0xc
     field public static final int CONNECTION_POLICY_ALLOWED = 100; // 0x64
     field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0
     field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff
+    field public static final int HEADSET_CLIENT = 16; // 0x10
     field public static final int PAN = 5; // 0x5
+    field public static final int PBAP_CLIENT = 17; // 0x11
     field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0
     field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
   }
@@ -1636,6 +1641,7 @@
     field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
     field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
+    field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
     field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
     field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
     field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -1940,6 +1946,7 @@
     field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
+    field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
     field public static final int MATCH_INSTANT = 8388608; // 0x800000
     field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
     field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
@@ -2088,6 +2095,15 @@
 
 }
 
+package android.debug {
+
+  public class AdbManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public boolean isAdbWifiQrSupported();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public boolean isAdbWifiSupported();
+  }
+
+}
+
 package android.hardware {
 
   public final class Sensor {
@@ -4038,8 +4054,8 @@
   public final class DvbDeviceInfo implements android.os.Parcelable {
     ctor public DvbDeviceInfo(int, int);
     method public int describeContents();
-    method public int getAdapterId();
-    method public int getDeviceId();
+    method @IntRange(from=0) public int getAdapterId();
+    method @IntRange(from=0) public int getDeviceId();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
   }
@@ -4294,6 +4310,32 @@
     field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
   }
 
+  public final class CaptivePortalData implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getByteLimit();
+    method public long getExpiryTimeMillis();
+    method public long getRefreshTimeMillis();
+    method @Nullable public android.net.Uri getUserPortalUrl();
+    method @Nullable public android.net.Uri getVenueInfoUrl();
+    method public boolean isCaptive();
+    method public boolean isSessionExtendable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+  }
+
+  public static class CaptivePortalData.Builder {
+    ctor public CaptivePortalData.Builder();
+    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+    method @NonNull public android.net.CaptivePortalData build();
+    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+  }
+
   public class ConnectivityManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
@@ -4425,6 +4467,8 @@
     method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
     method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
     method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
+    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
     method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
     method @Nullable public String getTcpBufferSizes();
     method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
@@ -4438,9 +4482,12 @@
     method public boolean isIpv6Provisioned();
     method public boolean isProvisioned();
     method public boolean isReachable(@NonNull java.net.InetAddress);
+    method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
     method public boolean removeDnsServer(@NonNull java.net.InetAddress);
     method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
     method public boolean removeRoute(@NonNull android.net.RouteInfo);
+    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
     method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
     method public void setPrivateDnsServerName(@Nullable String);
     method public void setTcpBufferSizes(@Nullable String);
@@ -4469,10 +4516,30 @@
     field public final int netId;
   }
 
+  public final class NetworkAgentConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getSubscriberId();
+    method public boolean isNat64DetectionEnabled();
+    method public boolean isProvisioningNotificationEnabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
+  }
+
+  public static class NetworkAgentConfig.Builder {
+    ctor public NetworkAgentConfig.Builder();
+    method @NonNull public android.net.NetworkAgentConfig build();
+    method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
+    method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
+    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+  }
+
   public final class NetworkCapabilities implements android.os.Parcelable {
     method public boolean deduceRestrictedCapability();
+    method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
+    method @Nullable public String getSSID();
     method @NonNull public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+    method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
     method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
     method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo);
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -4507,6 +4574,10 @@
     method public abstract void onRequestScores(android.net.NetworkKey[]);
   }
 
+  public class NetworkRequest implements android.os.Parcelable {
+    method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
+  }
+
   public static class NetworkRequest.Builder {
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
   }
@@ -4641,15 +4712,6 @@
     method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
   }
 
-  public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
-    ctor public StringNetworkSpecifier(@NonNull String);
-    method public int describeContents();
-    method public boolean satisfiedBy(android.net.NetworkSpecifier);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR;
-    field @NonNull public final String specifier;
-  }
-
   public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     method public boolean satisfiedBy(android.net.NetworkSpecifier);
   }
@@ -4843,7 +4905,7 @@
   }
 
   public final class IkeSessionConfiguration {
-    ctor public IkeSessionConfiguration();
+    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
     method @NonNull public String getRemoteApplicationVersion();
     method @NonNull public java.util.List<byte[]> getRemoteVendorIDs();
     method public boolean isIkeExtensionEnabled(int);
@@ -4852,6 +4914,7 @@
   }
 
   public final class IkeSessionParams {
+    method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests();
     method public long getHardLifetime();
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
     method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
@@ -4865,6 +4928,8 @@
 
   public static final class IkeSessionParams.Builder {
     ctor public IkeSessionParams.Builder();
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int);
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
     method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
@@ -4878,6 +4943,14 @@
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
   }
 
+  public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
+    method @Nullable public java.net.Inet4Address getAddress();
+  }
+
+  public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
+    method @Nullable public java.net.Inet6Address getAddress();
+  }
+
   public abstract static class IkeSessionParams.IkeAuthConfig {
   }
 
@@ -4899,6 +4972,9 @@
     method @NonNull public byte[] getPsk();
   }
 
+  public static interface IkeSessionParams.IkeConfigRequest {
+  }
+
   public final class IkeTrafficSelector {
     ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
     field public final int endPort;
@@ -4946,7 +5022,7 @@
   }
 
   public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
-    method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest> getConfigurationRequests();
+    method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests();
   }
 
   public static final class TunnelModeChildSessionParams.Builder {
@@ -4963,33 +5039,33 @@
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long);
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequest {
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
     method @Nullable public java.net.Inet4Address getAddress();
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
     method @Nullable public java.net.Inet4Address getAddress();
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
     method @Nullable public java.net.Inet4Address getAddress();
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
     method @Nullable public java.net.Inet6Address getAddress();
     method public int getPrefixLength();
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
     method @Nullable public java.net.Inet6Address getAddress();
   }
 
+  public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
+  }
+
 }
 
 package android.net.ipsec.ike.exceptions {
@@ -5473,7 +5549,7 @@
     method public boolean isPortableHotspotSupported();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
-    method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
+    method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
@@ -6890,6 +6966,7 @@
     field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
     field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
     field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
+    field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
     field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
     field public static final String DOZE_ALWAYS_ON = "doze_always_on";
     field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -7034,6 +7111,14 @@
 
 }
 
+package android.se.omapi {
+
+  public final class Reader {
+    method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED) public boolean reset();
+  }
+
+}
+
 package android.security.keystore {
 
   public abstract class AttestationUtils {
@@ -8835,7 +8920,7 @@
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
-    field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
@@ -9116,6 +9201,7 @@
   }
 
   public final class SmsManager {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean copyMessageToIcc(@Nullable byte[], @NonNull byte[], int);
     method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int);
     method public boolean disableCellBroadcastRange(int, int, int);
     method public boolean enableCellBroadcastRange(int, int, int);
@@ -9127,6 +9213,7 @@
 
   public class SmsMessage {
     method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean);
+    method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long);
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int);
   }
 
@@ -9791,7 +9878,8 @@
     field public static final int DIALSTRING_USSD = 2; // 0x2
     field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
-    field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+    field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+    field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
     field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
     field public static final String EXTRA_CNA = "cna";
     field public static final String EXTRA_CNAP = "cnap";
@@ -9823,8 +9911,8 @@
     method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
     method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
     method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
-    method public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
-    method public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+    method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+    method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
     method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
     method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
@@ -9832,7 +9920,7 @@
     method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionInviteParticipantsRequestDelivered();
     method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionMayHandover(int, int);
+    method @Deprecated public void callSessionMayHandover(int, int);
     method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
     method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -9854,6 +9942,9 @@
     method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
     method public void callSessionUssdMessageReceived(int, String);
+    method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+    method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
+    method public void onMayHandover(int, int);
   }
 
   public final class ImsConferenceState implements android.os.Parcelable {
@@ -9907,6 +9998,9 @@
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
     field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+    field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
   }
 
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -10189,7 +10283,27 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+    field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
+    field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
+    field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
+    field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
+    field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
+    field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
+    field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
+    field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
+    field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
+    field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
+    field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
     field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
+    field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
+    field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
+    field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
+    field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
+    field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
+    field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
+    field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
+    field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
+    field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
     field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
     field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
     field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
@@ -10199,16 +10313,52 @@
     field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
     field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
     field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
+    field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
+    field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
+    field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
+    field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
+    field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
+    field public static final int KEY_RTT_ENABLED = 66; // 0x42
+    field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
+    field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
+    field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
+    field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
+    field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
+    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
+    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
+    field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
+    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
+    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
+    field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
+    field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
+    field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
+    field public static final int KEY_SMS_FORMAT = 13; // 0xd
+    field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
+    field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
     field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
+    field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
+    field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
+    field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
+    field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
+    field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
+    field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
     field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
+    field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
     field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
+    field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
+    field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
+    field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
     field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+    field public static final int SMS_FORMAT_3GPP = 1; // 0x1
+    field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
     field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
+    field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
+    field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
   }
 
   public static class ProvisioningManager.Callback {
@@ -10526,6 +10676,7 @@
     method public int transact(android.os.Bundle);
     method public int updateCallBarring(int, int, String[]);
     method public int updateCallBarringForServiceClass(int, int, String[], int);
+    method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
     method public int updateCallForward(int, int, String, int, int);
     method public int updateCallWaiting(boolean, int);
     method public int updateClip(boolean);
diff --git a/api/test-current.txt b/api/test-current.txt
index e4997b7..98b224d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1361,6 +1361,32 @@
     field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
   }
 
+  public final class CaptivePortalData implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getByteLimit();
+    method public long getExpiryTimeMillis();
+    method public long getRefreshTimeMillis();
+    method @Nullable public android.net.Uri getUserPortalUrl();
+    method @Nullable public android.net.Uri getVenueInfoUrl();
+    method public boolean isCaptive();
+    method public boolean isSessionExtendable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+  }
+
+  public static class CaptivePortalData.Builder {
+    ctor public CaptivePortalData.Builder();
+    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+    method @NonNull public android.net.CaptivePortalData build();
+    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+  }
+
   public class ConnectivityManager {
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
@@ -1391,6 +1417,8 @@
     ctor public LinkProperties(@Nullable android.net.LinkProperties);
     method public boolean addDnsServer(@NonNull java.net.InetAddress);
     method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
+    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
     method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
     method @Nullable public String getTcpBufferSizes();
     method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
@@ -1401,9 +1429,12 @@
     method public boolean isIpv6Provisioned();
     method public boolean isProvisioned();
     method public boolean isReachable(@NonNull java.net.InetAddress);
+    method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
     method public boolean removeDnsServer(@NonNull java.net.InetAddress);
     method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
     method public boolean removeRoute(@NonNull android.net.RouteInfo);
+    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
     method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
     method public void setPrivateDnsServerName(@Nullable String);
     method public void setTcpBufferSizes(@Nullable String);
@@ -2853,6 +2884,8 @@
     method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
     method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri);
     method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setIsAdhocConferenceCall(boolean);
+    method @NonNull public android.telecom.ConnectionRequest.Builder setParticipants(@Nullable java.util.List<android.net.Uri>);
     method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean);
@@ -3153,7 +3186,8 @@
     field public static final int DIALSTRING_USSD = 2; // 0x2
     field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
-    field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+    field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+    field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
     field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
     field public static final String EXTRA_CNA = "cna";
     field public static final String EXTRA_CNAP = "cnap";
@@ -3186,8 +3220,8 @@
     method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
     method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
     method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
-    method public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
-    method public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+    method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+    method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
     method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
     method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
@@ -3195,7 +3229,7 @@
     method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionInviteParticipantsRequestDelivered();
     method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
-    method public void callSessionMayHandover(int, int);
+    method @Deprecated public void callSessionMayHandover(int, int);
     method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
     method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -3217,6 +3251,9 @@
     method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
     method public void callSessionUssdMessageReceived(int, String);
+    method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+    method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
+    method public void onMayHandover(int, int);
   }
 
   public final class ImsConferenceState implements android.os.Parcelable {
@@ -3270,6 +3307,9 @@
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
     method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
     field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+    field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+    field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
   }
 
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -3548,7 +3588,27 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+    field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
+    field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
+    field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
+    field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
+    field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
+    field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
+    field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
+    field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
+    field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
+    field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
+    field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
     field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
+    field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
+    field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
+    field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
+    field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
+    field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
+    field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
+    field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
+    field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
+    field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
     field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
     field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
     field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
@@ -3558,16 +3618,52 @@
     field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
     field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
     field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
+    field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
+    field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
+    field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
+    field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
+    field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
+    field public static final int KEY_RTT_ENABLED = 66; // 0x42
+    field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
+    field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
+    field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
+    field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
+    field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
+    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
+    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
+    field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
+    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
+    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
+    field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
+    field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
+    field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
+    field public static final int KEY_SMS_FORMAT = 13; // 0xd
+    field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
+    field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
     field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
+    field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
+    field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
+    field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
+    field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
+    field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
+    field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
     field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
+    field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
     field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
+    field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
+    field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
+    field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
     field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+    field public static final int SMS_FORMAT_3GPP = 1; // 0x1
+    field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
     field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
+    field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
+    field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
   }
 
   public static class ProvisioningManager.Callback {
@@ -3837,6 +3933,7 @@
     method public int transact(android.os.Bundle);
     method public int updateCallBarring(int, int, String[]);
     method public int updateCallBarringForServiceClass(int, int, String[], int);
+    method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
     method public int updateCallForward(int, int, String, int, int);
     method public int updateCallWaiting(boolean, int);
     method public int updateClip(boolean);
@@ -4038,138 +4135,10 @@
     method public void writeRawZigZag64(long);
   }
 
-  public final class ProtoOutputStream extends android.util.proto.ProtoStream {
-    ctor public ProtoOutputStream();
-    ctor public ProtoOutputStream(int);
-    ctor public ProtoOutputStream(java.io.OutputStream);
-    ctor public ProtoOutputStream(java.io.FileDescriptor);
-    method public static int checkFieldId(long, long);
-    method public void dump(String);
-    method public void end(long);
-    method @Deprecated public void endObject(long);
-    method @Deprecated public void endRepeatedObject(long);
-    method public void flush();
-    method public byte[] getBytes();
-    method public int getRawSize();
-    method public static long makeFieldId(int, long);
-    method public long start(long);
-    method @Deprecated public long startObject(long);
-    method @Deprecated public long startRepeatedObject(long);
-    method public void write(long, double);
-    method public void write(long, float);
-    method public void write(long, int);
-    method public void write(long, long);
-    method public void write(long, boolean);
-    method public void write(long, String);
-    method public void write(long, byte[]);
-    method @Deprecated public void writeBool(long, boolean);
-    method @Deprecated public void writeBytes(long, byte[]);
-    method @Deprecated public void writeDouble(long, double);
-    method @Deprecated public void writeEnum(long, int);
-    method @Deprecated public void writeFixed32(long, int);
-    method @Deprecated public void writeFixed64(long, long);
-    method @Deprecated public void writeFloat(long, float);
-    method @Deprecated public void writeInt32(long, int);
-    method @Deprecated public void writeInt64(long, long);
-    method @Deprecated public void writeObject(long, byte[]);
-    method @Deprecated public void writePackedBool(long, boolean[]);
-    method @Deprecated public void writePackedDouble(long, double[]);
-    method @Deprecated public void writePackedEnum(long, int[]);
-    method @Deprecated public void writePackedFixed32(long, int[]);
-    method @Deprecated public void writePackedFixed64(long, long[]);
-    method @Deprecated public void writePackedFloat(long, float[]);
-    method @Deprecated public void writePackedInt32(long, int[]);
-    method @Deprecated public void writePackedInt64(long, long[]);
-    method @Deprecated public void writePackedSFixed32(long, int[]);
-    method @Deprecated public void writePackedSFixed64(long, long[]);
-    method @Deprecated public void writePackedSInt32(long, int[]);
-    method @Deprecated public void writePackedSInt64(long, long[]);
-    method @Deprecated public void writePackedUInt32(long, int[]);
-    method @Deprecated public void writePackedUInt64(long, long[]);
-    method @Deprecated public void writeRepeatedBool(long, boolean);
-    method @Deprecated public void writeRepeatedBytes(long, byte[]);
-    method @Deprecated public void writeRepeatedDouble(long, double);
-    method @Deprecated public void writeRepeatedEnum(long, int);
-    method @Deprecated public void writeRepeatedFixed32(long, int);
-    method @Deprecated public void writeRepeatedFixed64(long, long);
-    method @Deprecated public void writeRepeatedFloat(long, float);
-    method @Deprecated public void writeRepeatedInt32(long, int);
-    method @Deprecated public void writeRepeatedInt64(long, long);
-    method @Deprecated public void writeRepeatedObject(long, byte[]);
-    method @Deprecated public void writeRepeatedSFixed32(long, int);
-    method @Deprecated public void writeRepeatedSFixed64(long, long);
-    method @Deprecated public void writeRepeatedSInt32(long, int);
-    method @Deprecated public void writeRepeatedSInt64(long, long);
-    method @Deprecated public void writeRepeatedString(long, String);
-    method @Deprecated public void writeRepeatedUInt32(long, int);
-    method @Deprecated public void writeRepeatedUInt64(long, long);
-    method @Deprecated public void writeSFixed32(long, int);
-    method @Deprecated public void writeSFixed64(long, long);
-    method @Deprecated public void writeSInt32(long, int);
-    method @Deprecated public void writeSInt64(long, long);
-    method @Deprecated public void writeString(long, String);
-    method public void writeTag(int, int);
-    method @Deprecated public void writeUInt32(long, int);
-    method @Deprecated public void writeUInt64(long, long);
-  }
-
   public class ProtoParseException extends java.lang.RuntimeException {
     ctor public ProtoParseException(String);
   }
 
-  public abstract class ProtoStream {
-    ctor public ProtoStream();
-    method public static int convertObjectIdToOrdinal(int);
-    method public static int getDepthFromToken(long);
-    method public static String getFieldCountString(long);
-    method public static String getFieldIdString(long);
-    method public static String getFieldTypeString(long);
-    method public static int getObjectIdFromToken(long);
-    method public static int getOffsetFromToken(long);
-    method public static boolean getRepeatedFromToken(long);
-    method public static int getTagSizeFromToken(long);
-    method public static String getWireTypeString(int);
-    method public static long makeFieldId(int, long);
-    method public static long makeToken(int, boolean, int, int, int);
-    method public static String token2String(long);
-    field public static final long FIELD_COUNT_MASK = 16492674416640L; // 0xf0000000000L
-    field public static final long FIELD_COUNT_PACKED = 5497558138880L; // 0x50000000000L
-    field public static final long FIELD_COUNT_REPEATED = 2199023255552L; // 0x20000000000L
-    field public static final int FIELD_COUNT_SHIFT = 40; // 0x28
-    field public static final long FIELD_COUNT_SINGLE = 1099511627776L; // 0x10000000000L
-    field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L
-    field public static final int FIELD_ID_MASK = -8; // 0xfffffff8
-    field public static final int FIELD_ID_SHIFT = 3; // 0x3
-    field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L
-    field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L
-    field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L
-    field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L
-    field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L
-    field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L
-    field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L
-    field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L
-    field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
-    field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
-    field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
-    field protected static final String[] FIELD_TYPE_NAMES;
-    field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
-    field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
-    field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
-    field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L
-    field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L
-    field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L
-    field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
-    field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
-    field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L
-    field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
-    field public static final int WIRE_TYPE_FIXED32 = 5; // 0x5
-    field public static final int WIRE_TYPE_FIXED64 = 1; // 0x1
-    field public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; // 0x2
-    field public static final int WIRE_TYPE_MASK = 7; // 0x7
-    field public static final int WIRE_TYPE_START_GROUP = 3; // 0x3
-    field public static final int WIRE_TYPE_VARINT = 0; // 0x0
-  }
-
   public class WireTypeMismatchException extends android.util.proto.ProtoParseException {
     ctor public WireTypeMismatchException(String);
   }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7d3cd28..6ff48ea 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -720,7 +720,18 @@
     /** @hide Capture the device's display contents and/or audio */
     @UnsupportedAppUsage
     public static final int OP_PROJECT_MEDIA = 46;
-    /** @hide Activate a VPN connection without user intervention. */
+
+    /**
+     * Start (without additional user intervention) a VPN connection, as used by {@link
+     * android.net.VpnService} along with as Platform VPN connections, as used by {@link
+     * android.net.VpnManager}
+     *
+     * <p>This appop is granted to apps that have already been given user consent to start
+     * VpnService based VPN connections. As this is a superset of OP_ACTIVATE_PLATFORM_VPN, this
+     * appop also allows the starting of Platform VPNs.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public static final int OP_ACTIVATE_VPN = 47;
     /** @hide Access the WallpaperManagerAPI to write wallpapers. */
@@ -840,10 +851,21 @@
     public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
     /** @hide Read location metadata from media */
     public static final int OP_ACCESS_MEDIA_LOCATION = 90;
+    /**
+     * Start (without additional user intervention) a Platform VPN connection, as used by {@link
+     * android.net.VpnManager}
+     *
+     * <p>This appop is granted to apps that have already been given user consent to start Platform
+     * VPN connections. This appop is insufficient to start VpnService based VPNs (but the opposite
+     * is true).
+     *
+     * @hide
+     */
+    public static final int OP_ACTIVATE_PLATFORM_VPN = 91;
 
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 91;
+    public static final int _NUM_OP = 92;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1122,6 +1144,8 @@
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
     /** @hide Read device identifiers */
     public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
+    /** @hide Start Platform VPN without user intervention */
+    public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
 
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
@@ -1285,6 +1309,7 @@
             OP_ACCESS_ACCESSIBILITY,            // ACCESS_ACCESSIBILITY
             OP_READ_DEVICE_IDENTIFIERS,         // READ_DEVICE_IDENTIFIERS
             OP_ACCESS_MEDIA_LOCATION,           // ACCESS_MEDIA_LOCATION
+            OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1382,6 +1407,7 @@
             OPSTR_ACCESS_ACCESSIBILITY,
             OPSTR_READ_DEVICE_IDENTIFIERS,
             OPSTR_ACCESS_MEDIA_LOCATION,
+            OPSTR_ACTIVATE_PLATFORM_VPN,
     };
 
     /**
@@ -1480,6 +1506,7 @@
             "ACCESS_ACCESSIBILITY",
             "READ_DEVICE_IDENTIFIERS",
             "ACCESS_MEDIA_LOCATION",
+            "ACTIVATE_PLATFORM_VPN"
     };
 
     /**
@@ -1579,6 +1606,7 @@
             null, // no permission for OP_ACCESS_ACCESSIBILITY
             null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
             Manifest.permission.ACCESS_MEDIA_LOCATION,
+            null, // no permission for OP_ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1678,6 +1706,7 @@
             null, // ACCESS_ACCESSIBILITY
             null, // READ_DEVICE_IDENTIFIERS
             null, // ACCESS_MEDIA_LOCATION
+            null, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1776,6 +1805,7 @@
             false, // ACCESS_ACCESSIBILITY
             false, // READ_DEVICE_IDENTIFIERS
             false, // ACCESS_MEDIA_LOCATION
+            false, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1873,6 +1903,7 @@
             AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
             AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
             AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
+            AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
@@ -1974,6 +2005,7 @@
             false, // ACCESS_ACCESSIBILITY
             false, // READ_DEVICE_IDENTIFIERS
             false, // ACCESS_MEDIA_LOCATION
+            false, // ACTIVATE_PLATFORM_VPN
     };
 
     /**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 88976e1..81f6d28 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -98,6 +98,7 @@
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
 import android.media.tv.TvInputManager;
+import android.net.ConnectivityDiagnosticsManager;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityThread;
 import android.net.EthernetManager;
@@ -112,6 +113,7 @@
 import android.net.NetworkWatchlistManager;
 import android.net.TestNetworkManager;
 import android.net.TetheringManager;
+import android.net.VpnManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
@@ -369,6 +371,27 @@
                 return new IpSecManager(ctx, service);
             }});
 
+        registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
+                new CachedServiceFetcher<VpnManager>() {
+            @Override
+            public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                return new VpnManager(ctx, service);
+            }});
+
+        registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+                ConnectivityDiagnosticsManager.class,
+                new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
+            @Override
+            public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                // ConnectivityDiagnosticsManager is backed by ConnectivityService
+                IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                return new ConnectivityDiagnosticsManager(ctx, service);
+            }});
+
         registerService(
                 Context.TEST_NETWORK_SERVICE,
                 TestNetworkManager.class,
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 8993de0..ee2cc6d 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -297,7 +297,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -345,7 +346,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1869c6f..bd0a39c 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1222,6 +1222,7 @@
             if (mService != null) {
                 return mService.factoryReset();
             }
+            Log.e(TAG, "factoryReset(): IBluetooth Service is null");
             SystemProperties.set("persist.bluetooth.factoryreset", "true");
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -1239,7 +1240,7 @@
      */
     @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @NonNull ParcelUuid[] getUuids() {
+    public @Nullable ParcelUuid[] getUuids() {
         if (getState() != STATE_ON) {
             return null;
         }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 6de1ffb..fbda9e9 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -584,7 +584,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothHeadsetClient service =
                 getService();
@@ -633,7 +634,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothHeadsetClient service =
                 getService();
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index c1233b8..26e3e27 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -416,7 +416,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -464,7 +465,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 4674706..1c62faa 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -340,7 +340,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -388,7 +389,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 0aa5aac..8d2aadd 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -271,7 +271,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -319,7 +320,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
         if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
         final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 024bb06..ec63fd0 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -30,6 +30,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.CloseGuard;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -50,10 +51,11 @@
  * @hide
  */
 @SystemApi
-public final class BluetoothPan implements BluetoothProfile {
+public final class BluetoothPan implements BluetoothProfile, AutoCloseable {
     private static final String TAG = "BluetoothPan";
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
+    private CloseGuard mCloseGuard;
 
     /**
      * Intent used to broadcast the change in connection state of the Pan
@@ -166,10 +168,15 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mContext = context;
         mProfileConnector.connect(context, listener);
+        mCloseGuard = new CloseGuard();
+        mCloseGuard.open("close");
     }
 
-    @UnsupportedAppUsage
-    /*package*/ void close() {
+    /**
+     * Closes the connection to the service and unregisters callbacks
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void close() {
         if (VDBG) log("close()");
         mProfileConnector.disconnect();
     }
@@ -178,8 +185,11 @@
         return mProfileConnector.getService();
     }
 
-
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     protected void finalize() {
+        if (mCloseGuard != null) {
+            mCloseGuard.warnIfOpen();
+        }
         close();
     }
 
@@ -316,6 +326,7 @@
      * @hide
      */
     @Override
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         if (VDBG) log("getDevicesMatchingStates()");
         final IBluetoothPan service = getService();
@@ -335,6 +346,7 @@
      * {@inheritDoc}
      */
     @Override
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getConnectionState(@Nullable BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
         final IBluetoothPan service = getService();
@@ -355,6 +367,7 @@
      *
      * @param value is whether to enable or disable bluetooth tethering
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public void setBluetoothTethering(boolean value) {
         String pkgName = mContext.getOpPackageName();
         if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -373,6 +386,7 @@
      *
      * @return true if tethering is on, false if not or some error occurred
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isTetheringOn() {
         if (VDBG) log("isTetheringOn()");
         final IBluetoothPan service = getService();
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 9618ba0..9563c68 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -271,7 +271,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) {
             log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         }
@@ -323,7 +324,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
         if (VDBG) {
             log("getConnectionPolicy(" + device + ")");
         }
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 638e6de..7538df8 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -146,7 +146,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     int A2DP_SINK = 11;
 
     /**
@@ -154,7 +154,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     int AVRCP_CONTROLLER = 12;
 
     /**
@@ -169,6 +169,7 @@
      *
      * @hide
      */
+    @SystemApi
     int HEADSET_CLIENT = 16;
 
     /**
@@ -176,6 +177,7 @@
      *
      * @hide
      */
+    @SystemApi
     int PBAP_CLIENT = 17;
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 8bf1b58..bfc3a4d 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -330,7 +330,8 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothSap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -378,7 +379,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothSap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cd4af96..472d956 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3296,6 +3296,7 @@
             CONNECTIVITY_SERVICE,
             //@hide: IP_MEMORY_STORE_SERVICE,
             IPSEC_SERVICE,
+            VPN_MANAGEMENT_SERVICE,
             TEST_NETWORK_SERVICE,
             //@hide: UPDATE_LOCK_SERVICE,
             //@hide: NETWORKMANAGEMENT_SERVICE,
@@ -3880,6 +3881,24 @@
     public static final String IPSEC_SERVICE = "ipsec";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.VpnManager} to
+     * manage profiles for the platform built-in VPN.
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics
+     * as well as receiving network connectivity information from the system.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.ConnectivityDiagnosticsManager
+     */
+    public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.net.TestNetworkManager} for building TUNs and limited-use Networks
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9cba7aa..d859a3af 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3619,7 +3619,9 @@
      * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
      * @hide
      */
-    @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi
     public static final String ACTION_USER_SWITCHED =
             "android.intent.action.USER_SWITCHED";
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0ae00fb..93b90b4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -545,9 +545,10 @@
     public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
 
     /**
-     * Internal flag used to indicate that a package is a hidden system app.
+     * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
      * @hide
      */
+    @SystemApi
     public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
 
     /**
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index ae3d794..0a76bed 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -16,15 +16,17 @@
 
 package android.debug;
 
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.RemoteException;
 
 /**
- * This class allows the control of ADB-related functions. Currently only ADB over USB is
- * supported, and none of the API is public.
- *
+ * This class allows the control of ADB-related functions.
  * @hide
  */
+@SystemApi
 @SystemService(Context.ADB_SERVICE)
 public class AdbManager {
     private static final String TAG = "AdbManager";
@@ -39,4 +41,33 @@
         mContext = context;
         mService = service;
     }
+
+    /**
+     * @return true if the device supports secure ADB over Wi-Fi.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
+    public boolean isAdbWifiSupported() {
+        try {
+            return mService.isAdbWifiSupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @return true if the device supports secure ADB over Wi-Fi and device pairing by
+     * QR code.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
+    public boolean isAdbWifiQrSupported() {
+        try {
+            return mService.isAdbWifiQrSupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
index 79e0794..c48fc07 100644
--- a/core/java/android/debug/IAdbManager.aidl
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -41,4 +41,15 @@
      * Clear all public keys installed for secure ADB debugging.
      */
     void clearDebuggingKeys();
+
+    /**
+     * Returns true if device supports secure Adb over Wi-Fi.
+     */
+    boolean isAdbWifiSupported();
+
+    /**
+     * Returns true if device supports secure Adb over Wi-Fi and device pairing by
+     * QR code.
+     */
+    boolean isAdbWifiQrSupported();
 }
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1142a07..2497ea9 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -32,6 +32,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.security.identity.IdentityCredential;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -401,6 +402,10 @@
             super(mac);
         }
 
+        public CryptoObject(@NonNull IdentityCredential credential) {
+            super(credential);
+        }
+
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -424,6 +429,14 @@
         public Mac getMac() {
             return super.getMac();
         }
+
+        /**
+         * Get {@link IdentityCredential} object.
+         * @return {@link IdentityCredential} object or null if this doesn't contain one.
+         */
+        public @Nullable IdentityCredential getIdentityCredential() {
+            return super.getIdentityCredential();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 787dc66..0af18df 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -17,6 +17,7 @@
 package android.hardware.biometrics;
 
 import android.annotation.NonNull;
+import android.security.identity.IdentityCredential;
 import android.security.keystore.AndroidKeyStoreProvider;
 
 import java.security.Signature;
@@ -26,7 +27,8 @@
 
 /**
  * A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
+ * {@link IdentityCredential} objects.
  * @hide
  */
 public class CryptoObject {
@@ -44,6 +46,10 @@
         mCrypto = mac;
     }
 
+    public CryptoObject(@NonNull IdentityCredential credential) {
+        mCrypto = credential;
+    }
+
     /**
      * Get {@link Signature} object.
      * @return {@link Signature} object or null if this doesn't contain one.
@@ -69,11 +75,23 @@
     }
 
     /**
+     * Get {@link IdentityCredential} object.
+     * @return {@link IdentityCredential} object or null if this doesn't contain one.
+     */
+    public IdentityCredential getIdentityCredential() {
+        return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
+    }
+
+    /**
      * @hide
      * @return the opId associated with this object or 0 if none
      */
     public final long getOpId() {
-        return mCrypto != null
-                ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+        if (mCrypto == null) {
+            return 0;
+        } else if (mCrypto instanceof IdentityCredential) {
+            return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
+        }
+        return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
     }
 };
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 315af32..16f9688 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -44,6 +44,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.security.identity.IdentityCredential;
 import android.util.Slog;
 
 import java.security.Signature;
@@ -125,6 +126,10 @@
             super(mac);
         }
 
+        public CryptoObject(@NonNull IdentityCredential credential) {
+            super(credential);
+        }
+
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -148,6 +153,14 @@
         public Mac getMac() {
             return super.getMac();
         }
+
+        /**
+         * Get {@link IdentityCredential} object.
+         * @return {@link IdentityCredential} object or null if this doesn't contain one.
+         */
+        public @Nullable IdentityCredential getIdentityCredential() {
+            return super.getIdentityCredential();
+        }
     }
 
     /**
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/CaptivePortalData.aidl
similarity index 84%
copy from core/java/android/net/NetworkMisc.aidl
copy to core/java/android/net/CaptivePortalData.aidl
index c65583f..1d57ee7 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/CaptivePortalData.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,4 +16,4 @@
 
 package android.net;
 
-parcelable NetworkMisc;
+@JavaOnlyStableParcelable parcelable CaptivePortalData;
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
new file mode 100644
index 0000000..1357803
--- /dev/null
+++ b/core/java/android/net/CaptivePortalData.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Metadata sent by captive portals, see https://www.ietf.org/id/draft-ietf-capport-api-03.txt.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class CaptivePortalData implements Parcelable {
+    private final long mRefreshTimeMillis;
+    @Nullable
+    private final Uri mUserPortalUrl;
+    @Nullable
+    private final Uri mVenueInfoUrl;
+    private final boolean mIsSessionExtendable;
+    private final long mByteLimit;
+    private final long mExpiryTimeMillis;
+    private final boolean mCaptive;
+
+    private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
+            boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) {
+        mRefreshTimeMillis = refreshTimeMillis;
+        mUserPortalUrl = userPortalUrl;
+        mVenueInfoUrl = venueInfoUrl;
+        mIsSessionExtendable = isSessionExtendable;
+        mByteLimit = byteLimit;
+        mExpiryTimeMillis = expiryTimeMillis;
+        mCaptive = captive;
+    }
+
+    private CaptivePortalData(Parcel p) {
+        this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
+                p.readLong(), p.readLong(), p.readBoolean());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mRefreshTimeMillis);
+        dest.writeParcelable(mUserPortalUrl, 0);
+        dest.writeParcelable(mVenueInfoUrl, 0);
+        dest.writeBoolean(mIsSessionExtendable);
+        dest.writeLong(mByteLimit);
+        dest.writeLong(mExpiryTimeMillis);
+        dest.writeBoolean(mCaptive);
+    }
+
+    /**
+     * A builder to create new {@link CaptivePortalData}.
+     */
+    public static class Builder {
+        private long mRefreshTime;
+        private Uri mUserPortalUrl;
+        private Uri mVenueInfoUrl;
+        private boolean mIsSessionExtendable;
+        private long mBytesRemaining = -1;
+        private long mExpiryTime = -1;
+        private boolean mCaptive;
+
+        /**
+         * Create an empty builder.
+         */
+        public Builder() {}
+
+        /**
+         * Create a builder copying all data from existing {@link CaptivePortalData}.
+         */
+        public Builder(@Nullable CaptivePortalData data) {
+            if (data == null) return;
+            setRefreshTime(data.mRefreshTimeMillis)
+                    .setUserPortalUrl(data.mUserPortalUrl)
+                    .setVenueInfoUrl(data.mVenueInfoUrl)
+                    .setSessionExtendable(data.mIsSessionExtendable)
+                    .setBytesRemaining(data.mByteLimit)
+                    .setExpiryTime(data.mExpiryTimeMillis)
+                    .setCaptive(data.mCaptive);
+        }
+
+        /**
+         * Set the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
+         */
+        @NonNull
+        public Builder setRefreshTime(long refreshTime) {
+            mRefreshTime = refreshTime;
+            return this;
+        }
+
+        /**
+         * Set the URL to be used for users to login to the portal, if captive.
+         */
+        @NonNull
+        public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) {
+            mUserPortalUrl = userPortalUrl;
+            return this;
+        }
+
+        /**
+         * Set the URL that can be used by users to view information about the network venue.
+         */
+        @NonNull
+        public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) {
+            mVenueInfoUrl = venueInfoUrl;
+            return this;
+        }
+
+        /**
+         * Set whether the portal supports extending a user session on the portal URL page.
+         */
+        @NonNull
+        public Builder setSessionExtendable(boolean sessionExtendable) {
+            mIsSessionExtendable = sessionExtendable;
+            return this;
+        }
+
+        /**
+         * Set the number of bytes remaining on the network before the portal closes.
+         */
+        @NonNull
+        public Builder setBytesRemaining(long bytesRemaining) {
+            mBytesRemaining = bytesRemaining;
+            return this;
+        }
+
+        /**
+         * Set the time at the session will expire, as per {@link System#currentTimeMillis()}.
+         */
+        @NonNull
+        public Builder setExpiryTime(long expiryTime) {
+            mExpiryTime = expiryTime;
+            return this;
+        }
+
+        /**
+         * Set whether the network is captive (portal closed).
+         */
+        @NonNull
+        public Builder setCaptive(boolean captive) {
+            mCaptive = captive;
+            return this;
+        }
+
+        /**
+         * Create a new {@link CaptivePortalData}.
+         */
+        @NonNull
+        public CaptivePortalData build() {
+            return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
+                    mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive);
+        }
+    }
+
+    /**
+     * Get the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
+     */
+    public long getRefreshTimeMillis() {
+        return mRefreshTimeMillis;
+    }
+
+    /**
+     * Get the URL to be used for users to login to the portal, or extend their session if
+     * {@link #isSessionExtendable()} is true.
+     */
+    @Nullable
+    public Uri getUserPortalUrl() {
+        return mUserPortalUrl;
+    }
+
+    /**
+     * Get the URL that can be used by users to view information about the network venue.
+     */
+    @Nullable
+    public Uri getVenueInfoUrl() {
+        return mVenueInfoUrl;
+    }
+
+    /**
+     * Indicates whether the user portal URL can be used to extend sessions, when the user is logged
+     * in and the session has a time or byte limit.
+     */
+    public boolean isSessionExtendable() {
+        return mIsSessionExtendable;
+    }
+
+    /**
+     * Get the remaining bytes on the captive portal session, at the time {@link CaptivePortalData}
+     * was refreshed. This may be different from the limit currently enforced by the portal.
+     * @return The byte limit, or -1 if not set.
+     */
+    public long getByteLimit() {
+        return mByteLimit;
+    }
+
+    /**
+     * Get the time at the session will expire, as per {@link System#currentTimeMillis()}.
+     * @return The expiry time, or -1 if unset.
+     */
+    public long getExpiryTimeMillis() {
+        return mExpiryTimeMillis;
+    }
+
+    /**
+     * Get whether the network is captive (portal closed).
+     */
+    public boolean isCaptive() {
+        return mCaptive;
+    }
+
+    @NonNull
+    public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
+        @Override
+        public CaptivePortalData createFromParcel(Parcel source) {
+            return new CaptivePortalData(source);
+        }
+
+        @Override
+        public CaptivePortalData[] newArray(int size) {
+            return new CaptivePortalData[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
+                mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CaptivePortalData)) return false;
+        final CaptivePortalData other = (CaptivePortalData) obj;
+        return mRefreshTimeMillis == other.mRefreshTimeMillis
+                && Objects.equals(mUserPortalUrl, other.mUserPortalUrl)
+                && Objects.equals(mVenueInfoUrl, other.mVenueInfoUrl)
+                && mIsSessionExtendable == other.mIsSessionExtendable
+                && mByteLimit == other.mByteLimit
+                && mExpiryTimeMillis == other.mExpiryTimeMillis
+                && mCaptive == other.mCaptive;
+    }
+
+    @Override
+    public String toString() {
+        return "CaptivePortalData {"
+                + "refreshTime: " + mRefreshTimeMillis
+                + ", userPortalUrl: " + mUserPortalUrl
+                + ", venueInfoUrl: " + mVenueInfoUrl
+                + ", isSessionExtendable: " + mIsSessionExtendable
+                + ", byteLimit: " + mByteLimit
+                + ", expiryTime: " + mExpiryTimeMillis
+                + ", captive: " + mCaptive
+                + "}";
+    }
+}
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/ConnectivityDiagnosticsManager.aidl
similarity index 69%
copy from core/java/android/net/NetworkMisc.aidl
copy to core/java/android/net/ConnectivityDiagnosticsManager.aidl
index c65583f..82ba0ca 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.aidl
@@ -1,11 +1,12 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
+/**
+ *
+ * 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
+ *     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,
@@ -16,4 +17,5 @@
 
 package android.net;
 
-parcelable NetworkMisc;
+parcelable ConnectivityDiagnosticsManager.ConnectivityReport;
+parcelable ConnectivityDiagnosticsManager.DataStallReport;
\ No newline at end of file
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 6afdb5e..4564813 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -18,10 +18,19 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -47,38 +56,169 @@
  * </ul>
  */
 public class ConnectivityDiagnosticsManager {
-    public static final int DETECTION_METHOD_DNS_EVENTS = 1;
-    public static final int DETECTION_METHOD_TCP_METRICS = 2;
+    private final Context mContext;
+    private final IConnectivityManager mService;
 
     /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-            prefix = {"DETECTION_METHOD_"},
-            value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
-    public @interface DetectionMethod {}
+    public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
+        mContext = Preconditions.checkNotNull(context, "missing context");
+        mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+    }
 
     /** @hide */
-    public ConnectivityDiagnosticsManager() {}
+    @VisibleForTesting
+    public static boolean persistableBundleEquals(
+            @Nullable PersistableBundle a, @Nullable PersistableBundle b) {
+        if (a == b) return true;
+        if (a == null || b == null) return false;
+        if (!Objects.equals(a.keySet(), b.keySet())) return false;
+        for (String key : a.keySet()) {
+            if (!Objects.equals(a.get(key), b.get(key))) return false;
+        }
+        return true;
+    }
 
     /** Class that includes connectivity information for a specific Network at a specific time. */
-    public static class ConnectivityReport {
+    public static final class ConnectivityReport implements Parcelable {
+        /**
+         * The overall status of the network is that it is invalid; it neither provides
+         * connectivity nor has been exempted from validation.
+         */
+        public static final int NETWORK_VALIDATION_RESULT_INVALID = 0;
+
+        /**
+         * The overall status of the network is that it is valid, this may be because it provides
+         * full Internet access (all probes succeeded), or because other properties of the network
+         * caused probes not to be run.
+         */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID
+        public static final int NETWORK_VALIDATION_RESULT_VALID = 1;
+
+        /**
+         * The overall status of the network is that it provides partial connectivity; some
+         * probed services succeeded but others failed.
+         */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
+        public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2;
+
+        /**
+         * Due to the properties of the network, validation was not performed.
+         */
+        public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3;
+
+        /** @hide */
+        @IntDef(
+                prefix = {"NETWORK_VALIDATION_RESULT_"},
+                value = {
+                        NETWORK_VALIDATION_RESULT_INVALID,
+                        NETWORK_VALIDATION_RESULT_VALID,
+                        NETWORK_VALIDATION_RESULT_PARTIALLY_VALID,
+                        NETWORK_VALIDATION_RESULT_SKIPPED
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface NetworkValidationResult {}
+
+        /**
+         * The overall validation result for the Network being reported on.
+         *
+         * <p>The possible values for this key are:
+         * {@link #NETWORK_VALIDATION_RESULT_INVALID},
+         * {@link #NETWORK_VALIDATION_RESULT_VALID},
+         * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
+         * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
+         *
+         * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+         */
+        @NetworkValidationResult
+        public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+
+        /** DNS probe. */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS
+        public static final int NETWORK_PROBE_DNS = 0x04;
+
+        /** HTTP probe. */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP
+        public static final int NETWORK_PROBE_HTTP = 0x08;
+
+        /** HTTPS probe. */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+        public static final int NETWORK_PROBE_HTTPS = 0x10;
+
+        /** Captive portal fallback probe. */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK
+        public static final int NETWORK_PROBE_FALLBACK = 0x20;
+
+        /** Private DNS (DNS over TLS) probd. */
+        // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS
+        public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40;
+
+        /** @hide */
+        @IntDef(
+                prefix = {"NETWORK_PROBE_"},
+                value = {
+                        NETWORK_PROBE_DNS,
+                        NETWORK_PROBE_HTTP,
+                        NETWORK_PROBE_HTTPS,
+                        NETWORK_PROBE_FALLBACK,
+                        NETWORK_PROBE_PRIVATE_DNS
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface NetworkProbe {}
+
+        /**
+         * A bitmask of network validation probes that succeeded.
+         *
+         * <p>The possible bits values reported by this key are:
+         * {@link #NETWORK_PROBE_DNS},
+         * {@link #NETWORK_PROBE_HTTP},
+         * {@link #NETWORK_PROBE_HTTPS},
+         * {@link #NETWORK_PROBE_FALLBACK},
+         * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+         */
+        @NetworkProbe
+        public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK =
+                "networkProbesSucceeded";
+
+        /**
+         * A bitmask of network validation probes that were attempted.
+         *
+         * <p>These probes may have failed or may be incomplete at the time of this report.
+         *
+         * <p>The possible bits values reported by this key are:
+         * {@link #NETWORK_PROBE_DNS},
+         * {@link #NETWORK_PROBE_HTTP},
+         * {@link #NETWORK_PROBE_HTTPS},
+         * {@link #NETWORK_PROBE_FALLBACK},
+         * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+         */
+        @NetworkProbe
+        public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
+                "networkProbesAttemped";
+
+        /** @hide */
+        @StringDef(prefix = {"KEY_"}, value = {
+                KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK,
+                KEY_NETWORK_PROBES_ATTEMPTED_BITMASK})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ConnectivityReportBundleKeys {}
+
         /** The Network for which this ConnectivityReport applied */
-        @NonNull public final Network network;
+        @NonNull private final Network mNetwork;
 
         /**
          * The timestamp for the report. The timestamp is taken from {@link
          * System#currentTimeMillis}.
          */
-        public final long reportTimestamp;
+        private final long mReportTimestamp;
 
         /** LinkProperties available on the Network at the reported timestamp */
-        @NonNull public final LinkProperties linkProperties;
+        @NonNull private final LinkProperties mLinkProperties;
 
         /** NetworkCapabilities available on the Network at the reported timestamp */
-        @NonNull public final NetworkCapabilities networkCapabilities;
+        @NonNull private final NetworkCapabilities mNetworkCapabilities;
 
         /** PersistableBundle that may contain additional info about the report */
-        @NonNull public final PersistableBundle additionalInfo;
+        @NonNull private final PersistableBundle mAdditionalInfo;
 
         /**
          * Constructor for ConnectivityReport.
@@ -101,30 +241,191 @@
                 @NonNull LinkProperties linkProperties,
                 @NonNull NetworkCapabilities networkCapabilities,
                 @NonNull PersistableBundle additionalInfo) {
-            this.network = network;
-            this.reportTimestamp = reportTimestamp;
-            this.linkProperties = linkProperties;
-            this.networkCapabilities = networkCapabilities;
-            this.additionalInfo = additionalInfo;
+            mNetwork = network;
+            mReportTimestamp = reportTimestamp;
+            mLinkProperties = linkProperties;
+            mNetworkCapabilities = networkCapabilities;
+            mAdditionalInfo = additionalInfo;
         }
+
+        /**
+         * Returns the Network for this ConnectivityReport.
+         *
+         * @return The Network for which this ConnectivityReport applied
+         */
+        @NonNull
+        public Network getNetwork() {
+            return mNetwork;
+        }
+
+        /**
+         * Returns the epoch timestamp (milliseconds) for when this report was taken.
+         *
+         * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+         */
+        public long getReportTimestamp() {
+            return mReportTimestamp;
+        }
+
+        /**
+         * Returns the LinkProperties available when this report was taken.
+         *
+         * @return LinkProperties available on the Network at the reported timestamp
+         */
+        @NonNull
+        public LinkProperties getLinkProperties() {
+            return new LinkProperties(mLinkProperties);
+        }
+
+        /**
+         * Returns the NetworkCapabilities when this report was taken.
+         *
+         * @return NetworkCapabilities available on the Network at the reported timestamp
+         */
+        @NonNull
+        public NetworkCapabilities getNetworkCapabilities() {
+            return new NetworkCapabilities(mNetworkCapabilities);
+        }
+
+        /**
+         * Returns a PersistableBundle with additional info for this report.
+         *
+         * @return PersistableBundle that may contain additional info about the report
+         */
+        @NonNull
+        public PersistableBundle getAdditionalInfo() {
+            return new PersistableBundle(mAdditionalInfo);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ConnectivityReport)) return false;
+            final ConnectivityReport that = (ConnectivityReport) o;
+
+            // PersistableBundle is optimized to avoid unparcelling data unless fields are
+            // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+            // {@link PersistableBundle#kindofEquals}.
+            return mReportTimestamp == that.mReportTimestamp
+                    && mNetwork.equals(that.mNetwork)
+                    && mLinkProperties.equals(that.mLinkProperties)
+                    && mNetworkCapabilities.equals(that.mNetworkCapabilities)
+                    && persistableBundleEquals(mAdditionalInfo, that.mAdditionalInfo);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                    mNetwork,
+                    mReportTimestamp,
+                    mLinkProperties,
+                    mNetworkCapabilities,
+                    mAdditionalInfo);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeParcelable(mNetwork, flags);
+            dest.writeLong(mReportTimestamp);
+            dest.writeParcelable(mLinkProperties, flags);
+            dest.writeParcelable(mNetworkCapabilities, flags);
+            dest.writeParcelable(mAdditionalInfo, flags);
+        }
+
+        /** Implement the Parcelable interface */
+        public static final @NonNull Creator<ConnectivityReport> CREATOR =
+                new Creator<ConnectivityReport>() {
+                    public ConnectivityReport createFromParcel(Parcel in) {
+                        return new ConnectivityReport(
+                                in.readParcelable(null),
+                                in.readLong(),
+                                in.readParcelable(null),
+                                in.readParcelable(null),
+                                in.readParcelable(null));
+                    }
+
+                    public ConnectivityReport[] newArray(int size) {
+                        return new ConnectivityReport[size];
+                    }
+                };
     }
 
     /** Class that includes information for a suspected data stall on a specific Network */
-    public static class DataStallReport {
+    public static final class DataStallReport implements Parcelable {
+        public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+        public static final int DETECTION_METHOD_TCP_METRICS = 2;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                prefix = {"DETECTION_METHOD_"},
+                value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
+        public @interface DetectionMethod {}
+
+        /**
+         * This key represents the period in milliseconds over which other included TCP metrics
+         * were measured.
+         *
+         * <p>This key will be included if the data stall detection method is
+         * {@link #DETECTION_METHOD_TCP_METRICS}.
+         *
+         * <p>This value is an int.
+         */
+        public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS =
+                "tcpMetricsCollectionPeriodMillis";
+
+        /**
+         * This key represents the fail rate of TCP packets when the suspected data stall was
+         * detected.
+         *
+         * <p>This key will be included if the data stall detection method is
+         * {@link #DETECTION_METHOD_TCP_METRICS}.
+         *
+         * <p>This value is an int percentage between 0 and 100.
+         */
+        public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+
+        /**
+         * This key represents the consecutive number of DNS timeouts that have occurred.
+         *
+         * <p>The consecutive count will be reset any time a DNS response is received.
+         *
+         * <p>This key will be included if the data stall detection method is
+         * {@link #DETECTION_METHOD_DNS_EVENTS}.
+         *
+         * <p>This value is an int.
+         */
+        public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @StringDef(prefix = {"KEY_"}, value = {
+                KEY_TCP_PACKET_FAIL_RATE,
+                KEY_DNS_CONSECUTIVE_TIMEOUTS
+        })
+        public @interface DataStallReportBundleKeys {}
+
         /** The Network for which this DataStallReport applied */
-        @NonNull public final Network network;
+        @NonNull private final Network mNetwork;
 
         /**
          * The timestamp for the report. The timestamp is taken from {@link
          * System#currentTimeMillis}.
          */
-        public final long reportTimestamp;
+        private long mReportTimestamp;
 
         /** The detection method used to identify the suspected data stall */
-        @DetectionMethod public final int detectionMethod;
+        @DetectionMethod private final int mDetectionMethod;
 
         /** PersistableBundle that may contain additional information on the suspected data stall */
-        @NonNull public final PersistableBundle stallDetails;
+        @NonNull private final PersistableBundle mStallDetails;
 
         /**
          * Constructor for DataStallReport.
@@ -143,11 +444,104 @@
                 long reportTimestamp,
                 @DetectionMethod int detectionMethod,
                 @NonNull PersistableBundle stallDetails) {
-            this.network = network;
-            this.reportTimestamp = reportTimestamp;
-            this.detectionMethod = detectionMethod;
-            this.stallDetails = stallDetails;
+            mNetwork = network;
+            mReportTimestamp = reportTimestamp;
+            mDetectionMethod = detectionMethod;
+            mStallDetails = stallDetails;
         }
+
+        /**
+         * Returns the Network for this DataStallReport.
+         *
+         * @return The Network for which this DataStallReport applied
+         */
+        @NonNull
+        public Network getNetwork() {
+            return mNetwork;
+        }
+
+        /**
+         * Returns the epoch timestamp (milliseconds) for when this report was taken.
+         *
+         * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+         */
+        public long getReportTimestamp() {
+            return mReportTimestamp;
+        }
+
+        /**
+         * Returns the detection method used to identify this suspected data stall.
+         *
+         * @return The detection method used to identify the suspected data stall
+         */
+        public int getDetectionMethod() {
+            return mDetectionMethod;
+        }
+
+        /**
+         * Returns a PersistableBundle with additional info for this report.
+         *
+         * <p>Gets a bundle with details about the suspected data stall including information
+         * specific to the monitoring method that detected the data stall.
+         *
+         * @return PersistableBundle that may contain additional information on the suspected data
+         *     stall
+         */
+        @NonNull
+        public PersistableBundle getStallDetails() {
+            return new PersistableBundle(mStallDetails);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) return true;
+            if (!(o instanceof DataStallReport)) return false;
+            final DataStallReport that = (DataStallReport) o;
+
+            // PersistableBundle is optimized to avoid unparcelling data unless fields are
+            // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+            // {@link PersistableBundle#kindofEquals}.
+            return mReportTimestamp == that.mReportTimestamp
+                    && mDetectionMethod == that.mDetectionMethod
+                    && mNetwork.equals(that.mNetwork)
+                    && persistableBundleEquals(mStallDetails, that.mStallDetails);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeParcelable(mNetwork, flags);
+            dest.writeLong(mReportTimestamp);
+            dest.writeInt(mDetectionMethod);
+            dest.writeParcelable(mStallDetails, flags);
+        }
+
+        /** Implement the Parcelable interface */
+        public static final @NonNull Creator<DataStallReport> CREATOR =
+                new Creator<DataStallReport>() {
+                    public DataStallReport createFromParcel(Parcel in) {
+                        return new DataStallReport(
+                                in.readParcelable(null),
+                                in.readLong(),
+                                in.readInt(),
+                                in.readParcelable(null));
+                    }
+
+                    public DataStallReport[] newArray(int size) {
+                        return new DataStallReport[size];
+                    }
+                };
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e8740c8..11c1a9c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3173,8 +3173,8 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkMisc misc) {
-        return registerNetworkAgent(messenger, ni, lp, nc, score, misc, NetworkProvider.ID_NONE);
+            NetworkCapabilities nc, int score, NetworkAgentConfig config) {
+        return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
     }
 
     /**
@@ -3184,9 +3184,10 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkMisc misc, int providerId) {
+            NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+
         try {
-            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc, providerId);
+            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3aee4d5..186196bd 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,9 +20,9 @@
 import android.net.ConnectionInfo;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
@@ -153,7 +153,8 @@
     void declareNetworkRequestUnfulfillable(in NetworkRequest request);
 
     Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
-            in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber);
+            in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+            in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
new file mode 100644
index 0000000..42b4da1
--- /dev/null
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.internal.util.Preconditions.checkStringNotEmpty;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.Credentials;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The Ikev2VpnProfile is a configuration for the platform setup of IKEv2/IPsec VPNs.
+ *
+ * <p>Together with VpnManager, this allows apps to provision IKEv2/IPsec VPNs that do not require
+ * the VPN app to constantly run in the background.
+ *
+ * @see VpnManager
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296 - Internet Key
+ *     Exchange, Version 2 (IKEv2)</a>
+ */
+public final class Ikev2VpnProfile extends PlatformVpnProfile {
+    private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
+    private static final String EMPTY_CERT = "";
+
+    @NonNull private final String mServerAddr;
+    @NonNull private final String mUserIdentity;
+
+    // PSK authentication
+    @Nullable private final byte[] mPresharedKey;
+
+    // Username/Password, RSA authentication
+    @Nullable private final X509Certificate mServerRootCaCert;
+
+    // Username/Password authentication
+    @Nullable private final String mUsername;
+    @Nullable private final String mPassword;
+
+    // RSA Certificate authentication
+    @Nullable private final PrivateKey mRsaPrivateKey;
+    @Nullable private final X509Certificate mUserCert;
+
+    @Nullable private final ProxyInfo mProxyInfo;
+    @NonNull private final List<String> mAllowedAlgorithms;
+    private final boolean mIsBypassable; // Defaults in builder
+    private final boolean mIsMetered; // Defaults in builder
+    private final int mMaxMtu; // Defaults in builder
+
+    private Ikev2VpnProfile(
+            int type,
+            @NonNull String serverAddr,
+            @NonNull String userIdentity,
+            @Nullable byte[] presharedKey,
+            @Nullable X509Certificate serverRootCaCert,
+            @Nullable String username,
+            @Nullable String password,
+            @Nullable PrivateKey rsaPrivateKey,
+            @Nullable X509Certificate userCert,
+            @Nullable ProxyInfo proxyInfo,
+            @NonNull List<String> allowedAlgorithms,
+            boolean isBypassable,
+            boolean isMetered,
+            int maxMtu) {
+        super(type);
+
+        checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
+        checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+        checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
+
+        mServerAddr = serverAddr;
+        mUserIdentity = userIdentity;
+        mPresharedKey =
+                presharedKey == null ? null : Arrays.copyOf(presharedKey, presharedKey.length);
+        mServerRootCaCert = serverRootCaCert;
+        mUsername = username;
+        mPassword = password;
+        mRsaPrivateKey = rsaPrivateKey;
+        mUserCert = userCert;
+        mProxyInfo = new ProxyInfo(proxyInfo);
+
+        // UnmodifiableList doesn't make a defensive copy by default.
+        mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
+
+        mIsBypassable = isBypassable;
+        mIsMetered = isMetered;
+        mMaxMtu = maxMtu;
+
+        validate();
+    }
+
+    private void validate() {
+        // Server Address not validated except to check an address was provided. This allows for
+        // dual-stack servers and hostname based addresses.
+        checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
+        checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+
+        // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
+        // networks, the VPN must provide a link fulfilling the stricter of the two conditions
+        // (at least that of the IPv6 MTU).
+        if (mMaxMtu < LinkProperties.MIN_MTU_V6) {
+            throw new IllegalArgumentException(
+                    "Max MTU must be at least" + LinkProperties.MIN_MTU_V6);
+        }
+
+        switch (mType) {
+            case TYPE_IKEV2_IPSEC_USER_PASS:
+                checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
+                checkNotNull(mPassword, MISSING_PARAM_MSG_TMPL, "Password");
+
+                if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
+
+                break;
+            case TYPE_IKEV2_IPSEC_PSK:
+                checkNotNull(mPresharedKey, MISSING_PARAM_MSG_TMPL, "Preshared Key");
+                break;
+            case TYPE_IKEV2_IPSEC_RSA:
+                checkNotNull(mUserCert, MISSING_PARAM_MSG_TMPL, "User cert");
+                checkNotNull(mRsaPrivateKey, MISSING_PARAM_MSG_TMPL, "RSA Private key");
+
+                checkCert(mUserCert);
+                if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
+
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid auth method set");
+        }
+
+        VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms);
+    }
+
+    /** Retrieves the server address string. */
+    @NonNull
+    public String getServerAddr() {
+        return mServerAddr;
+    }
+
+    /** Retrieves the user identity. */
+    @NonNull
+    public String getUserIdentity() {
+        return mUserIdentity;
+    }
+
+    /**
+     * Retrieves the pre-shared key.
+     *
+     * <p>May be null if the profile is not using Pre-shared key authentication.
+     */
+    @Nullable
+    public byte[] getPresharedKey() {
+        return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
+    }
+
+    /**
+     * Retrieves the certificate for the server's root CA.
+     *
+     * <p>May be null if the profile is not using RSA Digital Signature Authentication or
+     * Username/Password authentication
+     */
+    @Nullable
+    public X509Certificate getServerRootCaCert() {
+        return mServerRootCaCert;
+    }
+
+    /**
+     * Retrieves the username.
+     *
+     * <p>May be null if the profile is not using Username/Password authentication
+     */
+    @Nullable
+    public String getUsername() {
+        return mUsername;
+    }
+
+    /**
+     * Retrieves the password.
+     *
+     * <p>May be null if the profile is not using Username/Password authentication
+     */
+    @Nullable
+    public String getPassword() {
+        return mPassword;
+    }
+
+    /**
+     * Retrieves the RSA private key.
+     *
+     * <p>May be null if the profile is not using RSA Digital Signature authentication
+     */
+    @Nullable
+    public PrivateKey getRsaPrivateKey() {
+        return mRsaPrivateKey;
+    }
+
+    /** Retrieves the user certificate, if any was set. */
+    @Nullable
+    public X509Certificate getUserCert() {
+        return mUserCert;
+    }
+
+    /** Retrieves the proxy information if any was set */
+    @Nullable
+    public ProxyInfo getProxyInfo() {
+        return mProxyInfo;
+    }
+
+    /** Returns all the algorithms allowed by this VPN profile. */
+    @NonNull
+    public List<String> getAllowedAlgorithms() {
+        return mAllowedAlgorithms;
+    }
+
+    /** Returns whether or not the VPN profile should be bypassable. */
+    public boolean isBypassable() {
+        return mIsBypassable;
+    }
+
+    /** Returns whether or not the VPN profile should be always considered metered. */
+    public boolean isMetered() {
+        return mIsMetered;
+    }
+
+    /** Retrieves the maximum MTU set for this VPN profile. */
+    public int getMaxMtu() {
+        return mMaxMtu;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mType,
+                mServerAddr,
+                mUserIdentity,
+                Arrays.hashCode(mPresharedKey),
+                mServerRootCaCert,
+                mUsername,
+                mPassword,
+                mRsaPrivateKey,
+                mUserCert,
+                mProxyInfo,
+                mAllowedAlgorithms,
+                mIsBypassable,
+                mIsMetered,
+                mMaxMtu);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Ikev2VpnProfile)) {
+            return false;
+        }
+
+        final Ikev2VpnProfile other = (Ikev2VpnProfile) obj;
+        return mType == other.mType
+                && Objects.equals(mServerAddr, other.mServerAddr)
+                && Objects.equals(mUserIdentity, other.mUserIdentity)
+                && Arrays.equals(mPresharedKey, other.mPresharedKey)
+                && Objects.equals(mServerRootCaCert, other.mServerRootCaCert)
+                && Objects.equals(mUsername, other.mUsername)
+                && Objects.equals(mPassword, other.mPassword)
+                && Objects.equals(mRsaPrivateKey, other.mRsaPrivateKey)
+                && Objects.equals(mUserCert, other.mUserCert)
+                && Objects.equals(mProxyInfo, other.mProxyInfo)
+                && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
+                && mIsBypassable == other.mIsBypassable
+                && mIsMetered == other.mIsMetered
+                && mMaxMtu == other.mMaxMtu;
+    }
+
+    /**
+     * Builds a VpnProfile instance for internal use, based on the stored IKEv2/IPsec parameters.
+     *
+     * <p>Redundant authentication information (from previous calls to other setAuth* methods) will
+     * be discarded.
+     *
+     * @hide
+     */
+    @NonNull
+    public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
+        final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
+        profile.type = mType;
+        profile.server = mServerAddr;
+        profile.ipsecIdentifier = mUserIdentity;
+        profile.proxy = mProxyInfo;
+        profile.setAllowedAlgorithms(mAllowedAlgorithms);
+        profile.isBypassable = mIsBypassable;
+        profile.isMetered = mIsMetered;
+        profile.maxMtu = mMaxMtu;
+        profile.areAuthParamsInline = true;
+        profile.saveLogin = true;
+
+        switch (mType) {
+            case TYPE_IKEV2_IPSEC_USER_PASS:
+                profile.username = mUsername;
+                profile.password = mPassword;
+                profile.ipsecCaCert =
+                        mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
+                break;
+            case TYPE_IKEV2_IPSEC_PSK:
+                profile.ipsecSecret = encodeForIpsecSecret(mPresharedKey);
+                break;
+            case TYPE_IKEV2_IPSEC_RSA:
+                profile.ipsecUserCert = certificateToPemString(mUserCert);
+                profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
+                profile.ipsecCaCert =
+                        mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid auth method set");
+        }
+
+        return profile;
+    }
+
+    /**
+     * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance.
+     *
+     * <p>Redundant authentication information (not related to profile type) will be discarded.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+            throws IOException, GeneralSecurityException {
+        final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
+        builder.setProxy(profile.proxy);
+        builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
+        builder.setBypassable(profile.isBypassable);
+        builder.setMetered(profile.isMetered);
+        builder.setMaxMtu(profile.maxMtu);
+
+        switch (profile.type) {
+            case TYPE_IKEV2_IPSEC_USER_PASS:
+                builder.setAuthUsernamePassword(
+                        profile.username,
+                        profile.password,
+                        certificateFromPemString(profile.ipsecCaCert));
+                break;
+            case TYPE_IKEV2_IPSEC_PSK:
+                builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
+                break;
+            case TYPE_IKEV2_IPSEC_RSA:
+                final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
+                final PrivateKey key = getPrivateKey(profile.ipsecSecret);
+                final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
+                builder.setAuthDigitalSignature(userCert, key, serverRootCa);
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid auth method set");
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Converts a X509 Certificate to a PEM-formatted string.
+     *
+     * <p>Must be public due to runtime-package restrictions.
+     *
+     * @hide
+     */
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static String certificateToPemString(@Nullable X509Certificate cert)
+            throws IOException, CertificateEncodingException {
+        if (cert == null) {
+            return EMPTY_CERT;
+        }
+
+        // Credentials.convertToPem outputs ASCII bytes.
+        return new String(Credentials.convertToPem(cert), StandardCharsets.US_ASCII);
+    }
+
+    /**
+     * Decodes the provided Certificate(s).
+     *
+     * <p>Will use the first one if the certStr encodes more than one certificate.
+     */
+    @Nullable
+    private static X509Certificate certificateFromPemString(@Nullable String certStr)
+            throws CertificateException {
+        if (certStr == null || EMPTY_CERT.equals(certStr)) {
+            return null;
+        }
+
+        try {
+            final List<X509Certificate> certs =
+                    Credentials.convertFromPem(certStr.getBytes(StandardCharsets.US_ASCII));
+            return certs.isEmpty() ? null : certs.get(0);
+        } catch (IOException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static String encodeForIpsecSecret(@NonNull byte[] secret) {
+        checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
+
+        return Base64.getEncoder().encodeToString(secret);
+    }
+
+    @NonNull
+    private static byte[] decodeFromIpsecSecret(@NonNull String encoded) {
+        checkNotNull(encoded, MISSING_PARAM_MSG_TMPL, "encoded");
+
+        return Base64.getDecoder().decode(encoded);
+    }
+
+    @NonNull
+    private static PrivateKey getPrivateKey(@NonNull String keyStr)
+            throws InvalidKeySpecException, NoSuchAlgorithmException {
+        final PKCS8EncodedKeySpec privateKeySpec =
+                new PKCS8EncodedKeySpec(decodeFromIpsecSecret(keyStr));
+        final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        return keyFactory.generatePrivate(privateKeySpec);
+    }
+
+    private static void checkCert(@NonNull X509Certificate cert) {
+        try {
+            certificateToPemString(cert);
+        } catch (GeneralSecurityException | IOException e) {
+            throw new IllegalArgumentException("Certificate could not be encoded");
+        }
+    }
+
+    private static @NonNull <T> T checkNotNull(
+            final T reference, final String messageTemplate, final Object... messageArgs) {
+        return Objects.requireNonNull(reference, String.format(messageTemplate, messageArgs));
+    }
+
+    /** A incremental builder for IKEv2 VPN profiles */
+    public static final class Builder {
+        private int mType = -1;
+        @NonNull private final String mServerAddr;
+        @NonNull private final String mUserIdentity;
+
+        // PSK authentication
+        @Nullable private byte[] mPresharedKey;
+
+        // Username/Password, RSA authentication
+        @Nullable private X509Certificate mServerRootCaCert;
+
+        // Username/Password authentication
+        @Nullable private String mUsername;
+        @Nullable private String mPassword;
+
+        // RSA Certificate authentication
+        @Nullable private PrivateKey mRsaPrivateKey;
+        @Nullable private X509Certificate mUserCert;
+
+        @Nullable private ProxyInfo mProxyInfo;
+        @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>();
+        private boolean mIsBypassable = false;
+        private boolean mIsMetered = true;
+        private int mMaxMtu = 1360;
+
+        /**
+         * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
+         *
+         * @param serverAddr the server that the VPN should connect to
+         * @param identity the identity string to be used for IKEv2 authentication
+         */
+        public Builder(@NonNull String serverAddr, @NonNull String identity) {
+            checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr");
+            checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity");
+
+            mServerAddr = serverAddr;
+            mUserIdentity = identity;
+        }
+
+        private void resetAuthParams() {
+            mPresharedKey = null;
+            mServerRootCaCert = null;
+            mUsername = null;
+            mPassword = null;
+            mRsaPrivateKey = null;
+            mUserCert = null;
+        }
+
+        /**
+         * Set the IKEv2 authentication to use the provided username/password.
+         *
+         * <p>Setting this will configure IKEv2 authentication using EAP-MSCHAPv2. Only one
+         * authentication method may be set. This method will overwrite any previously set
+         * authentication method.
+         *
+         * @param user the username to be used for EAP-MSCHAPv2 authentication
+         * @param pass the password to be used for EAP-MSCHAPv2 authentication
+         * @param serverRootCa the root certificate to be used for verifying the identity of the
+         *     server
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         * @throws IllegalArgumentException if any of the certificates were invalid or of an
+         *     unrecognized format
+         */
+        @NonNull
+        public Builder setAuthUsernamePassword(
+                @NonNull String user,
+                @NonNull String pass,
+                @Nullable X509Certificate serverRootCa) {
+            checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user");
+            checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass");
+
+            // Test to make sure all auth params can be encoded safely.
+            if (serverRootCa != null) checkCert(serverRootCa);
+
+            resetAuthParams();
+            mUsername = user;
+            mPassword = pass;
+            mServerRootCaCert = serverRootCa;
+            mType = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+            return this;
+        }
+
+        /**
+         * Set the IKEv2 authentication to use Digital Signature Authentication with the given key.
+         *
+         * <p>Setting this will configure IKEv2 authentication using a Digital Signature scheme.
+         * Only one authentication method may be set. This method will overwrite any previously set
+         * authentication method.
+         *
+         * @param userCert the username to be used for RSA Digital signiture authentication
+         * @param key the PrivateKey instance associated with the user ceritificate, used for
+         *     constructing the signature
+         * @param serverRootCa the root certificate to be used for verifying the identity of the
+         *     server
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         * @throws IllegalArgumentException if any of the certificates were invalid or of an
+         *     unrecognized format
+         */
+        @NonNull
+        public Builder setAuthDigitalSignature(
+                @NonNull X509Certificate userCert,
+                @NonNull PrivateKey key,
+                @Nullable X509Certificate serverRootCa) {
+            checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert");
+            checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key");
+
+            // Test to make sure all auth params can be encoded safely.
+            checkCert(userCert);
+            if (serverRootCa != null) checkCert(serverRootCa);
+
+            resetAuthParams();
+            mUserCert = userCert;
+            mRsaPrivateKey = key;
+            mServerRootCaCert = serverRootCa;
+            mType = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
+            return this;
+        }
+
+        /**
+         * Set the IKEv2 authentication to use Preshared keys.
+         *
+         * <p>Setting this will configure IKEv2 authentication using a Preshared Key. Only one
+         * authentication method may be set. This method will overwrite any previously set
+         * authentication method.
+         *
+         * @param psk the key to be used for Pre-Shared Key authentication
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         */
+        @NonNull
+        public Builder setAuthPsk(@NonNull byte[] psk) {
+            checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
+
+            resetAuthParams();
+            mPresharedKey = psk;
+            mType = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+            return this;
+        }
+
+        /**
+         * Sets whether apps can bypass this VPN connection.
+         *
+         * <p>By default, all traffic from apps are forwarded through the VPN interface and it is
+         * not possible for unprivileged apps to side-step the VPN. If a VPN is set to bypassable,
+         * apps may use methods such as {@link Network#getSocketFactory} or {@link
+         * Network#openConnection} to instead send/receive directly over the underlying network or
+         * any other network they have permissions for.
+         *
+         * @param isBypassable Whether or not the VPN should be considered bypassable. Defaults to
+         *     {@code false}.
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         */
+        @NonNull
+        public Builder setBypassable(boolean isBypassable) {
+            mIsBypassable = isBypassable;
+            return this;
+        }
+
+        /**
+         * Sets a proxy for the VPN network.
+         *
+         * <p>Note that this proxy is only a recommendation and it may be ignored by apps.
+         *
+         * @param proxy the ProxyInfo to be set for the VPN network
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         */
+        @NonNull
+        public Builder setProxy(@Nullable ProxyInfo proxy) {
+            mProxyInfo = proxy;
+            return this;
+        }
+
+        /**
+         * Set the upper bound of the maximum transmission unit (MTU) of the VPN interface.
+         *
+         * <p>If it is not set, a safe value will be used. Additionally, the actual link MTU will be
+         * dynamically calculated/updated based on the underlying link's mtu.
+         *
+         * @param mtu the MTU (in bytes) of the VPN interface
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         * @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
+         */
+        @NonNull
+        public Builder setMaxMtu(int mtu) {
+            // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
+            // networks, the VPN must provide a link fulfilling the stricter of the two conditions
+            // (at least that of the IPv6 MTU).
+            if (mtu < LinkProperties.MIN_MTU_V6) {
+                throw new IllegalArgumentException(
+                        "Max MTU must be at least " + LinkProperties.MIN_MTU_V6);
+            }
+            mMaxMtu = mtu;
+            return this;
+        }
+
+        /**
+         * Marks the VPN network as metered.
+         *
+         * <p>A VPN network is classified as metered when the user is sensitive to heavy data usage
+         * due to monetary costs and/or data limitations. In such cases, you should set this to
+         * {@code true} so that apps on the system can avoid doing large data transfers. Otherwise,
+         * set this to {@code false}. Doing so would cause VPN network to inherit its meteredness
+         * from the underlying network.
+         *
+         * @param isMetered {@code true} if the VPN network should be treated as metered regardless
+         *     of underlying network meteredness. Defaults to {@code true}.
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         * @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED
+         */
+        @NonNull
+        public Builder setMetered(boolean isMetered) {
+            mIsMetered = isMetered;
+            return this;
+        }
+
+        /**
+         * Sets the allowable set of IPsec algorithms
+         *
+         * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm}
+         *
+         * @param algorithmNames the list of supported IPsec algorithms
+         * @return this {@link Builder} object to facilitate chaining of method calls
+         * @see IpSecAlgorithm
+         */
+        @NonNull
+        public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
+            checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
+            VpnProfile.validateAllowedAlgorithms(algorithmNames);
+
+            mAllowedAlgorithms = algorithmNames;
+            return this;
+        }
+
+        /**
+         * Validates, builds and provisions the VpnProfile.
+         *
+         * @throws IllegalArgumentException if any of the required keys or values were invalid
+         */
+        @NonNull
+        public Ikev2VpnProfile build() {
+            return new Ikev2VpnProfile(
+                    mType,
+                    mServerAddr,
+                    mUserIdentity,
+                    mPresharedKey,
+                    mServerRootCaCert,
+                    mUsername,
+                    mPassword,
+                    mRsaPrivateKey,
+                    mUserCert,
+                    mProxyInfo,
+                    mAllowedAlgorithms,
+                    mIsBypassable,
+                    mIsMetered,
+                    mMaxMtu);
+        }
+    }
+}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2792c56..d25ee0e 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -70,9 +70,18 @@
     private String mTcpBufferSizes;
     private IpPrefix mNat64Prefix;
     private boolean mWakeOnLanSupported;
+    private Uri mCaptivePortalApiUrl;
+    private CaptivePortalData mCaptivePortalData;
+
+    /**
+     * Indicates whether parceling should preserve fields that are set based on permissions of
+     * the process receiving the {@link LinkProperties}.
+     */
+    private final transient boolean mParcelSensitiveFields;
 
     private static final int MIN_MTU    = 68;
-    private static final int MIN_MTU_V6 = 1280;
+    /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
+    static final int MIN_MTU_V6 = 1280;
     private static final int MAX_MTU    = 10000;
 
     private static final int INET6_ADDR_LENGTH = 16;
@@ -174,6 +183,7 @@
      * Constructs a new {@code LinkProperties} with default values.
      */
     public LinkProperties() {
+        mParcelSensitiveFields = false;
     }
 
     /**
@@ -182,26 +192,32 @@
     @SystemApi
     @TestApi
     public LinkProperties(@Nullable LinkProperties source) {
-        if (source != null) {
-            mIfaceName = source.mIfaceName;
-            mLinkAddresses.addAll(source.mLinkAddresses);
-            mDnses.addAll(source.mDnses);
-            mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
-            mUsePrivateDns = source.mUsePrivateDns;
-            mPrivateDnsServerName = source.mPrivateDnsServerName;
-            mPcscfs.addAll(source.mPcscfs);
-            mDomains = source.mDomains;
-            mRoutes.addAll(source.mRoutes);
-            mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
-            for (LinkProperties l: source.mStackedLinks.values()) {
-                addStackedLink(l);
-            }
-            setMtu(source.mMtu);
-            setDhcpServerAddress(source.getDhcpServerAddress());
-            mTcpBufferSizes = source.mTcpBufferSizes;
-            mNat64Prefix = source.mNat64Prefix;
-            mWakeOnLanSupported = source.mWakeOnLanSupported;
+        this(source, false /* parcelSensitiveFields */);
+    }
+
+    private LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
+        mParcelSensitiveFields = parcelSensitiveFields;
+        if (source == null) return;
+        mIfaceName = source.mIfaceName;
+        mLinkAddresses.addAll(source.mLinkAddresses);
+        mDnses.addAll(source.mDnses);
+        mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
+        mUsePrivateDns = source.mUsePrivateDns;
+        mPrivateDnsServerName = source.mPrivateDnsServerName;
+        mPcscfs.addAll(source.mPcscfs);
+        mDomains = source.mDomains;
+        mRoutes.addAll(source.mRoutes);
+        mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
+        for (LinkProperties l: source.mStackedLinks.values()) {
+            addStackedLink(l);
         }
+        setMtu(source.mMtu);
+        setDhcpServerAddress(source.getDhcpServerAddress());
+        mTcpBufferSizes = source.mTcpBufferSizes;
+        mNat64Prefix = source.mNat64Prefix;
+        mWakeOnLanSupported = source.mWakeOnLanSupported;
+        mCaptivePortalApiUrl = source.mCaptivePortalApiUrl;
+        mCaptivePortalData = source.mCaptivePortalData;
     }
 
     /**
@@ -860,6 +876,11 @@
      * Clears this object to its initial state.
      */
     public void clear() {
+        if (mParcelSensitiveFields) {
+            throw new UnsupportedOperationException(
+                    "Cannot clear LinkProperties when parcelSensitiveFields is set");
+        }
+
         mIfaceName = null;
         mLinkAddresses.clear();
         mDnses.clear();
@@ -875,6 +896,8 @@
         mTcpBufferSizes = null;
         mNat64Prefix = null;
         mWakeOnLanSupported = false;
+        mCaptivePortalApiUrl = null;
+        mCaptivePortalData = null;
     }
 
     /**
@@ -945,6 +968,14 @@
             resultJoiner.add(mDhcpServerAddress.toString());
         }
 
+        if (mCaptivePortalApiUrl != null) {
+            resultJoiner.add("CaptivePortalApiUrl: " + mCaptivePortalApiUrl);
+        }
+
+        if (mCaptivePortalData != null) {
+            resultJoiner.add("CaptivePortalData: " + mCaptivePortalData);
+        }
+
         if (mTcpBufferSizes != null) {
             resultJoiner.add("TcpBufferSizes:");
             resultJoiner.add(mTcpBufferSizes);
@@ -1479,6 +1510,28 @@
     }
 
     /**
+     * Compares this {@code LinkProperties}'s CaptivePortalApiUrl against the target.
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalCaptivePortalApiUrl(LinkProperties target) {
+        return Objects.equals(mCaptivePortalApiUrl, target.mCaptivePortalApiUrl);
+    }
+
+    /**
+     * Compares this {@code LinkProperties}'s CaptivePortalData against the target.
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalCaptivePortalData(LinkProperties target) {
+        return Objects.equals(mCaptivePortalData, target.mCaptivePortalData);
+    }
+
+    /**
      * Set whether the network interface supports WakeOnLAN
      *
      * @param supported WakeOnLAN supported value
@@ -1499,6 +1552,73 @@
     }
 
     /**
+     * Set the URL of the captive portal API endpoint to get more information about the network.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public void setCaptivePortalApiUrl(@Nullable Uri url) {
+        mCaptivePortalApiUrl = url;
+    }
+
+    /**
+     * Get the URL of the captive portal API endpoint to get more information about the network.
+     *
+     * <p>This is null unless the application has
+     * {@link android.Manifest.permission.NETWORK_SETTINGS} or
+     * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions, and the network provided
+     * the URL.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Nullable
+    public Uri getCaptivePortalApiUrl() {
+        return mCaptivePortalApiUrl;
+    }
+
+    /**
+     * Set the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public void setCaptivePortalData(@Nullable CaptivePortalData data) {
+        mCaptivePortalData = data;
+    }
+
+    /**
+     * Get the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+     *
+     * <p>This is null unless the application has
+     * {@link android.Manifest.permission.NETWORK_SETTINGS} or
+     * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Nullable
+    public CaptivePortalData getCaptivePortalData() {
+        return mCaptivePortalData;
+    }
+
+    /**
+     * Create a copy of this {@link LinkProperties} that will preserve fields that were set
+     * based on the permissions of the process that received this {@link LinkProperties}.
+     *
+     * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
+     * they should not be shared outside of the process that receives them without appropriate
+     * checks.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @NonNull
+    public LinkProperties makeSensitiveFieldsParcelingCopy() {
+        return new LinkProperties(this, true /* parcelSensitiveFields */);
+    }
+
+    /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
      * all their fields are equal in values.
@@ -1537,7 +1657,9 @@
                 && isIdenticalMtu(target)
                 && isIdenticalTcpBufferSizes(target)
                 && isIdenticalNat64Prefix(target)
-                && isIdenticalWakeOnLan(target);
+                && isIdenticalWakeOnLan(target)
+                && isIdenticalCaptivePortalApiUrl(target)
+                && isIdenticalCaptivePortalData(target);
     }
 
     /**
@@ -1655,7 +1777,8 @@
                 + mPcscfs.size() * 67
                 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
                 + Objects.hash(mNat64Prefix)
-                + (mWakeOnLanSupported ? 71 : 0);
+                + (mWakeOnLanSupported ? 71 : 0)
+                + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData);
     }
 
     /**
@@ -1694,6 +1817,8 @@
         dest.writeList(stackedLinks);
 
         dest.writeBoolean(mWakeOnLanSupported);
+        dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalApiUrl : null, 0);
+        dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalData : null, 0);
     }
 
     private static void writeAddresses(@NonNull Parcel dest, @NonNull List<InetAddress> list) {
@@ -1785,6 +1910,9 @@
                     netProp.addStackedLink(stackedLink);
                 }
                 netProp.setWakeOnLanSupported(in.readBoolean());
+
+                netProp.setCaptivePortalApiUrl(in.readParcelable(null));
+                netProp.setCaptivePortalData(in.readParcelable(null));
                 return netProp;
             }
 
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index a5f7d53..7316dfa 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -43,9 +43,10 @@
  *
  * @hide
  */
-public abstract class NetworkAgent extends Handler {
+public abstract class NetworkAgent {
     public final Network network;
 
+    private final Handler mHandler;
     private volatile AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
     private static final boolean DBG = true;
@@ -220,8 +221,8 @@
         this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
     }
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
-            NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
-        this(looper, context, logTag, ni, nc, lp, score, misc, NetworkProvider.ID_NONE);
+            NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
+        this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
     }
 
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
@@ -230,9 +231,9 @@
     }
 
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
-            NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc,
+            NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
             int providerId) {
-        super(looper);
+        mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mContext = context;
         mProviderId = providerId;
@@ -243,116 +244,124 @@
         if (VDBG) log("Registering NetworkAgent");
         ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        network = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
-                new LinkProperties(lp), new NetworkCapabilities(nc), score, misc, providerId);
+        network = cm.registerNetworkAgent(new Messenger(mHandler), new NetworkInfo(ni),
+                new LinkProperties(lp), new NetworkCapabilities(nc), score, config,
+                providerId);
     }
 
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
-                if (mAsyncChannel != null) {
-                    log("Received new connection while already connected!");
-                } else {
-                    if (VDBG) log("NetworkAgent fully connected");
-                    AsyncChannel ac = new AsyncChannel();
-                    ac.connected(null, this, msg.replyTo);
-                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
-                            AsyncChannel.STATUS_SUCCESSFUL);
-                    synchronized (mPreConnectedQueue) {
-                        mAsyncChannel = ac;
-                        for (Message m : mPreConnectedQueue) {
-                            ac.sendMessage(m);
-                        }
-                        mPreConnectedQueue.clear();
-                    }
-                }
-                break;
-            }
-            case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
-                if (VDBG) log("CMD_CHANNEL_DISCONNECT");
-                if (mAsyncChannel != null) mAsyncChannel.disconnect();
-                break;
-            }
-            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                if (DBG) log("NetworkAgent channel lost");
-                // let the client know CS is done with us.
-                unwanted();
-                synchronized (mPreConnectedQueue) {
-                    mAsyncChannel = null;
-                }
-                break;
-            }
-            case CMD_SUSPECT_BAD: {
-                log("Unhandled Message " + msg);
-                break;
-            }
-            case CMD_REQUEST_BANDWIDTH_UPDATE: {
-                long currentTimeMs = System.currentTimeMillis();
-                if (VDBG) {
-                    log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
-                }
-                if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
-                    mPollLceScheduled = false;
-                    if (mPollLcePending.getAndSet(true) == false) {
-                        pollLceData();
-                    }
-                } else {
-                    // deliver the request at a later time rather than discard it completely.
-                    if (!mPollLceScheduled) {
-                        long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
-                                currentTimeMs + 1;
-                        mPollLceScheduled = sendEmptyMessageDelayed(
-                                CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
-                    }
-                }
-                break;
-            }
-            case CMD_REPORT_NETWORK_STATUS: {
-                String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
-                if (VDBG) {
-                    log("CMD_REPORT_NETWORK_STATUS(" +
-                            (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
-                }
-                networkStatus(msg.arg1, redirectUrl);
-                break;
-            }
-            case CMD_SAVE_ACCEPT_UNVALIDATED: {
-                saveAcceptUnvalidated(msg.arg1 != 0);
-                break;
-            }
-            case CMD_START_SOCKET_KEEPALIVE: {
-                startSocketKeepalive(msg);
-                break;
-            }
-            case CMD_STOP_SOCKET_KEEPALIVE: {
-                stopSocketKeepalive(msg);
-                break;
-            }
+    private class NetworkAgentHandler extends Handler {
+        NetworkAgentHandler(Looper looper) {
+            super(looper);
+        }
 
-            case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
-                ArrayList<Integer> thresholds =
-                        ((Bundle) msg.obj).getIntegerArrayList("thresholds");
-                // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
-                // rather than convert to int[].
-                int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
-                for (int i = 0; i < intThresholds.length; i++) {
-                    intThresholds[i] = thresholds.get(i);
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+                    if (mAsyncChannel != null) {
+                        log("Received new connection while already connected!");
+                    } else {
+                        if (VDBG) log("NetworkAgent fully connected");
+                        AsyncChannel ac = new AsyncChannel();
+                        ac.connected(null, this, msg.replyTo);
+                        ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_SUCCESSFUL);
+                        synchronized (mPreConnectedQueue) {
+                            mAsyncChannel = ac;
+                            for (Message m : mPreConnectedQueue) {
+                                ac.sendMessage(m);
+                            }
+                            mPreConnectedQueue.clear();
+                        }
+                    }
+                    break;
                 }
-                setSignalStrengthThresholds(intThresholds);
-                break;
-            }
-            case CMD_PREVENT_AUTOMATIC_RECONNECT: {
-                preventAutomaticReconnect();
-                break;
-            }
-            case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
-                addKeepalivePacketFilter(msg);
-                break;
-            }
-            case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
-                removeKeepalivePacketFilter(msg);
-                break;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                    if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+                    if (mAsyncChannel != null) mAsyncChannel.disconnect();
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    if (DBG) log("NetworkAgent channel lost");
+                    // let the client know CS is done with us.
+                    unwanted();
+                    synchronized (mPreConnectedQueue) {
+                        mAsyncChannel = null;
+                    }
+                    break;
+                }
+                case CMD_SUSPECT_BAD: {
+                    log("Unhandled Message " + msg);
+                    break;
+                }
+                case CMD_REQUEST_BANDWIDTH_UPDATE: {
+                    long currentTimeMs = System.currentTimeMillis();
+                    if (VDBG) {
+                        log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+                    }
+                    if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+                        mPollLceScheduled = false;
+                        if (!mPollLcePending.getAndSet(true)) {
+                            pollLceData();
+                        }
+                    } else {
+                        // deliver the request at a later time rather than discard it completely.
+                        if (!mPollLceScheduled) {
+                            long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
+                                    - currentTimeMs + 1;
+                            mPollLceScheduled = sendEmptyMessageDelayed(
+                                    CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+                        }
+                    }
+                    break;
+                }
+                case CMD_REPORT_NETWORK_STATUS: {
+                    String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
+                    if (VDBG) {
+                        log("CMD_REPORT_NETWORK_STATUS("
+                                + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+                                + redirectUrl);
+                    }
+                    networkStatus(msg.arg1, redirectUrl);
+                    break;
+                }
+                case CMD_SAVE_ACCEPT_UNVALIDATED: {
+                    saveAcceptUnvalidated(msg.arg1 != 0);
+                    break;
+                }
+                case CMD_START_SOCKET_KEEPALIVE: {
+                    startSocketKeepalive(msg);
+                    break;
+                }
+                case CMD_STOP_SOCKET_KEEPALIVE: {
+                    stopSocketKeepalive(msg);
+                    break;
+                }
+
+                case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+                    ArrayList<Integer> thresholds =
+                            ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+                    // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+                    // rather than convert to int[].
+                    int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+                    for (int i = 0; i < intThresholds.length; i++) {
+                        intThresholds[i] = thresholds.get(i);
+                    }
+                    setSignalStrengthThresholds(intThresholds);
+                    break;
+                }
+                case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+                    preventAutomaticReconnect();
+                    break;
+                }
+                case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+                    addKeepalivePacketFilter(msg);
+                    break;
+                }
+                case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+                    removeKeepalivePacketFilter(msg);
+                    break;
+                }
             }
         }
     }
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/NetworkAgentConfig.aidl
similarity index 95%
rename from core/java/android/net/NetworkMisc.aidl
rename to core/java/android/net/NetworkAgentConfig.aidl
index c65583f..cb70bdd 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/NetworkAgentConfig.aidl
@@ -16,4 +16,4 @@
 
 package android.net;
 
-parcelable NetworkMisc;
+parcelable NetworkAgentConfig;
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
new file mode 100644
index 0000000..abc6b67
--- /dev/null
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Allows a network transport to provide the system with policy and configuration information about
+ * a particular network when registering a {@link NetworkAgent}. This information cannot change once
+ * the agent is registered.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NetworkAgentConfig implements Parcelable {
+
+    /**
+     * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+     * VPN. This is set by a {@link VpnService} and used by
+     * {@link ConnectivityManager} when creating a VPN.
+     *
+     * @hide
+     */
+    public boolean allowBypass;
+
+    /**
+     * Set if the network was manually/explicitly connected to by the user either from settings
+     * or a 3rd party app.  For example, turning on cell data is not explicit but tapping on a wifi
+     * ap in the wifi settings to trigger a connection is explicit.  A 3rd party app asking to
+     * connect to a particular access point is also explicit, though this may change in the future
+     * as we want apps to use the multinetwork apis.
+     *
+     * @hide
+     */
+    public boolean explicitlySelected;
+
+    /**
+     * Set if the user desires to use this network even if it is unvalidated. This field has meaning
+     * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
+     * appropriate value based on previous user choice.
+     *
+     * @hide
+     */
+    public boolean acceptUnvalidated;
+
+    /**
+     * Whether the user explicitly set that this network should be validated even if presence of
+     * only partial internet connectivity.
+     *
+     * @hide
+     */
+    public boolean acceptPartialConnectivity;
+
+    /**
+     * Set to avoid surfacing the "Sign in to network" notification.
+     * if carrier receivers/apps are registered to handle the carrier-specific provisioning
+     * procedure, a carrier specific provisioning notification will be placed.
+     * only one notification should be displayed. This field is set based on
+     * which notification should be used for provisioning.
+     *
+     * @hide
+     */
+    public boolean provisioningNotificationDisabled;
+
+    /**
+     *
+     * @return whether the sign in to network notification is enabled by this configuration.
+     */
+    public boolean isProvisioningNotificationEnabled() {
+        return !provisioningNotificationDisabled;
+    }
+
+    /**
+     * For mobile networks, this is the subscriber ID (such as IMSI).
+     *
+     * @hide
+     */
+    public String subscriberId;
+
+    /**
+     * @return the subscriber ID, or null if none.
+     */
+    @Nullable
+    public String getSubscriberId() {
+        return subscriberId;
+    }
+
+    /**
+     * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
+     * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
+     *
+     * @hide
+     */
+    public boolean skip464xlat;
+
+    /**
+     * @return whether NAT64 prefix detection is enabled.
+     */
+    public boolean isNat64DetectionEnabled() {
+        return !skip464xlat;
+    }
+
+    /**
+     * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
+     * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
+     *
+     * @hide
+     */
+    public boolean hasShownBroken;
+
+    /** @hide */
+    public NetworkAgentConfig() {
+    }
+
+    /** @hide */
+    public NetworkAgentConfig(@Nullable NetworkAgentConfig nac) {
+        if (nac != null) {
+            allowBypass = nac.allowBypass;
+            explicitlySelected = nac.explicitlySelected;
+            acceptUnvalidated = nac.acceptUnvalidated;
+            subscriberId = nac.subscriberId;
+            provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
+            skip464xlat = nac.skip464xlat;
+        }
+    }
+
+    /**
+     * Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
+     */
+    public static class Builder {
+        private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
+
+        /**
+         * Sets the subscriber ID for this network.
+         *
+         * @return this builder, to facilitate chaining.
+         */
+        @NonNull
+        public Builder setSubscriberId(@Nullable String subscriberId) {
+            mConfig.subscriberId = subscriberId;
+            return this;
+        }
+
+        /**
+         * Disables active detection of NAT64 (e.g., via RFC 7050 DNS lookups). Used to save power
+         * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
+         *
+         * @return this builder, to facilitate chaining.
+         */
+        @NonNull
+        public Builder disableNat64Detection() {
+            mConfig.skip464xlat = true;
+            return this;
+        }
+
+        /**
+         * Disables the "Sign in to network" notification. Used if the network transport will
+         * perform its own carrier-specific provisioning procedure.
+         *
+         * @return this builder, to facilitate chaining.
+         */
+        @NonNull
+        public Builder disableProvisioningNotification() {
+            mConfig.provisioningNotificationDisabled = true;
+            return this;
+        }
+
+        /**
+         * Returns the constructed {@link NetworkAgentConfig} object.
+         */
+        @NonNull
+        public NetworkAgentConfig build() {
+            return mConfig;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(allowBypass ? 1 : 0);
+        out.writeInt(explicitlySelected ? 1 : 0);
+        out.writeInt(acceptUnvalidated ? 1 : 0);
+        out.writeString(subscriberId);
+        out.writeInt(provisioningNotificationDisabled ? 1 : 0);
+        out.writeInt(skip464xlat ? 1 : 0);
+    }
+
+    public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
+            new Creator<NetworkAgentConfig>() {
+        @Override
+        public NetworkAgentConfig createFromParcel(Parcel in) {
+            NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+            networkAgentConfig.allowBypass = in.readInt() != 0;
+            networkAgentConfig.explicitlySelected = in.readInt() != 0;
+            networkAgentConfig.acceptUnvalidated = in.readInt() != 0;
+            networkAgentConfig.subscriberId = in.readString();
+            networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0;
+            networkAgentConfig.skip464xlat = in.readInt() != 0;
+            return networkAgentConfig;
+        }
+
+        @Override
+        public NetworkAgentConfig[] newArray(int size) {
+            return new NetworkAgentConfig[size];
+        }
+    };
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f43385d..6207661 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -35,6 +35,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
@@ -83,6 +86,7 @@
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
         mUids = null;
         mEstablishingVpnAppUid = INVALID_UID;
+        mAdministratorUids.clear();
         mSSID = null;
         mPrivateDnsBroken = false;
     }
@@ -101,6 +105,7 @@
         mSignalStrength = nc.mSignalStrength;
         setUids(nc.mUids); // Will make the defensive copy
         mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
+        setAdministratorUids(nc.mAdministratorUids);
         mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
         mSSID = nc.mSSID;
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
@@ -833,6 +838,56 @@
     }
 
     /**
+     * UIDs of packages that are administrators of this network, or empty if none.
+     *
+     * <p>This field tracks the UIDs of packages that have permission to manage this network.
+     *
+     * <p>Network owners will also be listed as administrators.
+     *
+     * <p>For NetworkCapability instances being sent from the System Server, this value MUST be
+     * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
+     * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
+     */
+    private final List<Integer> mAdministratorUids = new ArrayList<>();
+
+    /**
+     * Sets the list of UIDs that are administrators of this network.
+     *
+     * <p>UIDs included in administratorUids gain administrator privileges over this Network.
+     * Examples of UIDs that should be included in administratorUids are:
+     * <ul>
+     *     <li>Carrier apps with privileges for the relevant subscription
+     *     <li>Active VPN apps
+     *     <li>Other application groups with a particular Network-related role
+     * </ul>
+     *
+     * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
+     *
+     * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
+     * implicitly include administrator privileges.
+     *
+     * @param administratorUids the UIDs to be set as administrators of this Network.
+     * @hide
+     */
+    @SystemApi
+    public void setAdministratorUids(@NonNull final List<Integer> administratorUids) {
+        mAdministratorUids.clear();
+        mAdministratorUids.addAll(administratorUids);
+    }
+
+    /**
+     * Retrieves the list of UIDs that are administrators of this Network.
+     *
+     * @return the List of UIDs that are administrators of this Network
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public List<Integer> getAdministratorUids() {
+        return Collections.unmodifiableList(mAdministratorUids);
+    }
+
+    /**
      * Value indicating that link bandwidth is unspecified.
      * @hide
      */
@@ -1283,6 +1338,7 @@
      * Gets the SSID of this network, or null if none or unknown.
      * @hide
      */
+    @SystemApi
     public @Nullable String getSSID() {
         return mSSID;
     }
@@ -1470,6 +1526,7 @@
     public int describeContents() {
         return 0;
     }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mNetworkCapabilities);
@@ -1483,6 +1540,7 @@
         dest.writeArraySet(mUids);
         dest.writeString(mSSID);
         dest.writeBoolean(mPrivateDnsBroken);
+        dest.writeList(mAdministratorUids);
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1503,6 +1561,7 @@
                         null /* ClassLoader, null for default */);
                 netCap.mSSID = in.readString();
                 netCap.mPrivateDnsBroken = in.readBoolean();
+                netCap.setAdministratorUids(in.readArrayList(null));
                 return netCap;
             }
             @Override
@@ -1556,6 +1615,10 @@
             sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
         }
 
+        if (!mAdministratorUids.isEmpty()) {
+            sb.append(" AdministratorUids: ").append(mAdministratorUids);
+        }
+
         if (null != mSSID) {
             sb.append(" SSID: ").append(mSSID);
         }
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 824ddb8..e271037 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -115,13 +115,6 @@
      */
     private static final int CMD_SET_FILTER = BASE + 3;
 
-    /**
-     * Sent by NetworkFactory to ConnectivityService to indicate that a request is
-     * unfulfillable.
-     * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest).
-     */
-    public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4;
-
     private final Context mContext;
     private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
     private final String LOG_TAG;
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
deleted file mode 100644
index 4ad52d5..0000000
--- a/core/java/android/net/NetworkMisc.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2014 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;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A grab-bag of information (metadata, policies, properties, etc) about a
- * {@link Network}. Since this contains PII, it should not be sent outside the
- * system.
- *
- * @hide
- */
-public class NetworkMisc implements Parcelable {
-
-    /**
-     * If the {@link Network} is a VPN, whether apps are allowed to bypass the
-     * VPN. This is set by a {@link VpnService} and used by
-     * {@link ConnectivityManager} when creating a VPN.
-     */
-    public boolean allowBypass;
-
-    /**
-     * Set if the network was manually/explicitly connected to by the user either from settings
-     * or a 3rd party app.  For example, turning on cell data is not explicit but tapping on a wifi
-     * ap in the wifi settings to trigger a connection is explicit.  A 3rd party app asking to
-     * connect to a particular access point is also explicit, though this may change in the future
-     * as we want apps to use the multinetwork apis.
-     */
-    public boolean explicitlySelected;
-
-    /**
-     * Set if the user desires to use this network even if it is unvalidated. This field has meaning
-     * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
-     * appropriate value based on previous user choice.
-     */
-    public boolean acceptUnvalidated;
-
-    /**
-     * Whether the user explicitly set that this network should be validated even if presence of
-     * only partial internet connectivity.
-     */
-    public boolean acceptPartialConnectivity;
-
-    /**
-     * Set to avoid surfacing the "Sign in to network" notification.
-     * if carrier receivers/apps are registered to handle the carrier-specific provisioning
-     * procedure, a carrier specific provisioning notification will be placed.
-     * only one notification should be displayed. This field is set based on
-     * which notification should be used for provisioning.
-     */
-    public boolean provisioningNotificationDisabled;
-
-    /**
-     * For mobile networks, this is the subscriber ID (such as IMSI).
-     */
-    public String subscriberId;
-
-    /**
-     * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
-     * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
-     */
-    public boolean skip464xlat;
-
-    /**
-     * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
-     * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
-     */
-    public boolean hasShownBroken;
-
-    public NetworkMisc() {
-    }
-
-    public NetworkMisc(NetworkMisc nm) {
-        if (nm != null) {
-            allowBypass = nm.allowBypass;
-            explicitlySelected = nm.explicitlySelected;
-            acceptUnvalidated = nm.acceptUnvalidated;
-            subscriberId = nm.subscriberId;
-            provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
-            skip464xlat = nm.skip464xlat;
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(allowBypass ? 1 : 0);
-        out.writeInt(explicitlySelected ? 1 : 0);
-        out.writeInt(acceptUnvalidated ? 1 : 0);
-        out.writeString(subscriberId);
-        out.writeInt(provisioningNotificationDisabled ? 1 : 0);
-        out.writeInt(skip464xlat ? 1 : 0);
-    }
-
-    public static final @android.annotation.NonNull Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
-        @Override
-        public NetworkMisc createFromParcel(Parcel in) {
-            NetworkMisc networkMisc = new NetworkMisc();
-            networkMisc.allowBypass = in.readInt() != 0;
-            networkMisc.explicitlySelected = in.readInt() != 0;
-            networkMisc.acceptUnvalidated = in.readInt() != 0;
-            networkMisc.subscriberId = in.readString();
-            networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
-            networkMisc.skip464xlat = in.readInt() != 0;
-            return networkMisc;
-        }
-
-        @Override
-        public NetworkMisc[] newArray(int size) {
-            return new NetworkMisc[size];
-        }
-    };
-}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 3be49d5..ee4379a 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -467,6 +467,19 @@
     }
 
     /**
+     * Returns true iff. the capabilities requested in this NetworkRequest are satisfied by the
+     * provided {@link NetworkCapabilities}.
+     *
+     * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
+     *           satisfy any request.
+     * @hide
+     */
+    @SystemApi
+    public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
+        return networkCapabilities.satisfiedByNetworkCapabilities(nc);
+    }
+
+    /**
      * @see Builder#addTransportType(int)
      */
     public boolean hasTransport(@Transport int transportType) {
diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java
new file mode 100644
index 0000000..fbae637
--- /dev/null
+++ b/core/java/android/net/PlatformVpnProfile.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.GeneralSecurityException;
+
+/**
+ * PlatformVpnProfile represents a configuration for a platform-based VPN implementation.
+ *
+ * <p>Platform-based VPNs allow VPN applications to provide configuration and authentication options
+ * to leverage the Android OS' implementations of well-defined control plane (authentication, key
+ * negotiation) and data plane (per-packet encryption) protocols to simplify the creation of VPN
+ * tunnels. In contrast, {@link VpnService} based VPNs must implement both the control and data
+ * planes on a per-app basis.
+ *
+ * @see Ikev2VpnProfile
+ */
+public abstract class PlatformVpnProfile {
+    /**
+     * Alias to platform VPN related types from VpnProfile, for API use.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        TYPE_IKEV2_IPSEC_USER_PASS,
+        TYPE_IKEV2_IPSEC_PSK,
+        TYPE_IKEV2_IPSEC_RSA,
+    })
+    public static @interface PlatformVpnType {}
+
+    public static final int TYPE_IKEV2_IPSEC_USER_PASS = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+    public static final int TYPE_IKEV2_IPSEC_PSK = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+    public static final int TYPE_IKEV2_IPSEC_RSA = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
+
+    /** @hide */
+    @PlatformVpnType protected final int mType;
+
+    /** @hide */
+    PlatformVpnProfile(@PlatformVpnType int type) {
+        mType = type;
+    }
+    /** Returns the profile integer type. */
+    @PlatformVpnType
+    public final int getType() {
+        return mType;
+    }
+
+    /** Returns a type string describing the VPN profile type */
+    @NonNull
+    public final String getTypeString() {
+        switch (mType) {
+            case TYPE_IKEV2_IPSEC_USER_PASS:
+                return "IKEv2/IPsec Username/Password";
+            case TYPE_IKEV2_IPSEC_PSK:
+                return "IKEv2/IPsec Preshared key";
+            case TYPE_IKEV2_IPSEC_RSA:
+                return "IKEv2/IPsec RSA Digital Signature";
+            default:
+                return "Unknown VPN profile type";
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    public abstract VpnProfile toVpnProfile() throws IOException, GeneralSecurityException;
+
+    /** @hide */
+    @NonNull
+    public static PlatformVpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+            throws IOException, GeneralSecurityException {
+        switch (profile.type) {
+            case TYPE_IKEV2_IPSEC_USER_PASS: // fallthrough
+            case TYPE_IKEV2_IPSEC_PSK: // fallthrough
+            case TYPE_IKEV2_IPSEC_RSA:
+                return Ikev2VpnProfile.fromVpnProfile(profile);
+            default:
+                throw new IllegalArgumentException("Unknown VPN Profile type");
+        }
+    }
+}
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 83dbc63..6ae5971 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -27,7 +26,6 @@
 import java.util.Objects;
 
 /** @hide */
-@SystemApi
 public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
     /**
      * Arbitrary string used to pass (additional) information to the network factory.
diff --git a/core/java/android/net/Uri.aidl b/core/java/android/net/Uri.aidl
index 6bd3be5..b85f63b 100644
--- a/core/java/android/net/Uri.aidl
+++ b/core/java/android/net/Uri.aidl
@@ -16,4 +16,4 @@
 
 package android.net;
 
-parcelable Uri;
+@JavaOnlyStableParcelable parcelable Uri;
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
new file mode 100644
index 0000000..f95807a
--- /dev/null
+++ b/core/java/android/net/VpnManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * This class provides an interface for apps to manage platform VPN profiles
+ *
+ * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
+ * further app intermediation. When a VPN profile is present and the app is selected as an always-on
+ * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
+ * app (unlike VpnService).
+ *
+ * <p>VPN apps using supported protocols should preferentially use this API over the {@link
+ * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
+ * the guarantee that VPN network traffic is not subjected to on-device packet interception.
+ *
+ * @see Ikev2VpnProfile
+ */
+public class VpnManager {
+    @NonNull private final Context mContext;
+    @NonNull private final IConnectivityManager mService;
+
+    /**
+     * Create an instance of the VpnManger with the given context.
+     *
+     * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
+     * {@link Context.getSystemService()} method call.
+     *
+     * @hide
+     */
+    public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
+        mContext = checkNotNull(ctx, "missing Context");
+        mService = checkNotNull(service, "missing IConnectivityManager");
+    }
+
+    /**
+     * Install a VpnProfile configuration keyed on the calling app's package name.
+     *
+     * @param profile the PlatformVpnProfile provided by this package. Will override any previous
+     *     PlatformVpnProfile stored for this package.
+     * @return an intent to request user consent if needed (null otherwise).
+     */
+    @Nullable
+    public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /** Delete the VPN profile configuration that was provisioned by the calling app */
+    public void deleteProvisionedVpnProfile() {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /**
+     * Request the startup of a previously provisioned VPN.
+     *
+     * @throws SecurityException exception if user or device settings prevent this VPN from being
+     *     setup, or if user consent has not been granted
+     */
+    public void startProvisionedVpnProfile() {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /** Tear down the VPN provided by the calling app (if any) */
+    public void stopProvisionedVpnProfile() {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+}
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 8bb2df0..b9e6ff4 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -455,12 +455,12 @@
      *
      * @hide
      */
-    public synchronized void setConnectedTechnology(int technology) {
-        if (mConnectedTechnology == -1) {
-            mConnectedTechnology = technology;
-        } else {
-            throw new IllegalStateException("Close other technology first!");
+    public synchronized boolean setConnectedTechnology(int technology) {
+        if (mConnectedTechnology != -1) {
+            return false;
         }
+        mConnectedTechnology = technology;
+        return true;
     }
 
     /**
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
index b6b347c..ae468fe 100644
--- a/core/java/android/nfc/tech/BasicTagTechnology.java
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -75,7 +75,10 @@
 
             if (errorCode == ErrorCodes.SUCCESS) {
                 // Store this in the tag object
-                mTag.setConnectedTechnology(mSelectedTechnology);
+                if (!mTag.setConnectedTechnology(mSelectedTechnology)) {
+                    Log.e(TAG, "Close other technology first!");
+                    throw new IOException("Only one TagTechnology can be connected at a time.");
+                }
                 mIsConnected = true;
             } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) {
                 throw new UnsupportedOperationException("Connecting to " +
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 6daa5b4..9257496 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -21,6 +21,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import dalvik.system.VMRuntime;
+
 /**
  * AppZygote is responsible for interfacing with an application-specific zygote.
  *
@@ -113,7 +115,7 @@
                     "app_zygote",  // seInfo
                     abi,  // abi
                     abi, // acceptedAbiList
-                    null, // instructionSet
+                    VMRuntime.getInstructionSet(abi), // instructionSet
                     mZygoteUidGidMin,
                     mZygoteUidGidMax);
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1f3ae09..dd08070 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8571,6 +8571,7 @@
          *
          * @hide
          */
+        @SystemApi
         public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
 
         /**
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl
index a312c44..41244ab 100644
--- a/core/java/android/se/omapi/ISecureElementReader.aidl
+++ b/core/java/android/se/omapi/ISecureElementReader.aidl
@@ -48,4 +48,10 @@
      */
     void closeSessions();
 
+    /**
+     * Closes all the sessions opened on this reader and resets the reader.
+     * All the channels opened by all these sessions will be closed.
+     * @return true if the reset is successful, false otherwise.
+     */
+    boolean reset();
 }
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 80262f7..7f68d91 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -23,6 +23,8 @@
 package android.se.omapi;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
@@ -150,4 +152,25 @@
             } catch (RemoteException ignore) { }
         }
     }
+
+    /**
+     * Close all the sessions opened on this reader and reset the reader.
+     * All the channels opened by all these sessions will be closed.
+     * @return <code>true</code> if reset success, <code>false</code> otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED)
+    public boolean reset() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service is not connected");
+            return false;
+        }
+        synchronized (mLock) {
+            try {
+                closeSessions();
+                return mReader.reset();
+            } catch (RemoteException ignore) {return false;}
+        }
+    }
 }
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index 5330cff..f67af85 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -212,20 +212,16 @@
 
     private int getUiOptionsAsFlags() {
         int uiOptionsAsFlags = 0;
-        try {
-            ContentResolver contentResolver = mContext.getContentResolver();
-            int inversionEnabled = Settings.Secure.getInt(contentResolver,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
-            if (inversionEnabled == 1) {
-                uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
-            }
-            float fontScale = Settings.System.getFloat(contentResolver,
-                    Settings.System.FONT_SCALE);
-            if (fontScale > 1.0) {
-                uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
-            }
-        } catch (SettingNotFoundException e) {
-            Log.w(TAG, "Unexpected SettingNotFoundException");
+        ContentResolver contentResolver = mContext.getContentResolver();
+        int inversionEnabled = Settings.Secure.getInt(contentResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0);
+        if (inversionEnabled == 1) {
+            uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
+        }
+        float fontScale = Settings.System.getFloat(contentResolver,
+                Settings.System.FONT_SCALE, (float) 1.0);
+        if (fontScale > 1.0) {
+            uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
         }
         return uiOptionsAsFlags;
     }
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e08a06ab..f886cf5 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -178,6 +178,10 @@
      * Listen for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
      * background and foreground calls.
      *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @hide
      */
     @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@@ -187,13 +191,13 @@
     /**
      * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
      *
-     * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
      * or the calling app has carrier privileges
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onPreciseDataConnectionStateChanged
      */
-    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE            = 0x00001000;
 
     /**
@@ -318,26 +322,36 @@
      * Listen for call disconnect causes which contains {@link DisconnectCause} and
      * {@link PreciseDisconnectCause}.
      *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      */
     @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
 
     /**
      * Listen for changes to the call attributes of a currently active call.
-     * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
-     * READ_PRECISE_PHONE_STATE}
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onCallAttributesChanged
      * @hide
      */
     @SystemApi
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     public static final int LISTEN_CALL_ATTRIBUTES_CHANGED                 = 0x04000000;
 
     /**
      * Listen for IMS call disconnect causes which contains
      * {@link android.telephony.ims.ImsReasonInfo}
      *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
      */
     @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index ada59d6..e5bbdf4 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -43,6 +43,7 @@
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class TimeZoneMapping {
 
+        @NonNull
         private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
 
         TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) {
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
index 39dbe85..eebccf4 100644
--- a/core/java/android/timezone/TelephonyLookup.java
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -36,12 +36,8 @@
     @GuardedBy("sLock")
     private static TelephonyLookup sInstance;
 
-    @NonNull
-    private final libcore.timezone.TelephonyLookup mDelegate;
-
     /**
-     * Obtains an instance for use when resolving telephony time zone information. This method never
-     * returns {@code null}.
+     * Obtains an instance for use when resolving telephony time zone information.
      */
     @NonNull
     public static TelephonyLookup getInstance() {
@@ -53,6 +49,9 @@
         }
     }
 
+    @NonNull
+    private final libcore.timezone.TelephonyLookup mDelegate;
+
     private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) {
         mDelegate = Objects.requireNonNull(delegate);
     }
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index a81a516..079d088 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -23,7 +23,7 @@
 import java.util.Objects;
 
 /**
- * A class that can find telephony networks loaded via {@link TelephonyLookup}.
+ * A class that can find telephony network information loaded via {@link TelephonyLookup}.
  *
  * @hide
  */
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
index 15dfe62..9327b00 100644
--- a/core/java/android/timezone/TimeZoneFinder.java
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -22,8 +22,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Objects;
+
 /**
- * A class that can be used to find time zones.
+ * A class that can be used to find time zones using information like country and offset.
  *
  * @hide
  */
@@ -34,15 +36,8 @@
     @GuardedBy("sLock")
     private static TimeZoneFinder sInstance;
 
-    private final libcore.timezone.TimeZoneFinder mDelegate;
-
-    private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) {
-        mDelegate = delegate;
-    }
-
     /**
-     * Obtains an instance for use when resolving telephony time zone information. This method never
-     * returns {@code null}.
+     * Obtains the singleton instance.
      */
     @NonNull
     public static TimeZoneFinder getInstance() {
@@ -54,6 +49,22 @@
         return sInstance;
     }
 
+    @NonNull
+    private final libcore.timezone.TimeZoneFinder mDelegate;
+
+    private TimeZoneFinder(@NonNull libcore.timezone.TimeZoneFinder delegate) {
+        mDelegate = Objects.requireNonNull(delegate);
+    }
+
+    /**
+     * Returns the IANA rules version associated with the data. If there is no version information
+     * or there is a problem reading the file then {@code null} is returned.
+     */
+    @Nullable
+    public String getIanaVersion() {
+        return mDelegate.getIanaVersion();
+    }
+
     /**
      * Returns a {@link CountryTimeZones} object associated with the specified country code.
      * Caching is handled as needed. If the country code is not recognized or there is an error
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
new file mode 100644
index 0000000..aba7c4c
--- /dev/null
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -0,0 +1,154 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Version information associated with the set of time zone data on a device.
+ *
+ * <p>Time Zone Data Sets have a major ({@link #getFormatMajorVersion()}) and minor
+ * ({@link #currentFormatMinorVersion()}) version number:
+ * <ul>
+ *   <li>Major version numbers are mutually incompatible. e.g. v2 is not compatible with a v1 or a
+ *   v3 device.</li>
+ *   <li>Minor version numbers are backwards compatible. e.g. a v2.2 data set will work
+ *   on a v2.1 device but not a v2.3 device. The minor version is reset to 1 when the major version
+ *   is incremented.</li>
+ * </ul>
+ *
+ * <p>Data sets contain time zone rules and other data associated wtih a tzdb release
+ * ({@link #getRulesVersion()}) and an additional Android-specific revision number
+ * ({@link #getRevision()}).
+ *
+ * <p>See platform/system/timezone/README.android for more information.
+ * @hide
+ */
+@VisibleForTesting
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TzDataSetVersion {
+
+    /**
+     * Returns the major tz data format version supported by this device.
+     */
+    public static int currentFormatMajorVersion() {
+        return libcore.timezone.TzDataSetVersion.currentFormatMajorVersion();
+    }
+
+    /**
+     * Returns the minor tz data format version supported by this device.
+     */
+    public static int currentFormatMinorVersion() {
+        return libcore.timezone.TzDataSetVersion.currentFormatMinorVersion();
+    }
+
+    /**
+     * Returns true if the version information provided would be compatible with this device, i.e.
+     * with the current system image, and set of active modules.
+     */
+    public static boolean isCompatibleWithThisDevice(TzDataSetVersion tzDataSetVersion) {
+        return libcore.timezone.TzDataSetVersion.isCompatibleWithThisDevice(
+                tzDataSetVersion.mDelegate);
+    }
+
+    /**
+     * Reads the current Android time zone data set version file.
+     */
+    @NonNull
+    public static TzDataSetVersion read() throws IOException, TzDataSetException {
+        try {
+            return new TzDataSetVersion(
+                    libcore.timezone.TzDataSetVersion.readTimeZoneModuleVersion());
+        } catch (libcore.timezone.TzDataSetVersion.TzDataSetException e) {
+            throw new TzDataSetException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * A checked exception used in connection with time zone data sets.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static class TzDataSetException extends Exception {
+
+        /** Creates an instance with a message. */
+        public TzDataSetException(String message) {
+            super(message);
+        }
+
+        /** Creates an instance with a message and a cause. */
+        public TzDataSetException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    @NonNull
+    private final libcore.timezone.TzDataSetVersion mDelegate;
+
+    private TzDataSetVersion(@NonNull libcore.timezone.TzDataSetVersion delegate) {
+        mDelegate = Objects.requireNonNull(delegate);
+    }
+
+    /** Returns the major version number. See {@link TzDataSetVersion}. */
+    public int getFormatMajorVersion() {
+        return mDelegate.formatMajorVersion;
+    }
+
+    /** Returns the minor version number. See {@link TzDataSetVersion}. */
+    public int getFormatMinorVersion() {
+        return mDelegate.formatMinorVersion;
+    }
+
+    /** Returns the tzdb version string. See {@link TzDataSetVersion}. */
+    @NonNull
+    public String getRulesVersion() {
+        return mDelegate.rulesVersion;
+    }
+
+    /** Returns the Android revision. See {@link TzDataSetVersion}. */
+    public int getRevision() {
+        return mDelegate.revision;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TzDataSetVersion that = (TzDataSetVersion) o;
+        return mDelegate.equals(that.mDelegate);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDelegate);
+    }
+
+    @Override
+    public String toString() {
+        return mDelegate.toString();
+    }
+}
diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java
new file mode 100644
index 0000000..eb191e8
--- /dev/null
+++ b/core/java/android/timezone/ZoneInfoDb.java
@@ -0,0 +1,66 @@
+/*
+ * 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.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * Android's internal factory for java.util.TimeZone objects. Provides access to core library time
+ * zone metadata not available via {@link java.util.TimeZone}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ZoneInfoDb {
+
+    private static Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static ZoneInfoDb sInstance;
+
+    /**
+     * Obtains the singleton instance.
+     */
+    @NonNull
+    public static ZoneInfoDb getInstance() {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new ZoneInfoDb(libcore.timezone.ZoneInfoDB.getInstance());
+            }
+        }
+        return sInstance;
+    }
+
+    @NonNull
+    private final libcore.timezone.ZoneInfoDB mDelegate;
+
+    private ZoneInfoDb(libcore.timezone.ZoneInfoDB delegate) {
+        mDelegate = Objects.requireNonNull(delegate);
+    }
+
+    /**
+     * Returns the tzdb version in use.
+     */
+    @NonNull
+    public String getVersion() {
+        return mDelegate.getVersion();
+    }
+}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 8439f5a..dbd3f69 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -88,7 +88,7 @@
      *
      * <p>The list returned may be different from other on-device sources like
      * {@link android.icu.util.TimeZone#getRegion(String)} as it can be curated to avoid
-     * contentious mappings.
+     * contentious or obsolete mappings.
      *
      * @param countryCode the ISO 3166-1 alpha-2 code for the country as can be obtained using
      *     {@link java.util.Locale#getCountry()}
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 6efcfbf..7b24ba9 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -16,7 +16,8 @@
 
 package android.util.proto;
 
-import android.annotation.TestApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.Log;
 
 import java.io.FileDescriptor;
@@ -28,19 +29,23 @@
 /**
  * Class to write to a protobuf stream.
  *
- * Each write method takes an ID code from the protoc generated classes
- * and the value to write.  To make a nested object, call #start
- * and then #end when you are done.
+ * <p>
+ * This API is not as convenient or type safe as the standard protobuf
+ * classes. If possible, the best recommended library is to use protobuf lite.
+ * However, in environements (such as the Android platform itself), a
+ * more memory efficient version is necessary.
  *
- * The ID codes have type information embedded into them, so if you call
- * the incorrect function you will get an IllegalArgumentException.
+ * <p>Each write method takes an ID code from the protoc generated classes
+ * and the value to write.  To make a nested object, call {@link #start(long)}
+ * and then {@link #end(long)} when you are done.
  *
- * To retrieve the encoded protobuf stream, call getBytes().
+ * <p>The ID codes have type information embedded into them, so if you call
+ * the incorrect function you will get an {@link IllegalArgumentException}.
  *
- * TODO: Add a constructor that takes an OutputStream and write to that
+ * <p>To retrieve the encoded protobuf stream, call {@link #getBytes()}.
+ *
  * stream as the top-level objects are finished.
  *
- * @hide
  */
 
 /* IMPLEMENTATION NOTES
@@ -99,7 +104,6 @@
  * correctly matched pairs of #start and #end calls, and issue
  * errors if they are not matched.
  */
-@TestApi
 public final class ProtoOutputStream extends ProtoStream {
     /**
      * @hide
@@ -124,7 +128,9 @@
     /**
      * An ID given to objects and returned in the token from startObject
      * and stored in the buffer until endObject is called, where the two
-     * are checked.  Starts at -1 and becomes more negative, so the values
+     * are checked.
+     *
+     * <p>Starts at -1 and becomes more negative, so the values
      * aren't likely to alias with the size it will be overwritten with,
      * which tend to be small, and we will be more likely to catch when
      * the caller of endObject uses a stale token that they didn't intend
@@ -133,8 +139,9 @@
     private int mNextObjectId = -1;
 
     /**
-     * The object token we are expecting in endObject.  If another call to
-     * startObject happens, this is written to that location, which gives
+     * The object token we are expecting in endObject.
+     *
+     * <p>If another call to startObject happens, this is written to that location, which gives
      * us a stack, stored in the space for the as-yet unused size fields.
      */
     private long mExpectedObjectToken;
@@ -151,39 +158,45 @@
     private boolean mCompacted;
 
     /**
-     * Construct a ProtoOutputStream with the default chunk size.
+     * Construct a {@link ProtoOutputStream} with the default chunk size.
+     *
+     * <p>This is for an in-memory proto. The caller should use {@link #getBytes()} for the result.
      */
     public ProtoOutputStream() {
         this(0);
     }
 
     /**
-     * Construct a ProtoOutputStream with the given chunk size.
+     * Construct a {@link ProtoOutputStream with the given chunk size.
+     *
+     * <p>This is for an in-memory proto. The caller should use {@link #getBytes()} for the result.
      */
     public ProtoOutputStream(int chunkSize) {
         mBuffer = new EncodedBuffer(chunkSize);
     }
 
     /**
-     * Construct a ProtoOutputStream that sits on top of an OutputStream.
-     * @more
-     * The {@link #flush() flush()} method must be called when done writing
-     * to flush any remanining data, althought data *may* be written at intermediate
+     * Construct a {@link ProtoOutputStream} that sits on top of an {@link OutputStream}.
+     *
+     * <p>The {@link #flush()} method must be called when done writing
+     * to flush any remaining data, although data *may* be written at intermediate
      * points within the writing as well.
      */
-    public ProtoOutputStream(OutputStream stream) {
+    public ProtoOutputStream(@NonNull OutputStream stream) {
         this();
         mStream = stream;
     }
 
     /**
-     * Construct a ProtoOutputStream that sits on top of a FileDescriptor.
-     * @more
-     * The {@link #flush() flush()} method must be called when done writing
-     * to flush any remanining data, althought data *may* be written at intermediate
+     * Construct a {@link ProtoOutputStream} that sits on top of a {@link FileDescriptor}.
+     *
+     * <p>The {@link #flush()} method must be called when done writing
+     * to flush any remaining data, although data *may* be written at intermediate
      * points within the writing as well.
+     *
+     * @hide
      */
-    public ProtoOutputStream(FileDescriptor fd) {
+    public ProtoOutputStream(@NonNull FileDescriptor fd) {
         this(new FileOutputStream(fd));
     }
 
@@ -202,7 +215,7 @@
     /**
      * Write a value for the given fieldId.
      *
-     * Will automatically convert for the following field types, and
+     * <p>Will automatically convert for the following field types, and
      * throw an exception for others: double, float, int32, int64, uint32, uint64,
      * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
      *
@@ -337,7 +350,7 @@
     /**
      * Write a value for the given fieldId.
      *
-     * Will automatically convert for the following field types, and
+     * <p>Will automatically convert for the following field types, and
      * throw an exception for others: double, float, int32, int64, uint32, uint64,
      * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
      *
@@ -472,7 +485,7 @@
     /**
      * Write a value for the given fieldId.
      *
-     * Will automatically convert for the following field types, and
+     * <p>Will automatically convert for the following field types, and
      * throw an exception for others: double, float, int32, int64, uint32, uint64,
      * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
      *
@@ -607,7 +620,7 @@
     /**
      * Write a value for the given fieldId.
      *
-     * Will automatically convert for the following field types, and
+     * <p>Will automatically convert for the following field types, and
      * throw an exception for others: double, float, int32, int64, uint32, uint64,
      * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
      *
@@ -742,7 +755,7 @@
     /**
      * Write a boolean value for the given fieldId.
      *
-     * If the field is not a bool field, an exception will be thrown.
+     * <p>If the field is not a bool field, an {@link IllegalStateException} will be thrown.
      *
      * @param fieldId The field identifier constant from the generated class.
      * @param val The value.
@@ -771,12 +784,12 @@
     /**
      * Write a string value for the given fieldId.
      *
-     * If the field is not a string field, an exception will be thrown.
+     * <p>If the field is not a string field, an exception will be thrown.
      *
      * @param fieldId The field identifier constant from the generated class.
      * @param val The value.
      */
-    public void write(long fieldId, String val) {
+    public void write(long fieldId, @Nullable String val) {
         assertNotCompacted();
         final int id = (int)fieldId;
 
@@ -800,12 +813,12 @@
     /**
      * Write a byte[] value for the given fieldId.
      *
-     * If the field is not a bytes or object field, an exception will be thrown.
+     * <p>If the field is not a bytes or object field, an exception will be thrown.
      *
      * @param fieldId The field identifier constant from the generated class.
      * @param val The value.
      */
-    public void write(long fieldId, byte[] val) {
+    public void write(long fieldId, @Nullable byte[] val) {
         assertNotCompacted();
         final int id = (int)fieldId;
 
@@ -836,6 +849,9 @@
 
     /**
      * Start a sub object.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @return The token to call {@link #end(long)} with.
      */
     public long start(long fieldId) {
         assertNotCompacted();
@@ -855,6 +871,8 @@
 
     /**
      * End the object started by start() that returned token.
+     *
+     * @param token The token returned from {@link #start(long)}
      */
     public void end(long token) {
         endObjectImpl(token, getRepeatedFromToken(token));
@@ -870,7 +888,8 @@
     /**
      * Write a single proto "double" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, double)} instead.
+     * @hide
      */
     @Deprecated
     public void writeDouble(long fieldId, double val) {
@@ -890,7 +909,8 @@
     /**
      * Write a single repeated proto "double" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, double)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedDouble(long fieldId, double val) {
@@ -908,10 +928,11 @@
     /**
      * Write a list of packed proto "double" type field values.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, double)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedDouble(long fieldId, double[] val) {
+    public void writePackedDouble(long fieldId, @Nullable double[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_DOUBLE);
 
@@ -934,7 +955,8 @@
     /**
      * Write a single proto "float" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, float)} instead.
+     * @hide
      */
     @Deprecated
     public void writeFloat(long fieldId, float val) {
@@ -954,7 +976,8 @@
     /**
      * Write a single repeated proto "float" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, float)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedFloat(long fieldId, float val) {
@@ -972,10 +995,11 @@
     /**
      * Write a list of packed proto "float" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, float)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedFloat(long fieldId, float[] val) {
+    public void writePackedFloat(long fieldId, @Nullable float[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FLOAT);
 
@@ -999,7 +1023,7 @@
     /**
      * Writes a java int as an usigned varint.
      *
-     * The unadorned int32 type in protobuf is unfortunate because it
+     * <p>The unadorned int32 type in protobuf is unfortunate because it
      * is stored in memory as a signed value, but encodes as unsigned
      * varints, which are formally always longs.  So here, we encode
      * negative values as 64 bits, which will get the sign-extension,
@@ -1017,11 +1041,12 @@
     /**
      * Write a single proto "int32" type field value.
      *
-     * Note that these are stored in memory as signed values and written as unsigned
+     * <p>Note that these are stored in memory as signed values and written as unsigned
      * varints, which if negative, are 10 bytes long. If you know the data is likely
      * to be negative, use "sint32".
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeInt32(long fieldId, int val) {
@@ -1041,11 +1066,12 @@
     /**
      * Write a single repeated proto "int32" type field value.
      *
-     * Note that these are stored in memory as signed values and written as unsigned
+     * <p>Note that these are stored in memory as signed values and written as unsigned
      * varints, which if negative, are 10 bytes long. If you know the data is likely
      * to be negative, use "sint32".
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedInt32(long fieldId, int val) {
@@ -1063,14 +1089,15 @@
     /**
      * Write a list of packed proto "int32" type field value.
      *
-     * Note that these are stored in memory as signed values and written as unsigned
+     * <p>Note that these are stored in memory as signed values and written as unsigned
      * varints, which if negative, are 10 bytes long. If you know the data is likely
      * to be negative, use "sint32".
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedInt32(long fieldId, int[] val) {
+    public void writePackedInt32(long fieldId, @Nullable int[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_INT32);
 
@@ -1099,7 +1126,8 @@
     /**
      * Write a single proto "int64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeInt64(long fieldId, long val) {
@@ -1119,7 +1147,8 @@
     /**
      * Write a single repeated proto "int64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedInt64(long fieldId, long val) {
@@ -1137,10 +1166,11 @@
     /**
      * Write a list of packed proto "int64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedInt64(long fieldId, long[] val) {
+    public void writePackedInt64(long fieldId, @Nullable long[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_INT64);
 
@@ -1168,7 +1198,8 @@
     /**
      * Write a single proto "uint32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeUInt32(long fieldId, int val) {
@@ -1188,7 +1219,8 @@
     /**
      * Write a single repeated proto "uint32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedUInt32(long fieldId, int val) {
@@ -1206,10 +1238,11 @@
     /**
      * Write a list of packed proto "uint32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedUInt32(long fieldId, int[] val) {
+    public void writePackedUInt32(long fieldId, @Nullable int[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_UINT32);
 
@@ -1237,7 +1270,8 @@
     /**
      * Write a single proto "uint64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeUInt64(long fieldId, long val) {
@@ -1257,7 +1291,8 @@
     /**
      * Write a single proto "uint64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedUInt64(long fieldId, long val) {
@@ -1275,10 +1310,11 @@
     /**
      * Write a single proto "uint64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedUInt64(long fieldId, long[] val) {
+    public void writePackedUInt64(long fieldId, @Nullable long[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_UINT64);
 
@@ -1306,7 +1342,8 @@
     /**
      * Write a single proto "sint32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeSInt32(long fieldId, int val) {
@@ -1326,7 +1363,8 @@
     /**
      * Write a single repeated proto "sint32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedSInt32(long fieldId, int val) {
@@ -1344,10 +1382,11 @@
     /**
      * Write a list of packed proto "sint32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedSInt32(long fieldId, int[] val) {
+    public void writePackedSInt32(long fieldId, @Nullable int[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SINT32);
 
@@ -1375,7 +1414,8 @@
     /**
      * Write a single proto "sint64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeSInt64(long fieldId, long val) {
@@ -1395,7 +1435,8 @@
     /**
      * Write a single repeated proto "sint64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedSInt64(long fieldId, long val) {
@@ -1413,10 +1454,11 @@
     /**
      * Write a list of packed proto "sint64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedSInt64(long fieldId, long[] val) {
+    public void writePackedSInt64(long fieldId, @Nullable long[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SINT64);
 
@@ -1443,7 +1485,8 @@
     /**
      * Write a single proto "fixed32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeFixed32(long fieldId, int val) {
@@ -1463,7 +1506,8 @@
     /**
      * Write a single repeated proto "fixed32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedFixed32(long fieldId, int val) {
@@ -1481,10 +1525,11 @@
     /**
      * Write a list of packed proto "fixed32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedFixed32(long fieldId, int[] val) {
+    public void writePackedFixed32(long fieldId, @Nullable int[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FIXED32);
 
@@ -1507,7 +1552,8 @@
     /**
      * Write a single proto "fixed64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeFixed64(long fieldId, long val) {
@@ -1527,7 +1573,8 @@
     /**
      * Write a single repeated proto "fixed64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedFixed64(long fieldId, long val) {
@@ -1545,10 +1592,11 @@
     /**
      * Write a list of packed proto "fixed64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedFixed64(long fieldId, long[] val) {
+    public void writePackedFixed64(long fieldId, @Nullable long[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FIXED64);
 
@@ -1570,7 +1618,8 @@
     /**
      * Write a single proto "sfixed32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeSFixed32(long fieldId, int val) {
@@ -1590,7 +1639,8 @@
     /**
      * Write a single repeated proto "sfixed32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedSFixed32(long fieldId, int val) {
@@ -1608,10 +1658,11 @@
     /**
      * Write a list of packed proto "sfixed32" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedSFixed32(long fieldId, int[] val) {
+    public void writePackedSFixed32(long fieldId, @Nullable int[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SFIXED32);
 
@@ -1634,7 +1685,8 @@
     /**
      * Write a single proto "sfixed64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeSFixed64(long fieldId, long val) {
@@ -1654,7 +1706,8 @@
     /**
      * Write a single repeated proto "sfixed64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedSFixed64(long fieldId, long val) {
@@ -1672,10 +1725,11 @@
     /**
      * Write a list of packed proto "sfixed64" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, long)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedSFixed64(long fieldId, long[] val) {
+    public void writePackedSFixed64(long fieldId, @Nullable long[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SFIXED64);
 
@@ -1698,7 +1752,8 @@
     /**
      * Write a single proto "bool" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, boolean)} instead.
+     * @hide
      */
     @Deprecated
     public void writeBool(long fieldId, boolean val) {
@@ -1719,7 +1774,8 @@
     /**
      * Write a single repeated proto "bool" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, boolean)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedBool(long fieldId, boolean val) {
@@ -1737,10 +1793,11 @@
     /**
      * Write a list of packed proto "bool" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, boolean)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedBool(long fieldId, boolean[] val) {
+    public void writePackedBool(long fieldId, @Nullable boolean[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_BOOL);
 
@@ -1767,10 +1824,11 @@
     /**
      * Write a single proto "string" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, String)} instead.
+     * @hide
      */
     @Deprecated
-    public void writeString(long fieldId, String val) {
+    public void writeString(long fieldId, @Nullable String val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_STRING);
 
@@ -1786,10 +1844,11 @@
     /**
      * Write a single repeated proto "string" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, String)} instead.
+     * @hide
      */
     @Deprecated
-    public void writeRepeatedString(long fieldId, String val) {
+    public void writeRepeatedString(long fieldId, @Nullable String val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_STRING);
 
@@ -1828,10 +1887,11 @@
     /**
      * Write a single proto "bytes" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, byte[])} instead.
+     * @hide
      */
     @Deprecated
-    public void writeBytes(long fieldId, byte[] val) {
+    public void writeBytes(long fieldId, @Nullable byte[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES);
 
@@ -1848,10 +1908,11 @@
     /**
      * Write a single repeated proto "bytes" type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, byte[])} instead.
+     * @hide
      */
     @Deprecated
-    public void writeRepeatedBytes(long fieldId, byte[] val) {
+    public void writeRepeatedBytes(long fieldId, @Nullable byte[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BYTES);
 
@@ -1874,7 +1935,8 @@
     /**
      * Write a single proto enum type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeEnum(long fieldId, int val) {
@@ -1894,7 +1956,8 @@
     /**
      * Write a single repeated proto enum type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
     public void writeRepeatedEnum(long fieldId, int val) {
@@ -1912,10 +1975,11 @@
     /**
      * Write a list of packed proto enum type field value.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, int)} instead.
+     * @hide
      */
     @Deprecated
-    public void writePackedEnum(long fieldId, int[] val) {
+    public void writePackedEnum(long fieldId, @Nullable int[] val) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_ENUM);
 
@@ -1940,7 +2004,8 @@
      * Returns a token which should be passed to endObject.  Calls to endObject must be
      * nested properly.
      *
-     * @deprecated Use #start() instead.
+     * @deprecated Use {@link #start(long)} instead.
+     * @hide
      */
     @Deprecated
     public long startObject(long fieldId) {
@@ -1953,7 +2018,8 @@
     /**
      * End a child object. Pass in the token from the correspoinding startObject call.
      *
-     * @deprecated Use #end() instead.
+     * @deprecated Use {@link #end(long)} instead.
+     * @hide
      */
     @Deprecated
     public void endObject(long token) {
@@ -1968,7 +2034,8 @@
      * Returns a token which should be passed to endObject.  Calls to endObject must be
      * nested properly.
      *
-     * @deprecated Use #start() instead.
+     * @deprecated Use {@link #start(long)} instead.
+     * @hide
      */
     @Deprecated
     public long startRepeatedObject(long fieldId) {
@@ -1981,7 +2048,8 @@
     /**
      * End a child object. Pass in the token from the correspoinding startRepeatedObject call.
      *
-     * @deprecated Use #end() instead.
+     * @deprecated Use {@link #end(long)} instead.
+     * @hide
      */
     @Deprecated
     public void endRepeatedObject(long token) {
@@ -2064,12 +2132,13 @@
     }
 
     /**
-     * Write an object that has already been flattend.
+     * Write an object that has already been flattened.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, byte[])} instead.
+     * @hide
      */
     @Deprecated
-    public void writeObject(long fieldId, byte[] value) {
+    public void writeObject(long fieldId, @Nullable byte[] value) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE);
 
@@ -2084,12 +2153,13 @@
     }
 
     /**
-     * Write an object that has already been flattend.
+     * Write an object that has already been flattened.
      *
-     * @deprecated Use #write instead.
+     * @deprecated Use {@link #write(long, byte[])} instead.
+     * @hide
      */
     @Deprecated
-    public void writeRepeatedObject(long fieldId, byte[] value) {
+    public void writeRepeatedObject(long fieldId, @Nullable byte[] value) {
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE);
 
@@ -2115,11 +2185,11 @@
     }
 
     /**
-     * Validates that the fieldId providied is of the type and count from expectedType.
+     * Validates that the fieldId provided is of the type and count from expectedType.
      *
-     * The type must match exactly to pass this check.
+     * <p>The type must match exactly to pass this check.
      *
-     * The count must match according to this truth table to pass the check:
+     * <p>The count must match according to this truth table to pass the check:
      *
      *                  expectedFlags
      *                  UNKNOWN     SINGLE      REPEATED    PACKED
@@ -2129,7 +2199,7 @@
      *    REPEATED      x           false       true        false
      *    PACKED        x           false       true        true
      *
-     * @throws IllegalArgumentException if it is not.
+     * @throws {@link IllegalArgumentException} if it is not.
      *
      * @return The raw ID of that field.
      */
@@ -2201,7 +2271,7 @@
     }
 
     /**
-     * Write a field tage to the stream.
+     * Write a field tag to the stream.
      */
     public void writeTag(int id, int wireType) {
         mBuffer.writeRawVarint32((id << FIELD_ID_SHIFT) | wireType);
@@ -2239,10 +2309,10 @@
      * Finish the encoding of the data, and return a byte[] with
      * the protobuf formatted data.
      *
-     * After this call, do not call any of the write* functions. The
+     * <p>After this call, do not call any of the write* functions. The
      * behavior is undefined.
      */
-    public byte[] getBytes() {
+    public @NonNull byte[] getBytes() {
         compactIfNecessary();
 
         return mBuffer.getBytes(mBuffer.getReadableSize());
@@ -2289,7 +2359,7 @@
     }
 
     /**
-     * First compaction pass.  Iterate through the data, and fill in the
+     * First compaction pass. Iterate through the data, and fill in the
      * nested object sizes so the next pass can compact them.
      */
     private int editEncodedSize(int rawSize) {
@@ -2416,10 +2486,10 @@
     /**
      * Write remaining data to the output stream.  If there is no output stream,
      * this function does nothing. Any currently open objects (i.e. ones that
-     * have not had endObject called for them will not be written).  Whether this
+     * have not had {@link #end(long)} called for them will not be written).  Whether this
      * writes objects that are closed if there are remaining open objects is
      * undefined (current implementation does not write it, future ones will).
-     * For now, can either call getBytes() or flush(), but not both.
+     * For now, can either call {@link #getBytes()} or {@link #flush()}, but not both.
      */
     public void flush() {
         if (mStream == null) {
@@ -2457,7 +2527,7 @@
     /**
      * Dump debugging data about the buffers with the given log tag.
      */
-    public void dump(String tag) {
+    public void dump(@NonNull String tag) {
         Log.d(tag, mBuffer.getDebugString());
         mBuffer.dumpBuffers(tag);
     }
diff --git a/core/java/android/util/proto/ProtoStream.java b/core/java/android/util/proto/ProtoStream.java
index 9e2e95a..4969d8a 100644
--- a/core/java/android/util/proto/ProtoStream.java
+++ b/core/java/android/util/proto/ProtoStream.java
@@ -16,28 +16,104 @@
 
 package android.util.proto;
 
-import android.annotation.TestApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 /**
- * Abstract base class for both protobuf streams.
+ * Base utility class for protobuf streams.
  *
- * Contains a set of useful constants and methods used by both
- * ProtoOutputStream and ProtoInputStream
+ * Contains a set of constants and methods used in generated code for
+ * {@link ProtoOutputStream}.
  *
  * @hide
  */
-@TestApi
-public abstract class ProtoStream {
+public class ProtoStream {
 
+    /**
+     * Number of bits to shift the field number to form a tag.
+     *
+     * <pre>
+     * // Reading a field number from a tag.
+     * int fieldNumber = tag &gt;&gt;&gt; FIELD_ID_SHIFT;
+     *
+     * // Building a tag from a field number and a wire type.
+     * int tag = (fieldNumber &lt;&lt; FIELD_ID_SHIFT) | wireType;
+     * </pre>
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int FIELD_ID_SHIFT = 3;
+
+    /**
+     * Mask to select the wire type from a tag.
+     *
+     * <pre>
+     * // Reading a wire type from a tag.
+     * int wireType = tag &amp; WIRE_TYPE_MASK;
+     *
+     * // Building a tag from a field number and a wire type.
+     * int tag = (fieldNumber &lt;&lt; FIELD_ID_SHIFT) | wireType;
+     * </pre>
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1;
+
+    /**
+     * Mask to select the field id from a tag.
+     * @hide (not used by anything, and not actually useful, because you also want
+     * to shift when you mask the field id).
+     */
     public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK;
 
+    /**
+     * Varint wire type code.
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_VARINT = 0;
+
+    /**
+     * Fixed64 wire type code.
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_FIXED64 = 1;
+
+    /**
+     * Length delimited wire type code.
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_LENGTH_DELIMITED = 2;
+
+    /**
+     * Start group wire type code.
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_START_GROUP = 3;
+
+    /**
+     * End group wire type code.
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_END_GROUP = 4;
+
+    /**
+     * Fixed32 wire type code.
+     *
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final int WIRE_TYPE_FIXED32 = 5;
 
     /**
@@ -51,32 +127,147 @@
      */
     public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT;
 
+    /**
+     * Not a real wire type.
+     * @hide
+     */
     public static final long FIELD_TYPE_UNKNOWN = 0;
 
+
+    /*
+     * The FIELD_TYPE_ constants are copied from
+     * external/protobuf/src/google/protobuf/descriptor.h directly, so no
+     * extra mapping needs to be maintained in this case.
+     */
+
     /**
-     * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
-     * so no extra mapping needs to be maintained in this case.
+     * Field type code for double fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, double)
+     * ProtoOutputStream.write(long, double)} method.
      */
     public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for float fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, float)
+     * ProtoOutputStream.write(long, float)} method.
+     */
     public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for int64 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, long)
+     * ProtoOutputStream.write(long, long)} method.
+     */
     public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for uint64 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, long)
+     * ProtoOutputStream.write(long, long)} method.
+     */
     public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for int32 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
     public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for fixed64 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, long)
+     * ProtoOutputStream.write(long, long)} method.
+     */
     public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for fixed32 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
+
+    /**
+     * Field type code for fixed32 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
     public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for bool fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, boolean)
+     * ProtoOutputStream.write(long, boolean)} method.
+     */
     public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for string fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, String)
+     * ProtoOutputStream.write(long, String)} method.
+     */
     public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT;
+
     //  public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated.
+
+    /**
+     * Field type code for message fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#start(long)
+     * ProtoOutputStream.start(long)} method.
+     */
     public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for bytes fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, byte[])
+     * ProtoOutputStream.write(long, byte[])} method.
+     */
     public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for uint32 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
     public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for enum fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
     public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for sfixed32 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
     public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for sfixed64 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, long)
+     * ProtoOutputStream.write(long, long)} method.
+     */
     public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for sint32 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, int)
+     * ProtoOutputStream.write(long, int)} method.
+     */
     public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT;
+
+    /**
+     * Field type code for sint64 fields. Used to build constants in generated
+     * code for use with the {@link ProtoOutputStream#write(long, long)
+     * ProtoOutputStream.write(long, long)} method.
+     */
     public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT;
 
-    protected static final String[] FIELD_TYPE_NAMES = new String[]{
+    private static final @NonNull String[] FIELD_TYPE_NAMES = new String[]{
             "Double",
             "Float",
             "Int64",
@@ -100,19 +291,94 @@
     //
     // FieldId flags for whether the field is single, repeated or packed.
     //
+    /**
+     * Bit offset for building a field id to be used with a
+     * <code>{@link ProtoOutputStream}.write(...)</code>.
+     *
+     * @see #FIELD_COUNT_MASK
+     * @see #FIELD_COUNT_UNKNOWN
+     * @see #FIELD_COUNT_SINGLE
+     * @see #FIELD_COUNT_REPEATED
+     * @see #FIELD_COUNT_PACKED
+     */
     public static final int FIELD_COUNT_SHIFT = 40;
+
+    /**
+     * Bit mask for selecting the field count when reading a field id that
+     * is used with a <code>{@link ProtoOutputStream}.write(...)</code> method.
+     *
+     * @see #FIELD_COUNT_SHIFT
+     * @see #FIELD_COUNT_MASK
+     * @see #FIELD_COUNT_UNKNOWN
+     * @see #FIELD_COUNT_SINGLE
+     * @see #FIELD_COUNT_REPEATED
+     * @see #FIELD_COUNT_PACKED
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT;
 
+    /**
+     * Unknown field count, encoded into a field id used with a
+     * <code>{@link ProtoOutputStream}.write(...)</code> method.
+     *
+     * @see #FIELD_COUNT_SHIFT
+     * @see #FIELD_COUNT_MASK
+     * @see #FIELD_COUNT_SINGLE
+     * @see #FIELD_COUNT_REPEATED
+     * @see #FIELD_COUNT_PACKED
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final long FIELD_COUNT_UNKNOWN = 0;
+
+    /**
+     * Single field count, encoded into a field id used with a
+     * <code>{@link ProtoOutputStream}.write(...)</code> method.
+     *
+     * @see #FIELD_COUNT_SHIFT
+     * @see #FIELD_COUNT_MASK
+     * @see #FIELD_COUNT_UNKNOWN
+     * @see #FIELD_COUNT_REPEATED
+     * @see #FIELD_COUNT_PACKED
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
+
+    /**
+     * Repeated field count, encoded into a field id used with a
+     * <code>{@link ProtoOutputStream}.write(...)</code> method.
+     *
+     * @see #FIELD_COUNT_SHIFT
+     * @see #FIELD_COUNT_MASK
+     * @see #FIELD_COUNT_UNKNOWN
+     * @see #FIELD_COUNT_SINGLE
+     * @see #FIELD_COUNT_PACKED
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
+
+    /**
+     * Repeated packed field count, encoded into a field id used with a
+     * <code>{@link ProtoOutputStream}.write(...)</code> method.
+     *
+     * @see #FIELD_COUNT_SHIFT
+     * @see #FIELD_COUNT_MASK
+     * @see #FIELD_COUNT_UNKNOWN
+     * @see #FIELD_COUNT_SINGLE
+     * @see #FIELD_COUNT_REPEATED
+     * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * Encoding</a>
+     */
     public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
 
 
     /**
      * Get the developer-usable name of a field type.
      */
-    public static String getFieldTypeString(long fieldType) {
+    public static @Nullable String getFieldTypeString(long fieldType) {
         int index = ((int) ((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1;
         if (index >= 0 && index < FIELD_TYPE_NAMES.length) {
             return FIELD_TYPE_NAMES[index];
@@ -124,7 +390,7 @@
     /**
      * Get the developer-usable name of a field count.
      */
-    public static String getFieldCountString(long fieldCount) {
+    public static @Nullable String getFieldCountString(long fieldCount) {
         if (fieldCount == FIELD_COUNT_SINGLE) {
             return "";
         } else if (fieldCount == FIELD_COUNT_REPEATED) {
@@ -139,7 +405,7 @@
     /**
      * Get the developer-usable name of a wire type.
      */
-    public static String getWireTypeString(int wireType) {
+    public static @Nullable String getWireTypeString(int wireType) {
         switch (wireType) {
             case WIRE_TYPE_VARINT:
                 return "Varint";
@@ -161,7 +427,7 @@
     /**
      * Get a debug string for a fieldId.
      */
-    public static String getFieldIdString(long fieldId) {
+    public static @NonNull String getFieldIdString(long fieldId) {
         final long fieldCount = fieldId & FIELD_COUNT_MASK;
         String countString = getFieldCountString(fieldCount);
         if (countString == null) {
@@ -218,29 +484,39 @@
 
     /**
      * Get the encoded tag size from the token.
+     *
+     * @hide
      */
     public static int getTagSizeFromToken(long token) {
         return (int) (0x7 & (token >> 61));
     }
 
     /**
-     * Get whether this is a call to startObject (false) or startRepeatedObject (true).
+     * Get whether the token has the repeated bit set to true or false
+     *
+     * @hide
      */
     public static boolean getRepeatedFromToken(long token) {
         return (0x1 & (token >> 60)) != 0;
     }
 
     /**
-     * Get the nesting depth of startObject calls from the token.
+     * Get the nesting depth from the token.
+     *
+     * @hide
      */
     public static int getDepthFromToken(long token) {
         return (int) (0x01ff & (token >> 51));
     }
 
     /**
-     * Get the object ID from the token. The object ID is a serial number for the
+     * Get the object ID from the token.
+     *
+     * <p>The object ID is a serial number for the
      * startObject calls that have happened on this object.  The values are truncated
      * to 9 bits, but that is sufficient for error checking.
+     *
+     * @hide
      */
     public static int getObjectIdFromToken(long token) {
         return (int) (0x07ffff & (token >> 32));
@@ -248,6 +524,8 @@
 
     /**
      * Get the location of the offset recorded in the token.
+     *
+     * @hide
      */
     public static int getOffsetFromToken(long token) {
         return (int) token;
@@ -255,8 +533,11 @@
 
     /**
      * Convert the object ID to the ordinal value -- the n-th call to startObject.
-     * The object IDs start at -1 and count backwards, so that the value is unlikely
+     *
+     * <p>The object IDs start at -1 and count backwards, so that the value is unlikely
      * to alias with an actual size field that had been written.
+     *
+     * @hide
      */
     public static int convertObjectIdToOrdinal(int objectId) {
         return (-1 & 0x07ffff) - objectId;
@@ -265,7 +546,7 @@
     /**
      * Return a debugging string of a token.
      */
-    public static String token2String(long token) {
+    public static @NonNull String token2String(long token) {
         if (token == 0L) {
             return "Token(0)";
         } else {
@@ -277,4 +558,9 @@
                     + ')';
         }
     }
+
+    /**
+     * @hide
+     */
+    protected ProtoStream() {}
 }
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 03bf590..a71561b 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -24,12 +24,12 @@
 /**
  * This class contains a list of helper functions to write common proto in
  * //frameworks/base/core/proto/android/base directory
+ * @hide
  */
 public class ProtoUtils {
 
     /**
      * Dump AggStats to ProtoOutputStream
-     * @hide
      */
     public static void toAggStatsProto(ProtoOutputStream proto, long fieldId,
             long min, long average, long max) {
@@ -42,7 +42,6 @@
 
     /**
      * Dump Duration to ProtoOutputStream
-     * @hide
      */
     public static void toDuration(ProtoOutputStream proto, long fieldId, long startMs, long endMs) {
         final long token = proto.start(fieldId);
@@ -53,7 +52,6 @@
 
     /**
      * Helper function to write bit-wise flags to proto as repeated enums
-     * @hide
      */
     public static void writeBitWiseFlagsToProtoEnum(ProtoOutputStream proto, long fieldId,
             int flags, int[] origEnums, int[] protoEnums) {
diff --git a/core/java/android/util/proto/package.html b/core/java/android/util/proto/package.html
index a636bd4..ef1125b 100644
--- a/core/java/android/util/proto/package.html
+++ b/core/java/android/util/proto/package.html
@@ -1,5 +1,5 @@
+<html>
 <body>
 Provides utility classes to export protocol buffers from the system.
-
-{@hide}
 </body>
+</html>
\ No newline at end of file
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 4bb012a..bbae0273 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.net;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ProxyInfo;
 import android.os.Build;
@@ -23,21 +24,34 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 
 /**
- * Parcel-like entity class for VPN profiles. To keep things simple, all
- * fields are package private. Methods are provided for serialization, so
- * storage can be implemented easily. Two rules are set for this class.
- * First, all fields must be kept non-null. Second, always make a copy
- * using clone() before modifying.
+ * Profile storage class for a platform VPN.
+ *
+ * <p>This class supports both the Legacy VPN, as well as application-configurable platform VPNs
+ * (such as IKEv2/IPsec).
+ *
+ * <p>This class is serialized and deserialized via the {@link #encode()} and {@link #decode()}
+ * functions for persistent storage in the Android Keystore. The encoding is entirely custom, but
+ * must be kept for backward compatibility for devices upgrading between Android versions.
  *
  * @hide
  */
-public class VpnProfile implements Cloneable, Parcelable {
+public final class VpnProfile implements Cloneable, Parcelable {
     private static final String TAG = "VpnProfile";
 
+    @VisibleForTesting static final String VALUE_DELIMITER = "\0";
+    @VisibleForTesting static final String LIST_DELIMITER = ",";
+
     // Match these constants with R.array.vpn_types.
     public static final int TYPE_PPTP = 0;
     public static final int TYPE_L2TP_IPSEC_PSK = 1;
@@ -45,39 +59,85 @@
     public static final int TYPE_IPSEC_XAUTH_PSK = 3;
     public static final int TYPE_IPSEC_XAUTH_RSA = 4;
     public static final int TYPE_IPSEC_HYBRID_RSA = 5;
-    public static final int TYPE_MAX = 5;
+    public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
+    public static final int TYPE_IKEV2_IPSEC_PSK = 7;
+    public static final int TYPE_IKEV2_IPSEC_RSA = 8;
+    public static final int TYPE_MAX = 8;
 
     // Match these constants with R.array.vpn_proxy_settings.
     public static final int PROXY_NONE = 0;
     public static final int PROXY_MANUAL = 1;
 
+    private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0";
+
     // Entity fields.
     @UnsupportedAppUsage
-    public final String key;           // -1
+    public final String key;                                   // -1
+
     @UnsupportedAppUsage
-    public String name = "";           // 0
+    public String name = "";                                   // 0
+
     @UnsupportedAppUsage
-    public int type = TYPE_PPTP;       // 1
+    public int type = TYPE_PPTP;                               // 1
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public String server = "";         // 2
+    public String server = "";                                 // 2
+
     @UnsupportedAppUsage
-    public String username = "";       // 3
-    public String password = "";       // 4
-    public String dnsServers = "";     // 5
-    public String searchDomains = "";  // 6
-    public String routes = "";         // 7
-    public boolean mppe = true;        // 8
-    public String l2tpSecret = "";     // 9
-    public String ipsecIdentifier = "";// 10
-    public String ipsecSecret = "";    // 11
-    public String ipsecUserCert = "";  // 12
-    public String ipsecCaCert = "";    // 13
-    public String ipsecServerCert = "";// 14
-    public ProxyInfo proxy = null;     // 15~18
+    public String username = "";                               // 3
+    public String password = "";                               // 4
+    public String dnsServers = "";                             // 5
+    public String searchDomains = "";                          // 6
+    public String routes = "";                                 // 7
+    public boolean mppe = true;                                // 8
+    public String l2tpSecret = "";                             // 9
+    public String ipsecIdentifier = "";                        // 10
+
+    /**
+     * The RSA private key or pre-shared key used for authentication.
+     *
+     * <p>If areAuthParamsInline is {@code true}, this String will be either:
+     *
+     * <ul>
+     *   <li>If this is an IKEv2 RSA profile: a PKCS#8 encoded {@link java.security.PrivateKey}
+     *   <li>If this is an IKEv2 PSK profile: a string value representing the PSK.
+     * </ul>
+     */
+    public String ipsecSecret = "";                            // 11
+
+    /**
+     * The RSA certificate to be used for digital signature authentication.
+     *
+     * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link
+     * java.security.X509Certificate}
+     */
+    public String ipsecUserCert = "";                          // 12
+
+    /**
+     * The RSA certificate that should be used to verify the server's end/target certificate.
+     *
+     * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link
+     * java.security.X509Certificate}
+     */
+    public String ipsecCaCert = "";                            // 13
+    public String ipsecServerCert = "";                        // 14
+    public ProxyInfo proxy = null;                             // 15~18
+
+    /**
+     * The list of allowable algorithms.
+     *
+     * <p>This list is validated in the setter to ensure that encoding characters (list, value
+     * delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()}
+     */
+    private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19
+    public boolean isBypassable = false;                       // 20
+    public boolean isMetered = false;                          // 21
+    public int maxMtu = 1400;                                  // 22
+    public boolean areAuthParamsInline = false;                   // 23
 
     // Helper fields.
     @UnsupportedAppUsage
-    public boolean saveLogin = false;
+    public transient boolean saveLogin = false;
 
     public VpnProfile(String key) {
         this.key = key;
@@ -103,6 +163,34 @@
         ipsecServerCert = in.readString();
         saveLogin = in.readInt() != 0;
         proxy = in.readParcelable(null);
+        mAllowedAlgorithms = new ArrayList<>();
+        in.readList(mAllowedAlgorithms, null);
+        isBypassable = in.readBoolean();
+        isMetered = in.readBoolean();
+        maxMtu = in.readInt();
+        areAuthParamsInline = in.readBoolean();
+    }
+
+    /**
+     * Retrieves the list of allowed algorithms.
+     *
+     * <p>The contained elements are as listed in {@link IpSecAlgorithm}
+     */
+    public List<String> getAllowedAlgorithms() {
+        return Collections.unmodifiableList(mAllowedAlgorithms);
+    }
+
+    /**
+     * Validates and sets the list of algorithms that can be used for the IPsec transforms.
+     *
+     * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link
+     *     IpSecAlgorithm}.
+     * @throws IllegalArgumentException if any delimiters are used in algorithm names. See {@link
+     *     #VALUE_DELIMITER} and {@link LIST_DELIMITER}.
+     */
+    public void setAllowedAlgorithms(List<String> allowedAlgorithms) {
+        validateAllowedAlgorithms(allowedAlgorithms);
+        mAllowedAlgorithms = allowedAlgorithms;
     }
 
     @Override
@@ -125,8 +213,18 @@
         out.writeString(ipsecServerCert);
         out.writeInt(saveLogin ? 1 : 0);
         out.writeParcelable(proxy, flags);
+        out.writeList(mAllowedAlgorithms);
+        out.writeBoolean(isBypassable);
+        out.writeBoolean(isMetered);
+        out.writeInt(maxMtu);
+        out.writeBoolean(areAuthParamsInline);
     }
 
+    /**
+     * Decodes a VpnProfile instance from the encoded byte array.
+     *
+     * <p>See {@link #encode()}
+     */
     @UnsupportedAppUsage
     public static VpnProfile decode(String key, byte[] value) {
         try {
@@ -134,9 +232,11 @@
                 return null;
             }
 
-            String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1);
-            // There can be 14 - 19 Bytes in values.length.
-            if (values.length < 14 || values.length > 19) {
+            String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
+            // Acceptable numbers of values are:
+            // 14-19: Standard profile, with option for serverCert, proxy
+            // 24: Standard profile with serverCert, proxy and platform-VPN parameters.
+            if ((values.length < 14 || values.length > 19) && values.length != 24) {
                 return null;
             }
 
@@ -164,13 +264,23 @@
                 String port = (values.length > 16) ? values[16] : "";
                 String exclList = (values.length > 17) ? values[17] : "";
                 String pacFileUrl = (values.length > 18) ? values[18] : "";
-                if (pacFileUrl.isEmpty()) {
+                if (!host.isEmpty() || !port.isEmpty() || !exclList.isEmpty()) {
                     profile.proxy = new ProxyInfo(host, port.isEmpty() ?
                             0 : Integer.parseInt(port), exclList);
-                } else {
+                } else if (!pacFileUrl.isEmpty()) {
                     profile.proxy = new ProxyInfo(pacFileUrl);
                 }
-            } // else profle.proxy = null
+            } // else profile.proxy = null
+
+            // Either all must be present, or none must be.
+            if (values.length >= 24) {
+                profile.mAllowedAlgorithms = Arrays.asList(values[19].split(LIST_DELIMITER));
+                profile.isBypassable = Boolean.parseBoolean(values[20]);
+                profile.isMetered = Boolean.parseBoolean(values[21]);
+                profile.maxMtu = Integer.parseInt(values[22]);
+                profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
+            }
+
             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
             return profile;
         } catch (Exception e) {
@@ -179,36 +289,52 @@
         return null;
     }
 
+    /**
+     * Encodes a VpnProfile instance to a byte array for storage.
+     *
+     * <p>See {@link #decode(String, byte[])}
+     */
     public byte[] encode() {
         StringBuilder builder = new StringBuilder(name);
-        builder.append('\0').append(type);
-        builder.append('\0').append(server);
-        builder.append('\0').append(saveLogin ? username : "");
-        builder.append('\0').append(saveLogin ? password : "");
-        builder.append('\0').append(dnsServers);
-        builder.append('\0').append(searchDomains);
-        builder.append('\0').append(routes);
-        builder.append('\0').append(mppe);
-        builder.append('\0').append(l2tpSecret);
-        builder.append('\0').append(ipsecIdentifier);
-        builder.append('\0').append(ipsecSecret);
-        builder.append('\0').append(ipsecUserCert);
-        builder.append('\0').append(ipsecCaCert);
-        builder.append('\0').append(ipsecServerCert);
+        builder.append(VALUE_DELIMITER).append(type);
+        builder.append(VALUE_DELIMITER).append(server);
+        builder.append(VALUE_DELIMITER).append(saveLogin ? username : "");
+        builder.append(VALUE_DELIMITER).append(saveLogin ? password : "");
+        builder.append(VALUE_DELIMITER).append(dnsServers);
+        builder.append(VALUE_DELIMITER).append(searchDomains);
+        builder.append(VALUE_DELIMITER).append(routes);
+        builder.append(VALUE_DELIMITER).append(mppe);
+        builder.append(VALUE_DELIMITER).append(l2tpSecret);
+        builder.append(VALUE_DELIMITER).append(ipsecIdentifier);
+        builder.append(VALUE_DELIMITER).append(ipsecSecret);
+        builder.append(VALUE_DELIMITER).append(ipsecUserCert);
+        builder.append(VALUE_DELIMITER).append(ipsecCaCert);
+        builder.append(VALUE_DELIMITER).append(ipsecServerCert);
         if (proxy != null) {
-            builder.append('\0').append(proxy.getHost() != null ? proxy.getHost() : "");
-            builder.append('\0').append(proxy.getPort());
-            builder.append('\0').append(proxy.getExclusionListAsString() != null ?
-                    proxy.getExclusionListAsString() : "");
-            builder.append('\0').append(proxy.getPacFileUrl().toString());
+            builder.append(VALUE_DELIMITER).append(proxy.getHost() != null ? proxy.getHost() : "");
+            builder.append(VALUE_DELIMITER).append(proxy.getPort());
+            builder.append(VALUE_DELIMITER)
+                    .append(
+                            proxy.getExclusionListAsString() != null
+                                    ? proxy.getExclusionListAsString()
+                                    : "");
+            builder.append(VALUE_DELIMITER).append(proxy.getPacFileUrl().toString());
+        } else {
+            builder.append(ENCODED_NULL_PROXY_INFO);
         }
+
+        builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, mAllowedAlgorithms));
+        builder.append(VALUE_DELIMITER).append(isBypassable);
+        builder.append(VALUE_DELIMITER).append(isMetered);
+        builder.append(VALUE_DELIMITER).append(maxMtu);
+        builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
+
         return builder.toString().getBytes(StandardCharsets.UTF_8);
     }
 
     /**
-     * Tests if profile is valid for lockdown, which requires IPv4 address for
-     * both server and DNS. Server hostnames would require using DNS before
-     * connection.
+     * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS.
+     * Server hostnames would require using DNS before connection.
      */
     public boolean isValidLockdownProfile() {
         return isTypeValidForLockdown()
@@ -238,10 +364,7 @@
         return !TextUtils.isEmpty(dnsServers);
     }
 
-    /**
-     * Returns {@code true} if all DNS servers have numeric addresses,
-     * e.g. 8.8.8.8
-     */
+    /** Returns {@code true} if all DNS servers have numeric addresses, e.g. 8.8.8.8 */
     public boolean areDnsAddressesNumeric() {
         try {
             for (String dnsServer : dnsServers.split(" +")) {
@@ -253,6 +376,62 @@
         return true;
     }
 
+    /**
+     * Validates that the provided list of algorithms does not contain illegal characters.
+     *
+     * @param allowedAlgorithms The list to be validated
+     */
+    public static void validateAllowedAlgorithms(List<String> allowedAlgorithms) {
+        for (final String alg : allowedAlgorithms) {
+            if (alg.contains(VALUE_DELIMITER) || alg.contains(LIST_DELIMITER)) {
+                throw new IllegalArgumentException(
+                        "Algorithm contained illegal ('\0' or ',') character");
+            }
+        }
+    }
+
+    /** Generates a hashcode over the VpnProfile. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+            key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
+            l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
+            proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
+    }
+
+    /** Checks VPN profiles for interior equality. */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof VpnProfile)) {
+            return false;
+        }
+
+        final VpnProfile other = (VpnProfile) obj;
+        return Objects.equals(key, other.key)
+                && Objects.equals(name, other.name)
+                && type == other.type
+                && Objects.equals(server, other.server)
+                && Objects.equals(username, other.username)
+                && Objects.equals(password, other.password)
+                && Objects.equals(dnsServers, other.dnsServers)
+                && Objects.equals(searchDomains, other.searchDomains)
+                && Objects.equals(routes, other.routes)
+                && mppe == other.mppe
+                && Objects.equals(l2tpSecret, other.l2tpSecret)
+                && Objects.equals(ipsecIdentifier, other.ipsecIdentifier)
+                && Objects.equals(ipsecSecret, other.ipsecSecret)
+                && Objects.equals(ipsecUserCert, other.ipsecUserCert)
+                && Objects.equals(ipsecCaCert, other.ipsecCaCert)
+                && Objects.equals(ipsecServerCert, other.ipsecServerCert)
+                && Objects.equals(proxy, other.proxy)
+                && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
+                && isBypassable == other.isBypassable
+                && isMetered == other.isMetered
+                && maxMtu == other.maxMtu
+                && areAuthParamsInline == other.areAuthParamsInline;
+    }
+
+    @NonNull
     public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
         @Override
         public VpnProfile createFromParcel(Parcel in) {
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 13d0c5c..7adb27c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ApplicationErrorReport;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.type.DefaultMimeMapFactory;
 import android.os.Build;
@@ -34,6 +36,7 @@
 import com.android.internal.logging.AndroidConfig;
 import com.android.server.NetworkManagementSocketTagger;
 
+import dalvik.annotation.compat.VersionCodes;
 import dalvik.system.RuntimeHooks;
 import dalvik.system.ThreadPrioritySetter;
 import dalvik.system.VMRuntime;
@@ -64,8 +67,17 @@
 
     private static volatile boolean mCrashing = false;
 
+    /*
+     * Native heap allocations will now have a non-zero tag in the most significant byte.
+     * See {@linktourl https://source.android.com/devices/tech/debug/tagged-pointers}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+    private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
     private static final native void nativeFinishInit();
     private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
+    private static native void nativeDisableHeapPointerTagging();
 
     private static int Clog_e(String tag, String msg, Throwable tr) {
         return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -398,6 +410,20 @@
         if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
     }
 
+    private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
+        // Heap tagging needs to be disabled before any additional threads are created, but the
+        // AppCompat framework is not initialized enough at this point.
+        // Check if the change is enabled manually.
+        if (disabledCompatChanges != null) {
+            for (int i = 0; i < disabledCompatChanges.length; i++) {
+                if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
+                    nativeDisableHeapPointerTagging();
+                    break;
+                }
+            }
+        }
+    }
+
     protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
             String[] argv, ClassLoader classLoader) {
         // If the application calls System.exit(), terminate the process
@@ -410,6 +436,8 @@
         VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
         VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
 
+        maybeDisableHeapPointerTagging(disabledCompatChanges);
+
         final Arguments args = new Arguments(argv);
 
         // The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8e0e1c6..551541e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -47,6 +47,7 @@
 #include <signal.h>
 #include <dirent.h>
 #include <assert.h>
+#include <bionic/malloc.h>
 
 #include <string>
 #include <vector>
@@ -279,6 +280,14 @@
     gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
 }
 
+static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
+        JNIEnv* env, jobject clazz) {
+    HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
+    if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
+        ALOGE("ERROR: could not disable heap pointer tagging\n");
+    }
+}
+
 /*
  * JNI registration.
  */
@@ -286,10 +295,12 @@
 int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
 {
     const JNINativeMethod methods[] = {
-        { "nativeFinishInit", "()V",
-            (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
-        { "nativeSetExitWithoutCleanup", "(Z)V",
-            (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
+            {"nativeFinishInit", "()V",
+             (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
+            {"nativeSetExitWithoutCleanup", "(Z)V",
+             (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
+            {"nativeDisableHeapPointerTagging", "()V",
+             (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
     };
     return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
         methods, NELEM(methods));
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 52ce54b..7bf35f9 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -193,7 +193,8 @@
 {
     int err = memtrack_proc_get(p, pid);
     if (err != 0) {
-        ALOGW("failed to get memory consumption info: %d", err);
+        // The memtrack HAL may not be available, do not log to avoid flooding
+        // logcat.
         return err;
     }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 78ccba4..486df62 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1063,22 +1063,16 @@
 
   DropCapabilitiesBoundingSet(fail_fn);
 
-  bool use_native_bridge = !is_system_server &&
-                           instruction_set.has_value() &&
-                           android::NativeBridgeAvailable() &&
-                           android::NeedsNativeBridge(instruction_set.value().c_str());
+  bool need_pre_initialize_native_bridge =
+      !is_system_server &&
+      instruction_set.has_value() &&
+      android::NativeBridgeAvailable() &&
+      // Native bridge may be already initialized if this
+      // is an app forked from app-zygote.
+      !android::NativeBridgeInitialized() &&
+      android::NeedsNativeBridge(instruction_set.value().c_str());
 
-  if (use_native_bridge && !app_data_dir.has_value()) {
-    // The app_data_dir variable should never be empty if we need to use a
-    // native bridge.  In general, app_data_dir will never be empty for normal
-    // applications.  It can only happen in special cases (for isolated
-    // processes which are not associated with any app).  These are launched by
-    // the framework and should not be emulated anyway.
-    use_native_bridge = false;
-    ALOGW("Native bridge will not be used because managed_app_data_dir == nullptr.");
-  }
-
-  MountEmulatedStorage(uid, mount_external, use_native_bridge, fail_fn);
+  MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
 
   // If this zygote isn't root, it won't be able to create a process group,
   // since the directory is owned by root.
@@ -1094,11 +1088,12 @@
   SetGids(env, gids, fail_fn);
   SetRLimits(env, rlimits, fail_fn);
 
-  if (use_native_bridge) {
-    // Due to the logic behind use_native_bridge we know that both app_data_dir
-    // and instruction_set contain values.
-    android::PreInitializeNativeBridge(app_data_dir.value().c_str(),
-                                       instruction_set.value().c_str());
+  if (need_pre_initialize_native_bridge) {
+    // Due to the logic behind need_pre_initialize_native_bridge we know that
+    // instruction_set contains a value.
+    android::PreInitializeNativeBridge(
+        app_data_dir.has_value() ? app_data_dir.value().c_str() : nullptr,
+        instruction_set.value().c_str());
   }
 
   if (setresgid(gid, gid, gid) == -1) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 74ced89..4892faa 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,6 +14,7 @@
 # Frameworks
 ogunwale@google.com
 jjaggi@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
 
 # Launcher
 hyunyoungs@google.com
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4aa44fc..9bc0d96 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5393,6 +5393,10 @@
     <!-- Description of media type: presentation file, such as PPT. The 'extension' variable is the file name extension. [CHAR LIMIT=32] -->
     <string name="mime_type_presentation_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> presentation</string>
 
+    <!-- Strings for Bluetooth service -->
+    <!-- toast message informing user that Bluetooth stays on after airplane mode is turned on. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_airplane_mode_toast">Bluetooth will stay on during airplane mode</string>
+
     <!-- Strings for car -->
     <!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
     <string name="car_loading_profile">Loading</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 383fcd4..cd43e9f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3795,6 +3795,9 @@
   <java-symbol type="string" name="mime_type_presentation" />
   <java-symbol type="string" name="mime_type_presentation_ext" />
 
+  <!-- For Bluetooth service -->
+  <java-symbol type="string" name="bluetooth_airplane_mode_toast" />
+
   <!-- For high refresh rate displays -->
   <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
   <java-symbol type="integer" name="config_defaultRefreshRateInZone" />
diff --git a/core/tests/overlaytests/host/Android.bp b/core/tests/overlaytests/host/Android.bp
new file mode 100644
index 0000000..a2fcef5
--- /dev/null
+++ b/core/tests/overlaytests/host/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+java_test_host {
+    name: "OverlayHostTests",
+    srcs: ["src/**/*.java"],
+    libs: ["tradefed"],
+    test_suites: ["general-tests"],
+    target_required: [
+        "OverlayHostTests_NonPlatformSignatureOverlay",
+        "OverlayHostTests_PlatformSignatureStaticOverlay",
+        "OverlayHostTests_PlatformSignatureOverlay",
+        "OverlayHostTests_UpdateOverlay",
+        "OverlayHostTests_FrameworkOverlayV1",
+        "OverlayHostTests_FrameworkOverlayV2",
+        "OverlayHostTests_AppOverlayV1",
+        "OverlayHostTests_AppOverlayV2",
+    ],
+}
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
index e7348d5..d58d939 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/core/tests/overlaytests/host/Android.mk
@@ -14,23 +14,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := OverlayHostTests
-LOCAL_JAVA_LIBRARIES := tradefed
-LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_TARGET_REQUIRED_MODULES := \
-    OverlayHostTests_NonPlatformSignatureOverlay \
-    OverlayHostTests_PlatformSignatureStaticOverlay \
-    OverlayHostTests_PlatformSignatureOverlay \
-    OverlayHostTests_UpdateOverlay \
-    OverlayHostTests_FrameworkOverlayV1 \
-    OverlayHostTests_FrameworkOverlayV2 \
-    OverlayHostTests_AppOverlayV1 \
-    OverlayHostTests_AppOverlayV2
-include $(BUILD_HOST_JAVA_LIBRARY)
-
 # Include to build test-apps.
 include $(call all-makefiles-under,$(LOCAL_PATH))
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a818119..eec2072 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -67,7 +67,6 @@
     <privapp-permissions package="com.android.managedprovisioning">
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
         <permission name="android.permission.CHANGE_CONFIGURATION"/>
-        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.CRYPT_KEEPER"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
@@ -362,7 +361,6 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.vpndialogs">
-        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.CONTROL_VPN"/>
     </privapp-permissions>
 
diff --git a/identity/MODULE_LICENSE_APACHE2 b/identity/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/identity/MODULE_LICENSE_APACHE2
diff --git a/identity/NOTICE b/identity/NOTICE
new file mode 100644
index 0000000..64aaa8d
--- /dev/null
+++ b/identity/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2009, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/identity/OWNERS b/identity/OWNERS
new file mode 100644
index 0000000..533d90b
--- /dev/null
+++ b/identity/OWNERS
@@ -0,0 +1,3 @@
+swillden@google.com
+zeuthen@google.com
+
diff --git a/identity/java/android/security/identity/AccessControlProfile.java b/identity/java/android/security/identity/AccessControlProfile.java
new file mode 100644
index 0000000..10e451c
--- /dev/null
+++ b/identity/java/android/security/identity/AccessControlProfile.java
@@ -0,0 +1,131 @@
+/*
+ * 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.security.identity;
+
+import android.annotation.NonNull;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * A class used to specify access controls.
+ */
+public class AccessControlProfile {
+    private AccessControlProfileId mAccessControlProfileId = new AccessControlProfileId(0);
+    private X509Certificate mReaderCertificate = null;
+    private boolean mUserAuthenticationRequired = true;
+    private long mUserAuthenticationTimeout = 0;
+
+    private AccessControlProfile() {
+    }
+
+    AccessControlProfileId getAccessControlProfileId() {
+        return mAccessControlProfileId;
+    }
+
+    long getUserAuthenticationTimeout() {
+        return mUserAuthenticationTimeout;
+    }
+
+    boolean isUserAuthenticationRequired() {
+        return mUserAuthenticationRequired;
+    }
+
+    X509Certificate getReaderCertificate() {
+        return mReaderCertificate;
+    }
+
+    /**
+     * A builder for {@link AccessControlProfile}.
+     */
+    public static final class Builder {
+        private AccessControlProfile mProfile;
+
+        /**
+         * Each access control profile has numeric identifier that must be unique within the
+         * context of a Credential and may be used to reference the profile.
+         *
+         * <p>By default, the resulting {@link AccessControlProfile} will require user
+         * authentication with a timeout of zero, thus requiring the holder to authenticate for
+         * every presentation where data elements using this access control profile is used.</p>
+         *
+         * @param accessControlProfileId the access control profile identifier.
+         */
+        public Builder(@NonNull AccessControlProfileId accessControlProfileId) {
+            mProfile = new AccessControlProfile();
+            mProfile.mAccessControlProfileId = accessControlProfileId;
+        }
+
+        /**
+         * Set whether user authentication is required.
+         *
+         * <p>This should be used sparingly since disabling user authentication on just a single
+         * data element can easily create a
+         * <a href="https://en.wikipedia.org/wiki/Relay_attack">Relay Attack</a> if the device
+         * on which the credential is stored is compromised.</p>
+         *
+         * @param userAuthenticationRequired Set to true if user authentication is required,
+         *                                   false otherwise.
+         * @return The builder.
+         */
+        public @NonNull Builder setUserAuthenticationRequired(boolean userAuthenticationRequired) {
+            mProfile.mUserAuthenticationRequired = userAuthenticationRequired;
+            return this;
+        }
+
+        /**
+         * Sets the authentication timeout to use.
+         *
+         * <p>The authentication timeout specifies the amount of time, in milliseconds, for which a
+         * user authentication is valid, if user authentication is required (see
+         * {@link #setUserAuthenticationRequired(boolean)}).</p>
+         *
+         * <p>If the timeout is zero, then authentication is always required for each reader
+         * session.</p>
+         *
+         * @param userAuthenticationTimeoutMillis the authentication timeout, in milliseconds.
+         * @return The builder.
+         */
+        public @NonNull Builder setUserAuthenticationTimeout(long userAuthenticationTimeoutMillis) {
+            mProfile.mUserAuthenticationTimeout = userAuthenticationTimeoutMillis;
+            return this;
+        }
+
+        /**
+         * Sets the reader certificate to use when checking access control.
+         *
+         * <p>If set, this is checked against the certificate chain presented by
+         * reader. The access check is fulfilled only if one of the certificates
+         * in the chain, matches the certificate set by this method.</p>
+         *
+         * @param readerCertificate the certificate to use for the access control check.
+         * @return The builder.
+         */
+        public @NonNull Builder setReaderCertificate(@NonNull X509Certificate readerCertificate) {
+            mProfile.mReaderCertificate = readerCertificate;
+            return this;
+        }
+
+        /**
+         * Creates a new {@link AccessControlProfile} from the data supplied to the builder.
+         *
+         * @return The created {@link AccessControlProfile} object.
+         */
+        public @NonNull AccessControlProfile build() {
+            return mProfile;
+        }
+    }
+}
diff --git a/identity/java/android/security/identity/AccessControlProfileId.java b/identity/java/android/security/identity/AccessControlProfileId.java
new file mode 100644
index 0000000..3d59450
--- /dev/null
+++ b/identity/java/android/security/identity/AccessControlProfileId.java
@@ -0,0 +1,42 @@
+/*
+ * 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.security.identity;
+
+/**
+ * A class used to wrap an access control profile identifiers.
+ */
+public class AccessControlProfileId {
+    private int mId = 0;
+
+    /**
+     * Constructs a new object holding a numerical identifier.
+     *
+     * @param id the identifier.
+     */
+    public AccessControlProfileId(int id) {
+        this.mId = id;
+    }
+
+    /**
+     * Gets the numerical identifier wrapped by this object.
+     *
+     * @return the identifier.
+     */
+    public int getId() {
+        return this.mId;
+    }
+}
diff --git a/identity/java/android/security/identity/AlreadyPersonalizedException.java b/identity/java/android/security/identity/AlreadyPersonalizedException.java
new file mode 100644
index 0000000..1933882
--- /dev/null
+++ b/identity/java/android/security/identity/AlreadyPersonalizedException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to create a credential which already exists.
+ */
+public class AlreadyPersonalizedException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link AlreadyPersonalizedException} exception.
+     *
+     * @param message the detail message.
+     */
+    public AlreadyPersonalizedException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link AlreadyPersonalizedException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public AlreadyPersonalizedException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/CipherSuiteNotSupportedException.java b/identity/java/android/security/identity/CipherSuiteNotSupportedException.java
new file mode 100644
index 0000000..e7a6c89
--- /dev/null
+++ b/identity/java/android/security/identity/CipherSuiteNotSupportedException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to use a cipher suite which isn't supported.
+ */
+public class CipherSuiteNotSupportedException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link CipherSuiteNotSupportedException} exception.
+     *
+     * @param message the detail message.
+     */
+    public CipherSuiteNotSupportedException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link CipherSuiteNotSupportedException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public CipherSuiteNotSupportedException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
new file mode 100644
index 0000000..c520331
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+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.Collection;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyAgreement;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+class CredstoreIdentityCredential extends IdentityCredential {
+
+    private static final String TAG = "CredstoreIdentityCredential";
+    private String mCredentialName;
+    private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+    private Context mContext;
+    private ICredential mBinder;
+
+    CredstoreIdentityCredential(Context context, String credentialName,
+            @IdentityCredentialStore.Ciphersuite int cipherSuite,
+            ICredential binder) {
+        mContext = context;
+        mCredentialName = credentialName;
+        mCipherSuite = cipherSuite;
+        mBinder = binder;
+    }
+
+    private KeyPair mEphemeralKeyPair = null;
+    private SecretKey mSecretKey = null;
+    private SecretKey mReaderSecretKey = null;
+    private int mEphemeralCounter;
+    private int mReadersExpectedEphemeralCounter;
+
+    private void ensureEphemeralKeyPair() {
+        if (mEphemeralKeyPair != null) {
+            return;
+        }
+        try {
+            // This PKCS#12 blob is generated in credstore, using BoringSSL.
+            //
+            // The main reason for this convoluted approach and not just sending the decomposed
+            // key-pair is that this would require directly using (device-side) BouncyCastle which
+            // is tricky due to various API hiding efforts. So instead we have credstore generate
+            // this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
+            // doesn't support not using encryption when building a PKCS#12 blob).
+            //
+            byte[] pkcs12 = mBinder.createEphemeralKeyPair();
+            String alias = "ephemeralKey";
+            char[] password = {};
+
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
+            ks.load(bais, password);
+            PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
+
+            Certificate cert = ks.getCertificate(alias);
+            PublicKey pubKey = cert.getPublicKey();
+
+            mEphemeralKeyPair = new KeyPair(pubKey, privKey);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        } catch (KeyStoreException
+                | CertificateException
+                | UnrecoverableKeyException
+                | NoSuchAlgorithmException
+                | IOException e) {
+            throw new RuntimeException("Unexpected exception ", e);
+        }
+    }
+
+    @Override
+    public @NonNull KeyPair createEphemeralKeyPair() {
+        ensureEphemeralKeyPair();
+        return mEphemeralKeyPair;
+    }
+
+    @Override
+    public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException {
+        try {
+            byte[] uncompressedForm =
+                    Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
+            mBinder.setReaderEphemeralPublicKey(uncompressedForm);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+
+        ensureEphemeralKeyPair();
+
+        try {
+            KeyAgreement ka = KeyAgreement.getInstance("ECDH");
+            ka.init(mEphemeralKeyPair.getPrivate());
+            ka.doPhase(readerEphemeralPublicKey, true);
+            byte[] sharedSecret = ka.generateSecret();
+
+            byte[] salt = new byte[1];
+            byte[] info = new byte[0];
+
+            salt[0] = 0x01;
+            byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+            mSecretKey = new SecretKeySpec(derivedKey, "AES");
+
+            salt[0] = 0x00;
+            derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+            mReaderSecretKey = new SecretKeySpec(derivedKey, "AES");
+
+            mEphemeralCounter = 0;
+            mReadersExpectedEphemeralCounter = 0;
+
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("Error performing key agreement", e);
+        }
+    }
+
+    @Override
+    public @NonNull byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext) {
+        byte[] messageCiphertextAndAuthTag = null;
+        try {
+            ByteBuffer iv = ByteBuffer.allocate(12);
+            iv.putInt(0, 0x00000000);
+            iv.putInt(4, 0x00000001);
+            iv.putInt(8, mEphemeralCounter);
+            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+            GCMParameterSpec encryptionParameterSpec = new GCMParameterSpec(128, iv.array());
+            cipher.init(Cipher.ENCRYPT_MODE, mSecretKey, encryptionParameterSpec);
+            messageCiphertextAndAuthTag = cipher.doFinal(messagePlaintext);
+        } catch (BadPaddingException
+                | IllegalBlockSizeException
+                | NoSuchPaddingException
+                | InvalidKeyException
+                | NoSuchAlgorithmException
+                | InvalidAlgorithmParameterException e) {
+            throw new RuntimeException("Error encrypting message", e);
+        }
+        mEphemeralCounter += 1;
+        return messageCiphertextAndAuthTag;
+    }
+
+    @Override
+    public @NonNull byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
+            throws MessageDecryptionException {
+        ByteBuffer iv = ByteBuffer.allocate(12);
+        iv.putInt(0, 0x00000000);
+        iv.putInt(4, 0x00000000);
+        iv.putInt(8, mReadersExpectedEphemeralCounter);
+        byte[] plainText = null;
+        try {
+            final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+            cipher.init(Cipher.DECRYPT_MODE, mReaderSecretKey,
+                    new GCMParameterSpec(128, iv.array()));
+            plainText = cipher.doFinal(messageCiphertext);
+        } catch (BadPaddingException
+                | IllegalBlockSizeException
+                | InvalidAlgorithmParameterException
+                | InvalidKeyException
+                | NoSuchAlgorithmException
+                | NoSuchPaddingException e) {
+            throw new MessageDecryptionException("Error decrypting message", e);
+        }
+        mReadersExpectedEphemeralCounter += 1;
+        return plainText;
+    }
+
+    @Override
+    public @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain() {
+        try {
+            byte[] certsBlob = mBinder.getCredentialKeyCertificateChain();
+            ByteArrayInputStream bais = new ByteArrayInputStream(certsBlob);
+
+            Collection<? extends Certificate> certs = null;
+            try {
+                CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                certs = factory.generateCertificates(bais);
+            } catch (CertificateException e) {
+                throw new RuntimeException("Error decoding certificates", e);
+            }
+
+            LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+            for (Certificate cert : certs) {
+                x509Certs.add((X509Certificate) cert);
+            }
+            return x509Certs;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    private boolean mAllowUsingExhaustedKeys = true;
+
+    @Override
+    public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+        mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+    }
+
+    private boolean mOperationHandleSet = false;
+    private long mOperationHandle = 0;
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    @Override
+    public long getCredstoreOperationHandle() {
+        if (!mOperationHandleSet) {
+            try {
+                mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys);
+                mOperationHandleSet = true;
+            } catch (android.os.RemoteException e) {
+                throw new RuntimeException("Unexpected RemoteException ", e);
+            } catch (android.os.ServiceSpecificException e) {
+                if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+                    // The NoAuthenticationKeyAvailableException will be thrown when
+                    // the caller proceeds to call getEntries().
+                }
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+        return mOperationHandle;
+    }
+
+    @NonNull
+    @Override
+    public ResultData getEntries(
+            @Nullable byte[] requestMessage,
+            @NonNull Map<String, Collection<String>> entriesToRequest,
+            @Nullable byte[] sessionTranscript,
+            @Nullable byte[] readerSignature)
+            throws SessionTranscriptMismatchException, NoAuthenticationKeyAvailableException,
+            InvalidReaderSignatureException, EphemeralPublicKeyNotFoundException,
+            InvalidRequestMessageException {
+
+        RequestNamespaceParcel[] rnsParcels = new RequestNamespaceParcel[entriesToRequest.size()];
+        int n = 0;
+        for (String namespaceName : entriesToRequest.keySet()) {
+            Collection<String> entryNames = entriesToRequest.get(namespaceName);
+            rnsParcels[n] = new RequestNamespaceParcel();
+            rnsParcels[n].namespaceName = namespaceName;
+            rnsParcels[n].entries = new RequestEntryParcel[entryNames.size()];
+            int m = 0;
+            for (String entryName : entryNames) {
+                rnsParcels[n].entries[m] = new RequestEntryParcel();
+                rnsParcels[n].entries[m].name = entryName;
+                m++;
+            }
+            n++;
+        }
+
+        GetEntriesResultParcel resultParcel = null;
+        try {
+            resultParcel = mBinder.getEntries(
+                requestMessage != null ? requestMessage : new byte[0],
+                rnsParcels,
+                sessionTranscript != null ? sessionTranscript : new byte[0],
+                readerSignature != null ? readerSignature : new byte[0],
+                mAllowUsingExhaustedKeys);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
+                throw new EphemeralPublicKeyNotFoundException(e.getMessage(), e);
+            } else if (e.errorCode == ICredentialStore.ERROR_INVALID_READER_SIGNATURE) {
+                throw new InvalidReaderSignatureException(e.getMessage(), e);
+            } else if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+                throw new NoAuthenticationKeyAvailableException(e.getMessage(), e);
+            } else if (e.errorCode == ICredentialStore.ERROR_INVALID_ITEMS_REQUEST_MESSAGE) {
+                throw new InvalidRequestMessageException(e.getMessage(), e);
+            } else if (e.errorCode == ICredentialStore.ERROR_SESSION_TRANSCRIPT_MISMATCH) {
+                throw new SessionTranscriptMismatchException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+
+        byte[] mac = resultParcel.mac;
+        if (mac != null && mac.length == 0) {
+            mac = null;
+        }
+        CredstoreResultData.Builder resultDataBuilder = new CredstoreResultData.Builder(
+                resultParcel.staticAuthenticationData, resultParcel.deviceNameSpaces, mac);
+
+        for (ResultNamespaceParcel resultNamespaceParcel : resultParcel.resultNamespaces) {
+            for (ResultEntryParcel resultEntryParcel : resultNamespaceParcel.entries) {
+                if (resultEntryParcel.status == ICredential.STATUS_OK) {
+                    resultDataBuilder.addEntry(resultNamespaceParcel.namespaceName,
+                            resultEntryParcel.name, resultEntryParcel.value);
+                } else {
+                    resultDataBuilder.addErrorStatus(resultNamespaceParcel.namespaceName,
+                            resultEntryParcel.name,
+                            resultEntryParcel.status);
+                }
+            }
+        }
+        return resultDataBuilder.build();
+    }
+
+    @Override
+    public void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+        try {
+            mBinder.setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public @NonNull Collection<X509Certificate> getAuthKeysNeedingCertification() {
+        try {
+            AuthKeyParcel[] authKeyParcels = mBinder.getAuthKeysNeedingCertification();
+            LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+            for (AuthKeyParcel authKeyParcel : authKeyParcels) {
+                Collection<? extends Certificate> certs = null;
+                ByteArrayInputStream bais = new ByteArrayInputStream(authKeyParcel.x509cert);
+                certs = factory.generateCertificates(bais);
+                if (certs.size() != 1) {
+                    throw new RuntimeException("Returned blob yields more than one X509 cert");
+                }
+                X509Certificate authKeyCert = (X509Certificate) certs.iterator().next();
+                x509Certs.add(authKeyCert);
+            }
+            return x509Certs;
+        } catch (CertificateException e) {
+            throw new RuntimeException("Error decoding authenticationKey", e);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public void storeStaticAuthenticationData(X509Certificate authenticationKey,
+            byte[] staticAuthData)
+            throws UnknownAuthenticationKeyException {
+        try {
+            AuthKeyParcel authKeyParcel = new AuthKeyParcel();
+            authKeyParcel.x509cert = authenticationKey.getEncoded();
+            mBinder.storeStaticAuthenticationData(authKeyParcel, staticAuthData);
+        } catch (CertificateEncodingException e) {
+            throw new RuntimeException("Error encoding authenticationKey", e);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_AUTHENTICATION_KEY_NOT_FOUND) {
+                throw new UnknownAuthenticationKeyException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
+    @Override
+    public @NonNull int[] getAuthenticationDataUsageCount() {
+        try {
+            int[] usageCount = mBinder.getAuthenticationDataUsageCount();
+            return usageCount;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
new file mode 100644
index 0000000..dcc6b95
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.ServiceManager;
+
+class CredstoreIdentityCredentialStore extends IdentityCredentialStore {
+
+    private static final String TAG = "CredstoreIdentityCredentialStore";
+
+    private Context mContext = null;
+    private ICredentialStore mStore = null;
+
+    private CredstoreIdentityCredentialStore(@NonNull Context context, ICredentialStore store) {
+        mContext = context;
+        mStore = store;
+    }
+
+    static CredstoreIdentityCredentialStore getInstanceForType(@NonNull Context context,
+            int credentialStoreType) {
+        ICredentialStoreFactory storeFactory =
+                ICredentialStoreFactory.Stub.asInterface(
+                    ServiceManager.getService("android.security.identity"));
+
+        ICredentialStore credStore = null;
+        try {
+            credStore = storeFactory.getCredentialStore(credentialStoreType);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_GENERIC) {
+                return null;
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+        if (credStore == null) {
+            return null;
+        }
+
+        return new CredstoreIdentityCredentialStore(context, credStore);
+    }
+
+    private static CredstoreIdentityCredentialStore sInstanceDefault = null;
+    private static CredstoreIdentityCredentialStore sInstanceDirectAccess = null;
+
+    public static @Nullable IdentityCredentialStore getInstance(@NonNull Context context) {
+        if (sInstanceDefault == null) {
+            sInstanceDefault = getInstanceForType(context,
+                    ICredentialStoreFactory.CREDENTIAL_STORE_TYPE_DEFAULT);
+        }
+        return sInstanceDefault;
+    }
+
+    public static @Nullable IdentityCredentialStore getDirectAccessInstance(@NonNull
+            Context context) {
+        if (sInstanceDirectAccess == null) {
+            sInstanceDirectAccess = getInstanceForType(context,
+                    ICredentialStoreFactory.CREDENTIAL_STORE_TYPE_DIRECT_ACCESS);
+        }
+        return sInstanceDirectAccess;
+    }
+
+    @Override
+    public @NonNull String[] getSupportedDocTypes() {
+        try {
+            SecurityHardwareInfoParcel info;
+            info = mStore.getSecurityHardwareInfo();
+            return info.supportedDocTypes;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override public @NonNull WritableIdentityCredential createCredential(
+            @NonNull String credentialName,
+            @NonNull String docType) throws AlreadyPersonalizedException,
+            DocTypeNotSupportedException {
+        try {
+            IWritableCredential wc;
+            wc = mStore.createCredential(credentialName, docType);
+            return new CredstoreWritableIdentityCredential(mContext, credentialName, docType, wc);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_ALREADY_PERSONALIZED) {
+                throw new AlreadyPersonalizedException(e.getMessage(), e);
+            } else if (e.errorCode == ICredentialStore.ERROR_DOCUMENT_TYPE_NOT_SUPPORTED) {
+                throw new DocTypeNotSupportedException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
+    @Override public @Nullable IdentityCredential getCredentialByName(
+            @NonNull String credentialName,
+            @Ciphersuite int cipherSuite) throws CipherSuiteNotSupportedException {
+        try {
+            ICredential credstoreCredential;
+            credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
+            return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
+                    credstoreCredential);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+                return null;
+            } else if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
+                throw new CipherSuiteNotSupportedException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
+    @Override
+    public @Nullable byte[] deleteCredentialByName(@NonNull String credentialName) {
+        ICredential credstoreCredential = null;
+        try {
+            try {
+                credstoreCredential = mStore.getCredentialByName(credentialName,
+                        CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+            } catch (android.os.ServiceSpecificException e) {
+                if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+                    return null;
+                }
+            }
+            byte[] proofOfDeletion = credstoreCredential.deleteCredential();
+            return proofOfDeletion;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreResultData.java b/identity/java/android/security/identity/CredstoreResultData.java
new file mode 100644
index 0000000..ef7afca
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreResultData.java
@@ -0,0 +1,162 @@
+/*
+ * 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.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested from a {@link IdentityCredential}.
+ */
+class CredstoreResultData extends ResultData {
+
+    byte[] mStaticAuthenticationData = null;
+    byte[] mAuthenticatedData = null;
+    byte[] mMessageAuthenticationCode = null;
+
+    private Map<String, Map<String, EntryData>> mData = new LinkedHashMap<>();
+
+    private static class EntryData {
+        @Status
+        int mStatus;
+        byte[] mValue;
+
+        EntryData(byte[] value, @Status int status) {
+            this.mValue = value;
+            this.mStatus = status;
+        }
+    }
+
+    CredstoreResultData() {}
+
+    @Override
+    public @NonNull byte[] getAuthenticatedData() {
+        return mAuthenticatedData;
+    }
+
+    @Override
+    public @Nullable byte[] getMessageAuthenticationCode() {
+        return mMessageAuthenticationCode;
+    }
+
+    @Override
+    public @NonNull byte[] getStaticAuthenticationData() {
+        return mStaticAuthenticationData;
+    }
+
+    @Override
+    public @NonNull Collection<String> getNamespaceNames() {
+        return Collections.unmodifiableCollection(mData.keySet());
+    }
+
+    @Override
+    public @Nullable Collection<String> getEntryNames(@NonNull String namespaceName) {
+        Map<String, EntryData> innerMap = mData.get(namespaceName);
+        if (innerMap == null) {
+            return null;
+        }
+        return Collections.unmodifiableCollection(innerMap.keySet());
+    }
+
+    @Override
+    public @Nullable Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+        Map<String, EntryData> innerMap = mData.get(namespaceName);
+        if (innerMap == null) {
+            return null;
+        }
+        LinkedList<String> result = new LinkedList<String>();
+        for (Map.Entry<String, EntryData> entry : innerMap.entrySet()) {
+            if (entry.getValue().mStatus == STATUS_OK) {
+                result.add(entry.getKey());
+            }
+        }
+        return result;
+    }
+
+    private EntryData getEntryData(@NonNull String namespaceName, @NonNull String name) {
+        Map<String, EntryData> innerMap = mData.get(namespaceName);
+        if (innerMap == null) {
+            return null;
+        }
+        return innerMap.get(name);
+    }
+
+    @Override
+    @Status
+    public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+        EntryData value = getEntryData(namespaceName, name);
+        if (value == null) {
+            return STATUS_NOT_REQUESTED;
+        }
+        return value.mStatus;
+    }
+
+    @Override
+    public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+        EntryData value = getEntryData(namespaceName, name);
+        if (value == null) {
+            return null;
+        }
+        return value.mValue;
+    }
+
+    static class Builder {
+        private CredstoreResultData mResultData;
+
+        Builder(byte[] staticAuthenticationData,
+                byte[] authenticatedData,
+                byte[] messageAuthenticationCode) {
+            this.mResultData = new CredstoreResultData();
+            this.mResultData.mStaticAuthenticationData = staticAuthenticationData;
+            this.mResultData.mAuthenticatedData = authenticatedData;
+            this.mResultData.mMessageAuthenticationCode = messageAuthenticationCode;
+        }
+
+        private Map<String, EntryData> getOrCreateInnerMap(String namespaceName) {
+            Map<String, EntryData> innerMap = mResultData.mData.get(namespaceName);
+            if (innerMap == null) {
+                innerMap = new LinkedHashMap<>();
+                mResultData.mData.put(namespaceName, innerMap);
+            }
+            return innerMap;
+        }
+
+        Builder addEntry(String namespaceName, String name, byte[] value) {
+            Map<String, EntryData> innerMap = getOrCreateInnerMap(namespaceName);
+            innerMap.put(name, new EntryData(value, STATUS_OK));
+            return this;
+        }
+
+        Builder addErrorStatus(String namespaceName, String name, @Status int status) {
+            Map<String, EntryData> innerMap = getOrCreateInnerMap(namespaceName);
+            innerMap.put(name, new EntryData(null, status));
+            return this;
+        }
+
+        CredstoreResultData build() {
+            return mResultData;
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
new file mode 100644
index 0000000..335636c
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.security.GateKeeper;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
+
+    private static final String TAG = "CredstoreWritableIdentityCredential";
+
+    private String mDocType;
+    private String mCredentialName;
+    private Context mContext;
+    private IWritableCredential mBinder;
+
+    CredstoreWritableIdentityCredential(Context context,
+            @NonNull String credentialName,
+            @NonNull String docType,
+            IWritableCredential binder) {
+        mContext = context;
+        mDocType = docType;
+        mCredentialName = credentialName;
+        mBinder = binder;
+    }
+
+    @NonNull @Override
+    public Collection<X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[] challenge) {
+        try {
+            byte[] certsBlob = mBinder.getCredentialKeyCertificateChain(challenge);
+            ByteArrayInputStream bais = new ByteArrayInputStream(certsBlob);
+
+            Collection<? extends Certificate> certs = null;
+            try {
+                CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                certs = factory.generateCertificates(bais);
+            } catch (CertificateException e) {
+                throw new RuntimeException("Error decoding certificates", e);
+            }
+
+            LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+            for (Certificate cert : certs) {
+                x509Certs.add((X509Certificate) cert);
+            }
+            return x509Certs;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @NonNull @Override
+    public byte[] personalize(@NonNull PersonalizationData personalizationData) {
+
+        Collection<AccessControlProfile> accessControlProfiles =
+                personalizationData.getAccessControlProfiles();
+
+        AccessControlProfileParcel[] acpParcels =
+                new AccessControlProfileParcel[accessControlProfiles.size()];
+        boolean usingUserAuthentication = false;
+        int n = 0;
+        for (AccessControlProfile profile : accessControlProfiles) {
+            acpParcels[n] = new AccessControlProfileParcel();
+            acpParcels[n].id = profile.getAccessControlProfileId().getId();
+            X509Certificate cert = profile.getReaderCertificate();
+            if (cert != null) {
+                try {
+                    acpParcels[n].readerCertificate = cert.getEncoded();
+                } catch (CertificateException e) {
+                    throw new RuntimeException("Error encoding reader certificate", e);
+                }
+            } else {
+                acpParcels[n].readerCertificate = new byte[0];
+            }
+            acpParcels[n].userAuthenticationRequired = profile.isUserAuthenticationRequired();
+            acpParcels[n].userAuthenticationTimeoutMillis = profile.getUserAuthenticationTimeout();
+            if (profile.isUserAuthenticationRequired()) {
+                usingUserAuthentication = true;
+            }
+            n++;
+        }
+
+        Collection<String> namespaceNames = personalizationData.getNamespaceNames();
+
+        EntryNamespaceParcel[] ensParcels  = new EntryNamespaceParcel[namespaceNames.size()];
+        n = 0;
+        for (String namespaceName : namespaceNames) {
+            PersonalizationData.NamespaceData nsd =
+                    personalizationData.getNamespaceData(namespaceName);
+
+            ensParcels[n] = new EntryNamespaceParcel();
+            ensParcels[n].namespaceName = namespaceName;
+
+            Collection<String> entryNames = nsd.getEntryNames();
+            EntryParcel[] eParcels = new EntryParcel[entryNames.size()];
+            int m = 0;
+            for (String entryName : entryNames) {
+                eParcels[m] = new EntryParcel();
+                eParcels[m].name = entryName;
+                eParcels[m].value = nsd.getEntryValue(entryName);
+                Collection<AccessControlProfileId> acpIds =
+                        nsd.getAccessControlProfileIds(entryName);
+                eParcels[m].accessControlProfileIds = new int[acpIds.size()];
+                int o = 0;
+                for (AccessControlProfileId acpId : acpIds) {
+                    eParcels[m].accessControlProfileIds[o++] = acpId.getId();
+                }
+                m++;
+            }
+            ensParcels[n].entries = eParcels;
+            n++;
+        }
+
+        // Note: The value 0 is used to convey that no user-authentication is needed for this
+        // credential. This is to allow creating credentials w/o user authentication on devices
+        // where Secure lock screen is not enabled.
+        long secureUserId = 0;
+        if (usingUserAuthentication) {
+            secureUserId = getRootSid();
+        }
+        try {
+            byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
+                    secureUserId);
+            return personalizationReceipt;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    private static long getRootSid() {
+        long rootSid = GateKeeper.getSecureUserId();
+        if (rootSid == 0) {
+            throw new IllegalStateException("Secure lock screen must be enabled"
+                    + " to create credentials requiring user authentication");
+        }
+        return rootSid;
+    }
+
+
+}
diff --git a/identity/java/android/security/identity/DocTypeNotSupportedException.java b/identity/java/android/security/identity/DocTypeNotSupportedException.java
new file mode 100644
index 0000000..754e44a
--- /dev/null
+++ b/identity/java/android/security/identity/DocTypeNotSupportedException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to create a credential with an unsupported document type.
+ */
+public class DocTypeNotSupportedException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link DocTypeNotSupportedException} exception.
+     *
+     * @param message the detail message.
+     */
+    public DocTypeNotSupportedException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link DocTypeNotSupportedException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public DocTypeNotSupportedException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/EphemeralPublicKeyNotFoundException.java b/identity/java/android/security/identity/EphemeralPublicKeyNotFoundException.java
new file mode 100644
index 0000000..265f271
--- /dev/null
+++ b/identity/java/android/security/identity/EphemeralPublicKeyNotFoundException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if the ephemeral public key was not found in the session transcript
+ * passed to {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])}.
+ */
+public class EphemeralPublicKeyNotFoundException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link EphemeralPublicKeyNotFoundException} exception.
+     *
+     * @param message the detail message.
+     */
+    public EphemeralPublicKeyNotFoundException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link EphemeralPublicKeyNotFoundException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public EphemeralPublicKeyNotFoundException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
new file mode 100644
index 0000000..bd43919
--- /dev/null
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Class used to read data from a previously provisioned credential.
+ *
+ * Use {@link IdentityCredentialStore#getCredentialByName(String, int)} to get a
+ * {@link IdentityCredential} instance.
+ */
+public abstract class IdentityCredential {
+    /**
+     * @hide
+     */
+    protected IdentityCredential() {}
+
+    /**
+     * Create an ephemeral key pair to use to establish a secure channel with a reader.
+     *
+     * <p>Most applications will use only the public key, and only to send it to the reader,
+     * allowing the private key to be used internally for {@link #encryptMessageToReader(byte[])}
+     * and {@link #decryptMessageFromReader(byte[])}. The private key is also provided for
+     * applications that wish to use a cipher suite that is not supported by
+     * {@link IdentityCredentialStore}.
+     *
+     * @return ephemeral key pair to use to establish a secure channel with a reader.
+     */
+    public @NonNull abstract KeyPair createEphemeralKeyPair();
+
+    /**
+     * Set the ephemeral public key provided by the reader. This must be called before
+     * {@link #encryptMessageToReader} or {@link #decryptMessageFromReader} can be called.
+     *
+     * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
+     *                                 establish a secure session.
+     * @throws InvalidKeyException if the given key is invalid.
+     */
+    public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException;
+
+    /**
+     * Encrypt a message for transmission to the reader.
+     *
+     * @param messagePlaintext unencrypted message to encrypt.
+     * @return encrypted message.
+     */
+    public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
+
+    /**
+     * Decrypt a message received from the reader.
+     *
+     * @param messageCiphertext encrypted message to decrypt.
+     * @return decrypted message.
+     * @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+     */
+    public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
+            throws MessageDecryptionException;
+
+    /**
+     * Gets the X.509 certificate chain for the CredentialKey which identifies this
+     * credential to the issuing authority. This is the same certificate chain that
+     * was returned by {@link WritableIdentityCredential#getCredentialKeyCertificateChain(byte[])}
+     * when the credential was first created and its Android Keystore extension will
+     * contain the <code>challenge</code> data set at that time. See the documentation
+     * for that method for important information about this certificate chain.
+     *
+     * @return the certificate chain for this credential's CredentialKey.
+     */
+    public @NonNull abstract Collection<X509Certificate> getCredentialKeyCertificateChain();
+
+    /**
+     * Sets whether to allow using an authentication key which use count has been exceeded if no
+     * other key is available. This must be called prior to calling
+     * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object.
+     *
+     * By default this is set to true.
+     *
+     * @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
+     *                                has been exceeded if no other key is available.
+     */
+    public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    public abstract long getCredstoreOperationHandle();
+
+    /**
+     * Retrieve data entries and associated data from this {@code IdentityCredential}.
+     *
+     * <p>If an access control check fails for one of the requested entries or if the entry
+     * doesn't exist, the entry is simply not returned. The application can detect this
+     * by using the {@link ResultData#getStatus(String, String)} method on each of the requested
+     * entries.
+     *
+     * <p>It is the responsibility of the calling application to know if authentication is needed
+     * and use e.g. {@link android.hardware.biometrics.BiometricPrompt}) to make the user
+     * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
+     * references this object. If needed, this must be done before calling
+     * {@link #getEntries(byte[], Map, byte[], byte[])}.
+     *
+     * <p>If this method returns successfully (i.e. without throwing an exception), it must not be
+     * called again on this instance.
+     *
+     * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
+     * from the verifier. The content can be defined in the way appropriate for the credential, byt
+     * there are three requirements that must be met to work with this API:
+     * <ul>
+     * <li>The content must be a CBOR-encoded structure.</li>
+     * <li>The CBOR structure must be a map.</li>
+     * <li>The map must contain a tstr key "nameSpaces" whose value contains a map, as described in
+     *     the example below.</li>
+     * </ul>
+     *
+     * <p>Here's an example of CBOR which conforms to this requirement:
+     * <pre>
+     *   ItemsRequest = {
+     *     ? "docType" : DocType,
+     *     "nameSpaces" : NameSpaces,
+     *     ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
+     *   }
+     *
+     *   NameSpaces = {
+     *     + NameSpace => DataElements    ; Requested data elements for each NameSpace
+     *   }
+     *
+     *   NameSpace = tstr
+     *
+     *   DataElements = {
+     *     + DataElement => IntentToRetain
+     *   }
+     *
+     *   DataElement = tstr
+     *   IntentToRetain = bool
+     * </pre>
+     *
+     * <p>If the {@code sessionTranscript} parameter is not {@code null}, it must contain CBOR
+     * data conforming to the following CDDL schema:
+     *
+     * <pre>
+     *   SessionTranscript = [
+     *     DeviceEngagementBytes,
+     *     EReaderKeyBytes
+     *   ]
+     *
+     *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
+     *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+     * </pre>
+     *
+     * <p>If the SessionTranscript is not empty, a COSE_Key structure for the public part
+     * of the key-pair previously generated by {@link #createEphemeralKeyPair()} must appear
+     * somewhere in {@code DeviceEngagement} and the X and Y coordinates must both be present
+     * in uncompressed form.
+     *
+     * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a COSE_Sign1
+     * structure as defined in RFC 8152. For the payload nil shall be used and the
+     * detached payload is the ReaderAuthentication CBOR described below.
+     * <pre>
+     *     ReaderAuthentication = [
+     *       "ReaderAuthentication",
+     *       SessionTranscript,
+     *       ItemsRequestBytes
+     *     ]
+     *
+     *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)   ; Bytes of ItemsRequest
+     * </pre>
+     *
+     * <p>The public key corresponding to the key used to made signature, can be
+     * found in the {@code x5chain} unprotected header element of the COSE_Sign1
+     * structure (as as described in 'draft-ietf-cose-x509-04'). There will be at
+     * least one certificate in said element and there may be more (and if so,
+     * each certificate must be signed by its successor).
+     *
+     * <p>Data elements protected by reader authentication is returned if, and only if, they are
+     * mentioned in {@code requestMessage}, {@code requestMessage} is signed by the top-most
+     * certificate in {@code readerCertificateChain}, and the data element is configured
+     * with an {@link AccessControlProfile} with a {@link X509Certificate} in
+     * {@code readerCertificateChain}.
+     *
+     * <p>Note that only items referenced in {@code entriesToRequest} are returned - the
+     * {@code requestMessage} parameter is only used to for enforcing reader authentication.
+     *
+     * @param requestMessage         If not {@code null}, must contain CBOR data conforming to
+     *                               the schema mentioned above.
+     * @param entriesToRequest       The entries to request, organized as a map of namespace
+     *                               names with each value being a collection of data elements
+     *                               in the given namespace.
+     * @param readerSignature        COSE_Sign1 structure as described above or {@code null}
+     *                               if reader authentication is not being used.
+     * @return A {@link ResultData} object containing entry data organized by namespace and a
+     *         cryptographically authenticated representation of the same data.
+     * @throws SessionTranscriptMismatchException     Thrown when trying use multiple different
+     *                                                session transcripts in the same presentation
+     *                                                session.
+     * @throws NoAuthenticationKeyAvailableException  if authentication keys were never
+     *                                                provisioned, the method
+     *                                             {@link #setAvailableAuthenticationKeys(int, int)}
+     *                                                was called with {@code keyCount} set to 0,
+     *                                                the method
+     *                                                {@link #setAllowUsingExhaustedKeys(boolean)}
+     *                                                was called with {@code false} and all
+     *                                                available authentication keys have been
+     *                                                exhausted.
+     * @throws InvalidReaderSignatureException        if the reader signature is invalid, or it
+     *                                                doesn't contain a certificate chain, or if
+     *                                                the signature failed to validate.
+     * @throws InvalidRequestMessageException         if the requestMessage is malformed.
+     * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
+     *                                                the session transcript.
+     */
+    public abstract @NonNull ResultData getEntries(
+            @Nullable byte[] requestMessage,
+            @NonNull Map<String, Collection<String>> entriesToRequest,
+            @Nullable byte[] sessionTranscript,
+            @Nullable byte[] readerSignature)
+            throws SessionTranscriptMismatchException, NoAuthenticationKeyAvailableException,
+            InvalidReaderSignatureException, EphemeralPublicKeyNotFoundException,
+            InvalidRequestMessageException;
+
+    /**
+     * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain,
+     * and the number of times each should be used.
+     *
+     * <p>{@code IdentityCredential}s will select the least-used dynamic authentication key each
+     * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. {@code IdentityCredential}s
+     * for which this method has not been called behave as though it had been called wit
+     * {@code keyCount} 0 and {@code maxUsesPerKey} 1.
+     *
+     * @param keyCount      The number of active, certified dynamic authentication keys the
+     *                      {@code IdentityCredential} will try to keep available. This value
+     *                      must be non-negative.
+     * @param maxUsesPerKey The maximum number of times each of the keys will be used before it's
+     *                      eligible for replacement. This value must be greater than zero.
+     */
+    public abstract void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+
+    /**
+     * Gets a collection of dynamic authentication keys that need certification.
+     *
+     * <p>When there aren't enough certified dynamic authentication keys, either because the key
+     * count has been increased or because one or more keys have reached their usage count, this
+     * method will generate replacement keys and certificates and return them for issuer
+     * certification. The issuer certificates and associated static authentication data must then
+     * be provided back to the {@code IdentityCredential} using
+     * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}.
+     *
+     * <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
+     * can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
+     *
+     * @return A collection of X.509 certificates for dynamic authentication keys that need issuer
+     * certification.
+     */
+    public @NonNull abstract Collection<X509Certificate> getAuthKeysNeedingCertification();
+
+    /**
+     * Store authentication data associated with a dynamic authentication key.
+     *
+     * This should only be called for an authenticated key returned by
+     * {@link #getAuthKeysNeedingCertification()}.
+     *
+     * @param authenticationKey The dynamic authentication key for which certification and
+     *                          associated static
+     *                          authentication data is being provided.
+     * @param staticAuthData    Static authentication data provided by the issuer that validates
+     *                          the authenticity
+     *                          and integrity of the credential data fields.
+     * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+     */
+    public abstract void storeStaticAuthenticationData(
+            @NonNull X509Certificate authenticationKey,
+            @NonNull byte[] staticAuthData)
+            throws UnknownAuthenticationKeyException;
+
+    /**
+     * Get the number of times the dynamic authentication keys have been used.
+     *
+     * @return int array of dynamic authentication key usage counts.
+     */
+    public @NonNull abstract int[] getAuthenticationDataUsageCount();
+}
diff --git a/identity/java/android/security/identity/IdentityCredentialException.java b/identity/java/android/security/identity/IdentityCredentialException.java
new file mode 100644
index 0000000..c811380
--- /dev/null
+++ b/identity/java/android/security/identity/IdentityCredentialException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Base class for all Identity Credential exceptions.
+ */
+public class IdentityCredentialException extends Exception {
+    /**
+     * Constructs a new {@link IdentityCredentialException} exception.
+     *
+     * @param message the detail message.
+     */
+    public IdentityCredentialException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link IdentityCredentialException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public IdentityCredentialException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
new file mode 100644
index 0000000..a1dfc77
--- /dev/null
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An interface to a secure store for user identity documents.
+ *
+ * <p>This interface is deliberately fairly general and abstract.  To the extent possible,
+ * specification of the message formats and semantics of communication with credential
+ * verification devices and issuing authorities (IAs) is out of scope. It provides the
+ * interface with secure storage but a credential-specific Android application will be
+ * required to implement the presentation and verification protocols and processes
+ * appropriate for the specific credential type.
+ *
+ * <p>Multiple credentials can be created.  Each credential comprises:</p>
+ * <ul>
+ * <li>A document type, which is a string.</li>
+ *
+ * <li>A set of namespaces, which serve to disambiguate value names. It is recommended
+ * that namespaces be structured as reverse domain names so that IANA effectively serves
+ * as the namespace registrar.</li>
+ *
+ * <li>For each namespace, a set of name/value pairs, each with an associated set of
+ * access control profile IDs.  Names are strings and values are typed and can be any
+ * value supported by <a href="http://cbor.io/">CBOR</a>.</li>
+ *
+ * <li>A set of access control profiles, each with a profile ID and a specification
+ * of the conditions which satisfy the profile's requirements.</li>
+ *
+ * <li>An asymmetric key pair which is used to authenticate the credential to the Issuing
+ * Authority, called the <em>CredentialKey</em>.</li>
+ *
+ * <li>A set of zero or more named reader authentication public keys, which are used to
+ * authenticate an authorized reader to the credential.</li>
+ *
+ * <li>A set of named signing keys, which are used to sign collections of values and session
+ * transcripts.</li>
+ * </ul>
+ *
+ * <p>Implementing support for user identity documents in secure storage requires dedicated
+ * hardware-backed support and may not always be available.
+ *
+ * <p>Two different credential stores exist - the <em>default</em> store and the
+ * <em>direct access</em> store. Most often credentials will be accessed through the default
+ * store but that requires that the Android device be powered up and fully functional.
+ * It is desirable to allow identity credential usage when the Android device's battery is too
+ * low to boot the Android operating system, so direct access to the secure hardware via NFC
+ * may allow data retrieval, if the secure hardware chooses to implement it.
+ *
+ * <p>Credentials provisioned to the direct access store should <strong>always</strong> use reader
+ * authentication to protect data elements. The reason for this is user authentication or user
+ * approval of data release is not possible when the device is off.
+ */
+public abstract class IdentityCredentialStore {
+    IdentityCredentialStore() {}
+
+    /**
+     * Specifies that the cipher suite that will be used to secure communications between the reader
+     * is:
+     *
+     * <ul>
+     * <li>ECDHE with HKDF-SHA-256 for key agreement.</li>
+     * <li>AES-256 with GCM block mode for authenticated encryption (nonces are incremented by one
+     * for every message).</li>
+     * <li>ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
+     * man-in-the-middle attacks), signing keys are not ephemeral. See {@link IdentityCredential}
+     * for details on reader and prover signing keys.</li>
+     * </ul>
+     *
+     * <p>
+     * At present this is the only supported cipher suite.
+     */
+    public static final int CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1;
+
+    /**
+     * Gets the default {@link IdentityCredentialStore}.
+     *
+     * @param context the application context.
+     * @return the {@link IdentityCredentialStore} or {@code null} if the device doesn't
+     *     have hardware-backed support for secure storage of user identity documents.
+     */
+    public static @Nullable IdentityCredentialStore getInstance(@NonNull Context context) {
+        return CredstoreIdentityCredentialStore.getInstance(context);
+    }
+
+    /**
+     * Gets the {@link IdentityCredentialStore} for direct access.
+     *
+     * <p>Direct access requires specialized NFC hardware and may not be supported on all
+     * devices even if default store is available. Credentials provisioned to the direct
+     * access store should <strong>always</strong> use reader authentication to protect
+     * data elements.
+     *
+     * @param context the application context.
+     * @return the {@link IdentityCredentialStore} or {@code null} if direct access is not
+     *     supported on this device.
+     */
+    public static @Nullable IdentityCredentialStore getDirectAccessInstance(@NonNull
+            Context context) {
+        return CredstoreIdentityCredentialStore.getDirectAccessInstance(context);
+    }
+
+    /**
+     * Gets a list of supported document types.
+     *
+     * <p>Only the direct-access store may restrict the kind of document types that can be used for
+     * credentials. The default store always supports any document type.
+     *
+     * @return The supported document types or the empty array if any document type is supported.
+     */
+    public abstract @NonNull String[] getSupportedDocTypes();
+
+    /**
+     * Creates a new credential.
+     *
+     * @param credentialName The name used to identify the credential.
+     * @param docType        The document type for the credential.
+     * @return A @{link WritableIdentityCredential} that can be used to create a new credential.
+     * @throws AlreadyPersonalizedException if a credential with the given name already exists.
+     * @throws DocTypeNotSupportedException if the given document type isn't supported by the store.
+     */
+    public abstract @NonNull WritableIdentityCredential createCredential(
+            @NonNull String credentialName, @NonNull String docType)
+            throws AlreadyPersonalizedException, DocTypeNotSupportedException;
+
+    /**
+     * Retrieve a named credential.
+     *
+     * @param credentialName the name of the credential to retrieve.
+     * @param cipherSuite    the cipher suite to use for communicating with the verifier.
+     * @return The named credential, or null if not found.
+     */
+    public abstract @Nullable IdentityCredential getCredentialByName(@NonNull String credentialName,
+            @Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException;
+
+    /**
+     * Delete a named credential.
+     *
+     * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+     * with payload set to {@code ProofOfDeletion} as defined below:
+     *
+     * <pre>
+     *     ProofOfDeletion = [
+     *          "ProofOfDeletion",            ; tstr
+     *          tstr,                         ; DocType
+     *          bool                          ; true if this is a test credential, should
+     *                                        ; always be false.
+     *      ]
+     * </pre>
+     *
+     * @param credentialName the name of the credential to delete.
+     * @return {@code null} if the credential was not found, the COSE_Sign1 data structure above
+     *     if the credential was found and deleted.
+     */
+    public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
+
+    /** @hide */
+    @IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Ciphersuite {
+    }
+
+}
diff --git a/identity/java/android/security/identity/InvalidReaderSignatureException.java b/identity/java/android/security/identity/InvalidReaderSignatureException.java
new file mode 100644
index 0000000..3f70270
--- /dev/null
+++ b/identity/java/android/security/identity/InvalidReaderSignatureException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if the reader signature is invalid, or it doesn't contain a certificate chain, or if the
+ * signature failed to validate.
+ */
+public class InvalidReaderSignatureException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link InvalidReaderSignatureException} exception.
+     *
+     * @param message the detail message.
+     */
+    public InvalidReaderSignatureException(@NonNull String message) {
+        super(message);
+    }
+
+
+    /**
+     * Constructs a new {@link InvalidReaderSignatureException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public InvalidReaderSignatureException(@NonNull String message,
+            @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/InvalidRequestMessageException.java b/identity/java/android/security/identity/InvalidRequestMessageException.java
new file mode 100644
index 0000000..b0c073c
--- /dev/null
+++ b/identity/java/android/security/identity/InvalidRequestMessageException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if message with the request doesn't satisfy the requirements documented in
+ * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])}.
+ */
+public class InvalidRequestMessageException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link InvalidRequestMessageException} exception.
+     *
+     * @param message the detail message.
+     */
+    public InvalidRequestMessageException(@NonNull String message) {
+        super(message);
+    }
+
+
+    /**
+     * Constructs a new {@link InvalidRequestMessageException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public InvalidRequestMessageException(@NonNull String message,
+            @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/MessageDecryptionException.java b/identity/java/android/security/identity/MessageDecryptionException.java
new file mode 100644
index 0000000..7a6169e
--- /dev/null
+++ b/identity/java/android/security/identity/MessageDecryptionException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown when failing to decrypt a message from the reader device.
+ */
+public class MessageDecryptionException extends IdentityCredentialException {
+
+    /**
+     * Constructs a new {@link MessageDecryptionException} exception.
+     *
+     * @param message the detail message.
+     */
+    public MessageDecryptionException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link MessageDecryptionException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public MessageDecryptionException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/NoAuthenticationKeyAvailableException.java b/identity/java/android/security/identity/NoAuthenticationKeyAvailableException.java
new file mode 100644
index 0000000..7f40403
--- /dev/null
+++ b/identity/java/android/security/identity/NoAuthenticationKeyAvailableException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if no dynamic authentication keys are available.
+ */
+public class NoAuthenticationKeyAvailableException extends IdentityCredentialException {
+
+    /**
+     * Constructs a new {@link NoAuthenticationKeyAvailableException} exception.
+     *
+     * @param message the detail message.
+     */
+    public NoAuthenticationKeyAvailableException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link NoAuthenticationKeyAvailableException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public NoAuthenticationKeyAvailableException(@NonNull String message,
+            @NonNull Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java
new file mode 100644
index 0000000..44370a1
--- /dev/null
+++ b/identity/java/android/security/identity/PersonalizationData.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+/**
+ * An object that holds personalization data.
+ *
+ * This data includes access control profiles and a set of data entries and values, grouped by
+ * namespace.
+ *
+ * This is used to provision data into a {@link WritableIdentityCredential}.
+ *
+ * @see WritableIdentityCredential#personalize
+ */
+public class PersonalizationData {
+
+    private PersonalizationData() {
+    }
+
+    private LinkedList<AccessControlProfile> mProfiles = new LinkedList<>();
+
+    private LinkedHashMap<String, NamespaceData> mNamespaces = new LinkedHashMap<>();
+
+    Collection<AccessControlProfile> getAccessControlProfiles() {
+        return Collections.unmodifiableCollection(mProfiles);
+    }
+
+    Collection<String> getNamespaceNames() {
+        return Collections.unmodifiableCollection(mNamespaces.keySet());
+    }
+
+    NamespaceData getNamespaceData(String namespace) {
+        return mNamespaces.get(namespace);
+    }
+
+    static class NamespaceData {
+
+        private String mNamespace;
+        private LinkedHashMap<String, EntryData> mEntries = new LinkedHashMap<>();
+
+        private NamespaceData(String namespace) {
+            this.mNamespace = namespace;
+        }
+
+        String getNamespaceName() {
+            return mNamespace;
+        }
+
+        Collection<String> getEntryNames() {
+            return Collections.unmodifiableCollection(mEntries.keySet());
+        }
+
+        Collection<AccessControlProfileId> getAccessControlProfileIds(String name) {
+            EntryData value = mEntries.get(name);
+            if (value != null) {
+                return value.mAccessControlProfileIds;
+            }
+            return null;
+        }
+
+        byte[] getEntryValue(String name) {
+            EntryData value = mEntries.get(name);
+            if (value != null) {
+                return value.mValue;
+            }
+            return null;
+        }
+    }
+
+    private static class EntryData {
+        byte[] mValue;
+        Collection<AccessControlProfileId> mAccessControlProfileIds;
+
+        EntryData(byte[] value, Collection<AccessControlProfileId> accessControlProfileIds) {
+            this.mValue = value;
+            this.mAccessControlProfileIds = accessControlProfileIds;
+        }
+    }
+
+    /**
+     * A builder for {@link PersonalizationData}.
+     */
+    public static final class Builder {
+        private PersonalizationData mData;
+
+        /**
+         * Creates a new builder for a given namespace.
+         */
+        public Builder() {
+            this.mData = new PersonalizationData();
+        }
+
+        /**
+         * Adds a new entry to the builder.
+         *
+         * @param namespace               The namespace to use, e.g. {@code org.iso.18013-5.2019}.
+         * @param name                    The name of the entry, e.g. {@code height}.
+         * @param accessControlProfileIds A set of access control profiles to use.
+         * @param value                   The value to add, in CBOR encoding.
+         * @return The builder.
+         */
+        public @NonNull Builder setEntry(@NonNull String namespace, @NonNull String name,
+                @NonNull Collection<AccessControlProfileId> accessControlProfileIds,
+                @NonNull byte[] value) {
+            NamespaceData namespaceData = mData.mNamespaces.get(namespace);
+            if (namespaceData == null) {
+                namespaceData = new NamespaceData(namespace);
+                mData.mNamespaces.put(namespace, namespaceData);
+            }
+            // TODO: validate/verify that value is proper CBOR.
+            namespaceData.mEntries.put(name, new EntryData(value, accessControlProfileIds));
+            return this;
+        }
+
+        /**
+         * Adds a new access control profile to the builder.
+         *
+         * @param profile The access control profile.
+         * @return The builder.
+         */
+        public @NonNull Builder addAccessControlProfile(@NonNull AccessControlProfile profile) {
+            mData.mProfiles.add(profile);
+            return this;
+        }
+
+        /**
+         * Creates a new {@link PersonalizationData} with all the entries added to the builder.
+         *
+         * @return A new {@link PersonalizationData} instance.
+         */
+        public @NonNull PersonalizationData build() {
+            return mData;
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
new file mode 100644
index 0000000..0982c8a
--- /dev/null
+++ b/identity/java/android/security/identity/ResultData.java
@@ -0,0 +1,224 @@
+/*
+ * 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.security.identity;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.util.Collection;
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested from a {@link IdentityCredential}.
+ */
+public abstract class ResultData {
+
+    /** Value was successfully retrieved. */
+    public static final int STATUS_OK = 0;
+
+    /** Requested entry does not exist. */
+    public static final int STATUS_NO_SUCH_ENTRY = 1;
+
+    /** Requested entry was not requested. */
+    public static final int STATUS_NOT_REQUESTED = 2;
+
+    /** Requested entry wasn't in the request message. */
+    public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+    /** The requested entry was not retrieved because user authentication wasn't performed. */
+    public static final int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+    /** The requested entry was not retrieved because reader authentication wasn't performed. */
+    public static final int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+    /**
+     * The requested entry was not retrieved because it was configured without any access
+     * control profile.
+     */
+    public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+    /**
+     * @hide
+     */
+    protected ResultData() {}
+
+    /**
+     * Returns a CBOR structure containing the retrieved data.
+     *
+     * <p>This structure - along with the session transcript - may be cryptographically
+     * authenticated to prove to the reader that the data is from a trusted credential and
+     * {@link #getMessageAuthenticationCode()} can be used to get a MAC.
+     *
+     * <p>The CBOR structure which is cryptographically authenticated is the
+     * {@code DeviceAuthentication} structure according to the following
+     * <a href="https://tools.ietf.org/html/draft-ietf-cbor-cddl-06">CDDL</a> schema:
+     *
+     * <pre>
+     *   DeviceAuthentication = [
+     *     "DeviceAuthentication",
+     *     SessionTranscript,
+     *     DocType,
+     *     DeviceNameSpacesBytes
+     *   ]
+     *
+     *   DocType = tstr
+     *
+     *   SessionTranscript = [
+     *     DeviceEngagementBytes,
+     *     EReaderKeyBytes
+     *   ]
+     *
+     *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
+     *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+     *
+     *   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+     * </pre>
+     *
+     * where
+     *
+     * <pre>
+     *   DeviceNameSpaces = {
+     *     * NameSpace => DeviceSignedItems
+     *   }
+     *
+     *   DeviceSignedItems = {
+     *     + DataItemName => DataItemValue
+     *   }
+     *
+     *   NameSpace = tstr
+     *   DataItemName = tstr
+     *   DataItemValue = any
+     * </pre>
+     *
+     * <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
+     * as defined above.
+     *
+     * @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
+     */
+    public abstract @NonNull byte[] getAuthenticatedData();
+
+    /**
+     * Returns a message authentication code over the data returned by
+     * {@link #getAuthenticatedData}, to prove to the reader that the data is from a trusted
+     * credential.
+     *
+     * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
+     * produced by using the key agreement and key derivation function from the ciphersuite
+     * with the authentication private key and the reader ephemeral public key to compute a
+     * shared message authentication code (MAC) key, then using the MAC function from the
+     * ciphersuite to compute a MAC of the authenticated data.
+     *
+     * <p>If the {@code sessionTranscript} parameter passed to
+     * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])} was {@code null}
+     * or the reader ephmeral public key was never set using
+     * {@link IdentityCredential#setReaderEphemeralPublicKey(PublicKey)}, no message
+     * authencation code will be produced and this method will return {@code null}.
+     *
+     * @return A COSE_Mac0 structure with the message authentication code as described above
+     *         or {@code null} if the conditions specified above are not met.
+     */
+    public abstract @Nullable byte[] getMessageAuthenticationCode();
+
+    /**
+     * Returns the static authentication data associated with the dynamic authentication
+     * key used to sign or MAC the data returned by {@link #getAuthenticatedData()}.
+     *
+     * @return The static authentication data associated with dynamic authentication key used to
+     * MAC the data.
+     */
+    public abstract @NonNull byte[] getStaticAuthenticationData();
+
+    /**
+     * Gets the names of namespaces with retrieved entries.
+     *
+     * @return collection of name of namespaces containing retrieved entries. May be empty if no
+     *     data was retrieved.
+     */
+    public abstract @NonNull Collection<String> getNamespaceNames();
+
+    /**
+     * Get the names of all entries.
+     *
+     * This includes the name of entries that wasn't successfully retrieved.
+     *
+     * @param namespaceName the namespace name to get entries for.
+     * @return A collection of names or {@code null} if there are no entries for the given
+     *     namespace.
+     */
+    public abstract @Nullable Collection<String> getEntryNames(@NonNull String namespaceName);
+
+    /**
+     * Get the names of all entries that was successfully retrieved.
+     *
+     * This only return entries for which {@link #getStatus(String, String)} will return
+     * {@link #STATUS_OK}.
+     *
+     * @param namespaceName the namespace name to get entries for.
+     * @return A collection of names or {@code null} if there are no entries for the given
+     *     namespace.
+     */
+    public abstract @Nullable Collection<String> getRetrievedEntryNames(
+            @NonNull String namespaceName);
+
+    /**
+     * Gets the status of an entry.
+     *
+     * This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY}
+     * if the given entry wasn't retrieved, {@link #STATUS_NOT_REQUESTED} if it wasn't requested,
+     * {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if the request message was set but the entry wasn't
+     * present in the request message,
+     * {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value
+     * wasn't retrieved because the necessary user authentication wasn't performed,
+     * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain
+     * didn't match the set of certificates the entry was provisioned with, or
+     * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any
+     * access control profiles.
+     *
+     * @param namespaceName the namespace name of the entry.
+     * @param name the name of the entry to get the value for.
+     * @return the status indicating whether the value was retrieved and if not, why.
+     */
+    @Status
+    public abstract int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+    /**
+     * Gets the raw CBOR data for the value of an entry.
+     *
+     * This should only be called on an entry for which the {@link #getStatus(String, String)}
+     * method returns {@link #STATUS_OK}.
+     *
+     * @param namespaceName the namespace name of the entry.
+     * @param name the name of the entry to get the value for.
+     * @return the raw CBOR data or {@code null} if no entry with the given name exists.
+     */
+    public abstract @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
+
+    /**
+     * The type of the entry status.
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED, STATUS_NOT_IN_REQUEST_MESSAGE,
+                        STATUS_USER_AUTHENTICATION_FAILED, STATUS_READER_AUTHENTICATION_FAILED,
+                        STATUS_NO_ACCESS_CONTROL_PROFILES})
+    public @interface Status {
+    }
+}
diff --git a/identity/java/android/security/identity/SessionTranscriptMismatchException.java b/identity/java/android/security/identity/SessionTranscriptMismatchException.java
new file mode 100644
index 0000000..8c24060
--- /dev/null
+++ b/identity/java/android/security/identity/SessionTranscriptMismatchException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown when trying use multiple different session transcripts in the same presentation session.
+ */
+public class SessionTranscriptMismatchException extends IdentityCredentialException {
+
+    /**
+     * Constructs a new {@link SessionTranscriptMismatchException} exception.
+     *
+     * @param message the detail message.
+     */
+    public SessionTranscriptMismatchException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link SessionTranscriptMismatchException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public SessionTranscriptMismatchException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/UnknownAuthenticationKeyException.java b/identity/java/android/security/identity/UnknownAuthenticationKeyException.java
new file mode 100644
index 0000000..f454b2c
--- /dev/null
+++ b/identity/java/android/security/identity/UnknownAuthenticationKeyException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to certify an unknown dynamic authentication key.
+ */
+public class UnknownAuthenticationKeyException extends IdentityCredentialException {
+    /**
+     * Constructs a new {@link UnknownAuthenticationKeyException} exception.
+     *
+     * @param message the detail message.
+     */
+    public UnknownAuthenticationKeyException(@NonNull String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@link UnknownAuthenticationKeyException} exception.
+     *
+     * @param message the detail message.
+     * @param cause   the cause.
+     */
+    public UnknownAuthenticationKeyException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/identity/java/android/security/identity/Util.java b/identity/java/android/security/identity/Util.java
new file mode 100644
index 0000000..6eefeb8
--- /dev/null
+++ b/identity/java/android/security/identity/Util.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPoint;
+import java.util.Collection;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+class Util {
+    private static final String TAG = "Util";
+
+    static int[] integerCollectionToArray(Collection<Integer> collection) {
+        int[] result = new int[collection.size()];
+        int n = 0;
+        for (int item : collection) {
+            result[n++] = item;
+        }
+        return result;
+    }
+
+    static byte[] stripLeadingZeroes(byte[] value) {
+        int n = 0;
+        while (n < value.length && value[n] == 0) {
+            n++;
+        }
+        int newLen = value.length - n;
+        byte[] ret = new byte[newLen];
+        int m = 0;
+        while (n < value.length) {
+            ret[m++] = value[n++];
+        }
+        return ret;
+    }
+
+    static byte[] publicKeyEncodeUncompressedForm(PublicKey publicKey) {
+        ECPoint w = ((ECPublicKey) publicKey).getW();
+        // X and Y are always positive so for interop we remove any leading zeroes
+        // inserted by the BigInteger encoder.
+        byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
+        byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            baos.write(0x04);
+            baos.write(x);
+            baos.write(y);
+            return baos.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeException("Unexpected IOException", e);
+        }
+    }
+
+    /**
+     * Computes an HKDF.
+     *
+     * This is based on https://github.com/google/tink/blob/master/java/src/main/java/com/google
+     * /crypto/tink/subtle/Hkdf.java
+     * which is also Copyright (c) Google and also licensed under the Apache 2 license.
+     *
+     * @param macAlgorithm the MAC algorithm used for computing the Hkdf. I.e., "HMACSHA1" or
+     *                     "HMACSHA256".
+     * @param ikm          the input keying material.
+     * @param salt         optional salt. A possibly non-secret random value. If no salt is
+     *                     provided (i.e. if
+     *                     salt has length 0) then an array of 0s of the same size as the hash
+     *                     digest is used as salt.
+     * @param info         optional context and application specific information.
+     * @param size         The length of the generated pseudorandom string in bytes. The maximal
+     *                     size is
+     *                     255.DigestSize, where DigestSize is the size of the underlying HMAC.
+     * @return size pseudorandom bytes.
+     */
+    static byte[] computeHkdf(
+            String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size) {
+        Mac mac = null;
+        try {
+            mac = Mac.getInstance(macAlgorithm);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("No such algorithm: " + macAlgorithm, e);
+        }
+        if (size > 255 * mac.getMacLength()) {
+            throw new RuntimeException("size too large");
+        }
+        try {
+            if (salt == null || salt.length == 0) {
+                // According to RFC 5869, Section 2.2 the salt is optional. If no salt is provided
+                // then HKDF uses a salt that is an array of zeros of the same length as the hash
+                // digest.
+                mac.init(new SecretKeySpec(new byte[mac.getMacLength()], macAlgorithm));
+            } else {
+                mac.init(new SecretKeySpec(salt, macAlgorithm));
+            }
+            byte[] prk = mac.doFinal(ikm);
+            byte[] result = new byte[size];
+            int ctr = 1;
+            int pos = 0;
+            mac.init(new SecretKeySpec(prk, macAlgorithm));
+            byte[] digest = new byte[0];
+            while (true) {
+                mac.update(digest);
+                mac.update(info);
+                mac.update((byte) ctr);
+                digest = mac.doFinal();
+                if (pos + digest.length < size) {
+                    System.arraycopy(digest, 0, result, pos, digest.length);
+                    pos += digest.length;
+                    ctr++;
+                } else {
+                    System.arraycopy(digest, 0, result, pos, size - pos);
+                    break;
+                }
+            }
+            return result;
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException("Error MACing", e);
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
new file mode 100644
index 0000000..e2a389b
--- /dev/null
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+/**
+ * Class used to personalize a new identity credential.
+ *
+ * <p>Credentials cannot be updated or modified after creation; any changes require deletion and
+ * re-creation.
+ *
+ * Use {@link IdentityCredentialStore#createCredential(String, String)} to create a new credential.
+ */
+public abstract class WritableIdentityCredential {
+    /**
+     * @hide
+     */
+    protected WritableIdentityCredential() {}
+
+    /**
+     * Generates and returns an X.509 certificate chain for the CredentialKey which identifies this
+     * credential to the issuing authority. The certificate contains an
+     * <a href="https://source.android.com/security/keystore/attestation">Android Keystore</a>
+     * attestation extension which describes the key and the security hardware in which it lives.
+     *
+     * <p>Additionally, the attestation extension will contain the tag TODO_IC_KEY which indicates
+     * it is an Identity Credential key (which can only sign/MAC very specific messages) and not
+     * an Android Keystore key (which can be used to sign/MAC anything).
+     *
+     * <p>The issuer <b>MUST</b> carefully examine this certificate chain including (but not
+     * limited to) checking that the root certificate is well-known, the tag TODO_IC_KEY is
+     * present, the passed in challenge is present, the device has verified boot enabled, that each
+     * certificate in the chain is signed by its successor, that none of the certificates have been
+     * revoked and so on.
+     *
+     * <p>It is not strictly necessary to use this method to provision a credential if the issuing
+     * authority doesn't care about the nature of the security hardware. If called, however, this
+     * method must be called before {@link #personalize(PersonalizationData)}.
+     *
+     * @param challenge is a byte array whose contents should be unique, fresh and provided by
+     *                  the issuing authority. The value provided is embedded in the attestation
+     *                  extension and enables the issuing authority to verify that the attestation
+     *                  certificate is fresh.
+     * @return the X.509 certificate for this credential's CredentialKey.
+     */
+    public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain(
+            @NonNull byte[] challenge);
+
+    /**
+     * Stores all of the data in the credential, with the specified access control profiles.
+     *
+     * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey with payload
+     * set to {@code ProofOfProvisioning} as defined below.
+     *
+     * <pre>
+     *     ProofOfProvisioning = [
+     *          "ProofOfProvisioning",        ; tstr
+     *          tstr,                         ; DocType
+     *          [ * AccessControlProfile ],
+     *          ProvisionedData,
+     *          bool                          ; true if this is a test credential, should
+     *                                        ; always be false.
+     *      ]
+     *
+     *      AccessControlProfile = {
+     *          "id": uint,
+     *          ? "readerCertificate" : bstr,
+     *          ? (
+     *               "userAuthenticationRequired" : bool,
+     *               "timeoutMillis" : uint,
+     *          )
+     *      }
+     *
+     *      ProvisionedData = {
+     *          * Namespace =&gt; [ + Entry ]
+     *      },
+     *
+     *      Namespace = tstr
+     *
+     *      Entry = {
+     *          "name" : tstr,
+     *          "value" : any,
+     *          "accessControlProfiles" : [ * uint ],
+     *      }
+     * </pre>
+     *
+     * <p>This data structure provides a guarantee to the issuer about the data which may be
+     * returned in the CBOR returned by
+     * {@link ResultData#getAuthenticatedData()} during a credential
+     * presentation.
+     *
+     * @param personalizationData   The data to provision, including access control profiles
+     *                              and data elements and their values, grouped into namespaces.
+     * @return A COSE_Sign1 data structure, see above.
+     */
+    public abstract @NonNull byte[] personalize(
+            @NonNull PersonalizationData personalizationData);
+}
diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
index 96c8528..54fc39e 100644
--- a/media/java/android/media/tv/DvbDeviceInfo.java
+++ b/media/java/android/media/tv/DvbDeviceInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
@@ -23,9 +24,13 @@
 import android.util.Log;
 
 /**
- * Simple container for information about DVB device.
- * Not for third-party developers.
+ * A digital video broadcasting (DVB) device.
  *
+ * <p> Simple wrapper around a <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB
+ * v3</a> device.
+ *
+ * @see TvInputManager#getDvbDeviceList()
+ * @see TvInputManager#openDvbDevice(DvbDeviceInfo, int)
  * @hide
  */
 @SystemApi
@@ -67,17 +72,19 @@
     }
 
     /**
-     * Returns the adapter ID of DVB device, in terms of enumerating the DVB device adapters
-     * installed in the system. The adapter ID counts from zero.
+     * Returns the adapter ID.
+     *
+     * <p>DVB Adapters contain one or more devices.
      */
+    @IntRange(from = 0)
     public int getAdapterId() {
         return mAdapterId;
     }
 
     /**
-     * Returns the device ID of DVB device, in terms of enumerating the DVB devices attached to
-     * the same device adapter. The device ID counts from zero.
+     * Returns the device ID.
      */
+    @IntRange(from = 0)
     public int getDeviceId() {
         return mDeviceId;
     }
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 6197c70..5babb16 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -17,7 +17,6 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
@@ -179,6 +178,10 @@
  *         <td>TV content rating system for Canada (French)</td>
  *     </tr>
  *     <tr>
+ *         <td>DTMB</td>
+ *         <td>DTMB content rating system</td>
+ *     </tr>
+ *     <tr>
  *         <td>DVB</td>
  *         <td>DVB content rating system</td>
  *     </tr>
@@ -199,10 +202,18 @@
  *         <td>TV content rating system for South Korea</td>
  *     </tr>
  *     <tr>
+ *         <td>NZ_TV</td>
+ *         <td>TV content rating system for New Zealand</td>
+ *     </tr>
+ *     <tr>
  *         <td>SG_TV</td>
  *         <td>TV content rating system for Singapore</td>
  *     </tr>
  *     <tr>
+ *         <td>TH_TV</td>
+ *         <td>TV content rating system for Thailand</td>
+ *     </tr>
+ *     <tr>
  *         <td>US_MV</td>
  *         <td>Movie content rating system for the United States</td>
  *     </tr>
@@ -356,6 +367,67 @@
  *         <td>Only to be viewed by adults</td>
  *     </tr>
  *     <tr>
+ *         <td valign="top" rowspan="15">DTMB</td>
+ *         <td>DTMB_4</td>
+ *         <td>Recommended for ages 4 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_5</td>
+ *         <td>Recommended for ages 5 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_6</td>
+ *         <td>Recommended for ages 6 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_7</td>
+ *         <td>Recommended for ages 7 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_8</td>
+ *         <td>Recommended for ages 8 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_9</td>
+ *         <td>Recommended for ages 9 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_10</td>
+ *         <td>Recommended for ages 10 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_11</td>
+ *         <td>Recommended for ages 11 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_12</td>
+ *         <td>Recommended for ages 12 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_13</td>
+ *         <td>Recommended for ages 13 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_14</td>
+ *         <td>Recommended for ages 14 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_15</td>
+ *         <td>Recommended for ages 15 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_16</td>
+ *         <td>Recommended for ages 16 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_17</td>
+ *         <td>Recommended for ages 17 and over</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DTMB_18</td>
+ *         <td>Recommended for ages 18 and over</td>
+ *     </tr>
+ *     <tr>
  *         <td valign="top" rowspan="15">DVB</td>
  *         <td>DVB_4</td>
  *         <td>Recommended for ages 4 and over</td>
@@ -648,6 +720,22 @@
  *         <td>For adults only</td>
  *     </tr>
  *     <tr>
+ *         <td valign="top" rowspan="3">NZ_TV</td>
+ *         <td>NZ_TV_G</td>
+ *         <td>Programmes which exclude material likely to be unsuitable for children. Programmes
+ *         may not necessarily be designed for child viewers but should not contain material likely
+ *         to alarm or distress them</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NZ_TV_PGR</td>
+ *         <td>Programmes containing material more suited for mature audiences but not necessarily
+ *         unsuitable for child viewers when subject to the guidance of a parent or an adult</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NZ_TV_AO</td>
+ *         <td>Programmes containing adult themes and directed primarily at mature audiences</td>
+ *     </tr>
+ *     <tr>
  *         <td valign="top" rowspan="6">SG_TV</td>
  *         <td>SG_TV_G</td>
  *         <td>Suitable for all ages</td>
@@ -674,6 +762,31 @@
  *         <td>Suitable for adults aged 21 and above</td>
  *     </tr>
  *     <tr>
+ *         <td valign="top" rowspan="6">TH_TV</td>
+ *         <td>TH_TV_4</td>
+ *         <td>Suitable for audiences 3 to 5 years of age</td>
+ *     </tr>
+ *     <tr>
+ *         <td>TH_TV_6</td>
+ *         <td>Suitable for audiences 6 to 12 years of age</td>
+ *     </tr>
+ *     <tr>
+ *         <td>TH_TV_10</td>
+ *         <td>Suitable for all audiences</td>
+ *     </tr>
+ *     <tr>
+ *         <td>TH_TV_13</td>
+ *         <td>Parental guidance suggested for viewers age below 13</td>
+ *     </tr>
+ *     <tr>
+ *         <td>TH_TV_18</td>
+ *         <td>Parental guidance suggested for viewers age below 18</td>
+ *     </tr>
+ *     <tr>
+ *         <td>TH_TV_19</td>
+ *         <td>Not suitable for children and teenagers</td>
+ *     </tr>
+ *     <tr>
  *         <td valign="top" rowspan="5">US_MV</td>
  *         <td>US_MV_G</td>
  *         <td>General audiences</td>
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 854ea43..ed4fe4b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1664,7 +1664,7 @@
     }
 
     /**
-     * Returns the list of currently available DVB devices on the system.
+     * Returns the list of currently available DVB frontend devices on the system.
      *
      * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
      * @hide
@@ -1681,16 +1681,17 @@
     }
 
     /**
-     * Returns a {@link ParcelFileDescriptor} of a specified DVB device for a given
-     * {@link DvbDeviceInfo}
+     * Returns a {@link ParcelFileDescriptor} of a specified DVB device of a given type for a given
+     * {@link DvbDeviceInfo}.
      *
      * @param info A {@link DvbDeviceInfo} to open a DVB device.
-     * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX},
-     *            {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}.
+     * @param deviceType A DVB device type.
      * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
-     *         {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
-     *         failed to open.
+     * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
+     * failed to open.
      * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found.
+
+     * @see <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB API v3</a>
      * @hide
      */
     @SystemApi
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 4318a0a..d4c4a62 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -352,8 +352,7 @@
         if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
                 || !TextUtils.equals(mLanguage, obj.mLanguage)
                 || !TextUtils.equals(mDescription, obj.mDescription)
-                || mEncrypted != obj.mEncrypted
-                || !Objects.equals(mExtra, obj.mExtra)) {
+                || mEncrypted != obj.mEncrypted) {
             return false;
         }
 
@@ -381,7 +380,16 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(mId);
+        int result = Objects.hash(mId, mType, mLanguage, mDescription);
+
+        if (mType == TYPE_AUDIO) {
+            result = Objects.hash(result, mAudioChannelCount, mAudioSampleRate);
+        } else if (mType == TYPE_VIDEO) {
+            result = Objects.hash(result, mVideoWidth, mVideoHeight, mVideoFrameRate,
+                    mVideoPixelAspectRatio);
+        }
+
+        return result;
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<TvTrackInfo> CREATOR =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8d858dc..1fb50a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1061,7 +1061,7 @@
                 // keep compatibility with apps that aren't direct boot aware.
                 // SysUI should just ignore this broadcast because it was already received
                 // and processed previously.
-                if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+                if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                     // Guarantee mTelephonyCapable state after SysUI crash and restart
                     if (args.simState == State.ABSENT) {
                         mHandler.obtainMessage(MSG_TELEPHONY_CAPABLE, true).sendToTarget();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 17c200e..b2f4f16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -605,8 +605,7 @@
                     break;
                 case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                     // Avoid rebroadcast because SysUI is direct boot aware.
-                    if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
-                            false)) {
+                    if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                         break;
                     }
                     updateSimState(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 7a09455..4523374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -517,7 +517,7 @@
                 break;
             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                 // Avoid rebroadcast because SysUI is direct boot aware.
-                if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+                if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
                     break;
                 }
                 // Might have different subscriptions now.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index db6177a..21585ed 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -133,7 +133,7 @@
         Assert.assertTrue("onSimStateChanged not called",
                 mKeyguardUpdateMonitor.hasSimStateJustChanged());
 
-        intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+        intent.putExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, true);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
         mTestableLooper.processAllMessages();
         Assert.assertFalse("onSimStateChanged should have been skipped",
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
new file mode 100644
index 0000000..31cd5d5
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks
+ * whether we need to inform BluetoothManagerService on this change.
+ *
+ * The information of airplane mode turns on would not be passed to the BluetoothManagerService
+ * when Bluetooth is on and Bluetooth is in one of the following situations:
+ *   1. Bluetooth A2DP is connected.
+ *   2. Bluetooth Hearing Aid profile is connected.
+ */
+class BluetoothAirplaneModeListener {
+    private static final String TAG = "BluetoothAirplaneModeListener";
+    @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count";
+
+    private static final int MSG_AIRPLANE_MODE_CHANGED = 0;
+
+    @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times
+
+    private final BluetoothManagerService mBluetoothManager;
+    private final BluetoothAirplaneModeHandler mHandler;
+    private AirplaneModeHelper mAirplaneHelper;
+
+    @VisibleForTesting int mToastCount = 0;
+
+    BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) {
+        mBluetoothManager = service;
+
+        mHandler = new BluetoothAirplaneModeHandler(looper);
+        context.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
+                mAirplaneModeObserver);
+    }
+
+    private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
+        @Override
+        public void onChange(boolean unused) {
+            // Post from system main thread to android_io thread.
+            Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED);
+            mHandler.sendMessage(msg);
+        }
+    };
+
+    private class BluetoothAirplaneModeHandler extends Handler {
+        BluetoothAirplaneModeHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_AIRPLANE_MODE_CHANGED:
+                    handleAirplaneModeChange();
+                    break;
+                default:
+                    Log.e(TAG, "Invalid message: " + msg.what);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Call after boot complete
+     */
+    @VisibleForTesting
+    void start(AirplaneModeHelper helper) {
+        Log.i(TAG, "start");
+        mAirplaneHelper = helper;
+        mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
+    }
+
+    @VisibleForTesting
+    boolean shouldPopToast() {
+        if (mToastCount >= MAX_TOAST_COUNT) {
+            return false;
+        }
+        mToastCount++;
+        mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount);
+        return true;
+    }
+
+    @VisibleForTesting
+    void handleAirplaneModeChange() {
+        if (shouldSkipAirplaneModeChange()) {
+            Log.i(TAG, "Ignore airplane mode change");
+            // We have to store Bluetooth state here, so if user turns off Bluetooth
+            // after airplane mode is turned on, we don't forget to turn on Bluetooth
+            // when airplane mode turns off.
+            mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
+                    BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
+            if (shouldPopToast()) {
+                mAirplaneHelper.showToastMessage();
+            }
+            return;
+        }
+        mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager);
+    }
+
+    @VisibleForTesting
+    boolean shouldSkipAirplaneModeChange() {
+        if (mAirplaneHelper == null) {
+            return false;
+        }
+        if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn()
+                || !mAirplaneHelper.isA2dpOrHearingAidConnected()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Helper class that handles callout and callback methods without
+     * complex logic.
+     */
+    @VisibleForTesting
+    public static class AirplaneModeHelper {
+        private volatile BluetoothA2dp mA2dp;
+        private volatile BluetoothHearingAid mHearingAid;
+        private final BluetoothAdapter mAdapter;
+        private final Context mContext;
+
+        AirplaneModeHelper(Context context) {
+            mAdapter = BluetoothAdapter.getDefaultAdapter();
+            mContext = context;
+
+            mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
+            mAdapter.getProfileProxy(mContext, mProfileServiceListener,
+                    BluetoothProfile.HEARING_AID);
+        }
+
+        private final ServiceListener mProfileServiceListener = new ServiceListener() {
+            @Override
+            public void onServiceConnected(int profile, BluetoothProfile proxy) {
+                // Setup Bluetooth profile proxies
+                switch (profile) {
+                    case BluetoothProfile.A2DP:
+                        mA2dp = (BluetoothA2dp) proxy;
+                        break;
+                    case BluetoothProfile.HEARING_AID:
+                        mHearingAid = (BluetoothHearingAid) proxy;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+            @Override
+            public void onServiceDisconnected(int profile) {
+                // Clear Bluetooth profile proxies
+                switch (profile) {
+                    case BluetoothProfile.A2DP:
+                        mA2dp = null;
+                        break;
+                    case BluetoothProfile.HEARING_AID:
+                        mHearingAid = null;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        };
+
+        @VisibleForTesting
+        public boolean isA2dpOrHearingAidConnected() {
+            return isA2dpConnected() || isHearingAidConnected();
+        }
+
+        @VisibleForTesting
+        public boolean isBluetoothOn() {
+            final BluetoothAdapter adapter = mAdapter;
+            if (adapter == null) {
+                return false;
+            }
+            return adapter.getLeState() == BluetoothAdapter.STATE_ON;
+        }
+
+        @VisibleForTesting
+        public boolean isAirplaneModeOn() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+        }
+
+        @VisibleForTesting
+        public void onAirplaneModeChanged(BluetoothManagerService managerService) {
+            managerService.onAirplaneModeChanged();
+        }
+
+        @VisibleForTesting
+        public int getSettingsInt(String name) {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    name, 0);
+        }
+
+        @VisibleForTesting
+        public void setSettingsInt(String name, int value) {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    name, value);
+        }
+
+        @VisibleForTesting
+        public void showToastMessage() {
+            Resources r = mContext.getResources();
+            final CharSequence text = r.getString(
+                    R.string.bluetooth_airplane_mode_toast, 0);
+            Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+        }
+
+        private boolean isA2dpConnected() {
+            final BluetoothA2dp a2dp = mA2dp;
+            if (a2dp == null) {
+                return false;
+            }
+            return a2dp.getConnectedDevices().size() > 0;
+        }
+
+        private boolean isHearingAidConnected() {
+            final BluetoothHearingAid hearingAid = mHearingAid;
+            if (hearingAid == null) {
+                return false;
+            }
+            return hearingAid.getConnectedDevices().size() > 0;
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index cdfd310..fa8eda5 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -68,6 +68,7 @@
 import android.util.StatsLog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.pm.UserRestrictionsUtils;
 
@@ -138,7 +139,8 @@
     // Bluetooth persisted setting is on
     // but Airplane mode will affect Bluetooth state at start up
     // and Airplane mode will have higher priority.
-    private static final int BLUETOOTH_ON_AIRPLANE = 2;
+    @VisibleForTesting
+    static final int BLUETOOTH_ON_AIRPLANE = 2;
 
     private static final int SERVICE_IBLUETOOTH = 1;
     private static final int SERVICE_IBLUETOOTHGATT = 2;
@@ -159,6 +161,8 @@
     private boolean mBinding;
     private boolean mUnbinding;
 
+    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
+
     // used inside handler thread
     private boolean mQuietEnable = false;
     private boolean mEnable;
@@ -257,68 +261,65 @@
                 }
             };
 
-    private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
-        @Override
-        public void onChange(boolean unused) {
-            synchronized (this) {
-                if (isBluetoothPersistedStateOn()) {
-                    if (isAirplaneModeOn()) {
-                        persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
-                    } else {
-                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                    }
-                }
-
-                int st = BluetoothAdapter.STATE_OFF;
-                try {
-                    mBluetoothLock.readLock().lock();
-                    if (mBluetooth != null) {
-                        st = mBluetooth.getState();
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call getState", e);
-                    return;
-                } finally {
-                    mBluetoothLock.readLock().unlock();
-                }
-
-                Slog.d(TAG,
-                        "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
-                                st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
-
+    public void onAirplaneModeChanged() {
+        synchronized (this) {
+            if (isBluetoothPersistedStateOn()) {
                 if (isAirplaneModeOn()) {
-                    // Clear registered LE apps to force shut-off
-                    clearBleApps();
-
-                    // If state is BLE_ON make sure we trigger disableBLE
-                    if (st == BluetoothAdapter.STATE_BLE_ON) {
-                        try {
-                            mBluetoothLock.readLock().lock();
-                            if (mBluetooth != null) {
-                                addActiveLog(
-                                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                                        mContext.getPackageName(), false);
-                                mBluetooth.onBrEdrDown();
-                                mEnable = false;
-                                mEnableExternal = false;
-                            }
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "Unable to call onBrEdrDown", e);
-                        } finally {
-                            mBluetoothLock.readLock().unlock();
-                        }
-                    } else if (st == BluetoothAdapter.STATE_ON) {
-                        sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                                mContext.getPackageName());
-                    }
-                } else if (mEnableExternal) {
-                    sendEnableMsg(mQuietEnableExternal,
-                            BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                            mContext.getPackageName());
+                    persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
+                } else {
+                    persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
                 }
             }
+
+            int st = BluetoothAdapter.STATE_OFF;
+            try {
+                mBluetoothLock.readLock().lock();
+                if (mBluetooth != null) {
+                    st = mBluetooth.getState();
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to call getState", e);
+                return;
+            } finally {
+                mBluetoothLock.readLock().unlock();
+            }
+
+            Slog.d(TAG,
+                    "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
+                            st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
+
+            if (isAirplaneModeOn()) {
+                // Clear registered LE apps to force shut-off
+                clearBleApps();
+
+                // If state is BLE_ON make sure we trigger disableBLE
+                if (st == BluetoothAdapter.STATE_BLE_ON) {
+                    try {
+                        mBluetoothLock.readLock().lock();
+                        if (mBluetooth != null) {
+                            addActiveLog(
+                                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
+                                    mContext.getPackageName(), false);
+                            mBluetooth.onBrEdrDown();
+                            mEnable = false;
+                            mEnableExternal = false;
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to call onBrEdrDown", e);
+                    } finally {
+                        mBluetoothLock.readLock().unlock();
+                    }
+                } else if (st == BluetoothAdapter.STATE_ON) {
+                    sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
+                            mContext.getPackageName());
+                }
+            } else if (mEnableExternal) {
+                sendEnableMsg(mQuietEnableExternal,
+                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
+                        mContext.getPackageName());
+            }
         }
-    };
+    }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -430,9 +431,8 @@
                 Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
         if (airplaneModeRadios == null || airplaneModeRadios.contains(
                 Settings.Global.RADIO_BLUETOOTH)) {
-            mContentResolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
-                    mAirplaneModeObserver);
+            mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
+                    this, IoThread.get().getLooper(), context);
         }
 
         int systemUiUid = -1;
@@ -478,6 +478,17 @@
         return state != BLUETOOTH_OFF;
     }
 
+    private boolean isBluetoothPersistedStateOnAirplane() {
+        if (!supportBluetoothPersistedState()) {
+            return false;
+        }
+        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
+        if (DBG) {
+            Slog.d(TAG, "Bluetooth persisted state: " + state);
+        }
+        return state == BLUETOOTH_ON_AIRPLANE;
+    }
+
     /**
      *  Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
      */
@@ -954,10 +965,12 @@
         }
 
         synchronized (mReceiver) {
-            if (persist) {
-                persistBluetoothSetting(BLUETOOTH_OFF);
+            if (!isBluetoothPersistedStateOnAirplane()) {
+                if (persist) {
+                    persistBluetoothSetting(BLUETOOTH_OFF);
+                }
+                mEnableExternal = false;
             }
-            mEnableExternal = false;
             sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
                     packageName);
         }
@@ -1185,6 +1198,10 @@
             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
             mHandler.sendMessage(getMsg);
         }
+        if (mBluetoothAirplaneModeListener != null) {
+            mBluetoothAirplaneModeListener.start(
+                    new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext));
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index da42edd..11f0a04 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -39,6 +39,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -86,12 +87,11 @@
 import android.net.NattSocketKeepalive;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
-import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkProvider;
@@ -212,6 +212,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
@@ -1573,48 +1574,49 @@
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
         NetworkState state = getUnfilteredActiveNetworkState(uid);
-        return state.linkProperties;
+        if (state.linkProperties == null) return null;
+        return linkPropertiesRestrictedForCallerPermissions(state.linkProperties,
+                Binder.getCallingPid(), uid);
     }
 
     @Override
     public LinkProperties getLinkPropertiesForType(int networkType) {
         enforceAccessPermission();
         NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
-        if (nai != null) {
-            synchronized (nai) {
-                return new LinkProperties(nai.linkProperties);
-            }
-        }
-        return null;
+        final LinkProperties lp = getLinkProperties(nai);
+        if (lp == null) return null;
+        return linkPropertiesRestrictedForCallerPermissions(
+                lp, Binder.getCallingPid(), Binder.getCallingUid());
     }
 
     // TODO - this should be ALL networks
     @Override
     public LinkProperties getLinkProperties(Network network) {
         enforceAccessPermission();
-        return getLinkProperties(getNetworkAgentInfoForNetwork(network));
+        final LinkProperties lp = getLinkProperties(getNetworkAgentInfoForNetwork(network));
+        if (lp == null) return null;
+        return linkPropertiesRestrictedForCallerPermissions(
+                lp, Binder.getCallingPid(), Binder.getCallingUid());
     }
 
-    private LinkProperties getLinkProperties(NetworkAgentInfo nai) {
+    @Nullable
+    private LinkProperties getLinkProperties(@Nullable NetworkAgentInfo nai) {
         if (nai == null) {
             return null;
         }
         synchronized (nai) {
-            return new LinkProperties(nai.linkProperties);
+            return nai.linkProperties;
         }
     }
 
     private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
-        if (nai != null) {
-            synchronized (nai) {
-                if (nai.networkCapabilities != null) {
-                    return networkCapabilitiesRestrictedForCallerPermissions(
-                            nai.networkCapabilities,
-                            Binder.getCallingPid(), Binder.getCallingUid());
-                }
-            }
+        if (nai == null) return null;
+        synchronized (nai) {
+            if (nai.networkCapabilities == null) return null;
+            return networkCapabilitiesRestrictedForCallerPermissions(
+                    nai.networkCapabilities,
+                    Binder.getCallingPid(), Binder.getCallingUid());
         }
-        return null;
     }
 
     @Override
@@ -1633,13 +1635,38 @@
         if (newNc.getNetworkSpecifier() != null) {
             newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
         }
+        newNc.setAdministratorUids(Collections.EMPTY_LIST);
         return newNc;
     }
 
+    private LinkProperties linkPropertiesRestrictedForCallerPermissions(
+            LinkProperties lp, int callerPid, int callerUid) {
+        if (lp == null) return new LinkProperties();
+
+        // Only do a permission check if sanitization is needed, to avoid unnecessary binder calls.
+        final boolean needsSanitization =
+                (lp.getCaptivePortalApiUrl() != null || lp.getCaptivePortalData() != null);
+        if (!needsSanitization) {
+            return new LinkProperties(lp);
+        }
+
+        if (checkSettingsPermission(callerPid, callerUid)) {
+            return lp.makeSensitiveFieldsParcelingCopy();
+        }
+
+        final LinkProperties newLp = new LinkProperties(lp);
+        // Sensitive fields would not be parceled anyway, but sanitize for consistency before the
+        // object gets parceled.
+        newLp.setCaptivePortalApiUrl(null);
+        newLp.setCaptivePortalData(null);
+        return newLp;
+    }
+
     private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
         if (!checkSettingsPermission()) {
             nc.setSingleUid(Binder.getCallingUid());
         }
+        nc.setAdministratorUids(Collections.EMPTY_LIST);
     }
 
     private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
@@ -2624,8 +2651,8 @@
                     if (nai.everConnected) {
                         loge("ERROR: cannot call explicitlySelected on already-connected network");
                     }
-                    nai.networkMisc.explicitlySelected = toBool(msg.arg1);
-                    nai.networkMisc.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
+                    nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1);
+                    nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
                     // Mark the network as temporarily accepting partial connectivity so that it
                     // will be validated (and possibly become default) even if it only provides
                     // partial internet access. Note that if user connects to partial connectivity
@@ -2633,7 +2660,7 @@
                     // out of wifi coverage) and if the same wifi is available again, the device
                     // will auto connect to this wifi even though the wifi has "no internet".
                     // TODO: Evaluate using a separate setting in IpMemoryStore.
-                    nai.networkMisc.acceptPartialConnectivity = toBool(msg.arg2);
+                    nai.networkAgentConfig.acceptPartialConnectivity = toBool(msg.arg2);
                     break;
                 }
                 case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2665,10 +2692,10 @@
                         }
                         // Only show the notification when the private DNS is broken and the
                         // PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
-                        if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+                        if (privateDnsBroken && !nai.networkAgentConfig.hasShownBroken) {
                             showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
                         }
-                        nai.networkMisc.hasShownBroken = privateDnsBroken;
+                        nai.networkAgentConfig.hasShownBroken = privateDnsBroken;
                     } else if (nai.networkCapabilities.isPrivateDnsBroken()) {
                         // If probePrivateDnsCompleted is false but nai.networkCapabilities says
                         // private DNS is broken, it means this network is being reevaluated.
@@ -2678,7 +2705,7 @@
                         nai.networkCapabilities.setPrivateDnsBroken(false);
                         final int oldScore = nai.getCurrentScore();
                         updateCapabilities(oldScore, nai, nai.networkCapabilities);
-                        nai.networkMisc.hasShownBroken = false;
+                        nai.networkAgentConfig.hasShownBroken = false;
                     }
                     break;
                 }
@@ -2737,7 +2764,7 @@
                             // If network becomes valid, the hasShownBroken should be reset for
                             // that network so that the notification will be fired when the private
                             // DNS is broken again.
-                            nai.networkMisc.hasShownBroken = false;
+                            nai.networkAgentConfig.hasShownBroken = false;
                         }
                     } else if (partialConnectivityChanged) {
                         updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2796,9 +2823,10 @@
                             loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
                             break;
                         }
-                        if (!nai.networkMisc.provisioningNotificationDisabled) {
+                        if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
                             mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
-                                    (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
+                                    (PendingIntent) msg.obj,
+                                    nai.networkAgentConfig.explicitlySelected);
                         }
                     }
                     break;
@@ -2835,26 +2863,11 @@
             return true;
         }
 
-        // TODO: delete when direct use of registerNetworkFactory is no longer supported.
-        private boolean maybeHandleNetworkFactoryMessage(Message msg) {
-            switch (msg.what) {
-                default:
-                    return false;
-                case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: {
-                    handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid,
-                            /* callOnUnavailable */ true);
-                    break;
-                }
-            }
-            return true;
-        }
-
         @Override
         public void handleMessage(Message msg) {
             if (!maybeHandleAsyncChannelMessage(msg)
                     && !maybeHandleNetworkMonitorMessage(msg)
-                    && !maybeHandleNetworkAgentInfoMessage(msg)
-                    && !maybeHandleNetworkFactoryMessage(msg)) {
+                    && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
         }
@@ -3163,8 +3176,8 @@
             // This should never fail.  Specifying an already in use NetID will cause failure.
             if (networkAgent.isVPN()) {
                 mNetd.networkCreateVpn(networkAgent.network.netId,
-                        (networkAgent.networkMisc == null
-                                || !networkAgent.networkMisc.allowBypass));
+                        (networkAgent.networkAgentConfig == null
+                                || !networkAgent.networkAgentConfig.allowBypass));
             } else {
                 mNetd.networkCreatePhysical(networkAgent.network.netId,
                         getNetworkPermission(networkAgent.networkCapabilities));
@@ -3464,16 +3477,16 @@
             return;
         }
 
-        if (!nai.networkMisc.explicitlySelected) {
+        if (!nai.networkAgentConfig.explicitlySelected) {
             Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
         }
 
-        if (accept != nai.networkMisc.acceptUnvalidated) {
-            nai.networkMisc.acceptUnvalidated = accept;
+        if (accept != nai.networkAgentConfig.acceptUnvalidated) {
+            nai.networkAgentConfig.acceptUnvalidated = accept;
             // If network becomes partial connectivity and user already accepted to use this
             // network, we should respect the user's option and don't need to popup the
             // PARTIAL_CONNECTIVITY notification to user again.
-            nai.networkMisc.acceptPartialConnectivity = accept;
+            nai.networkAgentConfig.acceptPartialConnectivity = accept;
             rematchAllNetworksAndRequests();
             sendUpdatedScoreToFactories(nai);
         }
@@ -3510,8 +3523,8 @@
             return;
         }
 
-        if (accept != nai.networkMisc.acceptPartialConnectivity) {
-            nai.networkMisc.acceptPartialConnectivity = accept;
+        if (accept != nai.networkAgentConfig.acceptPartialConnectivity) {
+            nai.networkAgentConfig.acceptPartialConnectivity = accept;
         }
 
         // TODO: Use the current design or save the user choice into IpMemoryStore.
@@ -3736,7 +3749,7 @@
                 action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
                 // Don't bother the user with a high-priority notification if the network was not
                 // explicitly selected by the user.
-                highPriority = nai.networkMisc.explicitlySelected;
+                highPriority = nai.networkAgentConfig.explicitlySelected;
                 break;
             default:
                 Slog.wtf(TAG, "Unknown notification type " + type);
@@ -3769,14 +3782,15 @@
         // automatically connects to a network that has partial Internet access, the user will
         // always be able to use it, either because they've already chosen "don't ask again" or
         // because we have prompt them.
-        if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+        if (nai.partialConnectivity && !nai.networkAgentConfig.acceptPartialConnectivity) {
             return true;
         }
 
         // If a network has no Internet access, only prompt if the network was explicitly selected
         // and if the user has not already told us to use the network regardless of whether it
         // validated or not.
-        if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+        if (nai.networkAgentConfig.explicitlySelected
+                && !nai.networkAgentConfig.acceptUnvalidated) {
             return true;
         }
 
@@ -5501,9 +5515,9 @@
      */
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkMisc networkMisc) {
+            int currentScore, NetworkAgentConfig networkAgentConfig) {
         return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
-                currentScore, networkMisc, NetworkProvider.ID_NONE);
+                currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
     }
 
     /**
@@ -5518,13 +5532,13 @@
      *         later : see {@link #updateCapabilities}.
      * @param currentScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
-     * @param networkMisc metadata about the network. This is never updated.
+     * @param networkAgentConfig metadata about the network. This is never updated.
      * @param providerId the ID of the provider owning this NetworkAgent.
      * @return the network created for this agent.
      */
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkMisc networkMisc, int providerId) {
+            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
         enforceNetworkFactoryPermission();
 
         LinkProperties lp = new LinkProperties(linkProperties);
@@ -5536,8 +5550,8 @@
         ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
-                ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
-                mDnsResolver, mNMS, providerId);
+                ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this,
+                mNetd, mDnsResolver, mNMS, providerId);
         // Make sure the network capabilities reflect what the agent info says.
         nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
@@ -5807,6 +5821,19 @@
         return INetd.PERMISSION_NONE;
     }
 
+    private void updateNetworkPermissions(@NonNull final NetworkAgentInfo nai,
+            @NonNull final NetworkCapabilities newNc) {
+        final int oldPermission = getNetworkPermission(nai.networkCapabilities);
+        final int newPermission = getNetworkPermission(newNc);
+        if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
+            try {
+                mNMS.setNetworkPermission(nai.network.netId, newPermission);
+            } catch (RemoteException e) {
+                loge("Exception in setNetworkPermission: " + e);
+            }
+        }
+    }
+
     /**
      * Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
      * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
@@ -5847,11 +5874,6 @@
         } else {
             newNc.addCapability(NET_CAPABILITY_FOREGROUND);
         }
-        if (nai.isSuspended()) {
-            newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
-        } else {
-            newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
-        }
         if (nai.partialConnectivity) {
             newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
         } else {
@@ -5859,6 +5881,12 @@
         }
         newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
 
+        // TODO : remove this once all factories are updated to send NOT_SUSPENDED and NOT_ROAMING
+        if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+            newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+            newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
+        }
+
         return newNc;
     }
 
@@ -5878,21 +5906,11 @@
      * @param nai the network having its capabilities updated.
      * @param nc the new network capabilities.
      */
-    private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+    private void updateCapabilities(final int oldScore, @NonNull final NetworkAgentInfo nai,
+            @NonNull final NetworkCapabilities nc) {
         NetworkCapabilities newNc = mixInCapabilities(nai, nc);
-
         if (Objects.equals(nai.networkCapabilities, newNc)) return;
-
-        final int oldPermission = getNetworkPermission(nai.networkCapabilities);
-        final int newPermission = getNetworkPermission(newNc);
-        if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
-            try {
-                mNMS.setNetworkPermission(nai.network.netId, newPermission);
-            } catch (RemoteException e) {
-                loge("Exception in setNetworkPermission: " + e);
-            }
-        }
-
+        updateNetworkPermissions(nai, newNc);
         final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
 
         updateUids(nai, prevNc, newNc);
@@ -5903,6 +5921,19 @@
             // on this network. We might have been called by rematchNetworkAndRequests when a
             // network changed foreground state.
             processListenRequests(nai);
+            final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+            final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+            final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+            final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+            if (prevSuspended != suspended || prevRoaming != roaming) {
+                // TODO (b/73132094) : remove this call once the few users of onSuspended and
+                // onResumed have been removed.
+                notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+                        : ConnectivityManager.CALLBACK_RESUMED);
+                // updateNetworkInfo will mix in the suspended info from the capabilities and
+                // take appropriate action for the network having possibly changed state.
+                updateNetworkInfo(nai, nai.networkInfo);
+            }
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -5910,6 +5941,9 @@
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
+        // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
+        // never returns null), so mark the relevant members and functions in nai as @NonNull and
+        // remove this test
         if (prevNc != null) {
             final boolean oldMetered = prevNc.isMetered();
             final boolean newMetered = newNc.isMetered();
@@ -5958,7 +5992,7 @@
             LinkProperties lp) {
         if (nc == null || lp == null) return false;
         return nai.isVPN()
-                && !nai.networkMisc.allowBypass
+                && !nai.networkAgentConfig.allowBypass
                 && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID
                 && lp.getInterfaceName() != null
                 && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
@@ -6131,7 +6165,8 @@
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
                         networkAgent.networkCapabilities, nri.mPid, nri.mUid));
-                putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+                putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
+                        networkAgent.linkProperties, nri.mPid, nri.mUid));
                 // For this notification, arg1 contains the blocked status.
                 msg.arg1 = arg1;
                 break;
@@ -6148,7 +6183,8 @@
                 break;
             }
             case ConnectivityManager.CALLBACK_IP_CHANGED: {
-                putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+                putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
+                        networkAgent.linkProperties, nri.mPid, nri.mUid));
                 break;
             }
             case ConnectivityManager.CALLBACK_BLK_CHANGED: {
@@ -6257,6 +6293,30 @@
         }
     }
 
+    // An accumulator class to gather the list of changes that result from a rematch.
+    // TODO : enrich to represent an entire set of changes to apply.
+    private static class NetworkReassignment {
+        static class NetworkBgStatePair {
+            @NonNull final NetworkAgentInfo mNetwork;
+            final boolean mOldBackground;
+            NetworkBgStatePair(@NonNull final NetworkAgentInfo network,
+                    final boolean oldBackground) {
+                mNetwork = network;
+                mOldBackground = oldBackground;
+            }
+        }
+
+        @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
+
+        @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
+            return mRematchedNetworks;
+        }
+
+        void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
+            mRematchedNetworks.add(network);
+        }
+    }
+
     private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
             @NonNull final NetworkAgentInfo newNetwork) {
         final int score = newNetwork.getCurrentScore();
@@ -6302,8 +6362,8 @@
     //   needed. A network is needed if it is the best network for
     //   one or more NetworkRequests, or if it is a VPN.
     //
-    // - Tears down newNetwork if it just became validated
-    //   but turns out to be unneeded.
+    // - Writes into the passed reassignment object all changes that should be done for
+    //   rematching this network with all requests, to be applied later.
     //
     // NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
     // it does not remove NetworkRequests that other Networks could better satisfy.
@@ -6311,15 +6371,22 @@
     // This function should be used when possible instead of {@code rematchAllNetworksAndRequests}
     // as it performs better by a factor of the number of Networks.
     //
+    // TODO : stop writing to the passed reassignment. This is temporarily more useful, but
+    // it's unidiomatic Java and it's hard to read.
+    //
+    // @param changes a currently-building list of changes to write to
     // @param newNetwork is the network to be matched against NetworkRequests.
     // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
-    private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, long now) {
+    private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes,
+            @NonNull final NetworkAgentInfo newNetwork, final long now) {
         ensureRunningOnConnectivityServiceThread();
         if (!newNetwork.everConnected) return;
         boolean isNewDefault = false;
         NetworkAgentInfo oldDefaultNetwork = null;
 
-        final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork();
+        changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
+                newNetwork.isBackgroundNetwork()));
+
         final int score = newNetwork.getCurrentScore();
 
         if (VDBG || DDBG) log("rematching " + newNetwork.name());
@@ -6422,39 +6489,12 @@
         if (newNetwork.getCurrentScore() != score) {
             Slog.wtf(TAG, String.format(
                     "BUG: %s changed score during rematch: %d -> %d",
-                   newNetwork.name(), score, newNetwork.getCurrentScore()));
+                    newNetwork.name(), score, newNetwork.getCurrentScore()));
         }
 
         // Notify requested networks are available after the default net is switched, but
         // before LegacyTypeTracker sends legacy broadcasts
         for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
-
-        // Finally, process listen requests and update capabilities if the background state has
-        // changed for this network. For consistency with previous behavior, send onLost callbacks
-        // before onAvailable.
-        processNewlyLostListenRequests(newNetwork);
-
-        // Maybe the network changed background states. Update its capabilities.
-        final boolean backgroundChanged = wasBackgroundNetwork != newNetwork.isBackgroundNetwork();
-        if (backgroundChanged) {
-            final NetworkCapabilities newNc = mixInCapabilities(newNetwork,
-                    newNetwork.networkCapabilities);
-
-            final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
-            final int newPermission = getNetworkPermission(newNc);
-            if (oldPermission != newPermission) {
-                try {
-                    mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
-                } catch (RemoteException e) {
-                    loge("Exception in setNetworkPermission: " + e);
-                }
-            }
-
-            newNetwork.getAndSetNetworkCapabilities(newNc);
-            notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
-        }
-
-        processNewlySatisfiedListenRequests(newNetwork);
     }
 
     /**
@@ -6476,12 +6516,24 @@
         // scoring network and then a higher scoring network, which could produce multiple
         // callbacks.
         Arrays.sort(nais);
+        final NetworkReassignment changes = new NetworkReassignment();
         for (final NetworkAgentInfo nai : nais) {
-            rematchNetworkAndRequests(nai, now);
+            rematchNetworkAndRequests(changes, nai, now);
         }
 
         final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
 
+        for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
+            // Process listen requests and update capabilities if the background state has
+            // changed for this network. For consistency with previous behavior, send onLost
+            // callbacks before onAvailable.
+            processNewlyLostListenRequests(event.mNetwork);
+            if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) {
+                applyBackgroundChangeForRematch(event.mNetwork);
+            }
+            processNewlySatisfiedListenRequests(event.mNetwork);
+        }
+
         for (final NetworkAgentInfo nai : nais) {
             // Rematching may have altered the linger state of some networks, so update all linger
             // timers. updateLingerState reads the state from the network agent and does nothing
@@ -6513,6 +6565,24 @@
         }
     }
 
+    /**
+     * Apply a change in background state resulting from rematching networks with requests.
+     *
+     * During rematch, a network may change background states by starting to satisfy or stopping
+     * to satisfy a foreground request. Listens don't count for this. When a network changes
+     * background states, its capabilities need to be updated and callbacks fired for the
+     * capability change.
+     *
+     * @param nai The network that changed background states
+     */
+    private void applyBackgroundChangeForRematch(@NonNull final NetworkAgentInfo nai) {
+        final NetworkCapabilities newNc = mixInCapabilities(nai, nai.networkCapabilities);
+        if (Objects.equals(nai.networkCapabilities, newNc)) return;
+        updateNetworkPermissions(nai, newNc);
+        nai.getAndSetNetworkCapabilities(newNc);
+        notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+    }
+
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
             @Nullable final NetworkAgentInfo oldDefaultNetwork,
             @Nullable final NetworkAgentInfo newDefaultNetwork,
@@ -6604,10 +6674,31 @@
         }
     }
 
-    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+    @NonNull
+    private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
+        final NetworkInfo newInfo = new NetworkInfo(info);
+        // The suspended and roaming bits are managed in NetworkCapabilities.
+        final boolean suspended =
+                !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
+            // Only override the state with SUSPENDED if the network is currently in CONNECTED
+            // state. This is because the network could have been suspended before connecting,
+            // or it could be disconnecting while being suspended, and in both these cases
+            // the state should not be overridden. Note that the only detailed state that
+            // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to
+            // worry about multiple different substates of CONNECTED.
+            newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
+                    info.getExtraInfo());
+        }
+        newInfo.setRoaming(!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        return newInfo;
+    }
+
+    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) {
+        final NetworkInfo newInfo = mixInInfo(networkAgent, info);
+
         final NetworkInfo.State state = newInfo.getState();
         NetworkInfo oldInfo = null;
-        final int oldScore = networkAgent.getCurrentScore();
         synchronized (networkAgent) {
             oldInfo = networkAgent.networkInfo;
             networkAgent.networkInfo = newInfo;
@@ -6649,7 +6740,7 @@
             // command must be sent after updating LinkProperties to maximize chances of
             // NetworkMonitor seeing the correct LinkProperties when starting.
             // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
-            if (networkAgent.networkMisc.acceptPartialConnectivity) {
+            if (networkAgent.networkAgentConfig.acceptPartialConnectivity) {
                 networkAgent.networkMonitor().setAcceptPartialConnectivity();
             }
             networkAgent.networkMonitor().notifyNetworkConnected(
@@ -6689,17 +6780,6 @@
             }
         } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
                 state == NetworkInfo.State.SUSPENDED)) {
-            // going into or coming out of SUSPEND: re-score and notify
-            if (networkAgent.getCurrentScore() != oldScore) {
-                rematchAllNetworksAndRequests();
-            }
-            updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
-                    networkAgent.networkCapabilities);
-            // TODO (b/73132094) : remove this call once the few users of onSuspended and
-            // onResumed have been removed.
-            notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
-                    ConnectivityManager.CALLBACK_SUSPENDED :
-                    ConnectivityManager.CALLBACK_RESUMED));
             mLegacyTypeTracker.update(networkAgent);
         }
     }
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index 1ff455ea..c34dd98 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,15 +16,273 @@
 
 package com.android.server;
 
-import android.os.IBinder;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.timedetector.NetworkTimeSuggestion;
+import android.app.timedetector.TimeDetector;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
- * An interface for NetworkTimeUpdateService implementations. Eventually part or all of this service
- * will be subsumed into {@link com.android.server.timedetector.TimeDetectorService}. In the
- * meantime this interface allows Android to use either the old or new implementation.
+ * Monitors the network time. If looking up the network time fails for some reason, it tries a few
+ * times with a short interval and then resets to checking on longer intervals.
+ *
+ * <p>When available, the time is always suggested to the {@link
+ * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
+ * system clock, depending on user settings and what other signals are available.
  */
-public interface NetworkTimeUpdateService extends IBinder {
+public class NetworkTimeUpdateService extends Binder {
+
+    private static final String TAG = "NetworkTimeUpdateService";
+    private static final boolean DBG = false;
+
+    private static final int EVENT_AUTO_TIME_ENABLED = 1;
+    private static final int EVENT_POLL_NETWORK_TIME = 2;
+    private static final int EVENT_NETWORK_CHANGED = 3;
+
+    private static final String ACTION_POLL =
+            "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+    private static final int POLL_REQUEST = 0;
+
+    private Network mDefaultNetwork = null;
+
+    private final Context mContext;
+    private final NtpTrustedTime mTime;
+    private final AlarmManager mAlarmManager;
+    private final TimeDetector mTimeDetector;
+    private final ConnectivityManager mCM;
+    private final PendingIntent mPendingPollIntent;
+    private final PowerManager.WakeLock mWakeLock;
+
+    // NTP lookup is done on this thread and handler
+    private Handler mHandler;
+    private AutoTimeSettingObserver mAutoTimeSettingObserver;
+    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+
+    // Normal polling frequency
+    private final long mPollingIntervalMs;
+    // Try-again polling interval, in case the network request failed
+    private final long mPollingIntervalShorterMs;
+    // Number of times to try again
+    private final int mTryAgainTimesMax;
+    // Keeps track of how many quick attempts were made to fetch NTP time.
+    // During bootup, the network may not have been up yet, or it's taking time for the
+    // connection to happen.
+    private int mTryAgainCounter;
+
+    public NetworkTimeUpdateService(Context context) {
+        mContext = context;
+        mTime = NtpTrustedTime.getInstance(context);
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mTimeDetector = mContext.getSystemService(TimeDetector.class);
+        mCM = mContext.getSystemService(ConnectivityManager.class);
+
+        Intent pollIntent = new Intent(ACTION_POLL, null);
+        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+        mPollingIntervalMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpPollingInterval);
+        mPollingIntervalShorterMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+        mTryAgainTimesMax = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpRetry);
+
+        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, TAG);
+    }
 
     /** Initialize the receivers and initiate the first NTP request */
-    void systemRunning();
+    public void systemRunning() {
+        registerForAlarms();
+
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mHandler = new MyHandler(thread.getLooper());
+        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+        mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
+                EVENT_AUTO_TIME_ENABLED);
+        mAutoTimeSettingObserver.observe();
+    }
+
+    private void registerForAlarms() {
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+                    }
+                }, new IntentFilter(ACTION_POLL));
+    }
+
+    private void onPollNetworkTime(int event) {
+        // If we don't have any default network, don't bother.
+        if (mDefaultNetwork == null) return;
+        mWakeLock.acquire();
+        try {
+            onPollNetworkTimeUnderWakeLock(event);
+        } finally {
+            mWakeLock.release();
+        }
+    }
+
+    private void onPollNetworkTimeUnderWakeLock(int event) {
+        // Force an NTP fix when outdated
+        NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
+        if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
+            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
+            mTime.forceRefresh();
+            cachedNtpResult = mTime.getCachedTimeResult();
+        }
+
+        if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
+            // Obtained fresh fix; schedule next normal update
+            resetAlarm(mPollingIntervalMs);
+
+            // Suggest the time to the time detector. It may choose use it to set the system clock.
+            TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+                    cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
+            NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
+            timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
+            mTimeDetector.suggestNetworkTime(timeSuggestion);
+        } else {
+            // No fresh fix; schedule retry
+            mTryAgainCounter++;
+            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+                resetAlarm(mPollingIntervalShorterMs);
+            } else {
+                // Try much later
+                mTryAgainCounter = 0;
+                resetAlarm(mPollingIntervalMs);
+            }
+        }
+    }
+
+    /**
+     * Cancel old alarm and starts a new one for the specified interval.
+     *
+     * @param interval when to trigger the alarm, starting from now.
+     */
+    private void resetAlarm(long interval) {
+        mAlarmManager.cancel(mPendingPollIntent);
+        long now = SystemClock.elapsedRealtime();
+        long next = now + interval;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+    }
+
+    /** Handler to do the network accesses on */
+    private class MyHandler extends Handler {
+
+        MyHandler(Looper l) {
+            super(l);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_AUTO_TIME_ENABLED:
+                case EVENT_POLL_NETWORK_TIME:
+                case EVENT_NETWORK_CHANGED:
+                    onPollNetworkTime(msg.what);
+                    break;
+            }
+        }
+    }
+
+    private class NetworkTimeUpdateCallback extends NetworkCallback {
+        @Override
+        public void onAvailable(Network network) {
+            Log.d(TAG, String.format("New default network %s; checking time.", network));
+            mDefaultNetwork = network;
+            // Running on mHandler so invoke directly.
+            onPollNetworkTime(EVENT_NETWORK_CHANGED);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+        }
+    }
+
+    /**
+     * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
+     * is enabled.
+     */
+    private static class AutoTimeSettingObserver extends ContentObserver {
+
+        private final Context mContext;
+        private final int mMsg;
+        private final Handler mHandler;
+
+        AutoTimeSettingObserver(Context context, Handler handler, int msg) {
+            super(handler);
+            mContext = context;
+            mHandler = handler;
+            mMsg = msg;
+        }
+
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (isAutomaticTimeEnabled()) {
+                mHandler.obtainMessage(mMsg).sendToTarget();
+            }
+        }
+
+        /**
+         * Checks if the user prefers to automatically set the time.
+         */
+        private boolean isAutomaticTimeEnabled() {
+            ContentResolver resolver = mContext.getContentResolver();
+            return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        pw.print("PollingIntervalMs: ");
+        TimeUtils.formatDuration(mPollingIntervalMs, pw);
+        pw.print("\nPollingIntervalShorterMs: ");
+        TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+        pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+        pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+        NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
+        pw.println("NTP cache result: " + ntpResult);
+        if (ntpResult != null) {
+            pw.println("NTP result age: " + ntpResult.getAgeMillis());
+        }
+        pw.println();
+    }
 }
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
deleted file mode 100644
index 7894788..0000000
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.TimeDetector;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.TimestampedValue;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.NtpTrustedTime;
-import android.util.TimeUtils;
-
-import com.android.internal.util.DumpUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Monitors the network time. If looking up the network time fails for some reason, it tries a few
- * times with a short interval and then resets to checking on longer intervals.
- *
- * <p>When available, the time is always suggested to the {@link
- * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
- * system clock, depending on user settings and what other signals are available.
- */
-public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
-
-    private static final String TAG = "NetworkTimeUpdateService";
-    private static final boolean DBG = false;
-
-    private static final int EVENT_AUTO_TIME_ENABLED = 1;
-    private static final int EVENT_POLL_NETWORK_TIME = 2;
-    private static final int EVENT_NETWORK_CHANGED = 3;
-
-    private static final String ACTION_POLL =
-            "com.android.server.NetworkTimeUpdateService.action.POLL";
-
-    private static final int POLL_REQUEST = 0;
-
-    private Network mDefaultNetwork = null;
-
-    private final Context mContext;
-    private final NtpTrustedTime mTime;
-    private final AlarmManager mAlarmManager;
-    private final TimeDetector mTimeDetector;
-    private final ConnectivityManager mCM;
-    private final PendingIntent mPendingPollIntent;
-    private final PowerManager.WakeLock mWakeLock;
-
-    // NTP lookup is done on this thread and handler
-    private Handler mHandler;
-    private AutoTimeSettingObserver mAutoTimeSettingObserver;
-    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
-
-    // Normal polling frequency
-    private final long mPollingIntervalMs;
-    // Try-again polling interval, in case the network request failed
-    private final long mPollingIntervalShorterMs;
-    // Number of times to try again
-    private final int mTryAgainTimesMax;
-    // Keeps track of how many quick attempts were made to fetch NTP time.
-    // During bootup, the network may not have been up yet, or it's taking time for the
-    // connection to happen.
-    private int mTryAgainCounter;
-
-    public NetworkTimeUpdateServiceImpl(Context context) {
-        mContext = context;
-        mTime = NtpTrustedTime.getInstance(context);
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
-        mTimeDetector = mContext.getSystemService(TimeDetector.class);
-        mCM = mContext.getSystemService(ConnectivityManager.class);
-
-        Intent pollIntent = new Intent(ACTION_POLL, null);
-        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-
-        mPollingIntervalMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpPollingInterval);
-        mPollingIntervalShorterMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
-        mTryAgainTimesMax = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpRetry);
-
-        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
-                PowerManager.PARTIAL_WAKE_LOCK, TAG);
-    }
-
-    @Override
-    public void systemRunning() {
-        registerForAlarms();
-
-        HandlerThread thread = new HandlerThread(TAG);
-        thread.start();
-        mHandler = new MyHandler(thread.getLooper());
-        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
-        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
-
-        mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
-                EVENT_AUTO_TIME_ENABLED);
-        mAutoTimeSettingObserver.observe();
-    }
-
-    private void registerForAlarms() {
-        mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
-                }
-            }, new IntentFilter(ACTION_POLL));
-    }
-
-    private void onPollNetworkTime(int event) {
-        // If we don't have any default network, don't bother.
-        if (mDefaultNetwork == null) return;
-        mWakeLock.acquire();
-        try {
-            onPollNetworkTimeUnderWakeLock(event);
-        } finally {
-            mWakeLock.release();
-        }
-    }
-
-    private void onPollNetworkTimeUnderWakeLock(int event) {
-        // Force an NTP fix when outdated
-        NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
-        if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
-            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
-            mTime.forceRefresh();
-            cachedNtpResult = mTime.getCachedTimeResult();
-        }
-
-        if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
-            // Obtained fresh fix; schedule next normal update
-            resetAlarm(mPollingIntervalMs);
-
-            // Suggest the time to the time detector. It may choose use it to set the system clock.
-            TimestampedValue<Long> timeSignal = new TimestampedValue<>(
-                    cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
-            NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
-            timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
-            mTimeDetector.suggestNetworkTime(timeSuggestion);
-        } else {
-            // No fresh fix; schedule retry
-            mTryAgainCounter++;
-            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
-                resetAlarm(mPollingIntervalShorterMs);
-            } else {
-                // Try much later
-                mTryAgainCounter = 0;
-                resetAlarm(mPollingIntervalMs);
-            }
-        }
-    }
-
-    /**
-     * Cancel old alarm and starts a new one for the specified interval.
-     *
-     * @param interval when to trigger the alarm, starting from now.
-     */
-    private void resetAlarm(long interval) {
-        mAlarmManager.cancel(mPendingPollIntent);
-        long now = SystemClock.elapsedRealtime();
-        long next = now + interval;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
-    }
-
-    /** Handler to do the network accesses on */
-    private class MyHandler extends Handler {
-
-        public MyHandler(Looper l) {
-            super(l);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case EVENT_AUTO_TIME_ENABLED:
-                case EVENT_POLL_NETWORK_TIME:
-                case EVENT_NETWORK_CHANGED:
-                    onPollNetworkTime(msg.what);
-                    break;
-            }
-        }
-    }
-
-    private class NetworkTimeUpdateCallback extends NetworkCallback {
-        @Override
-        public void onAvailable(Network network) {
-            Log.d(TAG, String.format("New default network %s; checking time.", network));
-            mDefaultNetwork = network;
-            // Running on mHandler so invoke directly.
-            onPollNetworkTime(EVENT_NETWORK_CHANGED);
-        }
-
-        @Override
-        public void onLost(Network network) {
-            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
-        }
-    }
-
-    /**
-     * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
-     * is enabled.
-     */
-    private static class AutoTimeSettingObserver extends ContentObserver {
-
-        private final Context mContext;
-        private final int mMsg;
-        private final Handler mHandler;
-
-        AutoTimeSettingObserver(Context context, Handler handler, int msg) {
-            super(handler);
-            mContext = context;
-            mHandler = handler;
-            mMsg = msg;
-        }
-
-        void observe() {
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
-                    false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            if (isAutomaticTimeEnabled()) {
-                mHandler.obtainMessage(mMsg).sendToTarget();
-            }
-        }
-
-        /**
-         * Checks if the user prefers to automatically set the time.
-         */
-        private boolean isAutomaticTimeEnabled() {
-            ContentResolver resolver = mContext.getContentResolver();
-            return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
-        }
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        pw.print("PollingIntervalMs: ");
-        TimeUtils.formatDuration(mPollingIntervalMs, pw);
-        pw.print("\nPollingIntervalShorterMs: ");
-        TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
-        pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
-        pw.println("\nTryAgainCounter: " + mTryAgainCounter);
-        NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
-        pw.println("NTP cache result: " + ntpResult);
-        if (ntpResult != null) {
-            pw.println("NTP result age: " + ntpResult.getAgeMillis());
-        }
-        pw.println();
-    }
-}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 8b436c5..62743aa 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -287,7 +287,10 @@
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
+                | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+                | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+                | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+                | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES;
 
     static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
@@ -2397,8 +2400,14 @@
         }
 
         if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+            // check if calling app has either permission READ_PRECISE_PHONE_STATE
+            // or with carrier privileges
+            try {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+            } catch (SecurityException se) {
+                TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext, subId, message);
+            }
         }
 
         if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) {
@@ -2416,16 +2425,6 @@
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
         }
 
-        if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
-        }
-
-        if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
-        }
-
         if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -2436,11 +2435,6 @@
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
         }
 
-        if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
-        }
-
         return true;
     }
 
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7fd98e0..c125b1b 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -17,6 +17,7 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.debug.AdbManagerInternal;
 import android.debug.IAdbManager;
@@ -260,6 +261,30 @@
         }
     }
 
+    /**
+     * @return true if the device supports secure ADB over Wi-Fi.
+     * @hide
+     */
+    @Override
+    public boolean isAdbWifiSupported() {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.MANAGE_DEBUGGING, "AdbService");
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+    }
+
+    /**
+     * @return true if the device supports secure ADB over Wi-Fi and device pairing by
+     * QR code.
+     * @hide
+     */
+    @Override
+    public boolean isAdbWifiQrSupported() {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.MANAGE_DEBUGGING, "AdbService");
+        return isAdbWifiSupported() && mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_CAMERA_ANY);
+    }
+
     private void setAdbEnabled(boolean enable) {
         if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled);
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b311233..1452e25 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1538,6 +1538,9 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                             | Intent.FLAG_RECEIVER_FOREGROUND);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+                    // Also, add the UserHandle for mainline modules which can't use the @hide
+                    // EXTRA_USER_HANDLE.
+                    intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
                     mInjector.broadcastIntent(intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                             null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
@@ -1554,6 +1557,9 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                             | Intent.FLAG_RECEIVER_FOREGROUND);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+                    // Also, add the UserHandle for mainline modules which can't use the @hide
+                    // EXTRA_USER_HANDLE.
+                    intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
                     mInjector.broadcastIntent(intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                             null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
@@ -1563,6 +1569,9 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                         | Intent.FLAG_RECEIVER_FOREGROUND);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+                // Also, add the UserHandle for mainline modules which can't use the @hide
+                // EXTRA_USER_HANDLE.
+                intent.putExtra(Intent.EXTRA_USER, UserHandle.of(newUserId));
                 mInjector.broadcastIntent(intent,
                         null, null, 0, null, null,
                         new String[] {android.Manifest.permission.MANAGE_USERS},
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index aea6d8d..f636d67 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -116,7 +116,8 @@
                 && !lp.hasIpv4Address();
 
         // If the network tells us it doesn't use clat, respect that.
-        final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+        final boolean skip464xlat = (nai.netAgentConfig() != null)
+                && nai.netAgentConfig().skip464xlat;
 
         return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5e085ca..d66aec5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -23,9 +23,9 @@
 import android.net.INetworkMonitor;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
 import android.net.NetworkScore;
@@ -127,7 +127,7 @@
     // This should only be modified by ConnectivityService, via setNetworkCapabilities().
     // TODO: make this private with a getter.
     public NetworkCapabilities networkCapabilities;
-    public final NetworkMisc networkMisc;
+    public final NetworkAgentConfig networkAgentConfig;
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
     // This is a sticky bit; once set it is never cleared.
@@ -261,7 +261,7 @@
 
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, @NonNull NetworkScore ns, Context context,
-            Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd,
+            Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
         this.messenger = messenger;
         asyncChannel = ac;
@@ -274,7 +274,7 @@
         mConnService = connService;
         mContext = context;
         mHandler = handler;
-        networkMisc = misc;
+        networkAgentConfig = config;
         this.factorySerialNumber = factorySerialNumber;
     }
 
@@ -309,8 +309,8 @@
         return mConnService;
     }
 
-    public NetworkMisc netMisc() {
-        return networkMisc;
+    public NetworkAgentConfig netAgentConfig() {
+        return networkAgentConfig;
     }
 
     public Handler handler() {
@@ -451,15 +451,6 @@
                 && !isLingering();
     }
 
-    /**
-     * Returns whether this network is currently suspended. A network is suspended if it is still
-     * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
-     * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
-     */
-    public boolean isSuspended() {
-        return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
-    }
-
     // Does this network satisfy request?
     public boolean satisfies(NetworkRequest request) {
         return created &&
@@ -487,7 +478,8 @@
         // selected and we're trying to see what its score could be. This ensures that we don't tear
         // down an explicitly selected network before the user gets a chance to prefer it when
         // a higher-scoring network (e.g., Ethernet) is available.
-        if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+        if (networkAgentConfig.explicitlySelected
+                && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
             return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
         }
 
@@ -533,7 +525,8 @@
         synchronized (this) {
             // Network objects are outwardly immutable so there is no point in duplicating.
             // Duplicating also precludes sharing socket factories and connection pools.
-            final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
+            final String subscriberId = (networkAgentConfig != null)
+                    ? networkAgentConfig.subscriberId : null;
             return new NetworkState(new NetworkInfo(networkInfo),
                     new LinkProperties(linkProperties),
                     new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
@@ -641,13 +634,13 @@
                 + "nc{" + networkCapabilities + "}  Score{" + getCurrentScore() + "}  "
                 + "everValidated{" + everValidated + "}  lastValidated{" + lastValidated + "}  "
                 + "created{" + created + "} lingering{" + isLingering() + "} "
-                + "explicitlySelected{" + networkMisc.explicitlySelected + "} "
-                + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
+                + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} "
+                + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
                 + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
                 + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
                 + "captivePortalValidationPending{" + captivePortalValidationPending + "} "
                 + "partialConnectivity{" + partialConnectivity + "} "
-                + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
+                + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
                 + "clat{" + clatd + "} "
                 + "}";
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 08e73ef..69ab47a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -53,10 +53,10 @@
 import android.net.LocalSocketAddress;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
 import android.net.NetworkProvider;
 import android.net.RouteInfo;
 import android.net.UidRange;
@@ -914,7 +914,7 @@
      * has certain changes, in which case this method would just return {@code false}.
      */
     private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
-        // NetworkMisc cannot be updated without registering a new NetworkAgent.
+        // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
         if (oldConfig.allowBypass != mConfig.allowBypass) {
             Log.i(TAG, "Handover not possible due to changes to allowBypass");
             return false;
@@ -947,8 +947,8 @@
 
         mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
 
-        NetworkMisc networkMisc = new NetworkMisc();
-        networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
+        NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+        networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
 
         mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
         mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
@@ -957,7 +957,7 @@
         try {
             mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
                     mNetworkInfo, mNetworkCapabilities, lp,
-                    ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc,
+                    ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig,
                     NetworkProvider.ID_VPN) {
                             @Override
                             public void unwanted() {
diff --git a/services/core/java/com/android/server/lights/OWNERS b/services/core/java/com/android/server/lights/OWNERS
index c7c6d56..0e795b9 100644
--- a/services/core/java/com/android/server/lights/OWNERS
+++ b/services/core/java/com/android/server/lights/OWNERS
@@ -1,2 +1,3 @@
 michaelwr@google.com
-dangittik@google.com
+santoscordon@google.com
+flc@google.com
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c60fed0..9760185 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -169,6 +169,7 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IDeviceIdleController;
 import android.os.INetworkManagementService;
@@ -876,7 +877,8 @@
 
             // Listen for subscriber changes
             mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
-                    new OnSubscriptionsChangedListener(mHandler.getLooper()) {
+                    new HandlerExecutor(mHandler),
+                    new OnSubscriptionsChangedListener() {
                         @Override
                         public void onSubscriptionsChanged() {
                             updateNetworksInternal();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e991a91..609a415 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1159,7 +1159,7 @@
             }
             abandonSession = false;
 
-            if (!params.sessionParams.isStaged || !params.waitForStagedSessionReady) {
+            if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) {
                 pw.println("Success");
                 return 0;
             }
@@ -1197,7 +1197,7 @@
                         + si.getStagedSessionErrorMessage() + "]");
                 return 1;
             }
-            pw.println("Success");
+            pw.println("Success. Reboot device to apply staged session");
             return 0;
         } finally {
             if (abandonSession) {
@@ -2487,7 +2487,7 @@
         SessionParams sessionParams;
         String installerPackageName;
         int userId = UserHandle.USER_ALL;
-        boolean waitForStagedSessionReady = false;
+        boolean mWaitForStagedSessionReady = true;
         long timeoutMs = DEFAULT_WAIT_MS;
     }
 
@@ -2615,13 +2615,16 @@
                     sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
                     break;
                 case "--wait":
-                    params.waitForStagedSessionReady = true;
+                    params.mWaitForStagedSessionReady = true;
                     try {
                         params.timeoutMs = Long.parseLong(peekNextArg());
                         getNextArg();
                     } catch (NumberFormatException ignore) {
                     }
                     break;
+                case "--no-wait":
+                    params.mWaitForStagedSessionReady = false;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
diff --git a/services/core/java/com/android/server/policy/GlobalKeyManager.java b/services/core/java/com/android/server/policy/GlobalKeyManager.java
index e08c004..157f825 100644
--- a/services/core/java/com/android/server/policy/GlobalKeyManager.java
+++ b/services/core/java/com/android/server/policy/GlobalKeyManager.java
@@ -74,7 +74,7 @@
                 Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON)
                         .setComponent(component)
                         .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                        .putExtra(Intent.EXTRA_KEY_EVENT, event);
+                        .putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(event));
                 context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);
                 return true;
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b5b21f4..c566341 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1695,7 +1695,7 @@
             if (!isWatch && !disableNetworkTime) {
                 traceBeginAndSlog("StartNetworkTimeUpdateService");
                 try {
-                    networkTimeUpdater = new NetworkTimeUpdateServiceImpl(context);
+                    networkTimeUpdater = new NetworkTimeUpdateService(context);
                     ServiceManager.addService("network_time_update_service", networkTimeUpdater);
                 } catch (Throwable e) {
                     reportWtf("starting NetworkTimeUpdate service", e);
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
new file mode 100644
index 0000000..968a402
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.mockito.Mockito.*;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothAirplaneModeListenerTest {
+    private Context mContext;
+    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
+    private BluetoothAdapter mBluetoothAdapter;
+    private AirplaneModeHelper mHelper;
+
+    @Mock BluetoothManagerService mBluetoothManagerService;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+
+        mHelper = mock(AirplaneModeHelper.class);
+        when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
+                .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
+        doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
+        doNothing().when(mHelper).showToastMessage();
+        doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class));
+
+        mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
+                    mBluetoothManagerService, Looper.getMainLooper(), mContext);
+        mBluetoothAirplaneModeListener.start(mHelper);
+    }
+
+    @Test
+    public void testIgnoreOnAirplanModeChange() {
+        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+
+        when(mHelper.isBluetoothOn()).thenReturn(true);
+        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+
+        when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+
+        when(mHelper.isAirplaneModeOn()).thenReturn(true);
+        Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+    }
+
+    @Test
+    public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() {
+        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
+        verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService);
+    }
+
+    @Test
+    public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() {
+        mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
+        when(mHelper.isBluetoothOn()).thenReturn(true);
+        when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+        when(mHelper.isAirplaneModeOn()).thenReturn(true);
+        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
+
+        verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
+                BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
+        verify(mHelper, times(0)).showToastMessage();
+        verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
+    }
+
+    @Test
+    public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() {
+        mBluetoothAirplaneModeListener.mToastCount = 0;
+        when(mHelper.isBluetoothOn()).thenReturn(true);
+        when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+        when(mHelper.isAirplaneModeOn()).thenReturn(true);
+        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
+
+        verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
+                BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
+        verify(mHelper).showToastMessage();
+        verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
+    }
+
+    @Test
+    public void testIsPopToast_PopToast() {
+        mBluetoothAirplaneModeListener.mToastCount = 0;
+        Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast());
+        verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1);
+    }
+
+    @Test
+    public void testIsPopToast_NotPopToast() {
+        mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
+        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast());
+        verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt());
+    }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 826a89e..acf51f3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -547,8 +547,14 @@
          */
         public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000;
 
+        /**
+         * Indicates that the call is an adhoc conference call. This property can be set for both
+         * incoming and outgoing calls.
+         */
+        public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00002000
+        // Next PROPERTY value: 0x00004000
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -726,6 +732,9 @@
             if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) {
                 builder.append(" PROPERTY_VOIP_AUDIO_MODE");
             }
+            if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) {
+                builder.append(" PROPERTY_IS_ADHOC_CONFERENCE");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 456290c..6b0845f 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -69,6 +69,7 @@
         public void onConnectionEvent(Conference c, String event, Bundle extras) {}
         public void onCallerDisplayNameChanged(
                 Conference c, String callerDisplayName, int presentation) {}
+        public void onRingbackRequested(Conference c, boolean ringback) {}
     }
 
     private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -97,6 +98,7 @@
     private int mAddressPresentation;
     private String mCallerDisplayName;
     private int mCallerDisplayNamePresentation;
+    private boolean mRingbackRequested = false;
 
     private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
         @Override
@@ -170,6 +172,14 @@
     }
 
     /**
+     * Returns whether this conference is requesting that the system play a ringback tone
+     * on its behalf.
+     */
+    public final boolean isRingbackRequested() {
+        return mRingbackRequested;
+    }
+
+    /**
      * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
      * {@link Connection} for valid values.
      *
@@ -308,6 +318,35 @@
     public void onConnectionAdded(Connection connection) {}
 
     /**
+     * Notifies this Conference, which is in {@code STATE_RINGING}, of
+     * a request to accept.
+     * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+     * the default dialer's {@link InCallService}.
+     *
+     * @param videoState The video state in which to answer the connection.
+     */
+    public void onAnswer(int videoState) {}
+
+    /**
+     * Notifies this Conference, which is in {@code STATE_RINGING}, of
+     * a request to accept.
+     * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+     * the default dialer's {@link InCallService}.
+     * @hide
+     */
+    public final void onAnswer() {
+         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    /**
+     * Notifies this Conference, which is in {@code STATE_RINGING}, of
+     * a request to reject.
+     * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+     * the default dialer's {@link InCallService}.
+     */
+    public void onReject() {}
+
+    /**
      * Sets state to be on hold.
      */
     public final void setOnHold() {
@@ -322,9 +361,17 @@
     }
 
     /**
+     * Sets state to be ringing.
+     */
+    public final void setRinging() {
+        setState(Connection.STATE_RINGING);
+    }
+
+    /**
      * Sets state to be active.
      */
     public final void setActive() {
+        setRingbackRequested(false);
         setState(Connection.STATE_ACTIVE);
     }
 
@@ -436,6 +483,21 @@
     }
 
     /**
+     * Requests that the framework play a ringback tone. This is to be invoked by implementations
+     * that do not play a ringback tone themselves in the conference's audio stream.
+     *
+     * @param ringback Whether the ringback tone is to be played.
+     */
+    public final void setRingbackRequested(boolean ringback) {
+        if (mRingbackRequested != ringback) {
+            mRingbackRequested = ringback;
+            for (Listener l : mListeners) {
+                l.onRingbackRequested(this, ringback);
+            }
+        }
+    }
+
+    /**
      * Set the video state for the conference.
      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
      * {@link VideoProfile#STATE_BIDIRECTIONAL},
@@ -640,14 +702,6 @@
     }
 
     private void setState(int newState) {
-        if (newState != Connection.STATE_ACTIVE &&
-                newState != Connection.STATE_HOLDING &&
-                newState != Connection.STATE_DISCONNECTED) {
-            Log.w(this, "Unsupported state transition for Conference call.",
-                    Connection.stateToString(newState));
-            return;
-        }
-
         if (mState != newState) {
             int oldState = mState;
             mState = newState;
@@ -657,6 +711,37 @@
         }
     }
 
+    private static class FailureSignalingConference extends Conference {
+        private boolean mImmutable = false;
+        public FailureSignalingConference(DisconnectCause disconnectCause,
+                PhoneAccountHandle phoneAccount) {
+            super(phoneAccount);
+            setDisconnected(disconnectCause);
+            mImmutable = true;
+        }
+        public void checkImmutable() {
+            if (mImmutable) {
+                throw new UnsupportedOperationException("Conference is immutable");
+            }
+        }
+    }
+
+    /**
+     * Return a {@code Conference} which represents a failed conference attempt. The returned
+     * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
+     * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
+     * <p>
+     * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
+     * so users of this method need not maintain a reference to its return value to destroy it.
+     *
+     * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
+     * @return A {@code Conference} which indicates failure.
+     */
+    public @NonNull static Conference createFailedConference(
+            @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
+        return new FailureSignalingConference(disconnectCause, phoneAccount);
+    }
+
     private final void clearConferenceableList() {
         for (Connection c : mConferenceableConnections) {
             c.removeConnectionListener(mConnectionDeathListener);
@@ -667,11 +752,13 @@
     @Override
     public String toString() {
         return String.format(Locale.US,
-                "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
+                "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
+                + "isRingbackRequested: %s, ThisObject %s]",
                 Connection.stateToString(mState),
                 Call.Details.capabilitiesToString(mConnectionCapabilities),
                 getVideoState(),
                 getVideoProvider(),
+                isRingbackRequested() ? "Y" : "N",
                 super.toString());
     }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index f205ec6..c934625 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -497,8 +497,17 @@
     @TestApi
     public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
 
+    /**
+     * Set by the framework to indicate that it is an adhoc conference call.
+     * <p>
+     * This is used for Outgoing and incoming conference calls.
+     *
+     */
+    public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
+
+
     //**********************************************************************************************
-    // Next PROPERTY value: 1<<12
+    // Next PROPERTY value: 1<<13
     //**********************************************************************************************
 
     /**
@@ -1018,6 +1027,10 @@
             builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst");
         }
 
+        if ((properties & PROPERTY_IS_ADHOC_CONFERENCE) == PROPERTY_IS_ADHOC_CONFERENCE) {
+            builder.append(isLong ? " PROPERTY_IS_ADHOC_CONFERENCE" : " adhoc_conf");
+        }
+
         builder.append("]");
         return builder.toString();
     }
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 221f8f1..6d7ceca 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -26,6 +26,9 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Simple data container encapsulating a request to some entity to
  * create a new {@link Connection}.
@@ -46,6 +49,8 @@
         private boolean mShouldShowIncomingCallUi = false;
         private ParcelFileDescriptor mRttPipeToInCall;
         private ParcelFileDescriptor mRttPipeFromInCall;
+        private List<Uri> mParticipants;
+        private boolean mIsAdhocConference = false;
 
         public Builder() { }
 
@@ -59,6 +64,15 @@
         }
 
         /**
+         * Sets the participants for the resulting {@link ConnectionRequest}
+         * @param participants The participants to which the {@link Connection} is to connect.
+         */
+        public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+            this.mParticipants = participants;
+            return this;
+        }
+
+        /**
          * Sets the address for the resulting {@link ConnectionRequest}
          * @param address The address(e.g., phone number) to which the {@link Connection} is to
          *                connect.
@@ -108,6 +122,16 @@
         }
 
         /**
+         * Sets isAdhocConference for the resulting {@link ConnectionRequest}
+         * @param isAdhocConference {@code true} if it is a adhoc conference call
+         *                          {@code false}, if not a adhoc conference call
+         */
+        public @NonNull Builder setIsAdhocConferenceCall(boolean isAdhocConference) {
+            this.mIsAdhocConference = isAdhocConference;
+            return this;
+        }
+
+        /**
          * Sets the RTT pipe for transferring text into the {@link ConnectionService} for the
          * resulting {@link ConnectionRequest}
          * @param rttPipeFromInCall The data pipe to read from.
@@ -141,7 +165,9 @@
                     mTelecomCallId,
                     mShouldShowIncomingCallUi,
                     mRttPipeFromInCall,
-                    mRttPipeToInCall);
+                    mRttPipeToInCall,
+                    mParticipants,
+                    mIsAdhocConference);
         }
     }
 
@@ -155,6 +181,8 @@
     private final ParcelFileDescriptor mRttPipeFromInCall;
     // Cached return value of getRttTextStream -- we don't want to wrap it more than once.
     private Connection.RttTextStream mRttTextStream;
+    private List<Uri> mParticipants;
+    private final boolean mIsAdhocConference;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
@@ -214,6 +242,21 @@
             boolean shouldShowIncomingCallUi,
             ParcelFileDescriptor rttPipeFromInCall,
             ParcelFileDescriptor rttPipeToInCall) {
+        this(accountHandle, handle, extras, videoState, telecomCallId,
+                shouldShowIncomingCallUi, rttPipeFromInCall, rttPipeToInCall, null, false);
+    }
+
+    private ConnectionRequest(
+            PhoneAccountHandle accountHandle,
+            Uri handle,
+            Bundle extras,
+            int videoState,
+            String telecomCallId,
+            boolean shouldShowIncomingCallUi,
+            ParcelFileDescriptor rttPipeFromInCall,
+            ParcelFileDescriptor rttPipeToInCall,
+            List<Uri> participants,
+            boolean isAdhocConference) {
         mAccountHandle = accountHandle;
         mAddress = handle;
         mExtras = extras;
@@ -222,6 +265,8 @@
         mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
         mRttPipeFromInCall = rttPipeFromInCall;
         mRttPipeToInCall = rttPipeToInCall;
+        mParticipants = participants;
+        mIsAdhocConference = isAdhocConference;
     }
 
     private ConnectionRequest(Parcel in) {
@@ -233,6 +278,11 @@
         mShouldShowIncomingCallUi = in.readInt() == 1;
         mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader());
         mRttPipeToInCall = in.readParcelable(getClass().getClassLoader());
+
+        mParticipants = new ArrayList<Uri>();
+        in.readList(mParticipants, getClass().getClassLoader());
+
+        mIsAdhocConference = in.readInt() == 1;
     }
 
     /**
@@ -246,6 +296,11 @@
     public Uri getAddress() { return mAddress; }
 
     /**
+     * The participants to which the {@link Connection} is to connect.
+     */
+    public @Nullable List<Uri> getParticipants() { return mParticipants; }
+
+    /**
      * Application-specific extra data. Used for passing back information from an incoming
      * call {@code Intent}, and for any proprietary extensions arranged between a client
      * and servant {@code ConnectionService} which agree on a vocabulary for such data.
@@ -290,6 +345,13 @@
     }
 
     /**
+     * @return {@code true} if the call is a adhoc conference call else @return {@code false}
+     */
+    public boolean isAdhocConferenceCall() {
+        return mIsAdhocConference;
+    }
+
+    /**
      * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection
      * service to the in-call UI. In order to obtain an
      * {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use
@@ -345,11 +407,12 @@
 
     @Override
     public String toString() {
-        return String.format("ConnectionRequest %s %s",
+        return String.format("ConnectionRequest %s %s isAdhocConf: %s",
                 mAddress == null
                         ? Uri.EMPTY
                         : Connection.toLogSafePhoneNumber(mAddress.toString()),
-                bundleToString(mExtras));
+                bundleToString(mExtras),
+                isAdhocConferenceCall() ? "Y" : "N");
     }
 
     private static String bundleToString(Bundle extras){
@@ -406,5 +469,7 @@
         destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
         destination.writeParcelable(mRttPipeFromInCall, 0);
         destination.writeParcelable(mRttPipeToInCall, 0);
+        destination.writeList(mParticipants);
+        destination.writeInt(mIsAdhocConference ? 1 : 0);
     }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 3a0494e..440f044 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -154,6 +154,9 @@
     private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
     private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
     private static final String SESSION_HANDOVER_FAILED = "CS.haF";
+    private static final String SESSION_CREATE_CONF = "CS.crConf";
+    private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC";
+    private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF";
 
     private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
     private static final int MSG_CREATE_CONNECTION = 2;
@@ -188,6 +191,9 @@
     private static final int MSG_HANDOVER_FAILED = 32;
     private static final int MSG_HANDOVER_COMPLETE = 33;
     private static final int MSG_DEFLECT = 34;
+    private static final int MSG_CREATE_CONFERENCE = 35;
+    private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
+    private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
 
     private static Connection sNullConnection;
 
@@ -291,6 +297,63 @@
         }
 
         @Override
+        public void createConference(
+                PhoneAccountHandle connectionManagerPhoneAccount,
+                String id,
+                ConnectionRequest request,
+                boolean isIncoming,
+                boolean isUnknown,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONF);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = connectionManagerPhoneAccount;
+                args.arg2 = id;
+                args.arg3 = request;
+                args.arg4 = Log.createSubsession();
+                args.argi1 = isIncoming ? 1 : 0;
+                args.argi2 = isUnknown ? 1 : 0;
+                mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void createConferenceComplete(String id, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = id;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void createConferenceFailed(
+                PhoneAccountHandle connectionManagerPhoneAccount,
+                String callId,
+                ConnectionRequest request,
+                boolean isIncoming,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = request;
+                args.arg3 = Log.createSubsession();
+                args.arg4 = connectionManagerPhoneAccount;
+                args.argi1 = isIncoming ? 1 : 0;
+                mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void handoverFailed(String callId, ConnectionRequest request, int reason,
                                    Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
@@ -802,6 +865,106 @@
                     }
                     break;
                 }
+                case MSG_CREATE_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
+                    try {
+                        final PhoneAccountHandle connectionManagerPhoneAccount =
+                                (PhoneAccountHandle) args.arg1;
+                        final String id = (String) args.arg2;
+                        final ConnectionRequest request = (ConnectionRequest) args.arg3;
+                        final boolean isIncoming = args.argi1 == 1;
+                        final boolean isUnknown = args.argi2 == 1;
+                        if (!mAreAccountsInitialized) {
+                            Log.d(this, "Enqueueing pre-initconference request %s", id);
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR",
+                                            null /*lock*/) {
+                                @Override
+                                public void loggedRun() {
+                                    createConference(connectionManagerPhoneAccount,
+                                            id,
+                                            request,
+                                            isIncoming,
+                                            isUnknown);
+                                }
+                            }.prepare());
+                        } else {
+                            createConference(connectionManagerPhoneAccount,
+                                    id,
+                                    request,
+                                    isIncoming,
+                                    isUnknown);
+                        }
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+                case MSG_CREATE_CONFERENCE_COMPLETE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2,
+                            SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE);
+                    try {
+                        final String id = (String) args.arg1;
+                        if (!mAreAccountsInitialized) {
+                            Log.d(this, "Enqueueing pre-init conference request %s", id);
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE
+                                                    + ".pIConfR",
+                                            null /*lock*/) {
+                                        @Override
+                                        public void loggedRun() {
+                                            notifyCreateConferenceComplete(id);
+                                        }
+                                    }.prepare());
+                        } else {
+                            notifyCreateConferenceComplete(id);
+                        }
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+                case MSG_CREATE_CONFERENCE_FAILED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3, SESSION_HANDLER +
+                            SESSION_CREATE_CONN_FAILED);
+                    try {
+                        final String id = (String) args.arg1;
+                        final ConnectionRequest request = (ConnectionRequest) args.arg2;
+                        final boolean isIncoming = args.argi1 == 1;
+                        final PhoneAccountHandle connectionMgrPhoneAccount =
+                                (PhoneAccountHandle) args.arg4;
+                        if (!mAreAccountsInitialized) {
+                            Log.d(this, "Enqueueing pre-init conference request %s", id);
+                            mPreInitializationConnectionRequests.add(
+                                    new android.telecom.Logging.Runnable(
+                                            SESSION_HANDLER + SESSION_CREATE_CONF_FAILED
+                                                    + ".pIConfR",
+                                            null /*lock*/) {
+                                        @Override
+                                        public void loggedRun() {
+                                            createConferenceFailed(connectionMgrPhoneAccount, id,
+                                                    request, isIncoming);
+                                        }
+                                    }.prepare());
+                        } else {
+                            Log.i(this, "createConferenceFailed %s", id);
+                            createConferenceFailed(connectionMgrPhoneAccount, id, request,
+                                    isIncoming);
+                        }
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+
                 case MSG_HANDOVER_FAILED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     Log.continueSession((Session) args.arg3, SESSION_HANDLER +
@@ -1162,6 +1325,12 @@
         public void onStateChanged(Conference conference, int oldState, int newState) {
             String id = mIdByConference.get(conference);
             switch (newState) {
+                case Connection.STATE_RINGING:
+                    mAdapter.setRinging(id);
+                    break;
+                case Connection.STATE_DIALING:
+                    mAdapter.setDialing(id);
+                    break;
                 case Connection.STATE_ACTIVE:
                     mAdapter.setActive(id);
                     break;
@@ -1292,6 +1461,13 @@
                 mAdapter.onConnectionEvent(id, event, extras);
             }
         }
+
+        @Override
+        public void onRingbackRequested(Conference c, boolean ringback) {
+            String id = mIdByConference.get(c);
+            Log.d(this, "Adapter conference onRingback %b", ringback);
+            mAdapter.setRingbackRequested(id, ringback);
+        }
     };
 
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -1534,6 +1710,70 @@
         return super.onUnbind(intent);
     }
 
+
+    /**
+     * This can be used by telecom to either create a new outgoing conference call or attach
+     * to an existing incoming conference call. In either case, telecom will cycle through a
+     * set of services and call createConference until a connection service cancels the process
+     * or completes it successfully.
+     */
+    private void createConference(
+            final PhoneAccountHandle callManagerAccount,
+            final String callId,
+            final ConnectionRequest request,
+            boolean isIncoming,
+            boolean isUnknown) {
+
+        Conference conference = null;
+        conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request)
+                    : onCreateOutgoingConference(callManagerAccount, request);
+
+        Log.d(this, "createConference, conference: %s", conference);
+        if (conference == null) {
+            Log.i(this, "createConference, implementation returned null conference.");
+            conference = Conference.createFailedConference(
+                    new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"),
+                    request.getAccountHandle());
+        }
+        if (conference.getExtras() != null) {
+            conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+        }
+        mConferenceById.put(callId, conference);
+        mIdByConference.put(conference, callId);
+        conference.addListener(mConferenceListener);
+        ParcelableConference parcelableConference = new ParcelableConference(
+                request.getAccountHandle(),
+                conference.getState(),
+                conference.getConnectionCapabilities(),
+                conference.getConnectionProperties(),
+                Collections.<String>emptyList(), //connectionIds
+                conference.getVideoProvider() == null ?
+                        null : conference.getVideoProvider().getInterface(),
+                conference.getVideoState(),
+                conference.getConnectTimeMillis(),
+                conference.getConnectionStartElapsedRealTime(),
+                conference.getStatusHints(),
+                conference.getExtras(),
+                conference.getAddress(),
+                conference.getAddressPresentation(),
+                conference.getCallerDisplayName(),
+                conference.getCallerDisplayNamePresentation(),
+                conference.getDisconnectCause(),
+                conference.isRingbackRequested());
+        if (conference.getState() != Connection.STATE_DISCONNECTED) {
+            conference.setTelecomCallId(callId);
+            mAdapter.setVideoProvider(callId, conference.getVideoProvider());
+            mAdapter.setVideoState(callId, conference.getVideoState());
+            onConferenceAdded(conference);
+        }
+
+        Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId);
+        mAdapter.handleCreateConferenceComplete(
+                callId,
+                request,
+                parcelableConference);
+    }
+
     /**
      * This can be used by telecom to either create a new outgoing call or attach to an existing
      * incoming call. In either case, telecom will cycle through a set of services and call
@@ -1645,6 +1885,18 @@
         }
     }
 
+    private void createConferenceFailed(final PhoneAccountHandle callManagerAccount,
+                                        final String callId, final ConnectionRequest request,
+                                        boolean isIncoming) {
+
+        Log.i(this, "createConferenceFailed %s", callId);
+        if (isIncoming) {
+            onCreateIncomingConferenceFailed(callManagerAccount, request);
+        } else {
+            onCreateOutgoingConferenceFailed(callManagerAccount, request);
+        }
+    }
+
     private void handoverFailed(final String callId, final ConnectionRequest request,
                                         int reason) {
 
@@ -1669,6 +1921,24 @@
                 "notifyCreateConnectionComplete"));
     }
 
+    /**
+     * Called by Telecom when the creation of a new Conference has completed and it is now added
+     * to Telecom.
+     * @param callId The ID of the connection.
+     */
+    private void notifyCreateConferenceComplete(final String callId) {
+        Log.i(this, "notifyCreateConferenceComplete %s", callId);
+        if (callId == null) {
+            // This could happen if the conference fails quickly and is removed from the
+            // ConnectionService before Telecom sends the create conference complete callback.
+            Log.w(this, "notifyCreateConferenceComplete: callId is null.");
+            return;
+        }
+        onCreateConferenceComplete(findConferenceForAction(callId,
+                "notifyCreateConferenceComplete"));
+    }
+
+
     private void abort(String callId) {
         Log.d(this, "abort %s", callId);
         findConnectionForAction(callId, "abort").onAbort();
@@ -1676,12 +1946,20 @@
 
     private void answerVideo(String callId, int videoState) {
         Log.d(this, "answerVideo %s", callId);
-        findConnectionForAction(callId, "answer").onAnswer(videoState);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "answer").onAnswer(videoState);
+        } else {
+            findConferenceForAction(callId, "answer").onAnswer(videoState);
+        }
     }
 
     private void answer(String callId) {
         Log.d(this, "answer %s", callId);
-        findConnectionForAction(callId, "answer").onAnswer();
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "answer").onAnswer();
+        } else {
+            findConferenceForAction(callId, "answer").onAnswer();
+        }
     }
 
     private void deflect(String callId, Uri address) {
@@ -1691,7 +1969,11 @@
 
     private void reject(String callId) {
         Log.d(this, "reject %s", callId);
-        findConnectionForAction(callId, "reject").onReject();
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "reject").onReject();
+        } else {
+            findConferenceForAction(callId, "reject").onReject();
+        }
     }
 
     private void reject(String callId, String rejectWithMessage) {
@@ -2198,6 +2480,21 @@
             ConnectionRequest request) {
         return null;
     }
+    /**
+     * Create a {@code Connection} given an incoming request. This is used to attach to existing
+     * incoming conference call.
+     *
+     * @param connectionManagerPhoneAccount See description at
+     *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     * @param request Details about the incoming call.
+     * @return The {@code Connection} object to satisfy this call, or {@code null} to
+     *         not handle the call.
+     */
+    public @Nullable Conference onCreateIncomingConference(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+        return null;
+    }
 
     /**
      * Called after the {@link Connection} returned by
@@ -2212,6 +2509,19 @@
     }
 
     /**
+     * Called after the {@link Conference} returned by
+     * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)}
+     * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been
+     * added to the {@link ConnectionService} and sent to Telecom.
+     *
+     * @param conference the {@link Conference}.
+     * @hide
+     */
+    public void onCreateConferenceComplete(Conference conference) {
+    }
+
+
+    /**
      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
      * incoming {@link Connection} was denied.
      * <p>
@@ -2250,6 +2560,47 @@
     }
 
     /**
+     * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+     * incoming {@link Conference} was denied.
+     * <p>
+     * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+     * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time.
+     * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+     * {@link Conference}.
+     * <p>
+     * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+     *
+     * @param connectionManagerPhoneAccount See description at
+     *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     * @param request The incoming connection request.
+     */
+    public void onCreateIncomingConferenceFailed(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+    }
+
+    /**
+     * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+     * outgoing {@link Conference} was denied.
+     * <p>
+     * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+     * {@link Conference}, but Telecom has determined that the call cannot be placed at this time.
+     * The {@link ConnectionService} is responisible for informing the user that the
+     * {@link Conference} cannot be made at this time.
+     * <p>
+     * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+     *
+     * @param connectionManagerPhoneAccount See description at
+     *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     * @param request The outgoing connection request.
+     */
+    public void onCreateOutgoingConferenceFailed(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+    }
+
+
+    /**
      * Trigger recalculate functinality for conference calls. This is used when a Telephony
      * Connection is part of a conference controller but is not yet added to Connection
      * Service and hence cannot be added to the conference call.
@@ -2289,6 +2640,36 @@
     }
 
     /**
+     * Create a {@code Conference} given an outgoing request. This is used to initiate new
+     * outgoing conference call.
+     *
+     * @param connectionManagerPhoneAccount The connection manager account to use for managing
+     *         this call.
+     *         <p>
+     *         If this parameter is not {@code null}, it means that this {@code ConnectionService}
+     *         has registered one or more {@code PhoneAccount}s having
+     *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
+     *         one of these {@code PhoneAccount}s, while the {@code request} will contain another
+     *         (usually but not always distinct) {@code PhoneAccount} to be used for actually
+     *         making the connection.
+     *         <p>
+     *         If this parameter is {@code null}, it means that this {@code ConnectionService} is
+     *         being asked to make a direct connection. The
+     *         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
+     *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
+     *         making the connection.
+     * @param request Details about the outgoing call.
+     * @return The {@code Conference} object to satisfy this call, or the result of an invocation
+     *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     */
+    public @Nullable Conference onCreateOutgoingConference(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @Nullable ConnectionRequest request) {
+        return null;
+    }
+
+
+    /**
      * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
      * outgoing handover {@link Connection}.
      * <p>
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 04e930c..8f27323 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -100,6 +100,19 @@
         }
     }
 
+    void handleCreateConferenceComplete(
+            String id,
+            ConnectionRequest request,
+            ParcelableConference conference) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.handleCreateConferenceComplete(id, request, conference,
+                        Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /**
      * Sets a call's state to active (e.g., an ongoing call where two parties can actively
      * communicate).
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 60b2172f..79ad51b 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -75,6 +75,7 @@
     private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
     private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
     private static final int MSG_SET_CONFERENCE_STATE = 36;
+    private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -103,6 +104,19 @@
                     }
                     break;
                 }
+                case MSG_HANDLE_CREATE_CONFERENCE_COMPLETE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.handleCreateConferenceComplete(
+                                (String) args.arg1,
+                                (ConnectionRequest) args.arg2,
+                                (ParcelableConference) args.arg3,
+                                null /*Session.Info*/);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
                 case MSG_SET_ACTIVE:
                     mDelegate.setActive((String) msg.obj, null /*Session.Info*/);
                     break;
@@ -366,6 +380,20 @@
         }
 
         @Override
+        public void handleCreateConferenceComplete(
+                String id,
+                ConnectionRequest request,
+                ParcelableConference conference,
+                Session.Info sessionInfo) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = id;
+            args.arg2 = request;
+            args.arg3 = conference;
+            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+        }
+
+
+        @Override
         public void setActive(String connectionId, Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
         }
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index ede0594..90b69a3 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -47,6 +47,34 @@
     private final int mAddressPresentation;
     private final String mCallerDisplayName;
     private final int mCallerDisplayNamePresentation;
+    private DisconnectCause mDisconnectCause;
+    private boolean mRingbackRequested;
+
+    public ParcelableConference(
+            PhoneAccountHandle phoneAccount,
+            int state,
+            int connectionCapabilities,
+            int connectionProperties,
+            List<String> connectionIds,
+            IVideoProvider videoProvider,
+            int videoState,
+            long connectTimeMillis,
+            long connectElapsedTimeMillis,
+            StatusHints statusHints,
+            Bundle extras,
+            Uri address,
+            int addressPresentation,
+            String callerDisplayName,
+            int callerDisplayNamePresentation,
+            DisconnectCause disconnectCause,
+            boolean ringbackRequested) {
+        this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds,
+                videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis,
+                statusHints, extras, address, addressPresentation, callerDisplayName,
+                callerDisplayNamePresentation);
+        mDisconnectCause = disconnectCause;
+        mRingbackRequested = ringbackRequested;
+    }
 
     public ParcelableConference(
             PhoneAccountHandle phoneAccount,
@@ -79,6 +107,8 @@
         mAddressPresentation = addressPresentation;
         mCallerDisplayName = callerDisplayName;
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+        mDisconnectCause = null;
+        mRingbackRequested = false;
     }
 
     @Override
@@ -100,6 +130,10 @@
                 .append(mVideoState)
                 .append(", VideoProvider: ")
                 .append(mVideoProvider)
+                .append(", isRingbackRequested: ")
+                .append(mRingbackRequested)
+                .append(", disconnectCause: ")
+                .append(mDisconnectCause)
                 .toString();
     }
 
@@ -151,6 +185,13 @@
         return mAddress;
     }
 
+    public final DisconnectCause getDisconnectCause() {
+        return mDisconnectCause;
+    }
+
+    public boolean isRingbackRequested() {
+        return mRingbackRequested;
+    }
     public int getHandlePresentation() {
         return mAddressPresentation;
     }
@@ -177,11 +218,14 @@
             int addressPresentation = source.readInt();
             String callerDisplayName = source.readString();
             int callerDisplayNamePresentation = source.readInt();
+            DisconnectCause disconnectCause = source.readParcelable(classLoader);
+            boolean isRingbackRequested = source.readInt() == 1;
 
             return new ParcelableConference(phoneAccount, state, capabilities, properties,
                     connectionIds, videoCallProvider, videoState, connectTimeMillis,
                     connectElapsedTimeMillis, statusHints, extras, address, addressPresentation,
-                    callerDisplayName, callerDisplayNamePresentation);
+                    callerDisplayName, callerDisplayNamePresentation, disconnectCause,
+                    isRingbackRequested);
         }
 
         @Override
@@ -215,5 +259,7 @@
         destination.writeInt(mAddressPresentation);
         destination.writeString(mCallerDisplayName);
         destination.writeInt(mCallerDisplayNamePresentation);
+        destination.writeParcelable(mDisconnectCause, 0);
+        destination.writeInt(mRingbackRequested ? 1 : 0);
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index bb858cb..abb210f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -331,7 +331,17 @@
      */
     public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
 
-    /* NEXT CAPABILITY: 0x4000 */
+    /**
+     * An adhoc conference call is established by providing a list of addresses to
+     * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+     * {@link ConnectionService} is responsible for connecting all indicated participants
+     * to a conference simultaneously.
+     * This is in contrast to conferences formed by merging calls together (e.g. using
+     * {@link android.telecom.Call#mergeConference()}).
+     */
+    public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000;
+
+    /* NEXT CAPABILITY: 0x8000 */
 
     /**
      * URI scheme for telephone number URIs.
@@ -1054,6 +1064,9 @@
         if (hasCapabilities(CAPABILITY_RTT)) {
             sb.append("Rtt");
         }
+        if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) {
+            sb.append("AdhocConf");
+        }
         return sb.toString();
     }
 
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 1e73bd6..76640e0 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -101,6 +101,14 @@
         }
 
         @Override
+        public void handleCreateConferenceComplete(
+                String id,
+                ConnectionRequest request,
+                ParcelableConference parcel,
+                Session.Info info) {
+        }
+
+        @Override
         public void setActive(String callId, Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setActive")
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ffb2779..f1dca03 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1803,6 +1803,45 @@
     }
 
     /**
+     * Registers a new incoming conference. A {@link ConnectionService} should invoke this method
+     * when it has an incoming conference. For managed {@link ConnectionService}s, the specified
+     * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+     * the user must have enabled the corresponding {@link PhoneAccount}.  This can be checked using
+     * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+     * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+     * <p>
+     * The incoming conference you are adding is assumed to have a video state of
+     * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value
+     * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified.
+     * <p>
+     * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+     * associated with the {@link PhoneAccountHandle} and request additional information about the
+     * call (See {@link ConnectionService#onCreateIncomingConference}) before starting the incoming
+     * call UI.
+     * <p>
+     * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+     * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+     * the associated {@link PhoneAccount} is not currently enabled by the user.
+     *
+     * @param phoneAccount A {@link PhoneAccountHandle} registered with
+     *            {@link #registerPhoneAccount}.
+     * @param extras A bundle that will be passed through to
+     *            {@link ConnectionService#onCreateIncomingConference}.
+     */
+
+    public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
+            @NonNull Bundle extras) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().addNewIncomingConference(
+                        phoneAccount, extras == null ? new Bundle() : extras);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
+        }
+    }
+
+    /**
      * Registers a new unknown call with Telecom. This can only be called by the system Telephony
      * service. This is invoked when Telephony detects a new unknown connection that was neither
      * a new incoming call, nor an user-initiated outgoing call.
@@ -2006,6 +2045,42 @@
         }
     }
 
+
+    /**
+     * Place a new conference call with the provided participants using the system telecom service
+     * This method doesn't support placing of emergency calls.
+     *
+     * An adhoc conference call is established by providing a list of addresses to
+     * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+     * {@link ConnectionService} is responsible for connecting all indicated participants
+     * to a conference simultaneously.
+     * This is in contrast to conferences formed by merging calls together (e.g. using
+     * {@link android.telecom.Call#mergeConference()}).
+     *
+     * The following keys are supported in the supplied extras.
+     * <ul>
+     *   <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
+     *   <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
+     *   <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
+     * </ul>
+     *
+     * @param participants List of participants to start conference with
+     * @param extras Bundle of extras to use with the call
+     */
+    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    public void startConference(@NonNull List<Uri> participants,
+            @NonNull Bundle extras) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                service.startConference(participants, extras,
+                        mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+            }
+        }
+    }
+
     /**
      * Enables and disables specified phone account.
      *
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e35093c..96f2483 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -53,6 +53,20 @@
     void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
             in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
 
+    void createConference(
+            in PhoneAccountHandle connectionManagerPhoneAccount,
+            String callId,
+            in ConnectionRequest request,
+            boolean isIncoming,
+            boolean isUnknown,
+            in Session.Info sessionInfo);
+
+    void createConferenceComplete(String callId, in Session.Info sessionInfo);
+
+    void createConferenceFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
+            in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
+
+
     void abort(String callId, in Session.Info sessionInfo);
 
     void answerVideo(String callId, int videoState, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9cf098c..4f63e08 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -44,6 +44,12 @@
             in ParcelableConnection connection,
             in Session.Info sessionInfo);
 
+    void handleCreateConferenceComplete(
+            String callId,
+            in ConnectionRequest request,
+            in ParcelableConference connection,
+            in Session.Info sessionInfo);
+
     void setActive(String callId, in Session.Info sessionInfo);
 
     void setRinging(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 204c37e..9a47ae1 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -242,11 +242,22 @@
     void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
 
     /**
+     * @see TelecomServiceImpl#addNewIncomingConference
+     */
+    void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+    /**
      * @see TelecomServiceImpl#addNewUnknownCall
      */
     void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
 
     /**
+     * @see TelecomServiceImpl#startConference
+     */
+    void startConference(in List<Uri> participants, in Bundle extras,
+            String callingPackage);
+
+    /**
      * @see TelecomServiceImpl#placeCall
      */
     void placeCall(in Uri handle, in Bundle extras, String callingPackage);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 61e67be..71aaa6e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1079,6 +1079,14 @@
     public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
             "ignore_rtt_mode_setting_bool";
 
+
+    /**
+     * Determines whether adhoc conference calls are supported by a carrier.  When {@code true},
+     * adhoc conference calling is supported, {@code false otherwise}.
+     */
+    public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
+            "support_adhoc_conference_calls_bool";
+
     /**
      * Determines whether conference calls are supported by a carrier.  When {@code true},
      * conference calling is supported, {@code false otherwise}.
@@ -3111,7 +3119,6 @@
          * 1 - yes. This is default.
          * @hide
          */
-        // TODO(b/119567985): name this key properly
         public static final String KEY_SUPL_ES_STRING = KEY_PREFIX + "supl_es";
 
         /**
@@ -3523,6 +3530,7 @@
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
         sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
+        sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index c706d28..9b4292f 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -54,6 +54,43 @@
             "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
 
     /**
+     * An intent action indicating that IMS registration for WiFi calling has resulted in an error.
+     * Contains error information that should be displayed to the user.
+     * <p>
+     * This intent will contain the following extra key/value pairs:
+     * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
+     * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific
+     * error information that should be displayed to the user.
+     * <p>
+     * Usage: This intent is sent as an ordered broadcast. If the settings application is going
+     * to show the error information specified to the user, it should respond to
+     * {@link android.content.BroadcastReceiver#setResultCode(int)} with
+     * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the
+     * event was handled. If the framework does not receive a response to the ordered broadcast,
+     * it will then show a notification to the user indicating that there was a registration
+     * failure.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_WFC_IMS_REGISTRATION_ERROR =
+            "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+
+    /**
+     * An extra key corresponding to a String value which contains the carrier specific title to be
+     * displayed as part of the message shown to the user when there is an error registering for
+     * WiFi calling.
+     */
+    public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
+            "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+
+    /**
+     * An extra key corresponding to a String value which contains the carrier specific message to
+     * be displayed as part of the message shown to the user when there is an error registering for
+     * WiFi calling.
+     */
+    public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
+            "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+
+    /**
      * Use {@link Context#getSystemService(String)} to get an instance of this class.
      * @hide
      */
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 2f9e6ac..0074772 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -28,7 +28,6 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
-import android.location.CountryDetector;
 import android.net.Uri;
 import android.os.PersistableBundle;
 import android.provider.Contacts;
@@ -2032,6 +2031,7 @@
     private static boolean isEmergencyNumberInternal(int subId, String number,
                                                      String defaultCountryIso,
                                                      boolean useExactMatch) {
+        // TODO: clean up all the callers that pass in a defaultCountryIso, since it's ignored now.
         try {
             if (useExactMatch) {
                 return TelephonyManager.getDefault().isEmergencyNumber(number);
@@ -2193,18 +2193,7 @@
     private static boolean isLocalEmergencyNumberInternal(int subId, String number,
                                                           Context context,
                                                           boolean useExactMatch) {
-        String countryIso;
-        CountryDetector detector = (CountryDetector) context.getSystemService(
-                Context.COUNTRY_DETECTOR);
-        if (detector != null && detector.detectCountry() != null) {
-            countryIso = detector.detectCountry().getCountryIso();
-        } else {
-            Locale locale = context.getResources().getConfiguration().locale;
-            countryIso = locale.getCountry();
-            Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
-                    + countryIso);
-        }
-        return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
+        return isEmergencyNumberInternal(subId, number, null /* unused */, useExactMatch);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index b5dea7c..0ba36b1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1577,7 +1577,7 @@
     }
 
     /**
-     * Copy a raw SMS PDU to the ICC.
+     * Copies a raw SMS PDU to the ICC.
      * ICC (Integrated Circuit Card) is the card of the device.
      * For example, this can be the SIM or USIM for GSM.
      *
@@ -1591,21 +1591,26 @@
      * operation is performed on the correct subscription.
      * </p>
      *
-     * @param smsc the SMSC for this message, or NULL for the default SMSC
-     * @param pdu the raw PDU to store
-     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
-     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
-     * @return true for success
+     * @param smsc the SMSC for this messag or null for the default SMSC.
+     * @param pdu the raw PDU to store.
+     * @param status message status. One of these status:
+     *               <code>STATUS_ON_ICC_READ</code>
+     *               <code>STATUS_ON_ICC_UNREAD</code>
+     *               <code>STATUS_ON_ICC_SENT</code>
+     *               <code>STATUS_ON_ICC_UNSENT</code>
+     * @return true for success. Otherwise false.
      *
-     * @throws IllegalArgumentException if pdu is NULL
-     * {@hide}
+     * @throws IllegalArgumentException if pdu is null.
+     * @hide
      */
-    @UnsupportedAppUsage
-    public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+    public boolean copyMessageToIcc(
+            @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) {
         boolean success = false;
 
-        if (null == pdu) {
-            throw new IllegalArgumentException("pdu is NULL");
+        if (pdu == null) {
+            throw new IllegalArgumentException("pdu is null");
         }
         try {
             ISms iSms = getISmsService();
@@ -1622,7 +1627,7 @@
     }
 
     /**
-     * Delete the specified message from the ICC.
+     * Deletes the specified message from the ICC.
      * ICC (Integrated Circuit Card) is the card of the device.
      * For example, this can be the SIM or USIM for GSM.
      *
@@ -1706,7 +1711,7 @@
     }
 
     /**
-     * Retrieves all messages currently stored on ICC.
+     * Retrieves all messages currently stored on the ICC.
      * ICC (Integrated Circuit Card) is the card of the device.
      * For example, this can be the SIM or USIM for GSM.
      *
@@ -1872,8 +1877,7 @@
     }
 
     /**
-     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
-     * records returned by <code>getAllMessagesFromIcc()</code>
+     * Creates a list of <code>SmsMessage</code>s from a list of SmsRawData records.
      *
      * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
      * applications or the Telephony framework and will never trigger an SMS disambiguation
@@ -1885,8 +1889,7 @@
      * operation is performed on the correct subscription.
      * </p>
      *
-     * @param records SMS EF records, returned by
-     *   <code>getAllMessagesFromIcc</code>
+     * @param records SMS EF records.
      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
      */
     private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
@@ -1897,7 +1900,7 @@
                 SmsRawData data = records.get(i);
                 // List contains all records, including "free" records (null)
                 if (data != null) {
-                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+                    SmsMessage sms = SmsMessage.createFromEfRecord(i + 1, data.getBytes(),
                             getSubscriptionId());
                     if (sms != null) {
                         messages.add(sms);
@@ -2037,6 +2040,17 @@
         return ret;
     }
 
+    /** @hide */
+    @IntDef(prefix = { "STATUS_ON_ICC_" }, value = {
+            STATUS_ON_ICC_FREE,
+            STATUS_ON_ICC_READ,
+            STATUS_ON_ICC_UNREAD,
+            STATUS_ON_ICC_SENT,
+            STATUS_ON_ICC_UNSENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusOnIcc {}
+
     // see SmsMessage.getStatusOnIcc
 
     /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 30a61c3..8d9b1dd 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -278,41 +278,24 @@
     }
 
     /**
-     * Create an SmsMessage from an SMS EF record.
+     * Creates an SmsMessage from an SMS EF record.
      *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param index Index of SMS EF record.
      * @param data Record data.
      * @return An SmsMessage representing the record.
      *
      * @hide
      */
     public static SmsMessage createFromEfRecord(int index, byte[] data) {
-        SmsMessageBase wrappedMessage;
-
-        if (isCdmaVoice()) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
-                    index, data);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
-                    index, data);
-        }
-
-        if (wrappedMessage != null) {
-            return new SmsMessage(wrappedMessage);
-        } else {
-            Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null");
-            return null;
-        }
+        return createFromEfRecord(index, data, SmsManager.getDefaultSmsSubscriptionId());
     }
 
     /**
-     * Create an SmsMessage from an SMS EF record.
+     * Creates an SmsMessage from an SMS EF record.
      *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param index Index of SMS EF record.
      * @param data Record data.
-     * @param subId Subscription Id of the SMS
+     * @param subId Subscription Id associated with the record.
      * @return An SmsMessage representing the record.
      *
      * @hide
@@ -602,13 +585,15 @@
      */
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      * This method will not attempt to use any GSM national language 7 bit encodings.
      *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message, boolean statusReportRequested) {
@@ -621,17 +606,16 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      * This method will not attempt to use any GSM national language 7 bit encodings.
      *
-     * @param scAddress Service Centre address.  Null means use default.
+     * @param scAddress Service Centre address. Null means use default.
      * @param destinationAddress the address of the destination for the message.
-     * @param message String representation of the message payload.
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @param subId Subscription of the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @param subId subscription of the message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      * @hide
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
@@ -649,17 +633,16 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
      * This method will not attempt to use any GSM national language 7 bit encodings.
      *
-     * @param scAddress Service Centre address. null == use default
-     * @param destinationAddress the address of the destination for the message
-     * @param destinationPort the port to deliver the message to at the
-     *        destination
-     * @param data the data for the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param destinationPort the port to deliver the message to at the destination.
+     * @param data the data for the message.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, short destinationPort, byte[] data,
@@ -677,6 +660,55 @@
         return new SubmitPdu(spb);
     }
 
+    // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
+    // DeliverPdu accordingly.
+
+    /**
+     * Gets an SMS PDU to store in the ICC.
+     *
+     * @param subId subscription of the message.
+     * @param status message status. One of these status:
+     *               <code>SmsManager.STATUS_ON_ICC_READ</code>
+     *               <code>SmsManager.STATUS_ON_ICC_UNREAD</code>
+     *               <code>SmsManager.STATUS_ON_ICC_SENT</code>
+     *               <code>SmsManager.STATUS_ON_ICC_UNSENT</code>
+     * @param scAddress Service Centre address. Null means use default.
+     * @param address destination or originating address.
+     * @param message string representation of the message payload.
+     * @param date the time stamp the message was received.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status,
+            @Nullable String scAddress, @NonNull String address, @NonNull String message,
+            long date) {
+        SubmitPduBase spb;
+        if (isCdmaVoice(subId)) { // 3GPP2 format
+            if (status == SmsManager.STATUS_ON_ICC_READ
+                    || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+                spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address,
+                        message, date);
+            } else { // Submit PDU
+                spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                        address, message, false /* statusReportRequested */, null /* smsHeader */);
+            }
+        } else { // 3GPP format
+            if (status == SmsManager.STATUS_ON_ICC_READ
+                    || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+                spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress,
+                        address, message, date);
+            } else { // Submit PDU
+                spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                        address, message, false /* statusReportRequested */, null /* header */);
+            }
+        }
+
+        return spb != null ? new SubmitPdu(spb) : null;
+    }
+
     /**
      * Get an SMS-SUBMIT PDU's encoded message.
      * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bd8321e..839889f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -196,11 +196,8 @@
                     NETWORK_SELECTION_MODE_MANUAL})
     public @interface NetworkSelectionMode {}
 
-    /** @hide */
     public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0;
-    /** @hide */
     public static final int NETWORK_SELECTION_MODE_AUTO = 1;
-    /** @hide */
     public static final int NETWORK_SELECTION_MODE_MANUAL = 2;
 
     /** The otaspMode passed to PhoneStateListener#onOtaspChanged */
@@ -2349,7 +2346,7 @@
     @UnsupportedAppUsage
     public boolean isNetworkRoaming(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
-        return getTelephonyProperty(subId, TelephonyProperties.operator_is_roaming(), false);
+        return getTelephonyProperty(phoneId, TelephonyProperties.operator_is_roaming(), false);
     }
 
     /**
@@ -7524,14 +7521,18 @@
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
-
-     * @return the network selection mode.
+     *  <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
-     * @hide
+     * @return the network selection mode.
      */
-    @NetworkSelectionMode
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public int getNetworkSelectionMode() {
+    @SuppressAutoDoc // No support for carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE
+    })
+    public @NetworkSelectionMode int getNetworkSelectionMode() {
         int mode = NETWORK_SELECTION_MODE_UNKNOWN;
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index d6dea2c..a7d553b 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -294,15 +294,30 @@
      *      updateImsCallRatFromExtras(Bundle)} to determine whether to set the
      * {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
      * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
+     * @deprecated the constants associated with this extra are hidden, instead use
+     * {@link #EXTRA_CALL_NETWORK_TYPE}.
      */
+    @Deprecated
     public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
 
     /**
+     * Extra key with an {@code int} value which can be set in {@link #setCallExtraInt(String, int)}
+     * to indicate the network type used for a call.
+     * <p>
+     * Valid values are defined by {@code TelephonyManager.NETWORK_TYPE_*} constants. An example may
+     * be {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+     */
+    public static final String EXTRA_CALL_NETWORK_TYPE =
+            "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+
+    /**
      * Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'.  Used to ensure
      * compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
      * extra key.  Should be removed when the non-compliant modems are fixed.
      * @hide
+     * @deprecated Use {@link #EXTRA_CALL_NETWORK_TYPE} instead.
      */
+    @Deprecated
     public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
 
     /** @hide */
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 5adc99e..1b583fd 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -25,8 +25,6 @@
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsVideoCallProvider;
 
-import java.util.Objects;
-
 /**
  * Provides the call initiation/termination, and media exchange between two IMS endpoints.
  * It directly communicates with IMS service which implements the IMS protocol behavior.
@@ -346,7 +344,7 @@
         }
 
         /**
-         * Called when an {@link ImsCallSession} may handover from one radio technology to another.
+         * Called when an {@link ImsCallSession} may handover from one network type to another.
          * For example, the session may handover from WIFI to LTE if conditions are right.
          * <p>
          * If handover is attempted,
@@ -355,24 +353,24 @@
          * called to indicate the success or failure of the handover.
          *
          * @param session IMS session object
-         * @param srcAccessTech original access technology
-         * @param targetAccessTech new access technology
+         * @param srcNetworkType original network type
+         * @param targetNetworkType new network type
          */
-        public void callSessionMayHandover(ImsCallSession session, int srcAccessTech,
-                int targetAccessTech) {
+        public void callSessionMayHandover(ImsCallSession session, int srcNetworkType,
+                int targetNetworkType) {
             // no-op
         }
 
         /**
-         * Called when session access technology changes
+         * Called when session network type changes
          *
          * @param session IMS session object
-         * @param srcAccessTech original access technology
-         * @param targetAccessTech new access technology
+         * @param srcNetworkType original network type
+         * @param targetNetworkType new network type
          * @param reasonInfo
          */
         public void callSessionHandover(ImsCallSession session,
-                                 int srcAccessTech, int targetAccessTech,
+                                 int srcNetworkType, int targetNetworkType,
                                  ImsReasonInfo reasonInfo) {
             // no-op
         }
@@ -381,12 +379,12 @@
          * Called when session access technology change fails
          *
          * @param session IMS session object
-         * @param srcAccessTech original access technology
-         * @param targetAccessTech new access technology
+         * @param srcNetworkType original access technology
+         * @param targetNetworkType new access technology
          * @param reasonInfo handover failure reason
          */
         public void callSessionHandoverFailed(ImsCallSession session,
-                                       int srcAccessTech, int targetAccessTech,
+                                       int srcNetworkType, int targetNetworkType,
                                        ImsReasonInfo reasonInfo) {
             // no-op
         }
@@ -1303,20 +1301,19 @@
         /**
          * Notifies of a case where a {@link ImsCallSession} may
          * potentially handover from one radio technology to another.
-         * @param srcAccessTech The source radio access technology; one of the access technology
-         *                      constants defined in {@link android.telephony.ServiceState}.  For
-         *                      example
-         *                      {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-         * @param targetAccessTech The target radio access technology; one of the access technology
-         *                      constants defined in {@link android.telephony.ServiceState}.  For
-         *                      example
-         *                      {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+         * @param srcNetworkType The source network type; one of the network type constants defined
+         *                       in {@link android.telephony.TelephonyManager}.  For example
+         *                      {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+         * @param targetNetworkType The target radio access technology; one of the network type
+         *                          constants defined in {@link android.telephony.TelephonyManager}.
+         *                          For example
+         *                          {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
          */
         @Override
-        public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+        public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
             if (mListener != null) {
-                mListener.callSessionMayHandover(ImsCallSession.this, srcAccessTech,
-                        targetAccessTech);
+                mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+                        targetNetworkType);
             }
         }
 
@@ -1324,11 +1321,11 @@
          * Notifies of handover information for this call
          */
         @Override
-        public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+        public void callSessionHandover(int srcNetworkType, int targetNetworkType,
                 ImsReasonInfo reasonInfo) {
             if (mListener != null) {
-                mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
-                        targetAccessTech, reasonInfo);
+                mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+                        targetNetworkType, reasonInfo);
             }
         }
 
@@ -1336,11 +1333,11 @@
          * Notifies of handover failure info for this call
          */
         @Override
-        public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+        public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
                 ImsReasonInfo reasonInfo) {
             if (mListener != null) {
-                mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
-                        targetAccessTech, reasonInfo);
+                mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+                        targetNetworkType, reasonInfo);
             }
         }
 
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index e11886f..025721c 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -17,10 +17,13 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.RemoteException;
+import android.telephony.Annotation;
 import android.telephony.CallQuality;
+import android.telephony.ServiceState;
 import android.telephony.ims.aidl.IImsCallSessionListener;
 import android.telephony.ims.stub.ImsCallSessionImplBase;
 
@@ -476,11 +479,27 @@
      * @param targetAccessTech The target radio access technology; one of the access technology
      * constants defined in {@link android.telephony.ServiceState}. For example
      * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     * @deprecated Uses hidden constants for radio access technology, use
+     * {@link #onMayHandover(int, int)} instead.
      */
-    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
-    {
+    @Deprecated
+    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+        // Use new API internally.
+        onMayHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
+    }
+
+    /**
+     * Notify the framework that the associated {@link ImsCallSession} may handover from one network
+     * type to another.
+     *
+     * @param srcNetworkType The source network type.
+     * @param targetNetworkType The target network type.
+     */
+    public void onMayHandover(@Annotation.NetworkType int srcNetworkType,
+            @Annotation.NetworkType int targetNetworkType) {
         try {
-            mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+            mListener.callSessionMayHandover(srcNetworkType, targetNetworkType);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -494,11 +513,29 @@
      * @param targetAccessTech new access technology, defined in
      * {@link android.telephony.ServiceState}.
      * @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+     * @deprecated Uses hidden radio access technology constants, use
+     * {@link #onHandover(int, int, ImsReasonInfo)} instead.
      */
+    @Deprecated
     public void callSessionHandover(int srcAccessTech, int targetAccessTech,
             ImsReasonInfo reasonInfo) {
+        // Use new API internally.
+        onHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+    }
+
+    /**
+     * Notify the framework that the associated {@link ImsCallSession} has handed over from one
+     * network type to another.
+     *
+     * @param srcNetworkType original network type.
+     * @param targetNetworkType target network type after handover..
+     * @param reasonInfo An optional {@link ImsReasonInfo} associated with this handover.
+     */
+    public void onHandover(@Annotation.NetworkType int srcNetworkType,
+            @Annotation.NetworkType int targetNetworkType, @Nullable ImsReasonInfo reasonInfo) {
         try {
-            mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+            mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -510,11 +547,28 @@
      * @param srcAccessTech original access technology
      * @param targetAccessTech new access technology
      * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+     * @deprecated Uses hidden radio access technology constants, use
+     * {@link #onHandoverFailed(int, int, ImsReasonInfo)} instead
      */
+    @Deprecated
     public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
             ImsReasonInfo reasonInfo) {
+        // Use new API internally.
+        onHandoverFailed(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+    }
+
+    /**
+     * The IMS call session's access technology change has failed..
+     *
+     * @param srcNetworkType original network type.
+     * @param targetNetworkType target network type that the handover failed for.
+     * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+     */
+    public void onHandoverFailed(@Annotation.NetworkType int srcNetworkType,
+            @Annotation.NetworkType int targetNetworkType, @NonNull ImsReasonInfo reasonInfo) {
         try {
-            mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+            mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 666a688..aa4f77d 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -102,6 +102,101 @@
 
     // Inheriting values from ImsConfig for backwards compatibility.
     /**
+     * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+     * <p>
+     * This corresponds to the {@code mode-set} parameter for the AMR codec.
+     * See 3GPP TS 26.101 Table 1A for more information.
+     * <p>
+     * <UL>
+     *     <LI>0 - AMR 4.75 kbit/s</LI>
+     *     <LI>1 - AMR 5.15 kbit/s</LI>
+     *     <LI>2 - AMR 5.90 kbit/s</LI>
+     *     <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+     *     <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+     *     <LI>5 - AMR 7.95 kbit/s</LI>
+     *     <LI>6 - AMR 10.2 kbit/s</LI>
+     *     <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+     * </UL>
+     * <p>
+     * Value is in String format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
+
+    /**
+     * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+     * <p>
+     * This corresponds to the {@code mode-set} parameter for the AMR wideband codec.
+     * See 3GPP TS 26.101 Table 1A for more information.
+     * <p>
+     * <UL>
+     *     <LI>0 - AMR 4.75 kbit/s</LI>
+     *     <LI>1 - AMR 5.15 kbit/s</LI>
+     *     <LI>2 - AMR 5.90 kbit/s</LI>
+     *     <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+     *     <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+     *     <LI>5 - AMR 7.95 kbit/s</LI>
+     *     <LI>6 - AMR 10.2 kbit/s</LI>
+     *     <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+     * </UL>
+     * <p>
+     * Value is in String format.
+     * @see #setProvisioningStringValue(int, String)
+     * @see #getProvisioningStringValue(int)
+     */
+    public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
+
+    /**
+     * SIP Session Timer value (seconds).
+     * <p>
+     * See RFC4028 for more information.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
+
+    /**
+     * Minimum SIP Session Expiration Timer in (seconds).
+     * <p>
+     * See RFC4028 for more information.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
+
+    /**
+     * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+     * <p>
+     * See RFC4028 for more information.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
+
+    /**
+     * Delay time when an iRAT transitions from eHRPD/HRPD/1xRTT to LTE.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
+
+    /**
+     * Silent redial status of Enabled (True), or Disabled (False).
+     * Value is in boolean format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_ENABLE_SILENT_REDIAL = 6;
+
+    /**
      * An integer key representing the SIP T1 timer value in milliseconds for the associated
      * subscription.
      * <p>
@@ -117,6 +212,28 @@
     public static final int KEY_T1_TIMER_VALUE_MS = 7;
 
     /**
+     * SIP T2 timer value in milliseconds.  See RFC 3261 for information.
+     * <p>
+     * The T2 timer is the maximum retransmit interval for non-INVITE requests and INVITE responses.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_T2_TIMER_VALUE_MS = 8;
+
+    /**
+     * SIP TF timer value in milliseconds.  See RFC 3261 for information.
+     * <p>
+     * The TF timer is the non-INVITE transaction timeout timer.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_TF_TIMER_VALUE_MS = 9;
+
+    /**
      * An integer key representing the voice over LTE (VoLTE) provisioning status for the
      * associated subscription. Determines whether the user can register for voice services over
      * LTE.
@@ -141,6 +258,43 @@
     public static final int KEY_VT_PROVISIONING_STATUS = 11;
 
     /**
+     * Domain Name for the device to populate the request URI for REGISTRATION.
+     * Value is in String format.
+     * @see #setProvisioningStringValue(int, String)
+     * @see #getProvisioningStringValue(int)
+     */
+    public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
+
+    /**
+     * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+     * Value is in Integer format.
+     * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SMS_FORMAT = 13;
+
+    /**
+     * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
+     * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+     */
+    public static final int SMS_FORMAT_3GPP2 = 0;
+
+    /**
+     * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
+     * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+     */
+    public static final int SMS_FORMAT_3GPP = 1;
+
+    /**
+     * Turns SMS over IMS ON/OFF on the device.
+     * Value is in Integer format. ON (1), OFF(0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SMS_OVER_IP_ENABLED = 14;
+
+    /**
      * An integer key associated with the carrier configured SIP PUBLISH timer, which dictates the
      * expiration time in seconds for published online availability in RCS presence.
      * <p>
@@ -223,7 +377,7 @@
     public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
 
     /**
-     * An integer associated with the expiration timer used duriing the SIP subscription of a
+     * An integer associated with the expiration timer used during the SIP subscription of a
      * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
      * book.
      * <p>
@@ -234,6 +388,14 @@
     public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
 
     /**
+     * Applies compression to LIST Subscription.
+     * Value is in Integer format. Enable (1), Disable(0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
+
+    /**
      * An integer key representing the RCS enhanced address book (EAB) provisioning status for the
      * associated subscription. Determines whether or not SIP OPTIONS or presence will be used to
      * retrieve RCS capabilities for the user's contacts.
@@ -271,6 +433,349 @@
     public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
 
     /**
+     * Enable voice over wifi.  Enabled (1), or Disabled (0).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
+
+    /**
+     * Mobile data enabled.
+     * Value is in Integer format. On (1), OFF(0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_MOBILE_DATA_ENABLED = 29;
+
+    /**
+     * VoLTE user opted in status.
+     * Value is in Integer format. Opted-in (1) Opted-out (0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
+
+    /**
+     * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+     * Value is in String format.
+     */
+    public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
+
+    /**
+     * Keep Alive Enabled for SIP.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
+
+    /**
+     * Registration retry Base Time value in seconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+
+    /**
+     * Registration retry Max Time value in seconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+
+    /**
+     * Smallest RTP port for speech codec.
+     * Value is in integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+
+    public static final int KEY_RTP_SPEECH_START_PORT = 35;
+
+    /**
+     * Largest RTP port for speech code.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RTP_SPEECH_END_PORT = 36;
+
+    /**
+     * SIP Timer A's value in milliseconds. Timer A is the INVITE request retransmit interval (in
+     * milliseconds), for UDP only.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
+
+    /**
+     * SIP Timer B's value in milliseconds. Timer B is the wait time for INVITE message to be,
+     * in milliseconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
+
+    /**
+     * SIP Timer D's value in milliseconds. Timer D is the wait time for response retransmits of
+     * the invite client transactions, in milliseconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
+
+    /**
+     * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE request retransmit
+     * interval (in milliseconds), for UDP only.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
+
+    /**
+     * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction timeout timer,
+     * in milliseconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
+
+    /**
+     * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+     * retransmit interval.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
+
+    /**
+     * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+     * ACK receipt.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
+
+    /**
+     * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+     * ACK retransmits.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
+
+    /**
+     * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+     * non-invite request retransmission.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
+
+    /**
+     * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+     * non-invite response retransmits.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
+
+    /**
+     * AMR WB octet aligned dynamic payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
+
+    /**
+     * AMR WB bandwidth efficient payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
+
+    /**
+     * AMR octet aligned dynamic payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
+
+    /**
+     * AMR bandwidth efficient payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
+
+    /**
+     * DTMF WB payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
+
+    /**
+     * DTMF NB payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
+
+    /**
+     * AMR Default encoding mode.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
+
+    /**
+     * SMS Public Service Identity.
+     * Value is in String format.
+     */
+    public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
+
+    /**
+     * Video Quality - VideoQualityFeatureValuesConstants.
+     * Valid values are: {@link #VIDEO_QUALITY_HIGH} and {@link #VIDEO_QUALITY_LOW}.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_VIDEO_QUALITY = 55;
+
+    /**
+     * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+     */
+    public static final int VIDEO_QUALITY_LOW = 0;
+
+    /**
+     * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+     */
+    public static final int VIDEO_QUALITY_HIGH = 1;
+
+    /**
+     * LTE to WIFI handover threshold.
+     * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= {@link #KEY_WIFI_THRESHOLD_A}.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_LTE_THRESHOLD_1 = 56;
+
+    /**
+     * WIFI to LTE handover threshold.
+     * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+     * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+     * Value is in Integer format.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_LTE_THRESHOLD_2 = 57;
+
+    /**
+     * LTE to WIFI handover threshold.
+     * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+     * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+     * Value is in Integer format.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_LTE_THRESHOLD_3 = 58;
+
+    /**
+     * 1x to WIFI handover threshold.
+     * Handover from 1x to WiFi if 1x < {@link #KEY_1X_THRESHOLD}.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_1X_THRESHOLD = 59;
+
+    /**
+     * LTE to WIFI threshold A.
+     * Handover from LTE to WiFi if LTE < {@link #KEY_LTE_THRESHOLD_1} and WiFi >= {@link
+     * #KEY_WIFI_THRESHOLD_A}.
+     * Value is in Integer format.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_WIFI_THRESHOLD_A = 60;
+
+    /**
+     * WiFi to LTRE handover threshold B.
+     * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi <
+     * {@link #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_WIFI_THRESHOLD_B = 61;
+
+    /**
+     * LTE ePDG timer (in seconds).
+     * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
+
+    /**
+     * WiFi ePDG timer (in seconds).
+     * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
+
+    /**
+     * 1x ePDG timer (in seconds).
+     * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+     */
+    public static final int KEY_1X_EPDG_TIMER_SEC = 64;
+
+    /**
+     * MultiEndpoint status: Enabled (1), or Disabled (0).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_MULTIENDPOINT_ENABLED = 65;
+
+    /**
+     * RTT status: Enabled (1), or Disabled (0).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     */
+    public static final int KEY_RTT_ENABLED = 66;
+
+    /**
      * Callback for IMS provisioning changes.
      */
     public static class Callback {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index d64e67a..cc2ebb9 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -92,11 +92,11 @@
     /**
      * Notifies of handover information for this call
      */
-    void callSessionHandover(int srcAccessTech, int targetAccessTech,
+    void callSessionHandover(int srcNetworkType, int targetNetworkType,
             in ImsReasonInfo reasonInfo);
-    void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+    void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
             in ImsReasonInfo reasonInfo);
-    void callSessionMayHandover(int srcAccessTech, int targetAccessTech);
+    void callSessionMayHandover(int srcNetworkType, int targetNetworkType);
 
     /**
      * Notifies the TTY mode change by remote party.
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index acab738..75bd6a7 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -20,6 +20,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.CallQuality;
+import android.telephony.ServiceState;
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsConferenceState;
@@ -547,19 +548,25 @@
         @Override
         public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
                 ImsReasonInfo reasonInfo) throws RemoteException {
-            mNewListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+            mNewListener.callSessionHandover(
+                    ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                    ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
         }
 
         @Override
         public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
                 int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
-            mNewListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+            mNewListener.callSessionHandoverFailed(
+                    ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                    ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
         }
 
         @Override
         public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
                 throws RemoteException {
-            mNewListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+            mNewListener.callSessionMayHandover(
+                    ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                    ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
         }
 
         @Override
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 3ec4f34..f13371c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -17,6 +17,8 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Bundle;
@@ -206,6 +208,13 @@
             return ImsUtImplBase.this.updateCallBarringForServiceClass(
                     cbType, action, barrList, serviceClass);
         }
+
+        @Override
+        public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
+                int serviceClass, String password) throws RemoteException {
+            return ImsUtImplBase.this.updateCallBarringWithPassword(
+                    cbType, action, barrList, serviceClass, password);
+        }
     };
 
     /**
@@ -328,6 +337,14 @@
     }
 
     /**
+     * Updates the configuration of the call barring for specified service class with password.
+     */
+    public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
+            int serviceClass, @NonNull String password) {
+        return -1;
+    }
+
+    /**
      * Updates the configuration of the call forward.
      */
     public int updateCallForward(int action, int condition, String number, int serviceClass,
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 0d86e2b..0f6ce13 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -135,374 +135,596 @@
         /**
          * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
          * Value is in String format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_CODEC_MODE_SET_VALUES} instead.
          */
-        public static final int VOCODER_AMRMODESET = CONFIG_START;
+        @Deprecated
+        public static final int VOCODER_AMRMODESET =
+                ProvisioningManager.KEY_AMR_CODEC_MODE_SET_VALUES;
 
         /**
          * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
          * Value is in String format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_CODEC_MODE_SET_VALUES} instead.
          */
-        public static final int VOCODER_AMRWBMODESET = 1;
+        @Deprecated
+        public static final int VOCODER_AMRWBMODESET =
+                ProvisioningManager.KEY_AMR_WB_CODEC_MODE_SET_VALUES;
 
         /**
          * SIP Session Timer value (seconds).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_SESSION_TIMER_SEC} instead.
          */
-        public static final int SIP_SESSION_TIMER = 2;
+        @Deprecated
+        public static final int SIP_SESSION_TIMER = ProvisioningManager.KEY_SIP_SESSION_TIMER_SEC;
 
         /**
          * Minimum SIP Session Expiration Timer in (seconds).
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC} instead.
          */
-        public static final int MIN_SE = 3;
+        @Deprecated
+        public static final int MIN_SE =
+                ProvisioningManager.KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC;
 
         /**
          * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_CANCELLATION_TIMER_MS} instead.
          */
-        public static final int CANCELLATION_TIMER = 4;
+        @Deprecated
+        public static final int CANCELLATION_TIMER =
+                ProvisioningManager.KEY_SIP_INVITE_CANCELLATION_TIMER_MS;
 
         /**
          * Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_TRANSITION_TO_LTE_DELAY_MS} instead.
          */
-        public static final int TDELAY = 5;
+        @Deprecated
+        public static final int TDELAY = ProvisioningManager.KEY_TRANSITION_TO_LTE_DELAY_MS;
 
         /**
          * Silent redial status of Enabled (True), or Disabled (False).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_ENABLE_SILENT_REDIAL} instead.
          */
-        public static final int SILENT_REDIAL_ENABLE = 6;
+        @Deprecated
+        public static final int SILENT_REDIAL_ENABLE = ProvisioningManager.KEY_ENABLE_SILENT_REDIAL;
 
         /**
          * SIP T1 timer value in milliseconds. See RFC 3261 for define.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_T1_TIMER_VALUE_MS} instead.
          */
+        @Deprecated
         public static final int SIP_T1_TIMER = ProvisioningManager.KEY_T1_TIMER_VALUE_MS;
 
         /**
          * SIP T2 timer value in milliseconds.  See RFC 3261 for define.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_T2_TIMER_VALUE_MS} instead.
          */
-        public static final int SIP_T2_TIMER  = 8;
+        @Deprecated
+        public static final int SIP_T2_TIMER  = ProvisioningManager.KEY_T2_TIMER_VALUE_MS;
 
-         /**
+        /**
          * SIP TF timer value in milliseconds.  See RFC 3261 for define.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_TF_TIMER_VALUE_MS} instead.
          */
-        public static final int SIP_TF_TIMER = 9;
+        @Deprecated
+        public static final int SIP_TF_TIMER = ProvisioningManager.KEY_TF_TIMER_VALUE_MS;
 
         /**
          * VoLTE status for VLT/s status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS} instead.
          */
+        @Deprecated
         public static final int VLT_SETTING_ENABLED =
                 ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
 
         /**
          * VoLTE status for LVC/s status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS} instead.
          */
+        @Deprecated
         public static final int LVC_SETTING_ENABLED =
                 ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
+
         /**
          * Domain Name for the device to populate the request URI for REGISTRATION.
          * Value is in String format.
+         * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_DOMAIN_NAME}.
          */
-        public static final int DOMAIN_NAME = 12;
+        @Deprecated
+        public static final int DOMAIN_NAME = ProvisioningManager.KEY_REGISTRATION_DOMAIN_NAME;
+
          /**
          * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
          * Value is in Integer format. 3GPP2(0), 3GPP(1)
-         */
-        public static final int SMS_FORMAT = 13;
+          * @deprecated use {@link ProvisioningManager#KEY_SMS_FORMAT}.
+          */
+         @Deprecated
+         public static final int SMS_FORMAT = ProvisioningManager.KEY_SMS_FORMAT;
+
          /**
          * Turns IMS ON/OFF on the device.
          * Value is in Integer format. ON (1), OFF(0).
-         */
-        public static final int SMS_OVER_IP = 14;
+          * @deprecated use {@link ProvisioningManager#KEY_SMS_OVER_IP_ENABLED}.
+          */
+         @Deprecated
+         public static final int SMS_OVER_IP = ProvisioningManager.KEY_SMS_OVER_IP_ENABLED;
+
         /**
          * Requested expiration for Published Online availability.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_SEC}.
          */
+        @Deprecated
         public static final int PUBLISH_TIMER = ProvisioningManager.KEY_RCS_PUBLISH_TIMER_SEC;
+
         /**
          * Requested expiration for Published Offline availability.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC}.
          */
+        @Deprecated
         public static final int PUBLISH_TIMER_EXTENDED =
                 ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC;
+
         /**
          *
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITY_DISCOVERY_ENABLED}.
          */
+        @Deprecated
         public static final int CAPABILITY_DISCOVERY_ENABLED =
                 ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED;
+
         /**
-         * Period of time the capability information of the  contact is cached on handset.
+         * Period of time the capability information of the contact is cached on handset.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC}.
          */
+        @Deprecated
         public static final int CAPABILITIES_CACHE_EXPIRATION =
                 ProvisioningManager.KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC;
+
         /**
          * Peiod of time the availability information of a contact is cached on device.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC}.
          */
+        @Deprecated
         public static final int AVAILABILITY_CACHE_EXPIRATION =
                 ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC;
+
         /**
          * Interval between successive capabilities polling.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC}.
          */
+        @Deprecated
         public static final int CAPABILITIES_POLL_INTERVAL =
                 ProvisioningManager.KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC;
+
         /**
          * Minimum time between two published messages from the device.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS}.
          */
+        @Deprecated
         public static final int SOURCE_THROTTLE_PUBLISH =
                 ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS;
+
         /**
          * The Maximum number of MDNs contained in one Request Contained List.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_MAX_NUM_ENTRIES_IN_RCL}.
          */
+        @Deprecated
         public static final int MAX_NUMENTRIES_IN_RCL =
                 ProvisioningManager.KEY_RCS_MAX_NUM_ENTRIES_IN_RCL;
+
         /**
          * Expiration timer for subscription of a Request Contained List, used in capability
          * polling.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC}.
          */
+        @Deprecated
         public static final int CAPAB_POLL_LIST_SUB_EXP =
                 ProvisioningManager.KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC;
+
         /**
          * Applies compression to LIST Subscription.
          * Value is in Integer format. Enable (1), Disable(0).
+         * @deprecated use {@link ProvisioningManager#KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION}.
          */
-        public static final int GZIP_FLAG = 24;
+        @Deprecated
+        public static final int GZIP_FLAG = ProvisioningManager.KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION;
+
         /**
          * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}.
          */
+        @Deprecated
         public static final int EAB_SETTING_ENABLED =
                 ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+
         /**
          * Wi-Fi calling roaming status.
          * Value is in Integer format. ON (1), OFF(0).
+         * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE}
+         * instead.
          */
+        @Deprecated
         public static final int VOICE_OVER_WIFI_ROAMING =
                 ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE;
+
         /**
-         * Wi-Fi calling modem - WfcModeFeatureValueConstants.
+         * Wi-Fi calling mode - WfcModeFeatureValueConstants.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_MODE_OVERRIDE}
+         * instead.
          */
+        @Deprecated
         public static final int VOICE_OVER_WIFI_MODE =
                 ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE;
+
         /**
          * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}.
          */
-        public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28;
+        @Deprecated
+        public static final int VOICE_OVER_WIFI_SETTING_ENABLED =
+                ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
+
+
         /**
          * Mobile data enabled.
          * Value is in Integer format. On (1), OFF(0).
+         * @deprecated use {@link ProvisioningManager#KEY_MOBILE_DATA_ENABLED}.
          */
-        public static final int MOBILE_DATA_ENABLED = 29;
+        @Deprecated
+        public static final int MOBILE_DATA_ENABLED = ProvisioningManager.KEY_MOBILE_DATA_ENABLED;
+
         /**
          * VoLTE user opted in status.
          * Value is in Integer format. Opted-in (1) Opted-out (0).
+         * @deprecated use {@link ProvisioningManager#KEY_VOLTE_USER_OPT_IN_STATUS}.
          */
-        public static final int VOLTE_USER_OPT_IN_STATUS = 30;
+        @Deprecated
+        public static final int VOLTE_USER_OPT_IN_STATUS =
+                ProvisioningManager.KEY_VOLTE_USER_OPT_IN_STATUS;
+
         /**
          * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
          * Value is in String format.
+         * @deprecated use {@link ProvisioningManager#KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS}.
          */
-        public static final int LBO_PCSCF_ADDRESS = 31;
+        @Deprecated
+        public static final int LBO_PCSCF_ADDRESS =
+                ProvisioningManager.KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS;
+
         /**
          * Keep Alive Enabled for SIP.
          * Value is in Integer format. On(1), OFF(0).
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_KEEP_ALIVE_ENABLED}.
          */
-        public static final int KEEP_ALIVE_ENABLED = 32;
+        @Deprecated
+        public static final int KEEP_ALIVE_ENABLED = ProvisioningManager.KEY_SIP_KEEP_ALIVE_ENABLED;
+
         /**
          * Registration retry Base Time value in seconds.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_RETRY_BASE_TIME_SEC}.
          */
-        public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+        @Deprecated
+        public static final int REGISTRATION_RETRY_BASE_TIME_SEC =
+                ProvisioningManager.KEY_REGISTRATION_RETRY_BASE_TIME_SEC;
+
         /**
          * Registration retry Max Time value in seconds.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_RETRY_MAX_TIME_SEC}.
          */
-        public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+        @Deprecated
+        public static final int REGISTRATION_RETRY_MAX_TIME_SEC =
+                ProvisioningManager.KEY_REGISTRATION_RETRY_MAX_TIME_SEC;
+
         /**
          * Smallest RTP port for speech codec.
          * Value is in integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RTP_SPEECH_START_PORT}.
          */
-        public static final int SPEECH_START_PORT = 35;
+        @Deprecated
+        public static final int SPEECH_START_PORT = ProvisioningManager.KEY_RTP_SPEECH_START_PORT;
+
         /**
          * Largest RTP port for speech code.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RTP_SPEECH_END_PORT}.
          */
-        public static final int SPEECH_END_PORT = 36;
+        @Deprecated
+        public static final int SPEECH_END_PORT = ProvisioningManager.KEY_RTP_SPEECH_END_PORT;
+
         /**
          * SIP Timer A's value in milliseconds. Timer A is the INVITE request
          * retransmit interval, for UDP only.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS}.
          */
-        public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37;
+        @Deprecated
+        public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC =
+                ProvisioningManager.KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS;
+
         /**
          * SIP Timer B's value in milliseconds. Timer B is the wait time for
          * INVITE message to be acknowledged.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_ACK_WAIT_TIME_MS}.
          */
-        public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38;
+        @Deprecated
+        public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC =
+                ProvisioningManager.KEY_SIP_INVITE_ACK_WAIT_TIME_MS;
+
         /**
          * SIP Timer D's value in milliseconds. Timer D is the wait time for
          * response retransmits of the invite client transactions.
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS}.
          */
-        public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39;
+        @Deprecated
+        public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC =
+                ProvisioningManager.KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS;
+
         /**
          * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE
          * request retransmit interval, for UDP only.
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS}.
          */
-        public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40;
+        @Deprecated
+        public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC =
+                ProvisioningManager.KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS;
+
         /**
          * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction
          * timeout timer.
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS}.
          */
-        public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41;
+        @Deprecated
+        public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC =
+                ProvisioningManager.KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS;
+
         /**
          * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
          * retransmit interval.
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS}.
          */
-        public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42;
+        @Deprecated
+        public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC =
+                ProvisioningManager.KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS;
+
         /**
          * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
          * ACK receipt.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS}.
          */
-        public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43;
+        @Deprecated
+        public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC =
+                ProvisioningManager.KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS;
+
         /**
          * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
          * ACK retransmits.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS}.
          */
-        public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44;
+        @Deprecated
+        public static final int SIP_ACK_RETX_WAIT_TIME_MSEC =
+                ProvisioningManager.KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS;
+
         /**
          * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
          * non-invite request retransmission.
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS}.
          */
-        public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45;
+        @Deprecated
+        public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC =
+                ProvisioningManager.KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS;
+
         /**
          * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
          * non-invite response retransmits.
          * Value is in Integer format.
+         * @deprecated use
+         * {@link ProvisioningManager#KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS}.
          */
-        public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46;
+        @Deprecated
+        public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC =
+                ProvisioningManager.KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS;
+
         /**
          * AMR WB octet aligned dynamic payload type.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE}.
          */
-        public static final int AMR_WB_OCTET_ALIGNED_PT = 47;
+        @Deprecated
+        public static final int AMR_WB_OCTET_ALIGNED_PT =
+                ProvisioningManager.KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE;
+
         /**
          * AMR WB bandwidth efficient payload type.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE}.
          */
-        public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48;
+        @Deprecated
+        public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT =
+                ProvisioningManager.KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE;
+
         /**
          * AMR octet aligned dynamic payload type.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE}.
          */
-        public static final int AMR_OCTET_ALIGNED_PT = 49;
+        @Deprecated
+        public static final int AMR_OCTET_ALIGNED_PT =
+                ProvisioningManager.KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE;
+
         /**
          * AMR bandwidth efficient payload type.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE}.
          */
-        public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50;
+        @Deprecated
+        public static final int AMR_BANDWIDTH_EFFICIENT_PT =
+                ProvisioningManager.KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE;
+
         /**
          * DTMF WB payload type.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_DTMF_WB_PAYLOAD_TYPE}.
          */
-        public static final int DTMF_WB_PT = 51;
+        @Deprecated
+        public static final int DTMF_WB_PT = ProvisioningManager.KEY_DTMF_WB_PAYLOAD_TYPE;
+
         /**
          * DTMF NB payload type.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_DTMF_NB_PAYLOAD_TYPE}.
          */
-        public static final int DTMF_NB_PT = 52;
+        @Deprecated
+        public static final int DTMF_NB_PT = ProvisioningManager.KEY_DTMF_NB_PAYLOAD_TYPE;
+
         /**
          * AMR Default encoding mode.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_AMR_DEFAULT_ENCODING_MODE}.
          */
-        public static final int AMR_DEFAULT_MODE = 53;
+        @Deprecated
+        public static final int AMR_DEFAULT_MODE =
+                ProvisioningManager.KEY_AMR_DEFAULT_ENCODING_MODE;
+
         /**
          * SMS Public Service Identity.
          * Value is in String format.
+         * @deprecated use {@link ProvisioningManager#KEY_SMS_PUBLIC_SERVICE_IDENTITY}.
          */
-        public static final int SMS_PSI = 54;
+        @Deprecated
+        public static final int SMS_PSI = ProvisioningManager.KEY_SMS_PUBLIC_SERVICE_IDENTITY;
+
         /**
          * Video Quality - VideoQualityFeatureValuesConstants.
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_VIDEO_QUALITY}.
          */
-        public static final int VIDEO_QUALITY = 55;
+        @Deprecated
+        public static final int VIDEO_QUALITY = ProvisioningManager.KEY_VIDEO_QUALITY;
+
         /**
          * LTE threshold.
          * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+         * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_1}.
          */
-        public static final int TH_LTE1 = 56;
+        @Deprecated
+        public static final int TH_LTE1 = ProvisioningManager.KEY_LTE_THRESHOLD_1;
+
         /**
          * LTE threshold.
          * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+         * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_2}.
          */
-        public static final int TH_LTE2 = 57;
+        @Deprecated
+        public static final int TH_LTE2 = ProvisioningManager.KEY_LTE_THRESHOLD_2;
+
         /**
          * LTE threshold.
          * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+         * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_3}.
          */
-        public static final int TH_LTE3 = 58;
+        @Deprecated
+        public static final int TH_LTE3 = ProvisioningManager.KEY_LTE_THRESHOLD_3;
+
         /**
          * 1x threshold.
          * Handover from 1x to WiFi if 1x < TH1x
+         * @deprecated use {@link ProvisioningManager#KEY_1X_THRESHOLD}.
          */
-        public static final int TH_1x = 59;
+        @Deprecated
+        public static final int TH_1x = ProvisioningManager.KEY_1X_THRESHOLD;
+
         /**
          * WiFi threshold.
          * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+         * @deprecated use {@link ProvisioningManager#KEY_WIFI_THRESHOLD_A}.
          */
-        public static final int VOWT_A = 60;
+        @Deprecated
+        public static final int VOWT_A = ProvisioningManager.KEY_WIFI_THRESHOLD_A;
+
         /**
          * WiFi threshold.
          * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+         * @deprecated use {@link ProvisioningManager#KEY_WIFI_THRESHOLD_B}.
          */
-        public static final int VOWT_B = 61;
+        @Deprecated
+        public static final int VOWT_B = ProvisioningManager.KEY_WIFI_THRESHOLD_B;
+
         /**
          * LTE ePDG timer.
          * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+         * @deprecated use {@link ProvisioningManager#KEY_LTE_EPDG_TIMER_SEC}.
          */
-        public static final int T_EPDG_LTE = 62;
+        @Deprecated
+        public static final int T_EPDG_LTE = ProvisioningManager.KEY_LTE_EPDG_TIMER_SEC;
+
         /**
          * WiFi ePDG timer.
          * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+         * @deprecated use {@link ProvisioningManager#KEY_WIFI_EPDG_TIMER_SEC}.
          */
-        public static final int T_EPDG_WIFI = 63;
+        @Deprecated
+        public static final int T_EPDG_WIFI = ProvisioningManager.KEY_WIFI_EPDG_TIMER_SEC;
+
         /**
          * 1x ePDG timer.
          * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+         * @deprecated use {@link ProvisioningManager#KEY_1X_EPDG_TIMER_SEC}.
          */
-        public static final int T_EPDG_1X = 64;
+        @Deprecated
+        public static final int T_EPDG_1X = ProvisioningManager.KEY_1X_EPDG_TIMER_SEC;
+
         /**
          * MultiEndpoint status: Enabled (1), or Disabled (0).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_MULTIENDPOINT_ENABLED}.
          */
-        public static final int VICE_SETTING_ENABLED = 65;
+        @Deprecated
+        public static final int VICE_SETTING_ENABLED = ProvisioningManager.KEY_MULTIENDPOINT_ENABLED;
 
         /**
          * RTT status: Enabled (1), or Disabled (0).
          * Value is in Integer format.
+         * @deprecated use {@link ProvisioningManager#KEY_RTT_ENABLED}.
          */
-        public static final int RTT_SETTING_ENABLED = 66;
+        @Deprecated
+        public static final int RTT_SETTING_ENABLED = ProvisioningManager.KEY_RTT_ENABLED;
 
         // Expand the operator config items as needed here, need to change
         // PROVISIONED_CONFIG_END after that.
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index 15f8371..4a5380e 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -166,6 +166,12 @@
             String[] barrList, int serviceClass);
 
     /**
+     * Modifies the configuration of the call barring for specified service class with password.
+     */
+    public void updateCallBarring(int cbType, int action, Message result,
+            String[] barrList, int serviceClass, String password);
+
+    /**
      * Modifies the configuration of the call forward.
      */
     public void updateCallForward(int action, int condition, String number,
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index 4f97cc5..302be65 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -122,4 +122,10 @@
      */
     int updateCallBarringForServiceClass(int cbType, int action, in String[] barrList,
             int serviceClass);
+
+    /**
+     * Updates the configuration of the call barring for specified service class with password.
+     */
+    int updateCallBarringWithPassword(int cbType, int action, in String[] barrList,
+            int serviceClass, String password);
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index b4d3ec9..eef96c4 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -205,8 +205,6 @@
     public static final String ACTION_SIM_STATE_CHANGED
             = Intent.ACTION_SIM_STATE_CHANGED;
 
-    public static final String EXTRA_REBROADCAST_ON_UNLOCK= "rebroadcastOnUnlock";
-
     /**
      * <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
      * <p class="note">.
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 832502c..d0c8024 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -148,10 +148,9 @@
     }
 
     /**
-     * Create an SmsMessage from an SMS EF record.
+     * Creates an SmsMessage from an SMS EF record.
      *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1.
+     * @param index Index of SMS EF record.
      * @param data Record data.
      * @return An SmsMessage representing the record.
      *
@@ -202,26 +201,16 @@
     }
 
     /**
-     * TODO(cleanup): why do getSubmitPdu methods take an scAddr input
-     * and do nothing with it?  GSM allows us to specify a SC (eg,
-     * when responding to an SMS that explicitly requests the response
-     * is sent to a specific SC), or pass null to use the default
-     * value.  Is there no similar notion in CDMA? Or do we just not
-     * have it hooked up?
-     */
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      *
-     * @param scAddr                Service Centre address.  Null means use default.
-     * @param destAddr              Address of the recipient.
-     * @param message               String representation of the message payload.
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @param smsHeader             Array containing the data for the User Data Header, preceded
-     *                              by the Element Identifiers.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddr Service Centre address. No use for this message.
+     * @param destAddr the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @param smsHeader array containing the data for the User Data Header, preceded by the Element
+     *                  Identifiers.
+     * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+     *         null on encode error.
      * @hide
      */
     @UnsupportedAppUsage
@@ -231,18 +220,17 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      *
-     * @param scAddr                Service Centre address.  Null means use default.
-     * @param destAddr              Address of the recipient.
-     * @param message               String representation of the message payload.
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @param smsHeader             Array containing the data for the User Data Header, preceded
-     *                              by the Element Identifiers.
-     * @param priority              Priority level of the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddr Service Centre address. No use for this message.
+     * @param destAddr the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @param smsHeader array containing the data for the User Data Header, preceded by the Element
+     *                  Identifiers.
+     * @param priority priority level of the message.
+     * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+     *         null on encode error.
      * @hide
      */
     @UnsupportedAppUsage
@@ -265,16 +253,15 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address and port.
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
      *
-     * @param scAddr Service Centre address. null == use default
-     * @param destAddr the address of the destination for the message
-     * @param destPort the port to deliver the message to at the
-     *        destination
-     * @param data the data for the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddr Service Centre address. No use for this message.
+     * @param destAddr the address of the destination for the message.
+     * @param destPort the port to deliver the message to at the destination.
+     * @param data the data for the message.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+     *         null on encode error.
      */
     @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
@@ -305,14 +292,13 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
      *
-     * @param destAddr the address of the destination for the message
-     * @param userData the data for the message
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param destAddr the address of the destination for the message.
+     * @param userData the data for the message.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+     *         null on encode error.
      */
     @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
@@ -321,15 +307,14 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
      *
-     * @param destAddr the address of the destination for the message
-     * @param userData the data for the message
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @param priority Priority level of the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param destAddr the address of the destination for the message.
+     * @param userData the data for the message.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @param priority Priority level of the message.
+     * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+     *         null on encode error.
      */
     @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
@@ -1059,6 +1044,72 @@
     }
 
     /**
+     * Gets an SMS-DELIVER PDU for a originating address and a message.
+     *
+     * @param origAddr the address of the originating for the message.
+     * @param message string representation of the message payload.
+     * @param date the time stamp the message was received.
+     * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+     *         null on encode error.
+     * @hide
+     */
+    public static SubmitPdu getDeliverPdu(String origAddr, String message, long date) {
+        if (origAddr == null || message == null) {
+            return null;
+        }
+
+        CdmaSmsAddress addr = CdmaSmsAddress.parse(origAddr);
+        if (addr == null) return null;
+
+        BearerData bearerData = new BearerData();
+        bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+
+        bearerData.messageId = 0;
+
+        bearerData.deliveryAckReq = false;
+        bearerData.userAckReq = false;
+        bearerData.readAckReq = false;
+        bearerData.reportReq = false;
+
+        bearerData.userData = new UserData();
+        bearerData.userData.payloadStr = message;
+
+        bearerData.msgCenterTimeStamp = BearerData.TimeStamp.fromMillis(date);
+
+        byte[] encodedBearerData = BearerData.encode(bearerData);
+        if (encodedBearerData == null) return null;
+
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
+            DataOutputStream dos = new DataOutputStream(baos);
+            dos.writeInt(SmsEnvelope.TELESERVICE_WMT);
+            dos.writeInt(0); // servicePresent
+            dos.writeInt(0); // serviceCategory
+            dos.write(addr.digitMode);
+            dos.write(addr.numberMode);
+            dos.write(addr.ton); // number_type
+            dos.write(addr.numberPlan);
+            dos.write(addr.numberOfDigits);
+            dos.write(addr.origBytes, 0, addr.origBytes.length); // digits
+            // Subaddress is not supported.
+            dos.write(0); // subaddressType
+            dos.write(0); // subaddr_odd
+            dos.write(0); // subaddr_nbr_of_digits
+            dos.write(encodedBearerData.length);
+            dos.write(encodedBearerData, 0, encodedBearerData.length);
+            dos.close();
+
+            SubmitPdu pdu = new SubmitPdu();
+            pdu.encodedMessage = baos.toByteArray();
+            pdu.encodedScAddress = null;
+            return pdu;
+        } catch (IOException ex) {
+            Rlog.e(LOG_TAG, "creating Deliver PDU failed: " + ex);
+        }
+        return null;
+    }
+
+    /**
      * Creates byte array (pseudo pdu) from SMS object.
      * Note: Do not call this method more than once per object!
      * @hide
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index ca03333..f4a96a6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -32,6 +32,7 @@
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.BitwiseOutputStream;
 
+import java.io.ByteArrayOutputStream;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -284,6 +285,33 @@
             return ts;
         }
 
+        public static TimeStamp fromMillis(long timeInMillis) {
+            TimeStamp ts = new TimeStamp();
+            LocalDateTime localDateTime =
+                    Instant.ofEpochMilli(timeInMillis).atZone(ts.mZoneId).toLocalDateTime();
+            int year = localDateTime.getYear();
+            if (year < 1996 || year > 2095) return null;
+            ts.year = year;
+            ts.month = localDateTime.getMonthValue();
+            ts.monthDay = localDateTime.getDayOfMonth();
+            ts.hour = localDateTime.getHour();
+            ts.minute = localDateTime.getMinute();
+            ts.second = localDateTime.getSecond();
+            return ts;
+        }
+
+        public byte[] toByteArray() {
+            int year = this.year % 100; // 00 - 99
+            ByteArrayOutputStream outStream = new ByteArrayOutputStream(6);
+            outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F));
+            outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F));
+            outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F));
+            outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F));
+            outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F));
+            outStream.write((((second / 10) << 4) & 0xF0) | ((second % 10) & 0x0F));
+            return outStream.toByteArray();
+        }
+
         public long toMillis() {
             LocalDateTime localDateTime =
                     LocalDateTime.of(year, month + 1, monthDay, hour, minute, second);
@@ -957,6 +985,12 @@
         }
     }
 
+    private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)
+            throws BitwiseOutputStream.AccessException {
+        outStream.write(8, 6);
+        outStream.writeByteArray(8 * 6, bData.msgCenterTimeStamp.toByteArray());
+    };
+
     /**
      * Create serialized representation for BearerData object.
      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
@@ -1021,6 +1055,10 @@
                 outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
                 encodeScpResults(bData, outStream);
             }
+            if (bData.msgCenterTimeStamp != null) {
+                outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
+                encodeMsgCenterTimeStamp(bData, outStream);
+            }
             return outStream.toByteArray();
         } catch (BitwiseOutputStream.AccessException ex) {
             Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index f54da3b..4538193 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -42,8 +42,11 @@
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.text.ParseException;
+import java.time.Instant;
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 
 /**
  * A Short Message Service message.
@@ -168,10 +171,9 @@
     }
 
     /**
-     * Create an SmsMessage from an SMS EF record.
+     * Creates an SmsMessage from an SMS EF record.
      *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param index Index of SMS EF record.
      * @param data Record data.
      * @return An SmsMessage representing the record.
      *
@@ -260,12 +262,15 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param header a byte array containing the data for the User Data Header.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      * @hide
      */
     @UnsupportedAppUsage
@@ -278,17 +283,19 @@
 
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message using the
-     * specified encoding.
+     * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
      *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @param encoding Encoding defined by constants in
-     *        com.android.internal.telephony.SmsConstants.ENCODING_*
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param header a byte array containing the data for the User Data Header.
+     * @param encoding encoding defined by constants in
+     *                 com.android.internal.telephony.SmsConstants.ENCODING_*
      * @param languageTable
      * @param languageShiftTable
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      * @hide
      */
     @UnsupportedAppUsage
@@ -301,18 +308,20 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message using the
-     * specified encoding.
+     * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
      *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @param encoding Encoding defined by constants in
-     *        com.android.internal.telephony.SmsConstants.ENCODING_*
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param header a byte array containing the data for the User Data Header.
+     * @param encoding encoding defined by constants in
+     *                 com.android.internal.telephony.SmsConstants.ENCODING_*
      * @param languageTable
      * @param languageShiftTable
      * @param validityPeriod Validity Period of the message in Minutes.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      * @hide
      */
     @UnsupportedAppUsage
@@ -484,12 +493,14 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      */
     @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
@@ -500,15 +511,15 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
      *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @param destinationAddress the address of the destination for the message
-     * @param statusReportRequested staus report of the message Requested
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
      * @param validityPeriod Validity Period of the message in Minutes.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      */
     @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
@@ -519,16 +530,15 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
      *
-     * @param scAddress Service Centre address. null == use default
-     * @param destinationAddress the address of the destination for the message
-     * @param destinationPort the port to deliver the message to at the
-     *        destination
-     * @param data the data for the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param destinationPort the port to deliver the message to at the destination.
+     * @param data the data for the message.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, int destinationPort, byte[] data,
@@ -552,8 +562,7 @@
 
         SubmitPdu ret = new SubmitPdu();
         ByteArrayOutputStream bo = getSubmitPduHead(
-                scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
-                                                            // TP-UDHI = true
+                scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */
                 statusReportRequested, ret);
         // Skip encoding pdu if error occurs when create pdu head and the error will be handled
         // properly later on encodedMessage sanity check.
@@ -580,16 +589,18 @@
     }
 
     /**
-     * Create the beginning of a SUBMIT PDU.  This is the part of the
-     * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
-     * one of which takes a byte array and the other of which takes a
+     * Creates the beginning of a SUBMIT PDU.
+     *
+     * This is the part of the SUBMIT PDU that is common to the two versions of
+     * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a
      * <code>String</code>.
      *
-     * @param scAddress Service Centre address. null == use default
-     * @param destinationAddress the address of the destination for the message
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
      * @param mtiByte
-     * @param ret <code>SubmitPdu</code> containing the encoded SC
-     *        address, if applicable, and the encoded message. Returns null on encode error.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param ret <code>SubmitPdu</code>.
+     * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress.
      */
     @UnsupportedAppUsage
     private static ByteArrayOutputStream getSubmitPduHead(
@@ -637,6 +648,161 @@
         return bo;
     }
 
+    /**
+     * Gets an SMS-DELIVER PDU for an originating address and a message.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param originatingAddress the address of the originating for the message.
+     * @param message string representation of the message payload.
+     * @param date the time stamp the message was received.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     * @hide
+     */
+    public static SubmitPdu getDeliverPdu(
+            String scAddress, String originatingAddress, String message, long date) {
+        if (originatingAddress == null || message == null) {
+            return null;
+        }
+
+        // Find the best encoding to use
+        TextEncodingDetails ted = calculateLength(message, false);
+        int encoding = ted.codeUnitSize;
+        int languageTable = ted.languageTable;
+        int languageShiftTable = ted.languageShiftTable;
+        byte[] header = null;
+
+        if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) {
+            SmsHeader smsHeader = new SmsHeader();
+            smsHeader.languageTable = languageTable;
+            smsHeader.languageShiftTable = languageShiftTable;
+            header = SmsHeader.toByteArray(smsHeader);
+        }
+
+        SubmitPdu ret = new SubmitPdu();
+
+        ByteArrayOutputStream bo = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40);
+
+        // SMSC address with length octet, or 0
+        if (scAddress == null) {
+            ret.encodedScAddress = null;
+        } else {
+            ret.encodedScAddress =
+                    PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(scAddress);
+        }
+
+        // TP-Message-Type-Indicator
+        bo.write(0); // SMS-DELIVER
+
+        byte[] oaBytes;
+
+        oaBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(originatingAddress);
+
+        // Return null for invalid originating address
+        if (oaBytes == null) return null;
+
+        // Originating address length in BCD digits, ignoring TON byte and pad
+        // TODO Should be better.
+        bo.write((oaBytes.length - 1) * 2 - ((oaBytes[oaBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
+
+        // Originating Address
+        bo.write(oaBytes, 0, oaBytes.length);
+
+        // TP-Protocol-Identifier
+        bo.write(0);
+
+        // User Data (and length)
+        byte[] userData;
+        try {
+            if (encoding == ENCODING_7BIT) {
+                userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
+                        languageTable, languageShiftTable);
+            } else { // Assume UCS-2
+                try {
+                    userData = encodeUCS2(message, header);
+                } catch (UnsupportedEncodingException uex) {
+                    Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+                    return null;
+                }
+            }
+        } catch (EncodeException ex) {
+            if (ex.getError() == EncodeException.ERROR_EXCEED_SIZE) {
+                Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex);
+                return null;
+            } else {
+                // Encoding to the 7-bit alphabet failed. Let's see if we can send it as a UCS-2
+                // encoded message
+                try {
+                    userData = encodeUCS2(message, header);
+                    encoding = ENCODING_16BIT;
+                } catch (EncodeException ex1) {
+                    Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex1);
+                    return null;
+                } catch (UnsupportedEncodingException uex) {
+                    Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+                    return null;
+                }
+            }
+        }
+
+        if (encoding == ENCODING_7BIT) {
+            if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
+                // Message too long
+                Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
+                return null;
+            }
+            // TP-Data-Coding-Scheme
+            // Default encoding, uncompressed
+            bo.write(0x00);
+        } else { // Assume UCS-2
+            if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
+                // Message too long
+                Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
+                return null;
+            }
+            // TP-Data-Coding-Scheme
+            // UCS-2 encoding, uncompressed
+            bo.write(0x08);
+        }
+
+        // TP-Service-Centre-Time-Stamp
+        byte[] scts = new byte[7];
+
+        ZonedDateTime zoneDateTime = Instant.ofEpochMilli(date).atZone(ZoneId.systemDefault());
+        LocalDateTime localDateTime = zoneDateTime.toLocalDateTime();
+
+        // It indicates the difference, expressed in quarters of an hour, between the local time and
+        // GMT.
+        int timezoneOffset = zoneDateTime.getOffset().getTotalSeconds() / 60 / 15;
+        boolean negativeOffset = timezoneOffset < 0;
+        if (negativeOffset) {
+            timezoneOffset = -timezoneOffset;
+        }
+        int year = localDateTime.getYear();
+        int month = localDateTime.getMonthValue();
+        int day = localDateTime.getDayOfMonth();
+        int hour = localDateTime.getHour();
+        int minute = localDateTime.getMinute();
+        int second = localDateTime.getSecond();
+
+        year = year > 2000 ? year - 2000 : year - 1900;
+        scts[0] = (byte) ((((year % 10) & 0x0F) << 4) | ((year / 10) & 0x0F));
+        scts[1] = (byte) ((((month % 10) & 0x0F) << 4) | ((month / 10) & 0x0F));
+        scts[2] = (byte) ((((day % 10) & 0x0F) << 4) | ((day / 10) & 0x0F));
+        scts[3] = (byte) ((((hour % 10) & 0x0F) << 4) | ((hour / 10) & 0x0F));
+        scts[4] = (byte) ((((minute % 10) & 0x0F) << 4) | ((minute / 10) & 0x0F));
+        scts[5] = (byte) ((((second % 10) & 0x0F) << 4) | ((second / 10) & 0x0F));
+        scts[6] = (byte) ((((timezoneOffset % 10) & 0x0F) << 4) | ((timezoneOffset / 10) & 0x0F));
+        if (negativeOffset) {
+            scts[0] |= 0x08; // Negative offset
+        }
+        bo.write(scts, 0, scts.length);
+
+        bo.write(userData, 0, userData.length);
+        ret.encodedMessage = bo.toByteArray();
+        return ret;
+    }
+
     private static class PduParser {
         @UnsupportedAppUsage
         byte mPdu[];
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index a7328ac..3f311c9 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -75,6 +75,9 @@
     private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
     private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
     private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+    private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi");
+    private static final CaptivePortalData CAPPORT_DATA = new CaptivePortalData.Builder()
+            .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
 
     private static InetAddress address(String addrString) {
         return InetAddresses.parseNumericAddress(addrString);
@@ -101,6 +104,8 @@
         assertFalse(lp.isIpv6Provisioned());
         assertFalse(lp.isPrivateDnsActive());
         assertFalse(lp.isWakeOnLanSupported());
+        assertNull(lp.getCaptivePortalApiUrl());
+        assertNull(lp.getCaptivePortalData());
     }
 
     private LinkProperties makeTestObject() {
@@ -124,6 +129,8 @@
         lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
         lp.setDhcpServerAddress(DHCPSERVER);
         lp.setWakeOnLanSupported(true);
+        lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
+        lp.setCaptivePortalData(CAPPORT_DATA);
         return lp;
     }
 
@@ -165,6 +172,12 @@
         assertTrue(source.isIdenticalWakeOnLan(target));
         assertTrue(target.isIdenticalWakeOnLan(source));
 
+        assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
+        assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+
+        assertTrue(source.isIdenticalCaptivePortalData(target));
+        assertTrue(target.isIdenticalCaptivePortalData(source));
+
         // Check result of equals().
         assertTrue(source.equals(target));
         assertTrue(target.equals(source));
@@ -963,6 +976,8 @@
         source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
 
         source.setWakeOnLanSupported(true);
+        source.setCaptivePortalApiUrl(CAPPORT_API_URL);
+        source.setCaptivePortalData(CAPPORT_DATA);
 
         source.setDhcpServerAddress((Inet4Address) GATEWAY1);
 
@@ -970,7 +985,13 @@
         stacked.setInterfaceName("test-stacked");
         source.addStackedLink(stacked);
 
-        assertParcelSane(source, 16 /* fieldCount */);
+        assertParcelSane(source.makeSensitiveFieldsParcelingCopy(), 18 /* fieldCount */);
+
+        // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
+        final LinkProperties sanitized = new LinkProperties(source);
+        sanitized.setCaptivePortalApiUrl(null);
+        sanitized.setCaptivePortalData(null);
+        assertEquals(sanitized, parcelingRoundTrip(source));
     }
 
     @Test
@@ -1113,4 +1134,22 @@
         lp.clear();
         assertFalse(lp.isWakeOnLanSupported());
     }
+
+    @Test
+    public void testCaptivePortalApiUrl() {
+        final LinkProperties lp = makeTestObject();
+        assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl());
+
+        lp.clear();
+        assertNull(lp.getCaptivePortalApiUrl());
+    }
+
+    @Test
+    public void testCaptivePortalData() {
+        final LinkProperties lp = makeTestObject();
+        assertEquals(CAPPORT_DATA, lp.getCaptivePortalData());
+
+        lp.clear();
+        assertNull(lp.getCaptivePortalData());
+    }
 }
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 1569112..797fd83 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -271,7 +271,7 @@
             .addCapability(NET_CAPABILITY_NOT_METERED);
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 12);
+        assertParcelSane(netCap, 13);
     }
 
     @Test
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 0bf64b9..1c69209 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -35,9 +36,9 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.net.NetworkProvider;
 import android.net.NetworkSpecifier;
 import android.net.SocketKeepalive;
@@ -74,6 +75,7 @@
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
         mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
             case TRANSPORT_ETHERNET:
@@ -114,7 +116,7 @@
         public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
             super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
                     wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
-                    new NetworkMisc(), NetworkProvider.ID_NONE);
+                    new NetworkAgentConfig(), NetworkProvider.ID_NONE);
             mWrapper = wrapper;
         }
 
@@ -206,13 +208,11 @@
     }
 
     public void suspend() {
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
-        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
     }
 
     public void resume() {
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        addCapability(NET_CAPABILITY_NOT_SUSPENDED);
     }
 
     public void disconnect() {
diff --git a/tests/net/java/android/net/CaptivePortalDataTest.kt b/tests/net/java/android/net/CaptivePortalDataTest.kt
new file mode 100644
index 0000000..0071438
--- /dev/null
+++ b/tests/net/java/android/net/CaptivePortalDataTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CaptivePortalDataTest {
+    private val data = CaptivePortalData.Builder()
+            .setRefreshTime(123L)
+            .setUserPortalUrl(Uri.parse("https://portal.example.com/test"))
+            .setVenueInfoUrl(Uri.parse("https://venue.example.com/test"))
+            .setSessionExtendable(true)
+            .setBytesRemaining(456L)
+            .setExpiryTime(789L)
+            .setCaptive(true)
+            .build()
+
+    private fun makeBuilder() = CaptivePortalData.Builder(data)
+
+    @Test
+    fun testParcelUnparcel() {
+        assertParcelSane(data, fieldCount = 7)
+
+        assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
+        assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
+    }
+
+    @Test
+    fun testEquals() {
+        assertEquals(data, makeBuilder().build())
+
+        assertNotEqualsAfterChange { it.setRefreshTime(456L) }
+        assertNotEqualsAfterChange { it.setUserPortalUrl(Uri.parse("https://example.com/")) }
+        assertNotEqualsAfterChange { it.setUserPortalUrl(null) }
+        assertNotEqualsAfterChange { it.setVenueInfoUrl(Uri.parse("https://example.com/")) }
+        assertNotEqualsAfterChange { it.setVenueInfoUrl(null) }
+        assertNotEqualsAfterChange { it.setSessionExtendable(false) }
+        assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
+        assertNotEqualsAfterChange { it.setExpiryTime(12L) }
+        assertNotEqualsAfterChange { it.setCaptive(false) }
+    }
+
+    private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
+            CaptivePortalData.Builder(this).apply { mutator(this) }.build()
+
+    private fun assertNotEqualsAfterChange(mutator: (CaptivePortalData.Builder) -> Unit) {
+        assertNotEquals(data, data.mutate(mutator))
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..065add4
--- /dev/null
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.PersistableBundle;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+    private static final int NET_ID = 1;
+    private static final int DETECTION_METHOD = 2;
+    private static final long TIMESTAMP = 10L;
+    private static final String INTERFACE_NAME = "interface";
+    private static final String BUNDLE_KEY = "key";
+    private static final String BUNDLE_VALUE = "value";
+
+    private ConnectivityReport createSampleConnectivityReport() {
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(INTERFACE_NAME);
+
+        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+        return new ConnectivityReport(
+                new Network(NET_ID), TIMESTAMP, linkProperties, networkCapabilities, bundle);
+    }
+
+    private ConnectivityReport createDefaultConnectivityReport() {
+        return new ConnectivityReport(
+                new Network(0),
+                0L,
+                new LinkProperties(),
+                new NetworkCapabilities(),
+                PersistableBundle.EMPTY);
+    }
+
+    @Test
+    public void testPersistableBundleEquals() {
+        assertFalse(
+                ConnectivityDiagnosticsManager.persistableBundleEquals(
+                        null, PersistableBundle.EMPTY));
+        assertFalse(
+                ConnectivityDiagnosticsManager.persistableBundleEquals(
+                        PersistableBundle.EMPTY, null));
+        assertTrue(
+                ConnectivityDiagnosticsManager.persistableBundleEquals(
+                        PersistableBundle.EMPTY, PersistableBundle.EMPTY));
+
+        final PersistableBundle a = new PersistableBundle();
+        a.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+        final PersistableBundle b = new PersistableBundle();
+        b.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+        final PersistableBundle c = new PersistableBundle();
+        c.putString(BUNDLE_KEY, null);
+
+        assertFalse(
+                ConnectivityDiagnosticsManager.persistableBundleEquals(PersistableBundle.EMPTY, a));
+        assertFalse(
+                ConnectivityDiagnosticsManager.persistableBundleEquals(a, PersistableBundle.EMPTY));
+
+        assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(a, b));
+        assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(b, a));
+
+        assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(a, c));
+        assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(c, a));
+    }
+
+    @Test
+    public void testConnectivityReportEquals() {
+        assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
+        assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(INTERFACE_NAME);
+
+        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+        assertNotEquals(
+                createDefaultConnectivityReport(),
+                new ConnectivityReport(
+                        new Network(NET_ID),
+                        0L,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultConnectivityReport(),
+                new ConnectivityReport(
+                        new Network(0),
+                        TIMESTAMP,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultConnectivityReport(),
+                new ConnectivityReport(
+                        new Network(0),
+                        0L,
+                        linkProperties,
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultConnectivityReport(),
+                new ConnectivityReport(
+                        new Network(0),
+                        TIMESTAMP,
+                        new LinkProperties(),
+                        networkCapabilities,
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultConnectivityReport(),
+                new ConnectivityReport(
+                        new Network(0),
+                        TIMESTAMP,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        bundle));
+    }
+
+    @Test
+    public void testConnectivityReportParcelUnparcel() {
+        assertParcelSane(createSampleConnectivityReport(), 5);
+    }
+
+    private DataStallReport createSampleDataStallReport() {
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+    }
+
+    private DataStallReport createDefaultDataStallReport() {
+        return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+    }
+
+    @Test
+    public void testDataStallReportEquals() {
+        assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
+        assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+        assertNotEquals(
+                createDefaultDataStallReport(),
+                new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultDataStallReport(),
+                new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultDataStallReport(),
+                new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+        assertNotEquals(
+                createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+    }
+
+    @Test
+    public void testDataStallReportParcelUnparcel() {
+        assertParcelSane(createSampleDataStallReport(), 4);
+    }
+}
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
new file mode 100644
index 0000000..d6a2176
--- /dev/null
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.net.VpnProfile;
+import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.x500.X500Principal;
+
+/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class Ikev2VpnProfileTest {
+    private static final String SERVER_ADDR_STRING = "1.2.3.4";
+    private static final String IDENTITY_STRING = "Identity";
+    private static final String USERNAME_STRING = "username";
+    private static final String PASSWORD_STRING = "pa55w0rd";
+    private static final String EXCL_LIST = "exclList";
+    private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+    private static final int TEST_MTU = 1300;
+
+    private final MockContext mMockContext =
+            new MockContext() {
+                @Override
+                public String getOpPackageName() {
+                    return "fooPackage";
+                }
+            };
+    private final ProxyInfo mProxy = new ProxyInfo(SERVER_ADDR_STRING, -1, EXCL_LIST);
+
+    private X509Certificate mUserCert;
+    private X509Certificate mServerRootCa;
+    private PrivateKey mPrivateKey;
+
+    @Before
+    public void setUp() throws Exception {
+        mServerRootCa = generateRandomCertAndKeyPair().cert;
+
+        final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
+        mUserCert = userCertKey.cert;
+        mPrivateKey = userCertKey.key;
+    }
+
+    private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
+        final Ikev2VpnProfile.Builder builder =
+                new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
+
+        builder.setBypassable(true);
+        builder.setProxy(mProxy);
+        builder.setMaxMtu(TEST_MTU);
+        builder.setMetered(true);
+
+        return builder;
+    }
+
+    @Test
+    public void testBuildValidProfileWithOptions() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        // Check non-auth parameters correctly stored
+        assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
+        assertEquals(IDENTITY_STRING, profile.getUserIdentity());
+        assertEquals(mProxy, profile.getProxyInfo());
+        assertTrue(profile.isBypassable());
+        assertTrue(profile.isMetered());
+        assertEquals(TEST_MTU, profile.getMaxMtu());
+    }
+
+    @Test
+    public void testBuildUsernamePasswordProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        assertEquals(USERNAME_STRING, profile.getUsername());
+        assertEquals(PASSWORD_STRING, profile.getPassword());
+        assertEquals(mServerRootCa, profile.getServerRootCaCert());
+
+        assertNull(profile.getPresharedKey());
+        assertNull(profile.getRsaPrivateKey());
+        assertNull(profile.getUserCert());
+    }
+
+    @Test
+    public void testBuildDigitalSignatureProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        assertEquals(profile.getUserCert(), mUserCert);
+        assertEquals(mPrivateKey, profile.getRsaPrivateKey());
+        assertEquals(profile.getServerRootCaCert(), mServerRootCa);
+
+        assertNull(profile.getPresharedKey());
+        assertNull(profile.getUsername());
+        assertNull(profile.getPassword());
+    }
+
+    @Test
+    public void testBuildPresharedKeyProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final Ikev2VpnProfile profile = builder.build();
+        assertNotNull(profile);
+
+        assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
+
+        assertNull(profile.getServerRootCaCert());
+        assertNull(profile.getUsername());
+        assertNull(profile.getPassword());
+        assertNull(profile.getRsaPrivateKey());
+        assertNull(profile.getUserCert());
+    }
+
+    @Test
+    public void testBuildNoAuthMethodSet() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        try {
+            builder.build();
+            fail("Expected exception due to lack of auth method");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testBuildInvalidMtu() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        try {
+            builder.setMaxMtu(500);
+            fail("Expected exception due to too-small MTU");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    private void verifyVpnProfileCommon(VpnProfile profile) {
+        assertEquals(SERVER_ADDR_STRING, profile.server);
+        assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
+        assertEquals(mProxy, profile.proxy);
+        assertTrue(profile.isBypassable);
+        assertTrue(profile.isMetered);
+        assertEquals(TEST_MTU, profile.maxMtu);
+    }
+
+    @Test
+    public void testPskConvertToVpnProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final VpnProfile profile = builder.build().toVpnProfile();
+
+        verifyVpnProfileCommon(profile);
+        assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
+
+        // Check nothing else is set
+        assertEquals("", profile.username);
+        assertEquals("", profile.password);
+        assertEquals("", profile.ipsecUserCert);
+        assertEquals("", profile.ipsecCaCert);
+    }
+
+    @Test
+    public void testUsernamePasswordConvertToVpnProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+
+        verifyVpnProfileCommon(profile);
+        assertEquals(USERNAME_STRING, profile.username);
+        assertEquals(PASSWORD_STRING, profile.password);
+        assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+        // Check nothing else is set
+        assertEquals("", profile.ipsecUserCert);
+        assertEquals("", profile.ipsecSecret);
+    }
+
+    @Test
+    public void testRsaConvertToVpnProfile() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+
+        verifyVpnProfileCommon(profile);
+        assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
+        assertEquals(
+                Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()),
+                profile.ipsecSecret);
+        assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+        // Check nothing else is set
+        assertEquals("", profile.username);
+        assertEquals("", profile.password);
+    }
+
+    @Test
+    public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final VpnProfile profile = builder.build().toVpnProfile();
+        profile.username = USERNAME_STRING;
+        profile.password = PASSWORD_STRING;
+        profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
+        profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+        final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+        assertNull(result.getUsername());
+        assertNull(result.getPassword());
+        assertNull(result.getUserCert());
+        assertNull(result.getRsaPrivateKey());
+        assertNull(result.getServerRootCaCert());
+    }
+
+    @Test
+    public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+        profile.ipsecSecret = new String(PSK_BYTES);
+        profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+        final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+        assertNull(result.getPresharedKey());
+        assertNull(result.getUserCert());
+        assertNull(result.getRsaPrivateKey());
+    }
+
+    @Test
+    public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final VpnProfile profile = builder.build().toVpnProfile();
+        profile.username = USERNAME_STRING;
+        profile.password = PASSWORD_STRING;
+
+        final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+        assertNull(result.getUsername());
+        assertNull(result.getPassword());
+        assertNull(result.getPresharedKey());
+    }
+
+    @Test
+    public void testPskConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthPsk(PSK_BYTES);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testUsernamePasswordConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    @Test
+    public void testRsaConversionIsLossless() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+        final Ikev2VpnProfile ikeProfile = builder.build();
+
+        assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+    }
+
+    private static class CertificateAndKey {
+        public final X509Certificate cert;
+        public final PrivateKey key;
+
+        CertificateAndKey(X509Certificate cert, PrivateKey key) {
+            this.cert = cert;
+            this.key = key;
+        }
+    }
+
+    private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
+        final Date validityBeginDate =
+                new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
+        final Date validityEndDate =
+                new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
+
+        // Generate a keypair
+        final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+        keyPairGenerator.initialize(512);
+        final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+        final X500Principal dnName = new X500Principal("CN=test.android.com");
+        final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+        certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+        certGen.setSubjectDN(dnName);
+        certGen.setIssuerDN(dnName);
+        certGen.setNotBefore(validityBeginDate);
+        certGen.setNotAfter(validityEndDate);
+        certGen.setPublicKey(keyPair.getPublic());
+        certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+        final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
+        return new CertificateAndKey(cert, keyPair.getPrivate());
+    }
+}
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
new file mode 100644
index 0000000..655c4d1
--- /dev/null
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.mockito.Mockito.mock;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link VpnManager}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VpnManagerTest {
+    private static final String VPN_PROFILE_KEY = "KEY";
+
+    private IConnectivityManager mMockCs;
+    private VpnManager mVpnManager;
+    private final MockContext mMockContext =
+            new MockContext() {
+                @Override
+                public String getOpPackageName() {
+                    return "fooPackage";
+                }
+            };
+
+    @Before
+    public void setUp() throws Exception {
+        mMockCs = mock(IConnectivityManager.class);
+        mVpnManager = new VpnManager(mMockContext, mMockCs);
+    }
+
+    @Test
+    public void testProvisionVpnProfile() throws Exception {
+        try {
+            mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class));
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    @Test
+    public void testDeleteProvisionedVpnProfile() throws Exception {
+        try {
+            mVpnManager.deleteProvisionedVpnProfile();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    @Test
+    public void testStartProvisionedVpnProfile() throws Exception {
+        try {
+            mVpnManager.startProvisionedVpnProfile();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    @Test
+    public void testStopProvisionedVpnProfile() throws Exception {
+        try {
+            mVpnManager.stopProvisionedVpnProfile();
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+}
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
new file mode 100644
index 0000000..8a4b533
--- /dev/null
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.IpSecAlgorithm;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+/** Unit tests for {@link VpnProfile}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class VpnProfileTest {
+    private static final String DUMMY_PROFILE_KEY = "Test";
+
+    @Test
+    public void testDefaults() throws Exception {
+        final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
+
+        assertEquals(DUMMY_PROFILE_KEY, p.key);
+        assertEquals("", p.name);
+        assertEquals(VpnProfile.TYPE_PPTP, p.type);
+        assertEquals("", p.server);
+        assertEquals("", p.username);
+        assertEquals("", p.password);
+        assertEquals("", p.dnsServers);
+        assertEquals("", p.searchDomains);
+        assertEquals("", p.routes);
+        assertTrue(p.mppe);
+        assertEquals("", p.l2tpSecret);
+        assertEquals("", p.ipsecIdentifier);
+        assertEquals("", p.ipsecSecret);
+        assertEquals("", p.ipsecUserCert);
+        assertEquals("", p.ipsecCaCert);
+        assertEquals("", p.ipsecServerCert);
+        assertEquals(null, p.proxy);
+        assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
+        assertFalse(p.isBypassable);
+        assertFalse(p.isMetered);
+        assertEquals(1400, p.maxMtu);
+        assertFalse(p.areAuthParamsInline);
+    }
+
+    private VpnProfile getSampleIkev2Profile(String key) {
+        final VpnProfile p = new VpnProfile(key);
+
+        p.name = "foo";
+        p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+        p.server = "bar";
+        p.username = "baz";
+        p.password = "qux";
+        p.dnsServers = "8.8.8.8";
+        p.searchDomains = "";
+        p.routes = "0.0.0.0/0";
+        p.mppe = false;
+        p.l2tpSecret = "";
+        p.ipsecIdentifier = "quux";
+        p.ipsecSecret = "quuz";
+        p.ipsecUserCert = "corge";
+        p.ipsecCaCert = "grault";
+        p.ipsecServerCert = "garply";
+        p.proxy = null;
+        p.setAllowedAlgorithms(
+                Arrays.asList(
+                        IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+                        IpSecAlgorithm.AUTH_HMAC_SHA512,
+                        IpSecAlgorithm.CRYPT_AES_CBC));
+        p.isBypassable = true;
+        p.isMetered = true;
+        p.maxMtu = 1350;
+        p.areAuthParamsInline = true;
+
+        // Not saved, but also not compared.
+        p.saveLogin = true;
+
+        return p;
+    }
+
+    @Test
+    public void testEquals() {
+        assertEquals(
+                getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
+
+        final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        modified.maxMtu--;
+        assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
+    }
+
+    @Test
+    public void testSetInvalidAlgorithmValueDelimiter() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+        try {
+            profile.setAllowedAlgorithms(
+                    Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test"));
+            fail("Expected failure due to value separator in algorithm name");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testSetInvalidAlgorithmListDelimiter() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+        try {
+            profile.setAllowedAlgorithms(
+                    Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test"));
+            fail("Expected failure due to value separator in algorithm name");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testEncodeDecode() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+        assertEquals(profile, decoded);
+    }
+
+    @Test
+    public void testEncodeDecodeTooManyValues() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        final byte[] tooManyValues =
+                (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
+
+        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
+    }
+
+    @Test
+    public void testEncodeDecodeInvalidNumberOfValues() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        final String encoded = new String(profile.encode());
+        final byte[] tooFewValues =
+                encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
+
+        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
+    }
+
+    @Test
+    public void testEncodeDecodeLoginsNotSaved() {
+        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+        profile.saveLogin = false;
+
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+        assertNotEquals(profile, decoded);
+
+        // Add the username/password back, everything else must be equal.
+        decoded.username = profile.username;
+        decoded.password = profile.password;
+        assertEquals(profile, decoded);
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1901a1d..783f8d1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -21,6 +21,8 @@
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -114,6 +116,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
@@ -129,6 +132,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.net.CaptivePortalData;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.ConnectivityManager.PacketKeepalive;
@@ -165,6 +169,7 @@
 import android.net.RouteInfo;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
+import android.net.Uri;
 import android.net.metrics.IpConnectivityLog;
 import android.net.shared.NetworkMonitorUtils;
 import android.net.shared.PrivateDnsConfig;
@@ -243,8 +248,10 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -347,6 +354,8 @@
 
         @Spy private Resources mResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
+        // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+        private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
 
         MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
@@ -417,13 +426,39 @@
         }
 
         @Override
+        public int checkPermission(String permission, int pid, int uid) {
+            final Integer granted = mMockedPermissions.get(permission);
+            if (granted == null) {
+                // All non-mocked permissions should be held by the test or unnecessary: check as
+                // normal to make sure the code does not rely on unexpected permissions.
+                return super.checkPermission(permission, pid, uid);
+            }
+            return granted;
+        }
+
+        @Override
         public void enforceCallingOrSelfPermission(String permission, String message) {
-            // The mainline permission can only be held if signed with the network stack certificate
-            // Skip testing for this permission.
-            if (NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK.equals(permission)) return;
-            // All other permissions should be held by the test or unnecessary: check as normal to
-            // make sure the code does not rely on unexpected permissions.
-            super.enforceCallingOrSelfPermission(permission, message);
+            final Integer granted = mMockedPermissions.get(permission);
+            if (granted == null) {
+                super.enforceCallingOrSelfPermission(permission, message);
+                return;
+            }
+
+            if (!granted.equals(PERMISSION_GRANTED)) {
+                throw new SecurityException("[Test] permission denied: " + permission);
+            }
+        }
+
+        /**
+         * Mock checks for the specified permission, and have them behave as per {@code granted}.
+         *
+         * <p>Passing null reverts to default behavior, which does a real permission check on the
+         * test package.
+         * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+         *                {@link PackageManager#PERMISSION_DENIED}.
+         */
+        public void setPermission(String permission, Integer granted) {
+            mMockedPermissions.put(permission, granted);
         }
 
         @Override
@@ -1750,6 +1785,66 @@
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
     }
 
+    private void doNetworkCallbacksSanitizationTest(boolean sanitized) throws Exception {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        mCm.registerNetworkCallback(wifiRequest, callback);
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        final LinkProperties newLp = new LinkProperties();
+        final Uri capportUrl = Uri.parse("https://capport.example.com/api");
+        final CaptivePortalData capportData = new CaptivePortalData.Builder()
+                .setCaptive(true).build();
+        newLp.setCaptivePortalApiUrl(capportUrl);
+        newLp.setCaptivePortalData(capportData);
+        mWiFiNetworkAgent.sendLinkProperties(newLp);
+
+        final Uri expectedCapportUrl = sanitized ? null : capportUrl;
+        final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
+        callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+                Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())
+                && Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+        defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+                Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())
+                && Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+
+        final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
+        assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl());
+        assertEquals(expectedCapportData, lp.getCaptivePortalData());
+    }
+
+    @Test
+    public void networkCallbacksSanitizationTest_Sanitize() throws Exception {
+        mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                PERMISSION_DENIED);
+        mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+                PERMISSION_DENIED);
+        doNetworkCallbacksSanitizationTest(true /* sanitized */);
+    }
+
+    @Test
+    public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception {
+        mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                PERMISSION_GRANTED);
+        mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
+        doNetworkCallbacksSanitizationTest(false /* sanitized */);
+    }
+
+    @Test
+    public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception {
+        mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                PERMISSION_DENIED);
+        mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+        doNetworkCallbacksSanitizationTest(false /* sanitized */);
+    }
+
     @Test
     public void testMultipleLingering() throws Exception {
         // This test would be flaky with the default 120ms timer: that is short enough that
@@ -2628,6 +2723,8 @@
         final String testKey = "testkey";
         final String testValue = "testvalue";
         testBundle.putString(testKey, testValue);
+        mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                PERMISSION_GRANTED);
         mCm.startCaptivePortalApp(wifiNetwork, testBundle);
         final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS);
         assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction());
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 82cb193..e863266 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -37,7 +37,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.net.NetworkProvider;
 import android.net.NetworkScore;
 import android.os.INetworkManagementService;
@@ -75,7 +74,6 @@
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNMS;
     @Mock Context mCtx;
-    @Mock NetworkMisc mMisc;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
 
@@ -358,7 +356,7 @@
         NetworkScore ns = new NetworkScore();
         ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
-                caps, ns, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
+                caps, ns, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
                 NetworkProvider.ID_NONE);
         nai.everValidated = true;
         return nai;
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index b709af1..9b24887 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -33,8 +33,8 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkInfo;
-import android.net.NetworkMisc;
 import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.test.TestLooper;
@@ -63,7 +63,6 @@
     static final int NETID = 42;
 
     @Mock ConnectivityService mConnectivity;
-    @Mock NetworkMisc mMisc;
     @Mock IDnsResolver mDnsResolver;
     @Mock INetd mNetd;
     @Mock INetworkManagementService mNms;
@@ -72,6 +71,7 @@
 
     TestLooper mLooper;
     Handler mHandler;
+    NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
 
     Nat464Xlat makeNat464Xlat() {
         return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
@@ -93,7 +93,7 @@
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
         when(mNai.connService()).thenReturn(mConnectivity);
-        when(mNai.netMisc()).thenReturn(mMisc);
+        when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
         when(mNai.handler()).thenReturn(mHandler);
 
         when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -104,7 +104,7 @@
         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
-                mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+                mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
         assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
     }
@@ -113,7 +113,7 @@
         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
-                mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+                mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
         assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
     }
@@ -151,11 +151,11 @@
                 assertRequiresClat(true, mNai);
                 assertShouldStartClat(true, mNai);
 
-                mMisc.skip464xlat = true;
+                mAgentConfig.skip464xlat = true;
                 assertRequiresClat(false, mNai);
                 assertShouldStartClat(false, mNai);
 
-                mMisc.skip464xlat = false;
+                mAgentConfig.skip464xlat = false;
                 assertRequiresClat(true, mNai);
                 assertShouldStartClat(true, mNai);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f85bb5f..b9eb3f2 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3194,27 +3194,27 @@
      * soft AP state and number of connected devices immediately after a successful call to this API
      * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
      * indicates that the latest attempt to start soft AP has failed. Caller can unregister a
-     * previously registered callback using {@link unregisterSoftApCallback}
+     * previously registered callback using {@link #unregisterSoftApCallback}
      * <p>
      * Applications should have the
      * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
      * without the permission will trigger a {@link java.lang.SecurityException}.
      * <p>
      *
-     * @param executor The executor to execute the callbacks of the {@code executor}
-     *                 object. If null, then the application's main executor will be used.
+     * @param executor The Executor on whose thread to execute the callbacks of the {@code callback}
+     *                 object.
      * @param callback Callback for soft AP events
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public void registerSoftApCallback(@Nullable @CallbackExecutor Executor executor,
+    public void registerSoftApCallback(@NonNull @CallbackExecutor Executor executor,
                                        @NonNull SoftApCallback callback) {
+        if (executor == null) throw new IllegalArgumentException("executor cannot be null");
         if (callback == null) throw new IllegalArgumentException("callback cannot be null");
         Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", executor=" + executor);
 
-        executor = (executor == null) ? mContext.getMainExecutor() : executor;
         Binder binder = new Binder();
         try {
             mService.registerSoftApCallback(binder, new SoftApCallbackProxy(executor, callback),
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 4260c20..b130c17 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -693,6 +693,18 @@
     }
 
     /**
+     * Verify an IllegalArgumentException is thrown if executor is null.
+     */
+    @Test
+    public void registerSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForExecutor() {
+        try {
+            mWifiManager.registerSoftApCallback(null, mSoftApCallback);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    /**
      * Verify an IllegalArgumentException is thrown if callback is not provided.
      */
     @Test
@@ -705,16 +717,6 @@
     }
 
     /**
-     * Verify main looper is used when handler is not provided.
-     */
-    @Test
-    public void registerSoftApCallbackUsesMainLooperOnNullArgumentForHandler() {
-        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
-        mWifiManager.registerSoftApCallback(null, mSoftApCallback);
-        verify(mContext).getMainExecutor();
-    }
-
-    /**
      * Verify the call to registerSoftApCallback goes to WifiServiceImpl.
      */
     @Test